MiLightClient.cpp 9.7 KB

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