MiLightClient.cpp 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. #include <MiLightClient.h>
  2. #include <MiLightRadioConfig.h>
  3. MiLightRadio* MiLightClient::getRadio(const MiLightRadioType type) {
  4. MiLightRadioStack* stack = NULL;
  5. if (type == RGBW) {
  6. stack = rgbwRadio;
  7. } else if (type == CCT) {
  8. stack = cctRadio;
  9. } else if (type == RGB_CCT) {
  10. stack = rgbCctRadio;
  11. }
  12. if (stack != NULL) {
  13. MiLightRadio *radio = stack->getRadio();
  14. if (currentRadio != stack->type) {
  15. radio->configure();
  16. }
  17. currentRadio = stack->type;
  18. return radio;
  19. }
  20. return NULL;
  21. }
  22. void MiLightClient::setResendCount(const unsigned int resendCount) {
  23. this->resendCount = resendCount;
  24. }
  25. uint8_t MiLightClient::nextSequenceNum() {
  26. return sequenceNum++;
  27. }
  28. bool MiLightClient::available(const MiLightRadioType radioType) {
  29. MiLightRadio* radio = getRadio(radioType);
  30. if (radio == NULL) {
  31. return false;
  32. }
  33. return radio->available();
  34. }
  35. void MiLightClient::read(const MiLightRadioType radioType, uint8_t packet[]) {
  36. MiLightRadio* radio = getRadio(radioType);
  37. if (radio == NULL) {
  38. return;
  39. }
  40. size_t length;
  41. radio->read(packet, length);
  42. }
  43. void MiLightClient::write(const MiLightRadioType radioType,
  44. uint8_t packet[]) {
  45. MiLightRadio* radio = getRadio(radioType);
  46. if (radio == NULL) {
  47. return;
  48. }
  49. for (int i = 0; i < this->resendCount; i++) {
  50. radio->write(packet, MILIGHT_PACKET_LENGTH);
  51. }
  52. }
  53. void MiLightClient::writeRgbw(
  54. const uint16_t deviceId,
  55. const uint8_t color,
  56. const uint8_t brightness,
  57. const uint8_t groupId,
  58. const uint8_t button) {
  59. uint8_t packet[MilightRgbwConfig.packetLength];
  60. size_t packetPtr = 0;
  61. packet[packetPtr++] = RGBW;
  62. packet[packetPtr++] = deviceId >> 8;
  63. packet[packetPtr++] = deviceId & 0xFF;
  64. packet[packetPtr++] = color;
  65. packet[packetPtr++] = (brightness << 3) | (groupId & 0x07);
  66. packet[packetPtr++] = button;
  67. packet[packetPtr++] = nextSequenceNum();
  68. write(RGBW, packet);
  69. }
  70. void MiLightClient::writeCct(const uint16_t deviceId,
  71. const uint8_t groupId,
  72. const uint8_t button) {
  73. uint8_t packet[MilightRgbwConfig.packetLength];
  74. uint8_t sequenceNum = nextSequenceNum();
  75. size_t packetPtr = 0;
  76. packet[packetPtr++] = CCT;
  77. packet[packetPtr++] = deviceId >> 8;
  78. packet[packetPtr++] = deviceId & 0xFF;
  79. packet[packetPtr++] = groupId;
  80. packet[packetPtr++] = button;
  81. packet[packetPtr++] = sequenceNum;
  82. packet[packetPtr++] = sequenceNum;
  83. write(CCT, packet);
  84. }
  85. void MiLightClient::updateColorRaw(const uint16_t deviceId, const uint8_t groupId, const uint16_t color) {
  86. writeRgbw(deviceId, color, 0, groupId, RGBW_COLOR);
  87. }
  88. void MiLightClient::updateHue(const uint16_t deviceId, const uint8_t groupId, const uint16_t hue) {
  89. // Map color as a Hue value in [0, 359] to [0, 255]. The protocol also has
  90. // 0 being roughly magenta (#FF00FF)
  91. const int16_t remappedColor = (hue + 40) % 360;
  92. const uint8_t adjustedColor = round(remappedColor * (255 / 360.0));
  93. writeRgbw(deviceId, adjustedColor, 0, groupId, RGBW_COLOR);
  94. }
  95. void MiLightClient::updateBrightness(const uint16_t deviceId, const uint8_t groupId, const uint8_t brightness) {
  96. // Expect an input value in [0, 100]. Map it down to [0, 25].
  97. const uint8_t adjustedBrightness = round(brightness * (25 / 100.0));
  98. // The actual protocol uses a bizarre range where min is 16, max is 23:
  99. // [16, 15, ..., 0, 31, ..., 23]
  100. const uint8_t packetBrightnessValue = (
  101. ((31 - adjustedBrightness) + 17) % 32
  102. );
  103. writeRgbw(deviceId, 0, packetBrightnessValue, groupId, RGBW_BRIGHTNESS);
  104. }
  105. void MiLightClient::updateStatus(const MiLightRadioType type, const uint16_t deviceId, const uint8_t groupId, MiLightStatus status) {
  106. if (type == RGBW) {
  107. uint8_t button = RGBW_GROUP_1_ON + ((groupId - 1)*2) + status;
  108. writeRgbw(deviceId, 0, 0, groupId, button);
  109. } else {
  110. writeCct(deviceId, groupId, getCctStatusButton(groupId, status));
  111. }
  112. }
  113. void MiLightClient::updateColorWhite(const uint16_t deviceId, const uint8_t groupId) {
  114. uint8_t button = RGBW_GROUP_1_MAX_LEVEL + ((groupId - 1)*2);
  115. pressButton(RGBW, deviceId, groupId, button);
  116. }
  117. void MiLightClient::pair(const MiLightRadioType type, const uint16_t deviceId, const uint8_t groupId) {
  118. updateStatus(type, deviceId, groupId, ON);
  119. }
  120. void MiLightClient::unpair(const MiLightRadioType type, const uint16_t deviceId, const uint8_t groupId) {
  121. if (type == RGBW) {
  122. updateStatus(RGBW, deviceId, groupId, ON);
  123. delay(1);
  124. updateColorWhite(deviceId, groupId);
  125. } else if (type == CCT) {
  126. for (int i = 0; i < 5; i++) {
  127. updateStatus(CCT, deviceId, groupId, ON);
  128. delay(1);
  129. }
  130. }
  131. }
  132. void MiLightClient::pressButton(const MiLightRadioType type, const uint16_t deviceId, const uint8_t groupId, const uint8_t button) {
  133. if (type == RGBW) {
  134. writeRgbw(deviceId, 0, 0, groupId, button);
  135. } else if (type == CCT) {
  136. writeCct(deviceId, groupId, button);
  137. }
  138. }
  139. void MiLightClient::allOn(const MiLightRadioType type, const uint16_t deviceId) {
  140. if (type == RGBW) {
  141. writeRgbw(deviceId, 0, 0, 0, RGBW_ALL_ON);
  142. } else if (type == CCT) {
  143. writeCct(deviceId, 0, CCT_ALL_ON);
  144. }
  145. }
  146. void MiLightClient::allOff(const MiLightRadioType type, const uint16_t deviceId) {
  147. if (type == RGBW) {
  148. writeRgbw(deviceId, 0, 0, 0, RGBW_ALL_OFF);
  149. } else if (type == CCT) {
  150. writeCct(deviceId, 0, CCT_ALL_OFF);
  151. }
  152. }
  153. void MiLightClient::increaseCctBrightness(const uint16_t deviceId, const uint8_t groupId) {
  154. writeCct(deviceId, groupId, CCT_BRIGHTNESS_UP);
  155. }
  156. void MiLightClient::decreaseCctBrightness(const uint16_t deviceId, const uint8_t groupId) {
  157. writeCct(deviceId, groupId, CCT_BRIGHTNESS_DOWN);
  158. }
  159. void MiLightClient::updateCctBrightness(const uint16_t deviceId, const uint8_t groupId, const uint8_t brightness) {
  160. for (int i = 0; i < MILIGHT_CCT_INTERVALS; i++) {
  161. decreaseCctBrightness(deviceId, groupId);
  162. }
  163. for (int i = 0; i < brightness/10; i++) {
  164. increaseCctBrightness(deviceId, groupId);
  165. }
  166. }
  167. void MiLightClient::increaseTemperature(const uint16_t deviceId, const uint8_t groupId) {
  168. writeCct(deviceId, groupId, CCT_TEMPERATURE_UP);
  169. }
  170. void MiLightClient::decreaseTemperature(const uint16_t deviceId, const uint8_t groupId) {
  171. writeCct(deviceId, groupId, CCT_TEMPERATURE_DOWN);
  172. }
  173. void MiLightClient::updateTemperature(const uint16_t deviceId, const uint8_t groupId, const uint8_t temperature) {
  174. for (int i = 0; i < MILIGHT_CCT_INTERVALS; i++) {
  175. decreaseTemperature(deviceId, groupId);
  176. }
  177. for (int i = 0; i < temperature; i++) {
  178. increaseTemperature(deviceId, groupId);
  179. }
  180. }
  181. uint8_t MiLightClient::getCctStatusButton(uint8_t groupId, MiLightStatus status) {
  182. uint8_t button = 0;
  183. if (status == ON) {
  184. switch(groupId) {
  185. case 1:
  186. button = CCT_GROUP_1_ON;
  187. break;
  188. case 2:
  189. button = CCT_GROUP_2_ON;
  190. break;
  191. case 3:
  192. button = CCT_GROUP_3_ON;
  193. break;
  194. case 4:
  195. button = CCT_GROUP_4_ON;
  196. break;
  197. }
  198. } else {
  199. switch(groupId) {
  200. case 1:
  201. button = CCT_GROUP_1_OFF;
  202. break;
  203. case 2:
  204. button = CCT_GROUP_2_OFF;
  205. break;
  206. case 3:
  207. button = CCT_GROUP_3_OFF;
  208. break;
  209. case 4:
  210. button = CCT_GROUP_4_OFF;
  211. break;
  212. }
  213. }
  214. return button;
  215. }
  216. MiLightRadioType MiLightClient::getRadioType(const String& typeName) {
  217. if (typeName.equalsIgnoreCase("rgbw")) {
  218. return RGBW;
  219. } else if (typeName.equalsIgnoreCase("cct")) {
  220. return CCT;
  221. } else if (typeName.equalsIgnoreCase("rgb_cct")) {
  222. return RGB_CCT;
  223. } else {
  224. return UNKNOWN;
  225. }
  226. }
  227. const MiLightRadioConfig& MiLightClient::getRadioConfig(const String& typeName) {
  228. switch (getRadioType(typeName)) {
  229. case RGBW:
  230. return MilightRgbwConfig;
  231. case CCT:
  232. return MilightCctConfig;
  233. case RGB_CCT:
  234. return MilightRgbCctConfig;
  235. default:
  236. Serial.print("Unknown radio type: ");
  237. Serial.println(typeName);
  238. return MilightRgbwConfig;
  239. }
  240. }
  241. void MiLightClient::formatPacket(MiLightRadioConfig& config, uint8_t* packet, char* buffer) {
  242. if (config.type == RGBW || config.type == CCT) {
  243. String format = String("Request type : %02X\n")
  244. + "Device ID : %02X%02X\n"
  245. + "b1 : %02X\n"
  246. + "b2 : %02X\n"
  247. + "b3 : %02X\n"
  248. + "Sequence Num. : %02X";
  249. sprintf(
  250. buffer,
  251. format.c_str(),
  252. packet[0],
  253. packet[1], packet[2],
  254. packet[3],
  255. packet[4],
  256. packet[5],
  257. packet[6]
  258. );
  259. } else {
  260. for (int i = 0; i < config.packetLength; i++) {
  261. sprintf(buffer, "%02X ", packet[i]);
  262. buffer += 3;
  263. }
  264. sprintf(buffer, "\n\n");
  265. }
  266. }