MiLightClient.cpp 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  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. numRadios(MiLightRadioConfig::NUM_CONFIGS),
  10. packetSentHandler(NULL)
  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. prepare(config.type, deviceId, groupId);
  52. }
  53. void MiLightClient::prepare(MiLightRadioType type,
  54. const uint16_t deviceId,
  55. const uint8_t groupId) {
  56. switchRadio(type);
  57. if (deviceId >= 0 && groupId >= 0) {
  58. formatter->prepare(deviceId, groupId);
  59. }
  60. }
  61. void MiLightClient::setResendCount(const unsigned int resendCount) {
  62. this->resendCount = resendCount;
  63. }
  64. bool MiLightClient::available() {
  65. if (currentRadio == NULL) {
  66. return false;
  67. }
  68. return currentRadio->available();
  69. }
  70. void MiLightClient::read(uint8_t packet[]) {
  71. if (currentRadio == NULL) {
  72. return;
  73. }
  74. size_t length = currentRadio->config().getPacketLength();
  75. currentRadio->read(packet, length);
  76. }
  77. void MiLightClient::write(uint8_t packet[]) {
  78. if (currentRadio == NULL) {
  79. return;
  80. }
  81. #ifdef DEBUG_PRINTF
  82. printf("Sending packet: ");
  83. for (int i = 0; i < currentRadio->config().getPacketLength(); i++) {
  84. printf("%02X", packet[i]);
  85. }
  86. printf("\n");
  87. int iStart = millis();
  88. #endif
  89. for (int i = 0; i < this->resendCount; i++) {
  90. currentRadio->write(packet, currentRadio->config().getPacketLength());
  91. }
  92. if (this->packetSentHandler) {
  93. this->packetSentHandler(packet, currentRadio->config());
  94. }
  95. #ifdef DEBUG_PRINTF
  96. int iElapsed = millis() - iStart;
  97. Serial.print("Elapsed: ");
  98. Serial.println(iElapsed);
  99. #endif
  100. }
  101. void MiLightClient::updateColorRaw(const uint8_t color) {
  102. formatter->updateColorRaw(color);
  103. flushPacket();
  104. }
  105. void MiLightClient::updateHue(const uint16_t hue) {
  106. formatter->updateHue(hue);
  107. flushPacket();
  108. }
  109. void MiLightClient::updateBrightness(const uint8_t brightness) {
  110. formatter->updateBrightness(brightness);
  111. flushPacket();
  112. }
  113. void MiLightClient::updateMode(uint8_t mode) {
  114. formatter->updateMode(mode);
  115. flushPacket();
  116. }
  117. void MiLightClient::nextMode() {
  118. formatter->nextMode();
  119. flushPacket();
  120. }
  121. void MiLightClient::previousMode() {
  122. formatter->previousMode();
  123. flushPacket();
  124. }
  125. void MiLightClient::modeSpeedDown() {
  126. formatter->modeSpeedDown();
  127. flushPacket();
  128. }
  129. void MiLightClient::modeSpeedUp() {
  130. formatter->modeSpeedUp();
  131. flushPacket();
  132. }
  133. void MiLightClient::updateStatus(MiLightStatus status, uint8_t groupId) {
  134. formatter->updateStatus(status, groupId);
  135. flushPacket();
  136. }
  137. void MiLightClient::updateStatus(MiLightStatus status) {
  138. formatter->updateStatus(status);
  139. flushPacket();
  140. }
  141. void MiLightClient::updateSaturation(const uint8_t value) {
  142. formatter->updateSaturation(value);
  143. flushPacket();
  144. }
  145. void MiLightClient::updateColorWhite() {
  146. formatter->updateColorWhite();
  147. flushPacket();
  148. }
  149. void MiLightClient::enableNightMode() {
  150. formatter->enableNightMode();
  151. flushPacket();
  152. }
  153. void MiLightClient::pair() {
  154. formatter->pair();
  155. flushPacket();
  156. }
  157. void MiLightClient::unpair() {
  158. formatter->unpair();
  159. flushPacket();
  160. }
  161. void MiLightClient::increaseBrightness() {
  162. formatter->increaseBrightness();
  163. flushPacket();
  164. }
  165. void MiLightClient::decreaseBrightness() {
  166. formatter->decreaseBrightness();
  167. flushPacket();
  168. }
  169. void MiLightClient::increaseTemperature() {
  170. formatter->increaseTemperature();
  171. flushPacket();
  172. }
  173. void MiLightClient::decreaseTemperature() {
  174. formatter->decreaseTemperature();
  175. flushPacket();
  176. }
  177. void MiLightClient::updateTemperature(const uint8_t temperature) {
  178. formatter->updateTemperature(temperature);
  179. flushPacket();
  180. }
  181. void MiLightClient::command(uint8_t command, uint8_t arg) {
  182. formatter->command(command, arg);
  183. flushPacket();
  184. }
  185. void MiLightClient::update(const JsonObject& request) {
  186. const uint8_t parsedStatus = this->parseStatus(request);
  187. // Always turn on first
  188. if (parsedStatus == ON) {
  189. this->updateStatus(ON);
  190. }
  191. if (request.containsKey("command")) {
  192. this->handleCommand(request["command"]);
  193. }
  194. if (request.containsKey("commands")) {
  195. JsonArray& commands = request["commands"];
  196. if (commands.success()) {
  197. for (size_t i = 0; i < commands.size(); i++) {
  198. this->handleCommand(commands.get<String>(i));
  199. }
  200. }
  201. }
  202. if (request.containsKey("hue")) {
  203. this->updateHue(request["hue"]);
  204. }
  205. if (request.containsKey("saturation")) {
  206. this->updateSaturation(request["saturation"]);
  207. }
  208. // Convert RGB to HSV
  209. if (request.containsKey("color")) {
  210. JsonObject& color = request["color"];
  211. uint8_t r = color["r"];
  212. uint8_t g = color["g"];
  213. uint8_t b = color["b"];
  214. double hsv[3];
  215. RGBConverter converter;
  216. converter.rgbToHsv(r, g, b, hsv);
  217. uint16_t hue = round(hsv[0]*360);
  218. uint8_t saturation = round(hsv[1]*100);
  219. this->updateHue(hue);
  220. this->updateSaturation(saturation);
  221. }
  222. if (request.containsKey("level")) {
  223. this->updateBrightness(request["level"]);
  224. }
  225. // HomeAssistant
  226. if (request.containsKey("brightness")) {
  227. uint8_t scaledBrightness = Units::rescale(request.get<uint8_t>("brightness"), 100, 255);
  228. this->updateBrightness(scaledBrightness);
  229. }
  230. if (request.containsKey("temperature")) {
  231. this->updateTemperature(request["temperature"]);
  232. }
  233. // HomeAssistant
  234. if (request.containsKey("color_temp")) {
  235. this->updateTemperature(
  236. Units::miredsToWhiteVal(request["color_temp"], 100)
  237. );
  238. }
  239. //Homeassistant - Switch to white if white value is set
  240. if (request.containsKey("white_value")) {
  241. this->updateColorWhite();
  242. }
  243. if (request.containsKey("mode")) {
  244. this->updateMode(request["mode"]);
  245. }
  246. // Always turn off last
  247. if (parsedStatus == OFF) {
  248. this->updateStatus(OFF);
  249. }
  250. }
  251. void MiLightClient::handleCommand(const String& command) {
  252. if (command == "unpair") {
  253. this->unpair();
  254. } else if (command == "pair") {
  255. this->pair();
  256. } else if (command == "set_white") {
  257. this->updateColorWhite();
  258. } else if (command == "night_mode") {
  259. this->enableNightMode();
  260. } else if (command == "level_up") {
  261. this->increaseBrightness();
  262. } else if (command == "level_down") {
  263. this->decreaseBrightness();
  264. } else if (command == "temperature_up") {
  265. this->increaseTemperature();
  266. } else if (command == "temperature_down") {
  267. this->decreaseTemperature();
  268. } else if (command == "next_mode") {
  269. this->nextMode();
  270. } else if (command == "previous_mode") {
  271. this->previousMode();
  272. } else if (command == "mode_speed_down") {
  273. this->modeSpeedDown();
  274. } else if (command == "mode_speed_up") {
  275. this->modeSpeedUp();
  276. }
  277. }
  278. uint8_t MiLightClient::parseStatus(const JsonObject& object) {
  279. String strStatus;
  280. if (object.containsKey("status")) {
  281. strStatus = object.get<char*>("status");
  282. } else if (object.containsKey("state")) {
  283. strStatus = object.get<char*>("state");
  284. } else {
  285. return 255;
  286. }
  287. return (strStatus.equalsIgnoreCase("on") || strStatus.equalsIgnoreCase("true")) ? ON : OFF;
  288. }
  289. void MiLightClient::formatPacket(uint8_t* packet, char* buffer) {
  290. formatter->format(packet, buffer);
  291. }
  292. void MiLightClient::flushPacket() {
  293. PacketStream& stream = formatter->buildPackets();
  294. const size_t prevNumRepeats = this->resendCount;
  295. // When sending multiple packets, normalize the number of repeats
  296. if (stream.numPackets > 1) {
  297. setResendCount(MILIGHT_DEFAULT_RESEND_COUNT);
  298. }
  299. while (stream.hasNext()) {
  300. write(stream.next());
  301. if (stream.hasNext()) {
  302. delay(10);
  303. }
  304. }
  305. setResendCount(prevNumRepeats);
  306. formatter->reset();
  307. }
  308. void MiLightClient::onPacketSent(PacketSentHandler handler) {
  309. this->packetSentHandler = handler;
  310. }