MiLightClient.cpp 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  1. #include <MiLightClient.h>
  2. #include <MiLightRadioConfig.h>
  3. #include <Arduino.h>
  4. #include <RGBConverter.h>
  5. #define COLOR_TEMP_MAX_MIREDS 370
  6. #define COLOR_TEMP_MIN_MIREDS 153
  7. MiLightClient::MiLightClient(MiLightRadioFactory* radioFactory)
  8. : resendCount(MILIGHT_DEFAULT_RESEND_COUNT),
  9. currentRadio(NULL),
  10. numRadios(MiLightRadioConfig::NUM_CONFIGS)
  11. {
  12. radios = new MiLightRadio*[numRadios];
  13. for (size_t i = 0; i < numRadios; i++) {
  14. radios[i] = radioFactory->create(*MiLightRadioConfig::ALL_CONFIGS[i]);
  15. }
  16. this->currentRadio = radios[0];
  17. this->currentRadio->configure();
  18. }
  19. void MiLightClient::begin() {
  20. for (size_t i = 0; i < numRadios; i++) {
  21. radios[i]->begin();
  22. }
  23. }
  24. void MiLightClient::setHeld(bool held) {
  25. formatter->setHeld(held);
  26. }
  27. MiLightRadio* MiLightClient::switchRadio(const MiLightRadioType type) {
  28. MiLightRadio* radio = NULL;
  29. for (int i = 0; i < numRadios; i++) {
  30. if (this->radios[i]->config().type == type) {
  31. radio = radios[i];
  32. break;
  33. }
  34. }
  35. if (radio != NULL) {
  36. if (currentRadio != radio) {
  37. radio->configure();
  38. }
  39. this->currentRadio = radio;
  40. this->formatter = radio->config().packetFormatter;
  41. return radio;
  42. } else {
  43. Serial.print(F("MiLightClient - tried to get radio for unknown type: "));
  44. Serial.println(type);
  45. }
  46. return NULL;
  47. }
  48. void MiLightClient::prepare(MiLightRadioConfig& config,
  49. const uint16_t deviceId,
  50. const uint8_t groupId) {
  51. switchRadio(config.type);
  52. if (deviceId >= 0 && groupId >= 0) {
  53. formatter->prepare(deviceId, groupId);
  54. }
  55. }
  56. void MiLightClient::setResendCount(const unsigned int resendCount) {
  57. this->resendCount = resendCount;
  58. }
  59. bool MiLightClient::available() {
  60. if (currentRadio == NULL) {
  61. return false;
  62. }
  63. return currentRadio->available();
  64. }
  65. void MiLightClient::read(uint8_t packet[]) {
  66. if (currentRadio == NULL) {
  67. return;
  68. }
  69. size_t length = currentRadio->config().getPacketLength();
  70. currentRadio->read(packet, length);
  71. }
  72. void MiLightClient::write(uint8_t packet[]) {
  73. if (currentRadio == NULL) {
  74. return;
  75. }
  76. #ifdef DEBUG_PRINTF
  77. printf("Sending packet: ");
  78. for (int i = 0; i < currentRadio->config().getPacketLength(); i++) {
  79. printf("%02X", packet[i]);
  80. }
  81. printf("\n");
  82. int iStart = millis();
  83. #endif
  84. for (int i = 0; i < this->resendCount; i++) {
  85. currentRadio->write(packet, currentRadio->config().getPacketLength());
  86. }
  87. #ifdef DEBUG_PRINTF
  88. int iElapsed = millis() - iStart;
  89. Serial.print("Elapsed: ");
  90. Serial.println(iElapsed);
  91. #endif
  92. }
  93. void MiLightClient::updateColorRaw(const uint8_t color) {
  94. formatter->updateColorRaw(color);
  95. flushPacket();
  96. }
  97. void MiLightClient::updateHue(const uint16_t hue) {
  98. formatter->updateHue(hue);
  99. flushPacket();
  100. }
  101. void MiLightClient::updateBrightness(const uint8_t brightness) {
  102. formatter->updateBrightness(brightness);
  103. flushPacket();
  104. }
  105. void MiLightClient::updateMode(uint8_t mode) {
  106. formatter->updateMode(mode);
  107. flushPacket();
  108. }
  109. void MiLightClient::nextMode() {
  110. formatter->nextMode();
  111. flushPacket();
  112. }
  113. void MiLightClient::previousMode() {
  114. formatter->previousMode();
  115. flushPacket();
  116. }
  117. void MiLightClient::modeSpeedDown() {
  118. formatter->modeSpeedDown();
  119. flushPacket();
  120. }
  121. void MiLightClient::modeSpeedUp() {
  122. formatter->modeSpeedUp();
  123. flushPacket();
  124. }
  125. void MiLightClient::updateStatus(MiLightStatus status, uint8_t groupId) {
  126. formatter->updateStatus(status, groupId);
  127. flushPacket();
  128. }
  129. void MiLightClient::updateStatus(MiLightStatus status) {
  130. formatter->updateStatus(status);
  131. flushPacket();
  132. }
  133. void MiLightClient::updateSaturation(const uint8_t value) {
  134. formatter->updateSaturation(value);
  135. flushPacket();
  136. }
  137. void MiLightClient::updateColorWhite() {
  138. formatter->updateColorWhite();
  139. flushPacket();
  140. }
  141. void MiLightClient::enableNightMode() {
  142. formatter->enableNightMode();
  143. flushPacket();
  144. }
  145. void MiLightClient::pair() {
  146. formatter->pair();
  147. flushPacket();
  148. }
  149. void MiLightClient::unpair() {
  150. formatter->unpair();
  151. flushPacket();
  152. }
  153. void MiLightClient::increaseBrightness() {
  154. formatter->increaseBrightness();
  155. flushPacket();
  156. }
  157. void MiLightClient::decreaseBrightness() {
  158. formatter->decreaseBrightness();
  159. flushPacket();
  160. }
  161. void MiLightClient::increaseTemperature() {
  162. formatter->increaseTemperature();
  163. flushPacket();
  164. }
  165. void MiLightClient::decreaseTemperature() {
  166. formatter->decreaseTemperature();
  167. flushPacket();
  168. }
  169. void MiLightClient::updateTemperature(const uint8_t temperature) {
  170. formatter->updateTemperature(temperature);
  171. flushPacket();
  172. }
  173. void MiLightClient::command(uint8_t command, uint8_t arg) {
  174. formatter->command(command, arg);
  175. flushPacket();
  176. }
  177. void MiLightClient::update(const JsonObject& request) {
  178. if (request.containsKey("status") || request.containsKey("state")) {
  179. String strStatus;
  180. if (request.containsKey("status")) {
  181. strStatus = request.get<char*>("status");
  182. } else {
  183. strStatus = request.get<char*>("state");
  184. }
  185. MiLightStatus status = (strStatus.equalsIgnoreCase("on") || strStatus.equalsIgnoreCase("true")) ? ON : OFF;
  186. this->updateStatus(status);
  187. }
  188. if (request.containsKey("command")) {
  189. if (request["command"] == "unpair") {
  190. this->unpair();
  191. }
  192. if (request["command"] == "pair") {
  193. this->pair();
  194. }
  195. if (request["command"] == "set_white") {
  196. this->updateColorWhite();
  197. }
  198. if (request["command"] == "night_mode") {
  199. this->enableNightMode();
  200. }
  201. if (request["command"] == "level_up") {
  202. this->increaseBrightness();
  203. }
  204. if (request["command"] == "level_down") {
  205. this->decreaseBrightness();
  206. }
  207. if (request["command"] == "temperature_up") {
  208. this->increaseTemperature();
  209. }
  210. if (request["command"] == "temperature_down") {
  211. this->decreaseTemperature();
  212. }
  213. if (request["command"] == "next_mode") {
  214. this->nextMode();
  215. }
  216. if (request["command"] == "previous_mode") {
  217. this->previousMode();
  218. }
  219. if (request["command"] == "mode_speed_down") {
  220. this->modeSpeedDown();
  221. }
  222. if (request["command"] == "mode_speed_up") {
  223. this->modeSpeedUp();
  224. }
  225. }
  226. if (request.containsKey("hue")) {
  227. this->updateHue(request["hue"]);
  228. }
  229. if (request.containsKey("saturation")) {
  230. this->updateSaturation(request["saturation"]);
  231. }
  232. // Convert RGB to HSV
  233. if (request.containsKey("color")) {
  234. JsonObject& color = request["color"];
  235. uint8_t r = color["r"];
  236. uint8_t g = color["g"];
  237. uint8_t b = color["b"];
  238. double hsv[3];
  239. RGBConverter converter;
  240. converter.rgbToHsv(r, g, b, hsv);
  241. uint16_t hue = round(hsv[0]*360);
  242. uint8_t saturation = round(hsv[1]*100);
  243. this->updateHue(hue);
  244. this->updateSaturation(saturation);
  245. }
  246. if (request.containsKey("level")) {
  247. this->updateBrightness(request["level"]);
  248. }
  249. // HomeAssistant
  250. if (request.containsKey("brightness")) {
  251. uint8_t scaledBrightness = round(request.get<uint8_t>("brightness") * (100/255.0));
  252. printf("brightness -> %u\n", scaledBrightness);
  253. this->updateBrightness(scaledBrightness);
  254. }
  255. if (request.containsKey("temperature")) {
  256. this->updateTemperature(request["temperature"]);
  257. }
  258. // HomeAssistant
  259. if (request.containsKey("color_temp")) {
  260. // MiLight CCT bulbs range from 2700K-6500K, or ~370.3-153.8 mireds. Note
  261. // that mireds are inversely correlated with color temperature.
  262. uint32_t tempMireds = request["color_temp"];
  263. tempMireds = tempMireds > COLOR_TEMP_MAX_MIREDS ? COLOR_TEMP_MAX_MIREDS : tempMireds;
  264. tempMireds = tempMireds < COLOR_TEMP_MIN_MIREDS ? COLOR_TEMP_MIN_MIREDS : tempMireds;
  265. uint8_t scaledTemp = round(
  266. 100*
  267. (tempMireds - COLOR_TEMP_MIN_MIREDS)
  268. /
  269. static_cast<double>(COLOR_TEMP_MAX_MIREDS - COLOR_TEMP_MIN_MIREDS)
  270. );
  271. this->updateTemperature(100 - scaledTemp);
  272. }
  273. if (request.containsKey("mode")) {
  274. this->updateMode(request["mode"]);
  275. }
  276. }
  277. void MiLightClient::formatPacket(uint8_t* packet, char* buffer) {
  278. formatter->format(packet, buffer);
  279. }
  280. void MiLightClient::flushPacket() {
  281. PacketStream& stream = formatter->buildPackets();
  282. const size_t prevNumRepeats = this->resendCount;
  283. // When sending multiple packets, normalize the number of repeats
  284. if (stream.numPackets > 1) {
  285. setResendCount(MILIGHT_DEFAULT_RESEND_COUNT);
  286. }
  287. while (stream.hasNext()) {
  288. write(stream.next());
  289. if (stream.hasNext()) {
  290. delay(10);
  291. }
  292. }
  293. setResendCount(prevNumRepeats);
  294. formatter->reset();
  295. }