MiLightClient.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. #include <MiLightClient.h>
  2. #include <MiLightRadioConfig.h>
  3. #include <Arduino.h>
  4. #include <RGBConverter.h>
  5. #include <Units.h>
  6. #include <TokenIterator.h>
  7. MiLightClient::MiLightClient(
  8. RadioSwitchboard& radioSwitchboard,
  9. PacketSender& packetSender,
  10. GroupStateStore* stateStore,
  11. Settings& settings
  12. ) : radioSwitchboard(radioSwitchboard)
  13. , updateBeginHandler(NULL)
  14. , updateEndHandler(NULL)
  15. , stateStore(stateStore)
  16. , settings(settings)
  17. , packetSender(packetSender)
  18. , repeatsOverride(0)
  19. { }
  20. void MiLightClient::setHeld(bool held) {
  21. currentRemote->packetFormatter->setHeld(held);
  22. }
  23. void MiLightClient::prepare(
  24. const MiLightRemoteConfig* config,
  25. const uint16_t deviceId,
  26. const uint8_t groupId
  27. ) {
  28. this->currentRemote = config;
  29. if (deviceId >= 0 && groupId >= 0) {
  30. currentRemote->packetFormatter->prepare(deviceId, groupId);
  31. }
  32. }
  33. void MiLightClient::prepare(
  34. const MiLightRemoteType type,
  35. const uint16_t deviceId,
  36. const uint8_t groupId
  37. ) {
  38. prepare(MiLightRemoteConfig::fromType(type), deviceId, groupId);
  39. }
  40. void MiLightClient::updateColorRaw(const uint8_t color) {
  41. #ifdef DEBUG_CLIENT_COMMANDS
  42. Serial.printf_P(PSTR("MiLightClient::updateColorRaw: Change color to %d\n"), color);
  43. #endif
  44. currentRemote->packetFormatter->updateColorRaw(color);
  45. flushPacket();
  46. }
  47. void MiLightClient::updateHue(const uint16_t hue) {
  48. #ifdef DEBUG_CLIENT_COMMANDS
  49. Serial.printf_P(PSTR("MiLightClient::updateHue: Change hue to %d\n"), hue);
  50. #endif
  51. currentRemote->packetFormatter->updateHue(hue);
  52. flushPacket();
  53. }
  54. void MiLightClient::updateBrightness(const uint8_t brightness) {
  55. #ifdef DEBUG_CLIENT_COMMANDS
  56. Serial.printf_P(PSTR("MiLightClient::updateBrightness: Change brightness to %d\n"), brightness);
  57. #endif
  58. currentRemote->packetFormatter->updateBrightness(brightness);
  59. flushPacket();
  60. }
  61. void MiLightClient::updateMode(uint8_t mode) {
  62. #ifdef DEBUG_CLIENT_COMMANDS
  63. Serial.printf_P(PSTR("MiLightClient::updateMode: Change mode to %d\n"), mode);
  64. #endif
  65. currentRemote->packetFormatter->updateMode(mode);
  66. flushPacket();
  67. }
  68. void MiLightClient::nextMode() {
  69. #ifdef DEBUG_CLIENT_COMMANDS
  70. Serial.println(F("MiLightClient::nextMode: Switch to next mode"));
  71. #endif
  72. currentRemote->packetFormatter->nextMode();
  73. flushPacket();
  74. }
  75. void MiLightClient::previousMode() {
  76. #ifdef DEBUG_CLIENT_COMMANDS
  77. Serial.println(F("MiLightClient::previousMode: Switch to previous mode"));
  78. #endif
  79. currentRemote->packetFormatter->previousMode();
  80. flushPacket();
  81. }
  82. void MiLightClient::modeSpeedDown() {
  83. #ifdef DEBUG_CLIENT_COMMANDS
  84. Serial.println(F("MiLightClient::modeSpeedDown: Speed down\n"));
  85. #endif
  86. currentRemote->packetFormatter->modeSpeedDown();
  87. flushPacket();
  88. }
  89. void MiLightClient::modeSpeedUp() {
  90. #ifdef DEBUG_CLIENT_COMMANDS
  91. Serial.println(F("MiLightClient::modeSpeedUp: Speed up"));
  92. #endif
  93. currentRemote->packetFormatter->modeSpeedUp();
  94. flushPacket();
  95. }
  96. void MiLightClient::updateStatus(MiLightStatus status, uint8_t groupId) {
  97. #ifdef DEBUG_CLIENT_COMMANDS
  98. Serial.printf_P(PSTR("MiLightClient::updateStatus: Status %s, groupId %d\n"), status == MiLightStatus::OFF ? "OFF" : "ON", groupId);
  99. #endif
  100. currentRemote->packetFormatter->updateStatus(status, groupId);
  101. flushPacket();
  102. }
  103. void MiLightClient::updateStatus(MiLightStatus status) {
  104. #ifdef DEBUG_CLIENT_COMMANDS
  105. Serial.printf_P(PSTR("MiLightClient::updateStatus: Status %s\n"), status == MiLightStatus::OFF ? "OFF" : "ON");
  106. #endif
  107. currentRemote->packetFormatter->updateStatus(status);
  108. flushPacket();
  109. }
  110. void MiLightClient::updateSaturation(const uint8_t value) {
  111. #ifdef DEBUG_CLIENT_COMMANDS
  112. Serial.printf_P(PSTR("MiLightClient::updateSaturation: Saturation %d\n"), value);
  113. #endif
  114. currentRemote->packetFormatter->updateSaturation(value);
  115. flushPacket();
  116. }
  117. void MiLightClient::updateColorWhite() {
  118. #ifdef DEBUG_CLIENT_COMMANDS
  119. Serial.println(F("MiLightClient::updateColorWhite: Color white"));
  120. #endif
  121. currentRemote->packetFormatter->updateColorWhite();
  122. flushPacket();
  123. }
  124. void MiLightClient::enableNightMode() {
  125. #ifdef DEBUG_CLIENT_COMMANDS
  126. Serial.println(F("MiLightClient::enableNightMode: Night mode"));
  127. #endif
  128. currentRemote->packetFormatter->enableNightMode();
  129. flushPacket();
  130. }
  131. void MiLightClient::pair() {
  132. #ifdef DEBUG_CLIENT_COMMANDS
  133. Serial.println(F("MiLightClient::pair: Pair"));
  134. #endif
  135. currentRemote->packetFormatter->pair();
  136. flushPacket();
  137. }
  138. void MiLightClient::unpair() {
  139. #ifdef DEBUG_CLIENT_COMMANDS
  140. Serial.println(F("MiLightClient::unpair: Unpair"));
  141. #endif
  142. currentRemote->packetFormatter->unpair();
  143. flushPacket();
  144. }
  145. void MiLightClient::increaseBrightness() {
  146. #ifdef DEBUG_CLIENT_COMMANDS
  147. Serial.println(F("MiLightClient::increaseBrightness: Increase brightness"));
  148. #endif
  149. currentRemote->packetFormatter->increaseBrightness();
  150. flushPacket();
  151. }
  152. void MiLightClient::decreaseBrightness() {
  153. #ifdef DEBUG_CLIENT_COMMANDS
  154. Serial.println(F("MiLightClient::decreaseBrightness: Decrease brightness"));
  155. #endif
  156. currentRemote->packetFormatter->decreaseBrightness();
  157. flushPacket();
  158. }
  159. void MiLightClient::increaseTemperature() {
  160. #ifdef DEBUG_CLIENT_COMMANDS
  161. Serial.println(F("MiLightClient::increaseTemperature: Increase temperature"));
  162. #endif
  163. currentRemote->packetFormatter->increaseTemperature();
  164. flushPacket();
  165. }
  166. void MiLightClient::decreaseTemperature() {
  167. #ifdef DEBUG_CLIENT_COMMANDS
  168. Serial.println(F("MiLightClient::decreaseTemperature: Decrease temperature"));
  169. #endif
  170. currentRemote->packetFormatter->decreaseTemperature();
  171. flushPacket();
  172. }
  173. void MiLightClient::updateTemperature(const uint8_t temperature) {
  174. #ifdef DEBUG_CLIENT_COMMANDS
  175. Serial.printf_P(PSTR("MiLightClient::updateTemperature: Set temperature to %d\n"), temperature);
  176. #endif
  177. currentRemote->packetFormatter->updateTemperature(temperature);
  178. flushPacket();
  179. }
  180. void MiLightClient::command(uint8_t command, uint8_t arg) {
  181. #ifdef DEBUG_CLIENT_COMMANDS
  182. Serial.printf_P(PSTR("MiLightClient::command: Execute command %d, argument %d\n"), command, arg);
  183. #endif
  184. currentRemote->packetFormatter->command(command, arg);
  185. flushPacket();
  186. }
  187. void MiLightClient::toggleStatus() {
  188. #ifdef DEBUG_CLIENT_COMMANDS
  189. Serial.printf_P(PSTR("MiLightClient::toggleStatus"));
  190. #endif
  191. currentRemote->packetFormatter->toggleStatus();
  192. flushPacket();
  193. }
  194. void MiLightClient::update(JsonObject request) {
  195. if (this->updateBeginHandler) {
  196. this->updateBeginHandler();
  197. }
  198. const uint8_t parsedStatus = this->parseStatus(request);
  199. // Always turn on first
  200. if (parsedStatus == ON) {
  201. this->updateStatus(ON);
  202. }
  203. if (request.containsKey("command")) {
  204. this->handleCommand(request["command"]);
  205. }
  206. if (request.containsKey("commands")) {
  207. JsonArray commands = request["commands"];
  208. if (! commands.isNull()) {
  209. for (size_t i = 0; i < commands.size(); i++) {
  210. this->handleCommand(commands[i].as<const char*>());
  211. }
  212. }
  213. }
  214. //Homeassistant - Handle effect
  215. if (request.containsKey("effect")) {
  216. this->handleEffect(request["effect"]);
  217. }
  218. if (request.containsKey("hue")) {
  219. this->updateHue(request["hue"]);
  220. }
  221. if (request.containsKey("saturation")) {
  222. this->updateSaturation(request["saturation"]);
  223. }
  224. // Convert RGB to HSV
  225. if (request.containsKey("color")) {
  226. uint16_t r, g, b;
  227. if (request["color"].is<JsonObject>()) {
  228. JsonObject color = request["color"];
  229. r = color["r"];
  230. g = color["g"];
  231. b = color["b"];
  232. } else if (request["color"].is<const char*>()) {
  233. String colorStr = request["color"];
  234. char colorCStr[colorStr.length()];
  235. uint8_t parsedRgbColors[3] = {0, 0, 0};
  236. strcpy(colorCStr, colorStr.c_str());
  237. TokenIterator colorValueItr(colorCStr, strlen(colorCStr), ',');
  238. for (size_t i = 0; i < 3 && colorValueItr.hasNext(); ++i) {
  239. parsedRgbColors[i] = atoi(colorValueItr.nextToken());
  240. }
  241. r = parsedRgbColors[0];
  242. g = parsedRgbColors[1];
  243. b = parsedRgbColors[2];
  244. } else {
  245. Serial.println(F("Unknown format for `color' command"));
  246. return;
  247. }
  248. // We consider an RGB color "white" if all color intensities are roughly the
  249. // same value. An unscientific value of 10 (~4%) is chosen.
  250. if ( abs(r - g) < RGB_WHITE_THRESHOLD
  251. && abs(g - b) < RGB_WHITE_THRESHOLD
  252. && abs(r - b) < RGB_WHITE_THRESHOLD) {
  253. this->updateColorWhite();
  254. } else {
  255. double hsv[3];
  256. RGBConverter converter;
  257. converter.rgbToHsv(r, g, b, hsv);
  258. uint16_t hue = round(hsv[0]*360);
  259. uint8_t saturation = round(hsv[1]*100);
  260. this->updateHue(hue);
  261. this->updateSaturation(saturation);
  262. }
  263. }
  264. if (request.containsKey("level")) {
  265. this->updateBrightness(request["level"]);
  266. }
  267. // HomeAssistant
  268. if (request.containsKey("brightness")) {
  269. uint8_t scaledBrightness = Units::rescale(request["brightness"].as<uint8_t>(), 100, 255);
  270. this->updateBrightness(scaledBrightness);
  271. }
  272. if (request.containsKey("temperature")) {
  273. this->updateTemperature(request["temperature"]);
  274. }
  275. if (request.containsKey("kelvin")) {
  276. this->updateTemperature(request["kelvin"]);
  277. }
  278. // HomeAssistant
  279. if (request.containsKey("color_temp")) {
  280. this->updateTemperature(
  281. Units::miredsToWhiteVal(request["color_temp"], 100)
  282. );
  283. }
  284. if (request.containsKey("mode")) {
  285. this->updateMode(request["mode"]);
  286. }
  287. // Raw packet command/args
  288. if (request.containsKey("button_id") && request.containsKey("argument")) {
  289. this->command(request["button_id"], request["argument"]);
  290. }
  291. // Always turn off last
  292. if (parsedStatus == OFF) {
  293. this->updateStatus(OFF);
  294. }
  295. if (this->updateEndHandler) {
  296. this->updateEndHandler();
  297. }
  298. }
  299. void MiLightClient::handleCommand(const String& command) {
  300. if (command == "unpair") {
  301. this->unpair();
  302. } else if (command == "pair") {
  303. this->pair();
  304. } else if (command == "set_white") {
  305. this->updateColorWhite();
  306. } else if (command == "night_mode") {
  307. this->enableNightMode();
  308. } else if (command == "level_up") {
  309. this->increaseBrightness();
  310. } else if (command == "level_down") {
  311. this->decreaseBrightness();
  312. } else if (command == "temperature_up") {
  313. this->increaseTemperature();
  314. } else if (command == "temperature_down") {
  315. this->decreaseTemperature();
  316. } else if (command == "next_mode") {
  317. this->nextMode();
  318. } else if (command == "previous_mode") {
  319. this->previousMode();
  320. } else if (command == "mode_speed_down") {
  321. this->modeSpeedDown();
  322. } else if (command == "mode_speed_up") {
  323. this->modeSpeedUp();
  324. } else if (command == "toggle") {
  325. this->toggleStatus();
  326. }
  327. }
  328. void MiLightClient::handleEffect(const String& effect) {
  329. if (effect == "night_mode") {
  330. this->enableNightMode();
  331. } else if (effect == "white" || effect == "white_mode") {
  332. this->updateColorWhite();
  333. } else { // assume we're trying to set mode
  334. this->updateMode(effect.toInt());
  335. }
  336. }
  337. uint8_t MiLightClient::parseStatus(JsonObject object) {
  338. JsonVariant status;
  339. if (object.containsKey("status")) {
  340. status = object["status"];
  341. } else if (object.containsKey("state")) {
  342. status = object["state"];
  343. } else {
  344. return 255;
  345. }
  346. if (status.is<bool>()) {
  347. return status.as<bool>() ? ON : OFF;
  348. } else {
  349. String strStatus(status.as<const char*>());
  350. return (strStatus.equalsIgnoreCase("on") || strStatus.equalsIgnoreCase("true")) ? ON : OFF;
  351. }
  352. }
  353. void MiLightClient::setRepeatsOverride(size_t repeats) {
  354. this->repeatsOverride = repeats;
  355. }
  356. void MiLightClient::clearRepeatsOverride() {
  357. this->repeatsOverride = PacketSender::DEFAULT_PACKET_SENDS_VALUE;
  358. }
  359. void MiLightClient::flushPacket() {
  360. PacketStream& stream = currentRemote->packetFormatter->buildPackets();
  361. while (stream.hasNext()) {
  362. packetSender.enqueue(stream.next(), currentRemote, repeatsOverride);
  363. }
  364. currentRemote->packetFormatter->reset();
  365. }
  366. void MiLightClient::onUpdateBegin(EventHandler handler) {
  367. this->updateBeginHandler = handler;
  368. }
  369. void MiLightClient::onUpdateEnd(EventHandler handler) {
  370. this->updateEndHandler = handler;
  371. }