V6MiLightUdpServer.cpp 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. #include <V6MiLightUdpServer.h>
  2. #include <ESP8266WiFi.h>
  3. #include <Arduino.h>
  4. uint8_t V6MiLightUdpServer::START_SESSION_COMMAND[] = {
  5. 0x20, 0x00, 0x00, 0x00, 0x16, 0x02, 0x62, 0x3A, 0xD5, 0xED, 0xA3, 0x01, 0xAE,
  6. 0x08, 0x2D, 0x46, 0x61, 0x41, 0xA7, 0xF6, 0xDC, 0xAF, 0xD3, 0xE6, 0x00, 0x00,
  7. 0x1E
  8. };
  9. uint8_t V6MiLightUdpServer::START_SESSION_RESPONSE[] = {
  10. 0x28, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02,
  11. 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // should be replaced with hw addr
  12. 0x69, 0xF0, 0x3C, 0x23, 0x00, 0x01,
  13. 0xFF, 0xFF, // should be replaced with a session ID
  14. 0x00
  15. };
  16. uint8_t V6MiLightUdpServer::COMMAND_HEADER[] = {
  17. 0x80, 0x00, 0x00, 0x00
  18. };
  19. uint8_t V6MiLightUdpServer::HEARTBEAT_HEADER[] = {
  20. 0xD0, 0x00, 0x00, 0x00, 0x02
  21. };
  22. uint8_t V6MiLightUdpServer::COMMAND_RESPONSE[] = {
  23. 0x88, 0x00, 0x00, 0x00, 0x03, 0x00, 0xFF, 0x00
  24. };
  25. template<typename T, size_t sz>
  26. size_t size(T(&)[sz]) {
  27. return sz;
  28. }
  29. V6MiLightUdpServer::~V6MiLightUdpServer() {
  30. V6Session* cur = firstSession;
  31. while (cur != NULL) {
  32. V6Session* next = cur->next;
  33. delete cur;
  34. cur = next;
  35. }
  36. }
  37. template <typename T>
  38. T V6MiLightUdpServer::readInt(uint8_t* packet) {
  39. size_t numBytes = sizeof(T);
  40. T value = 0;
  41. for (size_t i = 0; i < numBytes; i++) {
  42. value |= packet[i] << (8 * (numBytes - i - 1));
  43. }
  44. return value;
  45. }
  46. template <typename T>
  47. uint8_t* V6MiLightUdpServer::writeInt(const T& value, uint8_t* packet) {
  48. size_t numBytes = sizeof(T);
  49. for (size_t i = 0; i < numBytes; i++) {
  50. packet[i] = (value >> (8 * (numBytes - i - 1))) & 0xFF;
  51. }
  52. return packet + numBytes;
  53. }
  54. uint16_t V6MiLightUdpServer::beginSession() {
  55. const uint16_t id = sessionId++;
  56. V6Session* session = new V6Session(socket.remoteIP(), socket.remotePort(), id);
  57. session->next = firstSession;
  58. firstSession = session;
  59. if (numSessions >= V6_MAX_SESSIONS) {
  60. V6Session* cur = firstSession;
  61. for (size_t i = 1; i < V6_MAX_SESSIONS; i++) {
  62. cur = cur->next;
  63. }
  64. delete cur->next;
  65. cur->next = NULL;
  66. } else {
  67. numSessions++;
  68. }
  69. return id;
  70. }
  71. void V6MiLightUdpServer::handleStartSession() {
  72. size_t len = size(START_SESSION_RESPONSE);
  73. uint8_t response[len];
  74. uint16_t sessionId = beginSession();
  75. memcpy(response, START_SESSION_RESPONSE, len);
  76. WiFi.macAddress(response + 7);
  77. response[19] = sessionId >> 8;
  78. response[20] = sessionId & 0xFF;
  79. sendResponse(sessionId, response, len);
  80. }
  81. void V6MiLightUdpServer::sendResponse(uint16_t sessionId, uint8_t* responseBuffer, size_t responseSize) {
  82. V6Session* session = firstSession;
  83. while (session != NULL) {
  84. if (session->sessionId == sessionId) {
  85. break;
  86. }
  87. session = session->next;
  88. }
  89. if (session == NULL || session->sessionId != sessionId) {
  90. Serial.print("Tried to send response to untracked session id: ");
  91. Serial.println(sessionId);
  92. return;
  93. }
  94. #ifdef MILIGHT_UDP_DEBUG
  95. printf("Sending response to %s:%d\n", session->ipAddr.toString().c_str(), session->port);
  96. #endif
  97. socket.beginPacket(session->ipAddr, session->port);
  98. socket.write(responseBuffer, responseSize);
  99. socket.endPacket();
  100. }
  101. bool V6MiLightUdpServer::handleV1BulbCommand(uint8_t group, uint32_t _cmd, uint32_t _arg) {
  102. }
  103. bool V6MiLightUdpServer::handleV2BulbCommand(uint8_t group, uint32_t _cmd, uint32_t _arg) {
  104. const uint8_t cmd = _cmd & 0xFF;
  105. const uint8_t arg = _arg >> 24;
  106. client->prepare(MilightRgbCctConfig, deviceId, group);
  107. switch (cmd) {
  108. case V2_STATUS:
  109. if (arg == 0x01) {
  110. client->updateStatus(ON);
  111. } else if (arg == 0x02) {
  112. client->updateStatus(OFF);
  113. } else if (arg == 0x05) {
  114. client->updateBrightness(0);
  115. }
  116. break;
  117. case V2_COLOR:
  118. client->updateColorRaw(arg);
  119. break;
  120. case V2_KELVIN:
  121. client->updateTemperature(arg);
  122. break;
  123. case V2_BRIGHTNESS:
  124. client->updateBrightness(arg);
  125. break;
  126. case V2_SATURATION:
  127. client->updateSaturation(arg);
  128. break;
  129. default:
  130. return false;
  131. }
  132. return true;
  133. }
  134. void V6MiLightUdpServer::handleCommand(
  135. uint16_t sessionId,
  136. uint8_t sequenceNum,
  137. uint8_t* cmd,
  138. uint8_t group,
  139. uint8_t checksum
  140. ) {
  141. uint8_t cmdType = readInt<uint8_t>(cmd);
  142. uint32_t cmdHeader = readInt<uint32_t>(cmd+1);
  143. uint32_t cmdArg = readInt<uint32_t>(cmd+5);
  144. #ifdef MILIGHT_UDP_DEBUG
  145. printf("Command type: %02X, command: %08X, arg: %08X\n", cmdType, cmdHeader, cmdArg);
  146. #endif
  147. bool handled = false;
  148. if ((cmdHeader & 0x0800) == 0x0800) {
  149. handled = handleV2BulbCommand(group, cmdHeader, cmdArg);
  150. } else if ((cmdHeader & 0x0700) == 0x0700) {
  151. handled = handleV1BulbCommand(group, cmdHeader, cmdArg);
  152. }
  153. if (handled) {
  154. size_t len = size(COMMAND_RESPONSE);
  155. memcpy(responseBuffer, COMMAND_RESPONSE, len);
  156. responseBuffer[6] = sequenceNum;
  157. sendResponse(sessionId, responseBuffer, len);
  158. return;
  159. }
  160. #ifdef MILIGHT_UDP_DEBUG
  161. printf("V6MiLightUdpServer - Unhandled command: ");
  162. for (size_t i = 0; i < V6_COMMAND_LEN; i++) {
  163. printf("%02X ", cmd[i]);
  164. }
  165. printf("\n");
  166. #endif
  167. }
  168. void V6MiLightUdpServer::handleHeartbeat(uint16_t sessionId) {
  169. char header[] = { 0xD0, 0x00, 0x00, 0x00, 0x07 };
  170. memcpy(responseBuffer, header, size(header));
  171. WiFi.macAddress(responseBuffer+5);
  172. responseBuffer[11] = 0;
  173. sendResponse(sessionId, responseBuffer, 12);
  174. }
  175. void V6MiLightUdpServer::handlePacket(uint8_t* packet, size_t packetSize) {
  176. printf("Packet size: %d\n", packetSize);
  177. if (packetSize >= size(START_SESSION_COMMAND) && memcmp(START_SESSION_COMMAND, packet, size(START_SESSION_COMMAND)) == 0) {
  178. handleStartSession();
  179. } else if (packetSize >= size(HEARTBEAT_HEADER) && memcmp(HEARTBEAT_HEADER, packet, size(HEARTBEAT_HEADER)) == 0) {
  180. uint16_t sessionId = readInt<uint16_t>(packet+5);
  181. handleHeartbeat(sessionId);
  182. } else if (packetSize == 22 && memcmp(COMMAND_HEADER, packet, size(COMMAND_HEADER)) == 0) {
  183. uint16_t sessionId = readInt<uint16_t>(packet+5);
  184. uint8_t sequenceNum = packet[8];
  185. uint8_t* cmd = packet+10;
  186. uint8_t group = packet[19];
  187. uint8_t checksum = packet[21];
  188. #ifdef MILIGHT_UDP_DEBUG
  189. printf("session: %04X, sequence: %d, group: %d, checksum: %d\n", sessionId, sequenceNum, group, checksum);
  190. #endif
  191. handleCommand(sessionId, sequenceNum, cmd, group, checksum);
  192. } else {
  193. Serial.println("Unhandled V6 packet");
  194. }
  195. }