MiLightClient.cpp 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  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. }
  17. void MiLightClient::begin() {
  18. for (size_t i = 0; i < numRadios; i++) {
  19. radios[i]->begin();
  20. }
  21. this->currentRadio = radios[0];
  22. this->currentRadio->configure();
  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. this->handleCommand(request["command"]);
  190. }
  191. if (request.containsKey("commands")) {
  192. JsonArray& commands = request["commands"];
  193. if (commands.success()) {
  194. for (size_t i = 0; i < commands.size(); i++) {
  195. this->handleCommand(commands.get<String>(i));
  196. }
  197. }
  198. }
  199. if (request.containsKey("hue")) {
  200. this->updateHue(request["hue"]);
  201. }
  202. if (request.containsKey("saturation")) {
  203. this->updateSaturation(request["saturation"]);
  204. }
  205. // Convert RGB to HSV
  206. if (request.containsKey("color")) {
  207. JsonObject& color = request["color"];
  208. uint8_t r = color["r"];
  209. uint8_t g = color["g"];
  210. uint8_t b = color["b"];
  211. double hsv[3];
  212. RGBConverter converter;
  213. converter.rgbToHsv(r, g, b, hsv);
  214. uint16_t hue = round(hsv[0]*360);
  215. uint8_t saturation = round(hsv[1]*100);
  216. this->updateHue(hue);
  217. this->updateSaturation(saturation);
  218. }
  219. if (request.containsKey("level")) {
  220. this->updateBrightness(request["level"]);
  221. }
  222. // HomeAssistant
  223. if (request.containsKey("brightness")) {
  224. uint8_t scaledBrightness = round(request.get<uint8_t>("brightness") * (100/255.0));
  225. this->updateBrightness(scaledBrightness);
  226. }
  227. if (request.containsKey("temperature")) {
  228. this->updateTemperature(request["temperature"]);
  229. }
  230. // HomeAssistant
  231. if (request.containsKey("color_temp")) {
  232. // MiLight CCT bulbs range from 2700K-6500K, or ~370.3-153.8 mireds. Note
  233. // that mireds are inversely correlated with color temperature.
  234. uint32_t tempMireds = request["color_temp"];
  235. tempMireds = tempMireds > COLOR_TEMP_MAX_MIREDS ? COLOR_TEMP_MAX_MIREDS : tempMireds;
  236. tempMireds = tempMireds < COLOR_TEMP_MIN_MIREDS ? COLOR_TEMP_MIN_MIREDS : tempMireds;
  237. uint8_t scaledTemp = round(
  238. 100*
  239. (tempMireds - COLOR_TEMP_MIN_MIREDS)
  240. /
  241. static_cast<double>(COLOR_TEMP_MAX_MIREDS - COLOR_TEMP_MIN_MIREDS)
  242. );
  243. this->updateTemperature(100 - scaledTemp);
  244. }
  245. if (request.containsKey("mode")) {
  246. this->updateMode(request["mode"]);
  247. }
  248. }
  249. void MiLightClient::handleCommand(const String& command) {
  250. if (command == "unpair") {
  251. this->unpair();
  252. } else if (command == "pair") {
  253. this->pair();
  254. } else if (command == "set_white") {
  255. this->updateColorWhite();
  256. } else if (command == "night_mode") {
  257. this->enableNightMode();
  258. } else if (command == "level_up") {
  259. this->increaseBrightness();
  260. } else if (command == "level_down") {
  261. this->decreaseBrightness();
  262. } else if (command == "temperature_up") {
  263. this->increaseTemperature();
  264. } else if (command == "temperature_down") {
  265. this->decreaseTemperature();
  266. } else if (command == "next_mode") {
  267. this->nextMode();
  268. } else if (command == "previous_mode") {
  269. this->previousMode();
  270. } else if (command == "mode_speed_down") {
  271. this->modeSpeedDown();
  272. } else if (command == "mode_speed_up") {
  273. this->modeSpeedUp();
  274. }
  275. }
  276. void MiLightClient::formatPacket(uint8_t* packet, char* buffer) {
  277. formatter->format(packet, buffer);
  278. }
  279. void MiLightClient::flushPacket() {
  280. PacketStream& stream = formatter->buildPackets();
  281. const size_t prevNumRepeats = this->resendCount;
  282. // When sending multiple packets, normalize the number of repeats
  283. if (stream.numPackets > 1) {
  284. setResendCount(MILIGHT_DEFAULT_RESEND_COUNT);
  285. }
  286. while (stream.hasNext()) {
  287. write(stream.next());
  288. if (stream.hasNext()) {
  289. delay(10);
  290. }
  291. }
  292. setResendCount(prevNumRepeats);
  293. formatter->reset();
  294. }