Fork of the espurna firmware for `mhsw` switches
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

376 lines
9.1 KiB

  1. //
  2. //
  3. //
  4. #include "SSDPDevice.h"
  5. #include "lwip/igmp.h"
  6. SSDPDeviceClass::SSDPDeviceClass() :
  7. m_server(0),
  8. m_port(80),
  9. m_ttl(SSDP_MULTICAST_TTL)
  10. {
  11. m_uuid[0] = '\0';
  12. m_modelNumber[0] = '\0';
  13. sprintf(m_deviceType, "urn:schemas-upnp-org:device:Basic:1");
  14. m_friendlyName[0] = '\0';
  15. m_presentationURL[0] = '\0';
  16. m_serialNumber[0] = '\0';
  17. m_modelName[0] = '\0';
  18. m_modelURL[0] = '\0';
  19. m_manufacturer[0] = '\0';
  20. m_manufacturerURL[0] = '\0';
  21. sprintf(m_schemaURL, "ssdp/schema.xml");
  22. uint32_t chipId = ESP.getChipId();
  23. sprintf(m_uuid, "38323636-4558-4dda-9188-cda0e6%02x%02x%02x",
  24. (uint16_t)((chipId >> 16) & 0xff),
  25. (uint16_t)((chipId >> 8) & 0xff),
  26. (uint16_t)chipId & 0xff);
  27. for (int i = 0; i < SSDP_QUEUE_SIZE; i++) {
  28. m_queue[i].time = 0;
  29. }
  30. }
  31. void SSDPDeviceClass::update() {
  32. postNotifyUpdate();
  33. }
  34. bool SSDPDeviceClass::readLine(String &value) {
  35. char buffer[65];
  36. int bufferPos = 0;
  37. while (1) {
  38. int c = m_server->read();
  39. if (c < 0) {
  40. buffer[bufferPos] = '\0';
  41. break;
  42. }
  43. if (c == '\r' && m_server->peek() == '\n') {
  44. m_server->read();
  45. buffer[bufferPos] = '\0';
  46. break;
  47. }
  48. if (bufferPos < 64) {
  49. buffer[bufferPos++] = c;
  50. }
  51. }
  52. value = String(buffer);
  53. return bufferPos > 0;
  54. }
  55. bool SSDPDeviceClass::readKeyValue(String &key, String &value) {
  56. char buffer[65];
  57. int bufferPos = 0;
  58. while (1) {
  59. int c = m_server->read();
  60. if (c < 0) {
  61. if (bufferPos == 0) return false;
  62. buffer[bufferPos] = '\0';
  63. break;
  64. }
  65. if (c == ':') {
  66. buffer[bufferPos] = '\0';
  67. while (m_server->peek() == ' ') m_server->read();
  68. break;
  69. }
  70. else if (c == '\r' && m_server->peek() == '\n') {
  71. m_server->read();
  72. if (bufferPos == 0) return false;
  73. buffer[bufferPos] = '\0';
  74. key = String();
  75. value = String(buffer);
  76. return true;
  77. }
  78. if (bufferPos < 64) {
  79. buffer[bufferPos++] = c;
  80. }
  81. }
  82. key = String(buffer);
  83. readLine(value);
  84. return true;
  85. }
  86. void SSDPDeviceClass::postNotifyALive() {
  87. unsigned long time = millis();
  88. post(NOTIFY_ALIVE_INIT, ROOT_FOR_ALL, SSDP_MULTICAST_ADDR, SSDP_PORT, time + 10);
  89. post(NOTIFY_ALIVE_INIT, ROOT_BY_UUID, SSDP_MULTICAST_ADDR, SSDP_PORT, time + 55);
  90. post(NOTIFY_ALIVE_INIT, ROOT_BY_TYPE, SSDP_MULTICAST_ADDR, SSDP_PORT, time + 80);
  91. post(NOTIFY_ALIVE_INIT, ROOT_FOR_ALL, SSDP_MULTICAST_ADDR, SSDP_PORT, time + 210);
  92. post(NOTIFY_ALIVE_INIT, ROOT_BY_UUID, SSDP_MULTICAST_ADDR, SSDP_PORT, time + 255);
  93. post(NOTIFY_ALIVE_INIT, ROOT_BY_TYPE, SSDP_MULTICAST_ADDR, SSDP_PORT, time + 280);
  94. post(NOTIFY_ALIVE, ROOT_FOR_ALL, SSDP_MULTICAST_ADDR, SSDP_PORT, time + 610);
  95. post(NOTIFY_ALIVE, ROOT_BY_UUID, SSDP_MULTICAST_ADDR, SSDP_PORT, time + 655);
  96. post(NOTIFY_ALIVE, ROOT_BY_TYPE, SSDP_MULTICAST_ADDR, SSDP_PORT, time + 680);
  97. }
  98. void SSDPDeviceClass::postNotifyUpdate() {
  99. unsigned long time = millis();
  100. post(NOTIFY_UPDATE, ROOT_FOR_ALL, SSDP_MULTICAST_ADDR, SSDP_PORT, time + 10);
  101. post(NOTIFY_UPDATE, ROOT_BY_UUID, SSDP_MULTICAST_ADDR, SSDP_PORT, time + 55);
  102. post(NOTIFY_UPDATE, ROOT_BY_TYPE, SSDP_MULTICAST_ADDR, SSDP_PORT, time + 80);
  103. }
  104. void SSDPDeviceClass::postResponse(long mx) {
  105. unsigned long time = millis();
  106. unsigned long delay = random(0, mx) * 900L; // 1000 ms - 100 ms
  107. IPAddress address = m_server->remoteIP();
  108. uint16_t port = m_server->remotePort();
  109. post(RESPONSE, ROOT_FOR_ALL, address, port, time + delay / 3);
  110. post(RESPONSE, ROOT_BY_UUID, address, port, time + delay / 3 * 2);
  111. post(RESPONSE, ROOT_BY_TYPE, address, port, time + delay);
  112. }
  113. void SSDPDeviceClass::postResponse(ssdp_udn_t udn, long mx) {
  114. post(RESPONSE, udn, m_server->remoteIP(), m_server->remotePort(), millis() + random(0, mx) * 900L); // 1000 ms - 100 ms
  115. }
  116. void SSDPDeviceClass::post(ssdp_message_t type, ssdp_udn_t udn, IPAddress address, uint16_t port, unsigned long time) {
  117. for (int i = 0; i < SSDP_QUEUE_SIZE; i++) {
  118. if (m_queue[i].time == 0) {
  119. m_queue[i].type = type;
  120. m_queue[i].udn = udn;
  121. m_queue[i].address = address;
  122. m_queue[i].port = port;
  123. m_queue[i].time = time;
  124. break;
  125. }
  126. }
  127. }
  128. void SSDPDeviceClass::send(ssdp_send_parameters_t *parameters) {
  129. char buffer[1460];
  130. unsigned int ip = WiFi.localIP();
  131. const char *typeTemplate;
  132. const char *uri, *usn1, *usn2, *usn3;
  133. switch (parameters->type) {
  134. case NOTIFY_ALIVE_INIT:
  135. case NOTIFY_ALIVE:
  136. typeTemplate = SSDP_NOTIFY_ALIVE_TEMPLATE;
  137. break;
  138. case NOTIFY_UPDATE:
  139. typeTemplate = SSDP_NOTIFY_UPDATE_TEMPLATE;
  140. break;
  141. default: // RESPONSE
  142. typeTemplate = SSDP_RESPONSE_TEMPLATE;
  143. break;
  144. }
  145. String uuid = "uuid:" + String(m_uuid);
  146. switch (parameters->udn) {
  147. case ROOT_FOR_ALL:
  148. uri = "upnp:rootdevice";
  149. usn1 = uuid.c_str();
  150. usn2 = "::";
  151. usn3 = "upnp:rootdevice";
  152. break;
  153. case ROOT_BY_UUID:
  154. uri = uuid.c_str();
  155. usn1 = uuid.c_str();
  156. usn2 = "";
  157. usn3 = "";
  158. break;
  159. case ROOT_BY_TYPE:
  160. uri = m_deviceType;
  161. usn1 = uuid.c_str();
  162. usn2 = "::";
  163. usn3 = m_deviceType;
  164. break;
  165. }
  166. int len = snprintf_P(buffer, sizeof(buffer),
  167. SSDP_PACKET_TEMPLATE, typeTemplate,
  168. SSDP_INTERVAL, m_modelName, m_modelNumber, usn1, usn2, usn3, parameters->type == RESPONSE ? "ST" : "NT", uri,
  169. IP2STR(&ip), m_port, m_schemaURL
  170. );
  171. if (parameters->address == SSDP_MULTICAST_ADDR) {
  172. m_server->beginPacketMulticast(parameters->address, parameters->port, m_ttl);
  173. }
  174. else {
  175. m_server->beginPacket(parameters->address, parameters->port);
  176. }
  177. m_server->write(buffer, len);
  178. m_server->endPacket();
  179. parameters->time = parameters->type == NOTIFY_ALIVE ? parameters->time + SSDP_INTERVAL * 900L : 0; // 1000 ms - 100 ms
  180. }
  181. String SSDPDeviceClass::schema() {
  182. char buffer[1024];
  183. uint32_t ip = WiFi.localIP();
  184. snprintf(buffer, sizeof(buffer), SSDP_SCHEMA_TEMPLATE,
  185. IP2STR(&ip), m_port, m_schemaURL,
  186. m_deviceType,
  187. m_friendlyName,
  188. m_presentationURL,
  189. m_serialNumber,
  190. m_modelName,
  191. m_modelNumber,
  192. m_modelURL,
  193. m_manufacturer,
  194. m_manufacturerURL,
  195. m_uuid
  196. );
  197. return String(buffer);
  198. }
  199. void SSDPDeviceClass::handleClient() {
  200. IPAddress current = WiFi.localIP();
  201. if (m_last != current) {
  202. m_last = current;
  203. for (int i = 0; i < SSDP_QUEUE_SIZE; i++) {
  204. m_queue[i].time = 0;
  205. }
  206. if (current != INADDR_NONE) {
  207. if (!m_server) m_server = new WiFiUDP();
  208. m_server->beginMulticast(current, SSDP_MULTICAST_ADDR, SSDP_PORT);
  209. postNotifyALive();
  210. }
  211. else if (m_server) {
  212. m_server->stop();
  213. }
  214. }
  215. if (m_server && m_server->parsePacket()) {
  216. String value;
  217. if (readLine(value) && value.equalsIgnoreCase("M-SEARCH * HTTP/1.1")) {
  218. String key, st;
  219. bool host = false, man = false;
  220. long mx = 0;
  221. while (readKeyValue(key, value)) {
  222. if (key.equalsIgnoreCase("HOST") && value.equals("239.255.255.250:1900")) {
  223. host = true;
  224. }
  225. else if (key.equalsIgnoreCase("MAN") && value.equals("\"ssdp:discover\"")) {
  226. man = true;
  227. }
  228. else if (key.equalsIgnoreCase("ST")) {
  229. st = value;
  230. }
  231. else if (key.equalsIgnoreCase("MX")) {
  232. mx = value.toInt();
  233. }
  234. }
  235. if (host && man && mx > 0) {
  236. if (st.equals("ssdp:all")) {
  237. postResponse(mx);
  238. }
  239. else if (st.equals("upnp:rootdevice")) {
  240. postResponse(ROOT_FOR_ALL, mx);
  241. }
  242. else if (st.equals("uuid:" + String(m_uuid))) {
  243. postResponse(ROOT_BY_UUID, mx);
  244. }
  245. else if (st.equals(m_deviceType)) {
  246. postResponse(ROOT_BY_TYPE, mx);
  247. }
  248. }
  249. }
  250. m_server->flush();
  251. }
  252. else {
  253. unsigned long time = millis();
  254. for (int i = 0; i < SSDP_QUEUE_SIZE; i++) {
  255. if (m_queue[i].time > 0 && m_queue[i].time < time) {
  256. send(&m_queue[i]);
  257. }
  258. }
  259. }
  260. }
  261. void SSDPDeviceClass::setSchemaURL(const char *url) {
  262. strlcpy(m_schemaURL, url, sizeof(m_schemaURL));
  263. }
  264. void SSDPDeviceClass::setHTTPPort(uint16_t port) {
  265. m_port = port;
  266. }
  267. void SSDPDeviceClass::setDeviceType(const char *deviceType) {
  268. strlcpy(m_deviceType, deviceType, sizeof(m_deviceType));
  269. }
  270. void SSDPDeviceClass::setName(const char *name) {
  271. strlcpy(m_friendlyName, name, sizeof(m_friendlyName));
  272. }
  273. void SSDPDeviceClass::setURL(const char *url) {
  274. strlcpy(m_presentationURL, url, sizeof(m_presentationURL));
  275. }
  276. void SSDPDeviceClass::setSerialNumber(const char *serialNumber) {
  277. strlcpy(m_serialNumber, serialNumber, sizeof(m_serialNumber));
  278. }
  279. void SSDPDeviceClass::setSerialNumber(const uint32_t serialNumber) {
  280. snprintf(m_serialNumber, sizeof(uint32_t) * 2 + 1, "%08X", serialNumber);
  281. }
  282. void SSDPDeviceClass::setModelName(const char *name) {
  283. strlcpy(m_modelName, name, sizeof(m_modelName));
  284. }
  285. void SSDPDeviceClass::setModelNumber(const char *num) {
  286. strlcpy(m_modelNumber, num, sizeof(m_modelNumber));
  287. }
  288. void SSDPDeviceClass::setModelURL(const char *url) {
  289. strlcpy(m_modelURL, url, sizeof(m_modelURL));
  290. }
  291. void SSDPDeviceClass::setManufacturer(const char *name) {
  292. strlcpy(m_manufacturer, name, sizeof(m_manufacturer));
  293. }
  294. void SSDPDeviceClass::setManufacturerURL(const char *url) {
  295. strlcpy(m_manufacturerURL, url, sizeof(m_manufacturerURL));
  296. }
  297. void SSDPDeviceClass::setTTL(const uint8_t ttl) {
  298. m_ttl = ttl;
  299. }
  300. SSDPDeviceClass SSDPDevice;