RgbCctPacketFormatter.cpp 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. #include <RgbCctPacketFormatter.h>
  2. #include <Units.h>
  3. #define V2_OFFSET(byte, key, jumpStart) ( \
  4. pgm_read_byte(&V2_OFFSETS[byte-1][key%4]) \
  5. + \
  6. ((jumpStart > 0 && key >= jumpStart && key <= jumpStart+0x80) ? 0x80 : 0) \
  7. )
  8. #define GROUP_COMMAND_ARG(status, groupId) ( groupId + (status == OFF ? 5 : 0) )
  9. uint8_t const RgbCctPacketFormatter::V2_OFFSETS[][4] = {
  10. { 0x45, 0x1F, 0x14, 0x5C }, // request type
  11. { 0x2B, 0xC9, 0xE3, 0x11 }, // id 1
  12. { 0x6D, 0x5F, 0x8A, 0x2B }, // id 2
  13. { 0xAF, 0x03, 0x1D, 0xF3 }, // command
  14. { 0x1A, 0xE2, 0xF0, 0xD1 }, // argument
  15. { 0x04, 0xD8, 0x71, 0x42 }, // sequence
  16. { 0xAF, 0x04, 0xDD, 0x07 }, // group
  17. { 0x61, 0x13, 0x38, 0x64 } // checksum
  18. };
  19. void RgbCctPacketFormatter::initializePacket(uint8_t* packet) {
  20. size_t packetPtr = 0;
  21. // Always encode with 0x00 key. No utility in varying it.
  22. packet[packetPtr++] = 0x00;
  23. packet[packetPtr++] = 0x20;
  24. packet[packetPtr++] = deviceId >> 8;
  25. packet[packetPtr++] = deviceId & 0xFF;
  26. packet[packetPtr++] = 0;
  27. packet[packetPtr++] = 0;
  28. packet[packetPtr++] = sequenceNum++;
  29. packet[packetPtr++] = groupId;
  30. packet[packetPtr++] = 0;
  31. }
  32. void RgbCctPacketFormatter::unpair() {
  33. for (size_t i = 0; i < 5; i++) {
  34. updateStatus(ON, 0);
  35. }
  36. }
  37. void RgbCctPacketFormatter::command(uint8_t command, uint8_t arg) {
  38. pushPacket();
  39. if (held) {
  40. command |= 0x80;
  41. }
  42. currentPacket[RGB_CCT_COMMAND_INDEX] = command;
  43. currentPacket[RGB_CCT_ARGUMENT_INDEX] = arg;
  44. }
  45. void RgbCctPacketFormatter::updateStatus(MiLightStatus status, uint8_t groupId) {
  46. command(RGB_CCT_ON, GROUP_COMMAND_ARG(status, groupId));
  47. }
  48. void RgbCctPacketFormatter::modeSpeedDown() {
  49. command(RGB_CCT_ON, RGB_CCT_MODE_SPEED_DOWN);
  50. }
  51. void RgbCctPacketFormatter::modeSpeedUp() {
  52. command(RGB_CCT_ON, RGB_CCT_MODE_SPEED_UP);
  53. }
  54. void RgbCctPacketFormatter::updateMode(uint8_t mode) {
  55. lastMode = mode;
  56. command(RGB_CCT_MODE, mode);
  57. }
  58. void RgbCctPacketFormatter::nextMode() {
  59. updateMode((lastMode+1)%RGB_CCT_NUM_MODES);
  60. }
  61. void RgbCctPacketFormatter::previousMode() {
  62. updateMode((lastMode-1)%RGB_CCT_NUM_MODES);
  63. }
  64. void RgbCctPacketFormatter::updateBrightness(uint8_t brightness) {
  65. command(RGB_CCT_BRIGHTNESS, RGB_CCT_BRIGHTNESS_OFFSET + brightness);
  66. }
  67. void RgbCctPacketFormatter::updateHue(uint16_t value) {
  68. uint8_t remapped = Units::rescale(value, 255, 360);
  69. updateColorRaw(remapped);
  70. }
  71. void RgbCctPacketFormatter::updateColorRaw(uint8_t value) {
  72. command(RGB_CCT_COLOR, RGB_CCT_COLOR_OFFSET + value);
  73. }
  74. void RgbCctPacketFormatter::updateTemperature(uint8_t value) {
  75. command(RGB_CCT_KELVIN, RGB_CCT_KELVIN_OFFSET - (value*2));
  76. }
  77. void RgbCctPacketFormatter::updateSaturation(uint8_t value) {
  78. uint8_t remapped = value + RGB_CCT_SATURATION_OFFSET;
  79. command(RGB_CCT_SATURATION, remapped);
  80. }
  81. void RgbCctPacketFormatter::updateColorWhite() {
  82. updateTemperature(0);
  83. }
  84. void RgbCctPacketFormatter::enableNightMode() {
  85. uint8_t arg = GROUP_COMMAND_ARG(OFF, groupId);
  86. command(RGB_CCT_ON | 0x80, arg);
  87. }
  88. void RgbCctPacketFormatter::finalizePacket(uint8_t* packet) {
  89. encodeV2Packet(packet);
  90. }
  91. void RgbCctPacketFormatter::parsePacket(const uint8_t *packet, JsonObject& result) {
  92. uint8_t packetCopy[RGB_CCT_PACKET_LEN];
  93. memcpy(packetCopy, packet, RGB_CCT_PACKET_LEN);
  94. decodeV2Packet(packetCopy);
  95. result["device_id"] = (packetCopy[2] << 8) | packetCopy[3];
  96. result["group_id"] = packetCopy[7];
  97. result["device_type"] = "rgb_cct";
  98. uint8_t command = (packetCopy[RGB_CCT_COMMAND_INDEX] & 0x7F);
  99. uint8_t arg = packetCopy[RGB_CCT_ARGUMENT_INDEX];
  100. if (command == RGB_CCT_ON) {
  101. if (arg == RGB_CCT_MODE_SPEED_DOWN) {
  102. result["command"] = "mode_speed_down";
  103. } else if (arg == RGB_CCT_MODE_SPEED_UP) {
  104. result["command"] = "mode_speed_up";
  105. } else if (arg < 5) { // Group is not reliably encoded in group byte. Extract from arg byte
  106. result["state"] = "ON";
  107. result["group_id"] = arg;
  108. } else {
  109. result["state"] = "OFF";
  110. result["group_id"] = arg-5;
  111. }
  112. } else if (command == RGB_CCT_COLOR) {
  113. uint8_t rescaledColor = (arg - RGB_CCT_COLOR_OFFSET) % 0x100;
  114. uint16_t hue = Units::rescale<uint16_t, uint16_t>(rescaledColor, 360, 255.0);
  115. result["hue"] = hue;
  116. } else if (command == RGB_CCT_KELVIN) {
  117. uint8_t temperature =
  118. static_cast<uint8_t>(
  119. // Range in packets is 180 - 220 or something like that. Shift to
  120. // 0..224. Then strip out values out of range [0..24), and (224..255]
  121. constrain(
  122. static_cast<uint8_t>(arg + RGB_CCT_KELVIN_REMOTE_OFFSET),
  123. 24,
  124. 224
  125. )
  126. +
  127. // Shift 24 down to 0
  128. RGB_CCT_KELVIN_REMOTE_START
  129. )/2; // values are in increments of 2
  130. result["color_temp"] = Units::whiteValToMireds(temperature, 100);
  131. // brightness == saturation
  132. } else if (command == RGB_CCT_BRIGHTNESS && arg >= (RGB_CCT_BRIGHTNESS_OFFSET - 15)) {
  133. uint8_t level = constrain(arg - RGB_CCT_BRIGHTNESS_OFFSET, 0, 100);
  134. result["brightness"] = Units::rescale<uint8_t, uint8_t>(level, 255, 100);
  135. } else if (command == RGB_CCT_SATURATION) {
  136. result["saturation"] = constrain(arg - RGB_CCT_SATURATION_OFFSET, 0, 100);
  137. } else if (command == RGB_CCT_MODE) {
  138. result["mode"] = arg;
  139. } else {
  140. result["button_id"] = command;
  141. result["argument"] = arg;
  142. }
  143. if (! result.containsKey("state")) {
  144. result["state"] = "ON";
  145. }
  146. }
  147. uint8_t RgbCctPacketFormatter::xorKey(uint8_t key) {
  148. // Generate most significant nibble
  149. const uint8_t shift = (key & 0x0F) < 0x04 ? 0 : 1;
  150. const uint8_t x = (((key & 0xF0) >> 4) + shift + 6) % 8;
  151. const uint8_t msn = (((4 + x) ^ 1) & 0x0F) << 4;
  152. // Generate least significant nibble
  153. const uint8_t lsn = ((((key & 0xF) + 4)^2) & 0x0F);
  154. return ( msn | lsn );
  155. }
  156. uint8_t RgbCctPacketFormatter::decodeByte(uint8_t byte, uint8_t s1, uint8_t xorKey, uint8_t s2) {
  157. uint8_t value = byte - s2;
  158. value = value ^ xorKey;
  159. value = value - s1;
  160. return value;
  161. }
  162. uint8_t RgbCctPacketFormatter::encodeByte(uint8_t byte, uint8_t s1, uint8_t xorKey, uint8_t s2) {
  163. uint8_t value = byte + s1;
  164. value = value ^ xorKey;
  165. value = value + s2;
  166. return value;
  167. }
  168. void RgbCctPacketFormatter::decodeV2Packet(uint8_t *packet) {
  169. uint8_t key = xorKey(packet[0]);
  170. for (size_t i = 1; i <= 8; i++) {
  171. packet[i] = decodeByte(packet[i], 0, key, V2_OFFSET(i, packet[0], V2_OFFSET_JUMP_START));
  172. }
  173. }
  174. void RgbCctPacketFormatter::encodeV2Packet(uint8_t *packet) {
  175. uint8_t key = xorKey(packet[0]);
  176. uint8_t sum = key;
  177. for (size_t i = 1; i <= 7; i++) {
  178. sum += packet[i];
  179. packet[i] = encodeByte(packet[i], 0, key, V2_OFFSET(i, packet[0], V2_OFFSET_JUMP_START));
  180. }
  181. packet[8] = encodeByte(sum, 2, key, V2_OFFSET(8, packet[0], 0));
  182. }
  183. void RgbCctPacketFormatter::format(uint8_t const* packet, char* buffer) {
  184. buffer += sprintf_P(buffer, PSTR("Raw packet: "));
  185. for (int i = 0; i < packetLength; i++) {
  186. buffer += sprintf_P(buffer, PSTR("%02X "), packet[i]);
  187. }
  188. uint8_t decodedPacket[packetLength];
  189. memcpy(decodedPacket, packet, packetLength);
  190. decodeV2Packet(decodedPacket);
  191. buffer += sprintf_P(buffer, PSTR("\n\nDecoded:\n"));
  192. buffer += sprintf_P(buffer, PSTR("Key : %02X\n"), decodedPacket[0]);
  193. buffer += sprintf_P(buffer, PSTR("b1 : %02X\n"), decodedPacket[1]);
  194. buffer += sprintf_P(buffer, PSTR("ID : %02X%02X\n"), decodedPacket[2], decodedPacket[3]);
  195. buffer += sprintf_P(buffer, PSTR("Command : %02X\n"), decodedPacket[4]);
  196. buffer += sprintf_P(buffer, PSTR("Argument : %02X\n"), decodedPacket[5]);
  197. buffer += sprintf_P(buffer, PSTR("Sequence : %02X\n"), decodedPacket[6]);
  198. buffer += sprintf_P(buffer, PSTR("Group : %02X\n"), decodedPacket[7]);
  199. buffer += sprintf_P(buffer, PSTR("Checksum : %02X"), decodedPacket[8]);
  200. }