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