CctPacketFormatter.cpp 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. #include <CctPacketFormatter.h>
  2. #include <MiLightCommands.h>
  3. static const uint8_t CCT_PROTOCOL_ID = 0x5A;
  4. bool CctPacketFormatter::canHandle(const uint8_t *packet, const size_t len) {
  5. return len == packetLength && packet[0] == CCT_PROTOCOL_ID;
  6. }
  7. void CctPacketFormatter::initializePacket(uint8_t* packet) {
  8. size_t packetPtr = 0;
  9. // Byte 0: Packet length = 7 bytes
  10. // Byte 1: CCT protocol
  11. packet[packetPtr++] = CCT_PROTOCOL_ID;
  12. // Byte 2 and 3: Device ID
  13. packet[packetPtr++] = deviceId >> 8;
  14. packet[packetPtr++] = deviceId & 0xFF;
  15. // Byte 4: Zone
  16. packet[packetPtr++] = groupId;
  17. // Byte 5: Bulb command, filled in later
  18. packet[packetPtr++] = 0;
  19. // Byte 6: Packet sequence number 0..255
  20. packet[packetPtr++] = sequenceNum++;
  21. // Byte 7: Checksum over previous bytes, including packet length = 7
  22. // The checksum will be calculated when setting the command field
  23. packet[packetPtr++] = 0;
  24. // Byte 8: CRC LSB
  25. // Byte 9: CRC MSB
  26. }
  27. void CctPacketFormatter::finalizePacket(uint8_t* packet) {
  28. uint8_t checksum;
  29. // Calculate checksum over packet length .. sequenceNum
  30. checksum = 7; // Packet length is not part of packet
  31. for (uint8_t i = 0; i < 6; i++) {
  32. checksum += currentPacket[i];
  33. }
  34. // Store the checksum in the sixth byte
  35. currentPacket[6] = checksum;
  36. }
  37. void CctPacketFormatter::updateBrightness(uint8_t value) {
  38. const GroupState* state = this->stateStore->get(deviceId, groupId, MiLightRemoteType::REMOTE_TYPE_CCT);
  39. int8_t knownValue = (state != NULL && state->isSetBrightness()) ? state->getBrightness() : -1;
  40. valueByStepFunction(
  41. &PacketFormatter::increaseBrightness,
  42. &PacketFormatter::decreaseBrightness,
  43. CCT_INTERVALS,
  44. value / CCT_INTERVALS,
  45. knownValue / CCT_INTERVALS
  46. );
  47. }
  48. void CctPacketFormatter::updateTemperature(uint8_t value) {
  49. const GroupState* state = this->stateStore->get(deviceId, groupId, MiLightRemoteType::REMOTE_TYPE_CCT);
  50. int8_t knownValue = (state != NULL && state->isSetKelvin()) ? state->getKelvin() : -1;
  51. valueByStepFunction(
  52. &PacketFormatter::increaseTemperature,
  53. &PacketFormatter::decreaseTemperature,
  54. CCT_INTERVALS,
  55. value / CCT_INTERVALS,
  56. knownValue / CCT_INTERVALS
  57. );
  58. }
  59. void CctPacketFormatter::command(uint8_t command, uint8_t arg) {
  60. pushPacket();
  61. if (held) {
  62. command |= 0x80;
  63. }
  64. currentPacket[CCT_COMMAND_INDEX] = command;
  65. }
  66. void CctPacketFormatter::updateStatus(MiLightStatus status, uint8_t groupId) {
  67. command(getCctStatusButton(groupId, status), 0);
  68. }
  69. void CctPacketFormatter::increaseTemperature() {
  70. command(CCT_TEMPERATURE_UP, 0);
  71. }
  72. void CctPacketFormatter::decreaseTemperature() {
  73. command(CCT_TEMPERATURE_DOWN, 0);
  74. }
  75. void CctPacketFormatter::increaseBrightness() {
  76. command(CCT_BRIGHTNESS_UP, 0);
  77. }
  78. void CctPacketFormatter::decreaseBrightness() {
  79. command(CCT_BRIGHTNESS_DOWN, 0);
  80. }
  81. void CctPacketFormatter::enableNightMode() {
  82. command(getCctStatusButton(groupId, OFF) | 0x10, 0);
  83. }
  84. uint8_t CctPacketFormatter::getCctStatusButton(uint8_t groupId, MiLightStatus status) {
  85. uint8_t button = 0;
  86. if (status == ON) {
  87. switch(groupId) {
  88. case 0:
  89. button = CCT_ALL_ON;
  90. break;
  91. case 1:
  92. button = CCT_GROUP_1_ON;
  93. break;
  94. case 2:
  95. button = CCT_GROUP_2_ON;
  96. break;
  97. case 3:
  98. button = CCT_GROUP_3_ON;
  99. break;
  100. case 4:
  101. button = CCT_GROUP_4_ON;
  102. break;
  103. }
  104. } else {
  105. switch(groupId) {
  106. case 0:
  107. button = CCT_ALL_OFF;
  108. break;
  109. case 1:
  110. button = CCT_GROUP_1_OFF;
  111. break;
  112. case 2:
  113. button = CCT_GROUP_2_OFF;
  114. break;
  115. case 3:
  116. button = CCT_GROUP_3_OFF;
  117. break;
  118. case 4:
  119. button = CCT_GROUP_4_OFF;
  120. break;
  121. }
  122. }
  123. return button;
  124. }
  125. uint8_t CctPacketFormatter::cctCommandIdToGroup(uint8_t command) {
  126. switch (command & 0xF) {
  127. case CCT_GROUP_1_ON:
  128. case CCT_GROUP_1_OFF:
  129. return 1;
  130. case CCT_GROUP_2_ON:
  131. case CCT_GROUP_2_OFF:
  132. return 2;
  133. case CCT_GROUP_3_ON:
  134. case CCT_GROUP_3_OFF:
  135. return 3;
  136. case CCT_GROUP_4_ON:
  137. case CCT_GROUP_4_OFF:
  138. return 4;
  139. case CCT_ALL_ON:
  140. case CCT_ALL_OFF:
  141. return 0;
  142. }
  143. return 255;
  144. }
  145. MiLightStatus CctPacketFormatter::cctCommandToStatus(uint8_t command) {
  146. switch (command & 0xF) {
  147. case CCT_GROUP_1_ON:
  148. case CCT_GROUP_2_ON:
  149. case CCT_GROUP_3_ON:
  150. case CCT_GROUP_4_ON:
  151. case CCT_ALL_ON:
  152. return ON;
  153. case CCT_GROUP_1_OFF:
  154. case CCT_GROUP_2_OFF:
  155. case CCT_GROUP_3_OFF:
  156. case CCT_GROUP_4_OFF:
  157. case CCT_ALL_OFF:
  158. default:
  159. return OFF;
  160. }
  161. }
  162. BulbId CctPacketFormatter::parsePacket(const uint8_t* packet, JsonObject result) {
  163. uint8_t command = packet[CCT_COMMAND_INDEX] & 0x7F;
  164. uint8_t onOffGroupId = cctCommandIdToGroup(command);
  165. BulbId bulbId(
  166. (packet[1] << 8) | packet[2],
  167. onOffGroupId < 255 ? onOffGroupId : packet[3],
  168. REMOTE_TYPE_CCT
  169. );
  170. // Night mode
  171. if (command & 0x10) {
  172. result[GroupStateFieldNames::COMMAND] = MiLightCommandNames::NIGHT_MODE;
  173. } else if (onOffGroupId < 255) {
  174. result[GroupStateFieldNames::STATE] = cctCommandToStatus(command) == ON ? "ON" : "OFF";
  175. } else if (command == CCT_BRIGHTNESS_DOWN) {
  176. result[GroupStateFieldNames::COMMAND] = "brightness_down";
  177. } else if (command == CCT_BRIGHTNESS_UP) {
  178. result[GroupStateFieldNames::COMMAND] = "brightness_up";
  179. } else if (command == CCT_TEMPERATURE_DOWN) {
  180. result[GroupStateFieldNames::COMMAND] = MiLightCommandNames::TEMPERATURE_DOWN;
  181. } else if (command == CCT_TEMPERATURE_UP) {
  182. result[GroupStateFieldNames::COMMAND] = MiLightCommandNames::TEMPERATURE_UP;
  183. } else {
  184. result["button_id"] = command;
  185. }
  186. return bulbId;
  187. }
  188. void CctPacketFormatter::format(uint8_t const* packet, char* buffer) {
  189. PacketFormatter::formatV1Packet(packet, buffer);
  190. }