MiLightClient.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639
  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. #include <ParsedColor.h>
  8. #include <MiLightCommands.h>
  9. #include <functional>
  10. using namespace std::placeholders;
  11. const char* MiLightClient::FIELD_ORDERINGS[] = {
  12. // These are handled manually
  13. // GroupStateFieldNames::STATE,
  14. // GroupStateFieldNames::STATUS,
  15. GroupStateFieldNames::HUE,
  16. GroupStateFieldNames::SATURATION,
  17. GroupStateFieldNames::KELVIN,
  18. GroupStateFieldNames::TEMPERATURE,
  19. GroupStateFieldNames::COLOR_TEMP,
  20. GroupStateFieldNames::MODE,
  21. GroupStateFieldNames::EFFECT,
  22. GroupStateFieldNames::COLOR,
  23. // Level/Brightness must be processed last because they're specific to a particular bulb mode.
  24. // So make sure bulb mode is set before applying level/brightness.
  25. GroupStateFieldNames::LEVEL,
  26. GroupStateFieldNames::BRIGHTNESS,
  27. GroupStateFieldNames::COMMAND,
  28. GroupStateFieldNames::COMMANDS
  29. };
  30. const std::map<const char*, std::function<void(MiLightClient*, JsonVariant)>, MiLightClient::cmp_str> MiLightClient::FIELD_SETTERS = {
  31. {
  32. GroupStateFieldNames::STATUS,
  33. [](MiLightClient* client, JsonVariant val) {
  34. client->updateStatus(parseMilightStatus(val));
  35. }
  36. },
  37. {GroupStateFieldNames::LEVEL, &MiLightClient::updateBrightness},
  38. {
  39. GroupStateFieldNames::BRIGHTNESS,
  40. [](MiLightClient* client, uint16_t arg) {
  41. client->updateBrightness(Units::rescale<uint16_t, uint16_t>(arg, 100, 255));
  42. }
  43. },
  44. {GroupStateFieldNames::HUE, &MiLightClient::updateHue},
  45. {GroupStateFieldNames::SATURATION, &MiLightClient::updateSaturation},
  46. {GroupStateFieldNames::KELVIN, &MiLightClient::updateTemperature},
  47. {GroupStateFieldNames::TEMPERATURE, &MiLightClient::updateTemperature},
  48. {
  49. GroupStateFieldNames::COLOR_TEMP,
  50. [](MiLightClient* client, uint16_t arg) {
  51. client->updateTemperature(Units::miredsToWhiteVal(arg, 100));
  52. }
  53. },
  54. {GroupStateFieldNames::MODE, &MiLightClient::updateMode},
  55. {GroupStateFieldNames::COLOR, &MiLightClient::updateColor},
  56. {GroupStateFieldNames::EFFECT, &MiLightClient::handleEffect},
  57. {GroupStateFieldNames::COMMAND, &MiLightClient::handleCommand},
  58. {GroupStateFieldNames::COMMANDS, &MiLightClient::handleCommands}
  59. };
  60. MiLightClient::MiLightClient(
  61. RadioSwitchboard& radioSwitchboard,
  62. PacketSender& packetSender,
  63. GroupStateStore* stateStore,
  64. Settings& settings,
  65. TransitionController& transitions
  66. ) : radioSwitchboard(radioSwitchboard)
  67. , updateBeginHandler(NULL)
  68. , updateEndHandler(NULL)
  69. , stateStore(stateStore)
  70. , settings(settings)
  71. , packetSender(packetSender)
  72. , transitions(transitions)
  73. , repeatsOverride(0)
  74. { }
  75. void MiLightClient::setHeld(bool held) {
  76. currentRemote->packetFormatter->setHeld(held);
  77. }
  78. void MiLightClient::prepare(
  79. const MiLightRemoteConfig* config,
  80. const uint16_t deviceId,
  81. const uint8_t groupId
  82. ) {
  83. this->currentRemote = config;
  84. if (deviceId >= 0 && groupId >= 0) {
  85. currentRemote->packetFormatter->prepare(deviceId, groupId);
  86. }
  87. }
  88. void MiLightClient::prepare(
  89. const MiLightRemoteType type,
  90. const uint16_t deviceId,
  91. const uint8_t groupId
  92. ) {
  93. prepare(MiLightRemoteConfig::fromType(type), deviceId, groupId);
  94. }
  95. void MiLightClient::updateColorRaw(const uint8_t color) {
  96. #ifdef DEBUG_CLIENT_COMMANDS
  97. Serial.printf_P(PSTR("MiLightClient::updateColorRaw: Change color to %d\n"), color);
  98. #endif
  99. currentRemote->packetFormatter->updateColorRaw(color);
  100. flushPacket();
  101. }
  102. void MiLightClient::updateHue(const uint16_t hue) {
  103. #ifdef DEBUG_CLIENT_COMMANDS
  104. Serial.printf_P(PSTR("MiLightClient::updateHue: Change hue to %d\n"), hue);
  105. #endif
  106. currentRemote->packetFormatter->updateHue(hue);
  107. flushPacket();
  108. }
  109. void MiLightClient::updateBrightness(const uint8_t brightness) {
  110. #ifdef DEBUG_CLIENT_COMMANDS
  111. Serial.printf_P(PSTR("MiLightClient::updateBrightness: Change brightness to %d\n"), brightness);
  112. #endif
  113. currentRemote->packetFormatter->updateBrightness(brightness);
  114. flushPacket();
  115. }
  116. void MiLightClient::updateMode(uint8_t mode) {
  117. #ifdef DEBUG_CLIENT_COMMANDS
  118. Serial.printf_P(PSTR("MiLightClient::updateMode: Change mode to %d\n"), mode);
  119. #endif
  120. currentRemote->packetFormatter->updateMode(mode);
  121. flushPacket();
  122. }
  123. void MiLightClient::nextMode() {
  124. #ifdef DEBUG_CLIENT_COMMANDS
  125. Serial.println(F("MiLightClient::nextMode: Switch to next mode"));
  126. #endif
  127. currentRemote->packetFormatter->nextMode();
  128. flushPacket();
  129. }
  130. void MiLightClient::previousMode() {
  131. #ifdef DEBUG_CLIENT_COMMANDS
  132. Serial.println(F("MiLightClient::previousMode: Switch to previous mode"));
  133. #endif
  134. currentRemote->packetFormatter->previousMode();
  135. flushPacket();
  136. }
  137. void MiLightClient::modeSpeedDown() {
  138. #ifdef DEBUG_CLIENT_COMMANDS
  139. Serial.println(F("MiLightClient::modeSpeedDown: Speed down\n"));
  140. #endif
  141. currentRemote->packetFormatter->modeSpeedDown();
  142. flushPacket();
  143. }
  144. void MiLightClient::modeSpeedUp() {
  145. #ifdef DEBUG_CLIENT_COMMANDS
  146. Serial.println(F("MiLightClient::modeSpeedUp: Speed up"));
  147. #endif
  148. currentRemote->packetFormatter->modeSpeedUp();
  149. flushPacket();
  150. }
  151. void MiLightClient::updateStatus(MiLightStatus status, uint8_t groupId) {
  152. #ifdef DEBUG_CLIENT_COMMANDS
  153. Serial.printf_P(PSTR("MiLightClient::updateStatus: Status %s, groupId %d\n"), status == MiLightStatus::OFF ? "OFF" : "ON", groupId);
  154. #endif
  155. currentRemote->packetFormatter->updateStatus(status, groupId);
  156. flushPacket();
  157. }
  158. void MiLightClient::updateStatus(MiLightStatus status) {
  159. #ifdef DEBUG_CLIENT_COMMANDS
  160. Serial.printf_P(PSTR("MiLightClient::updateStatus: Status %s\n"), status == MiLightStatus::OFF ? "OFF" : "ON");
  161. #endif
  162. currentRemote->packetFormatter->updateStatus(status);
  163. flushPacket();
  164. }
  165. void MiLightClient::updateSaturation(const uint8_t value) {
  166. #ifdef DEBUG_CLIENT_COMMANDS
  167. Serial.printf_P(PSTR("MiLightClient::updateSaturation: Saturation %d\n"), value);
  168. #endif
  169. currentRemote->packetFormatter->updateSaturation(value);
  170. flushPacket();
  171. }
  172. void MiLightClient::updateColorWhite() {
  173. #ifdef DEBUG_CLIENT_COMMANDS
  174. Serial.println(F("MiLightClient::updateColorWhite: Color white"));
  175. #endif
  176. currentRemote->packetFormatter->updateColorWhite();
  177. flushPacket();
  178. }
  179. void MiLightClient::enableNightMode() {
  180. #ifdef DEBUG_CLIENT_COMMANDS
  181. Serial.println(F("MiLightClient::enableNightMode: Night mode"));
  182. #endif
  183. currentRemote->packetFormatter->enableNightMode();
  184. flushPacket();
  185. }
  186. void MiLightClient::pair() {
  187. #ifdef DEBUG_CLIENT_COMMANDS
  188. Serial.println(F("MiLightClient::pair: Pair"));
  189. #endif
  190. currentRemote->packetFormatter->pair();
  191. flushPacket();
  192. }
  193. void MiLightClient::unpair() {
  194. #ifdef DEBUG_CLIENT_COMMANDS
  195. Serial.println(F("MiLightClient::unpair: Unpair"));
  196. #endif
  197. currentRemote->packetFormatter->unpair();
  198. flushPacket();
  199. }
  200. void MiLightClient::increaseBrightness() {
  201. #ifdef DEBUG_CLIENT_COMMANDS
  202. Serial.println(F("MiLightClient::increaseBrightness: Increase brightness"));
  203. #endif
  204. currentRemote->packetFormatter->increaseBrightness();
  205. flushPacket();
  206. }
  207. void MiLightClient::decreaseBrightness() {
  208. #ifdef DEBUG_CLIENT_COMMANDS
  209. Serial.println(F("MiLightClient::decreaseBrightness: Decrease brightness"));
  210. #endif
  211. currentRemote->packetFormatter->decreaseBrightness();
  212. flushPacket();
  213. }
  214. void MiLightClient::increaseTemperature() {
  215. #ifdef DEBUG_CLIENT_COMMANDS
  216. Serial.println(F("MiLightClient::increaseTemperature: Increase temperature"));
  217. #endif
  218. currentRemote->packetFormatter->increaseTemperature();
  219. flushPacket();
  220. }
  221. void MiLightClient::decreaseTemperature() {
  222. #ifdef DEBUG_CLIENT_COMMANDS
  223. Serial.println(F("MiLightClient::decreaseTemperature: Decrease temperature"));
  224. #endif
  225. currentRemote->packetFormatter->decreaseTemperature();
  226. flushPacket();
  227. }
  228. void MiLightClient::updateTemperature(const uint8_t temperature) {
  229. #ifdef DEBUG_CLIENT_COMMANDS
  230. Serial.printf_P(PSTR("MiLightClient::updateTemperature: Set temperature to %d\n"), temperature);
  231. #endif
  232. currentRemote->packetFormatter->updateTemperature(temperature);
  233. flushPacket();
  234. }
  235. void MiLightClient::command(uint8_t command, uint8_t arg) {
  236. #ifdef DEBUG_CLIENT_COMMANDS
  237. Serial.printf_P(PSTR("MiLightClient::command: Execute command %d, argument %d\n"), command, arg);
  238. #endif
  239. currentRemote->packetFormatter->command(command, arg);
  240. flushPacket();
  241. }
  242. void MiLightClient::toggleStatus() {
  243. #ifdef DEBUG_CLIENT_COMMANDS
  244. Serial.printf_P(PSTR("MiLightClient::toggleStatus"));
  245. #endif
  246. currentRemote->packetFormatter->toggleStatus();
  247. flushPacket();
  248. }
  249. void MiLightClient::updateColor(JsonVariant json) {
  250. ParsedColor color = ParsedColor::fromJson(json);
  251. if (!color.success) {
  252. Serial.println(F("Error parsing color field, unrecognized format"));
  253. return;
  254. }
  255. // We consider an RGB color "white" if all color intensities are roughly the
  256. // same value. An unscientific value of 10 (~4%) is chosen.
  257. if ( abs(color.r - color.g) < RGB_WHITE_THRESHOLD
  258. && abs(color.g - color.b) < RGB_WHITE_THRESHOLD
  259. && abs(color.r - color.b) < RGB_WHITE_THRESHOLD) {
  260. this->updateColorWhite();
  261. } else {
  262. this->updateHue(color.hue);
  263. this->updateSaturation(color.saturation);
  264. }
  265. }
  266. void MiLightClient::update(JsonObject request) {
  267. if (this->updateBeginHandler) {
  268. this->updateBeginHandler();
  269. }
  270. const JsonVariant status = this->extractStatus(request);
  271. const uint8_t parsedStatus = this->parseStatus(status);
  272. const JsonVariant jsonTransition = request[RequestKeys::TRANSITION];
  273. float transition = 0;
  274. if (!jsonTransition.isNull()) {
  275. if (jsonTransition.is<float>()) {
  276. transition = jsonTransition.as<float>();
  277. } else if (jsonTransition.is<size_t>()) {
  278. transition = jsonTransition.as<size_t>();
  279. } else {
  280. Serial.println(F("MiLightClient - WARN: unsupported transition type. Must be float or int."));
  281. }
  282. }
  283. // Always turn on first
  284. if (parsedStatus == ON) {
  285. if (
  286. transition == 0
  287. // Do not generate a transition if a brightness field is also set, since that will also
  288. // generate a transition.
  289. || request.containsKey(GroupStateFieldNames::BRIGHTNESS)
  290. || request.containsKey(GroupStateFieldNames::LEVEL)
  291. ) {
  292. this->updateStatus(ON);
  293. } else {
  294. handleTransition(GroupStateField::STATUS, status, transition);
  295. }
  296. }
  297. for (const char* fieldName : FIELD_ORDERINGS) {
  298. if (request.containsKey(fieldName)) {
  299. auto handler = FIELD_SETTERS.find(fieldName);
  300. JsonVariant value = request[fieldName];
  301. if (handler != FIELD_SETTERS.end()) {
  302. if (transition != 0) {
  303. handleTransition(
  304. GroupStateFieldHelpers::getFieldByName(fieldName),
  305. value,
  306. transition
  307. );
  308. } else {
  309. handler->second(this, value);
  310. }
  311. }
  312. }
  313. }
  314. // Raw packet command/args
  315. if (request.containsKey("button_id") && request.containsKey("argument")) {
  316. this->command(request["button_id"], request["argument"]);
  317. }
  318. // Always turn off last
  319. if (parsedStatus == OFF) {
  320. if (transition == 0) {
  321. this->updateStatus(OFF);
  322. } else {
  323. handleTransition(GroupStateField::STATUS, status, transition);
  324. }
  325. }
  326. if (this->updateEndHandler) {
  327. this->updateEndHandler();
  328. }
  329. }
  330. void MiLightClient::handleCommands(JsonArray commands) {
  331. if (! commands.isNull()) {
  332. for (size_t i = 0; i < commands.size(); i++) {
  333. this->handleCommand(commands[i]);
  334. }
  335. }
  336. }
  337. void MiLightClient::handleCommand(JsonVariant command) {
  338. String cmdName;
  339. JsonObject args;
  340. if (command.is<JsonObject>()) {
  341. JsonObject cmdObj = command.as<JsonObject>();
  342. cmdName = cmdObj[GroupStateFieldNames::COMMAND].as<const char*>();
  343. args = cmdObj["args"];
  344. } else if (command.is<const char*>()) {
  345. cmdName = command.as<const char*>();
  346. }
  347. if (cmdName == MiLightCommandNames::UNPAIR) {
  348. this->unpair();
  349. } else if (cmdName == MiLightCommandNames::PAIR) {
  350. this->pair();
  351. } else if (cmdName == MiLightCommandNames::SET_WHITE) {
  352. this->updateColorWhite();
  353. } else if (cmdName == MiLightCommandNames::NIGHT_MODE) {
  354. this->enableNightMode();
  355. } else if (cmdName == MiLightCommandNames::LEVEL_UP) {
  356. this->increaseBrightness();
  357. } else if (cmdName == MiLightCommandNames::LEVEL_DOWN) {
  358. this->decreaseBrightness();
  359. } else if (cmdName == MiLightCommandNames::TEMPERATURE_UP) {
  360. this->increaseTemperature();
  361. } else if (cmdName == MiLightCommandNames::TEMPERATURE_DOWN) {
  362. this->decreaseTemperature();
  363. } else if (cmdName == MiLightCommandNames::NEXT_MODE) {
  364. this->nextMode();
  365. } else if (cmdName == MiLightCommandNames::PREVIOUS_MODE) {
  366. this->previousMode();
  367. } else if (cmdName == MiLightCommandNames::MODE_SPEED_DOWN) {
  368. this->modeSpeedDown();
  369. } else if (cmdName == MiLightCommandNames::MODE_SPEED_UP) {
  370. this->modeSpeedUp();
  371. } else if (cmdName == MiLightCommandNames::TOGGLE) {
  372. this->toggleStatus();
  373. } else if (cmdName == MiLightCommandNames::TRANSITION) {
  374. StaticJsonDocument<100> fakedoc;
  375. this->handleTransition(args, fakedoc);
  376. }
  377. }
  378. void MiLightClient::handleTransition(GroupStateField field, JsonVariant value, float duration) {
  379. BulbId bulbId = currentRemote->packetFormatter->currentBulbId();
  380. GroupState* currentState = stateStore->get(bulbId);
  381. std::shared_ptr<Transition::Builder> transitionBuilder = nullptr;
  382. if (currentState == nullptr) {
  383. Serial.println(F("Error planning transition: could not find current bulb state."));
  384. return;
  385. }
  386. if (!currentState->isSetField(field)) {
  387. Serial.println(F("Error planning transition: current state for field could not be determined"));
  388. return;
  389. }
  390. if (field == GroupStateField::COLOR) {
  391. ParsedColor currentColor = currentState->getColor();
  392. ParsedColor endColor = ParsedColor::fromJson(value);
  393. transitionBuilder = transitions.buildColorTransition(
  394. bulbId,
  395. currentColor,
  396. endColor
  397. );
  398. } else if (field == GroupStateField::STATUS || field == GroupStateField::STATE) {
  399. uint8_t startLevel = 0;
  400. MiLightStatus status = parseMilightStatus(value);
  401. if (currentState->isSetBrightness()) {
  402. startLevel = currentState->getBrightness();
  403. } else if (status == ON) {
  404. startLevel = 0;
  405. } else {
  406. startLevel = 100;
  407. }
  408. transitionBuilder = transitions.buildStatusTransition(bulbId, parseMilightStatus(value), startLevel);
  409. } else {
  410. uint16_t currentValue = currentState->getParsedFieldValue(field);
  411. uint16_t endValue = value;
  412. transitionBuilder = transitions.buildFieldTransition(
  413. bulbId,
  414. field,
  415. currentValue,
  416. endValue
  417. );
  418. }
  419. if (transitionBuilder == nullptr) {
  420. Serial.printf_P(PSTR("Unsupported transition field: %s\n"), GroupStateFieldHelpers::getFieldName(field));
  421. return;
  422. }
  423. transitionBuilder->setDuration(duration);
  424. transitions.addTransition(transitionBuilder->build());
  425. }
  426. bool MiLightClient::handleTransition(JsonObject args, JsonDocument& responseObj) {
  427. if (! args.containsKey(FS(TransitionParams::FIELD))
  428. || ! args.containsKey(FS(TransitionParams::END_VALUE))) {
  429. responseObj[F("error")] = F("Ignoring transition missing required arguments");
  430. return false;
  431. }
  432. const BulbId& bulbId = currentRemote->packetFormatter->currentBulbId();
  433. const char* fieldName = args[FS(TransitionParams::FIELD)];
  434. const GroupState* groupState = stateStore->get(bulbId);
  435. JsonVariant startValue = args[FS(TransitionParams::START_VALUE)];
  436. JsonVariant endValue = args[FS(TransitionParams::END_VALUE)];
  437. GroupStateField field = GroupStateFieldHelpers::getFieldByName(fieldName);
  438. std::shared_ptr<Transition::Builder> transitionBuilder = nullptr;
  439. if (field == GroupStateField::UNKNOWN) {
  440. char errorMsg[30];
  441. sprintf_P(errorMsg, PSTR("Unknown transition field: %s\n"), fieldName);
  442. responseObj[F("error")] = errorMsg;
  443. return false;
  444. }
  445. // These fields can be transitioned directly.
  446. switch (field) {
  447. case GroupStateField::HUE:
  448. case GroupStateField::SATURATION:
  449. case GroupStateField::BRIGHTNESS:
  450. case GroupStateField::LEVEL:
  451. case GroupStateField::KELVIN:
  452. case GroupStateField::COLOR_TEMP:
  453. transitionBuilder = transitions.buildFieldTransition(
  454. bulbId,
  455. field,
  456. startValue.isUndefined()
  457. ? groupState->getParsedFieldValue(field)
  458. : startValue.as<uint16_t>(),
  459. endValue
  460. );
  461. break;
  462. default:
  463. break;
  464. }
  465. // Color can be decomposed into hue/saturation and these can be transitioned separately
  466. if (field == GroupStateField::COLOR) {
  467. ParsedColor _startValue = startValue.isUndefined()
  468. ? groupState->getColor()
  469. : ParsedColor::fromJson(startValue);
  470. ParsedColor endColor = ParsedColor::fromJson(endValue);
  471. if (! _startValue.success) {
  472. responseObj[F("error")] = F("Transition - error parsing start color");
  473. return false;
  474. }
  475. if (! endColor.success) {
  476. responseObj[F("error")] = F("Transition - error parsing end color");
  477. return false;
  478. }
  479. transitionBuilder = transitions.buildColorTransition(
  480. bulbId,
  481. _startValue,
  482. endColor
  483. );
  484. }
  485. // Status is handled a little differently
  486. if (field == GroupStateField::STATUS || field == GroupStateField::STATE) {
  487. MiLightStatus toStatus = parseMilightStatus(endValue);
  488. uint8_t startLevel;
  489. if (groupState->isSetBrightness()) {
  490. startLevel = groupState->getBrightness();
  491. } else if (toStatus == ON) {
  492. startLevel = 0;
  493. } else {
  494. startLevel = 100;
  495. }
  496. transitionBuilder = transitions.buildStatusTransition(bulbId, toStatus, startLevel);
  497. }
  498. if (transitionBuilder == nullptr) {
  499. char errorMsg[30];
  500. sprintf_P(errorMsg, PSTR("Recognized, but unsupported transition field: %s\n"), fieldName);
  501. responseObj[F("error")] = errorMsg;
  502. return false;
  503. }
  504. if (args.containsKey(FS(TransitionParams::DURATION))) {
  505. transitionBuilder->setDuration(args[FS(TransitionParams::DURATION)]);
  506. }
  507. if (args.containsKey(FS(TransitionParams::PERIOD))) {
  508. transitionBuilder->setPeriod(args[FS(TransitionParams::PERIOD)]);
  509. }
  510. if (args.containsKey(FS(TransitionParams::NUM_PERIODS))) {
  511. transitionBuilder->setNumPeriods(args[FS(TransitionParams::NUM_PERIODS)]);
  512. }
  513. transitions.addTransition(transitionBuilder->build());
  514. return true;
  515. }
  516. void MiLightClient::handleEffect(const String& effect) {
  517. if (effect == MiLightCommandNames::NIGHT_MODE) {
  518. this->enableNightMode();
  519. } else if (effect == "white" || effect == "white_mode") {
  520. this->updateColorWhite();
  521. } else { // assume we're trying to set mode
  522. this->updateMode(effect.toInt());
  523. }
  524. }
  525. JsonVariant MiLightClient::extractStatus(JsonObject object) {
  526. JsonVariant status;
  527. if (object.containsKey(FS(GroupStateFieldNames::STATUS))) {
  528. return object[FS(GroupStateFieldNames::STATUS)];
  529. } else {
  530. return object[FS(GroupStateFieldNames::STATE)];
  531. }
  532. }
  533. uint8_t MiLightClient::parseStatus(JsonVariant val) {
  534. if (val.isUndefined()) {
  535. return 255;
  536. }
  537. return parseMilightStatus(val);
  538. }
  539. void MiLightClient::setRepeatsOverride(size_t repeats) {
  540. this->repeatsOverride = repeats;
  541. }
  542. void MiLightClient::clearRepeatsOverride() {
  543. this->repeatsOverride = PacketSender::DEFAULT_PACKET_SENDS_VALUE;
  544. }
  545. void MiLightClient::flushPacket() {
  546. PacketStream& stream = currentRemote->packetFormatter->buildPackets();
  547. while (stream.hasNext()) {
  548. packetSender.enqueue(stream.next(), currentRemote, repeatsOverride);
  549. }
  550. currentRemote->packetFormatter->reset();
  551. }
  552. void MiLightClient::onUpdateBegin(EventHandler handler) {
  553. this->updateBeginHandler = handler;
  554. }
  555. void MiLightClient::onUpdateEnd(EventHandler handler) {
  556. this->updateEndHandler = handler;
  557. }