MiLightClient.cpp 19 KB

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