diff --git a/ConfigFile.h b/ConfigFile.h index 12ec5ae..e95668f 100644 --- a/ConfigFile.h +++ b/ConfigFile.h @@ -83,7 +83,7 @@ class ShadeConfigFile : public ConfigFile { static bool restore(SomfyShadeController *somfy, const char *filename, restore_options_t &opts); bool begin(const char *filename, bool readOnly = false); bool begin(bool readOnly = false); - bool save(SomfyShadeController *sofmy); + bool save(SomfyShadeController *somfy); bool backup(SomfyShadeController *somfy); bool loadFile(SomfyShadeController *somfy, const char *filename = "/shades.cfg"); bool restoreFile(SomfyShadeController *somfy, const char *filename, restore_options_t &opts); diff --git a/Network.cpp b/Network.cpp index fb38a69..76e1223 100644 --- a/Network.cpp +++ b/Network.cpp @@ -63,7 +63,7 @@ void Network::loop() { } if(settings.ssdpBroadcast) { if(!SSDP.isStarted) SSDP.begin(); - SSDP.loop(); + if(SSDP.isStarted) SSDP.loop(); } else if(!settings.ssdpBroadcast && SSDP.isStarted) SSDP.end(); mqtt.loop(); @@ -187,11 +187,17 @@ void Network::setConnected(conn_types connType) { //SSDP.setSerialNumber(0, "C2496952-5610-47E6-A968-2FC19737A0DB"); //SSDP.setUUID(0, settings.uuid); SSDP.setModelName(0, "ESPSomfy RTS"); - SSDP.setModelNumber(0, "SS v1"); + if(strlen(settings.chipModel) == 0) SSDP.setModelNumber(0, "ESP32"); + else { + char sModel[20] = ""; + snprintf(sModel, sizeof(sModel), "ESP32-%S", settings.chipModel); + SSDP.setModelNumber(0, sModel); + } SSDP.setModelURL(0, "https://github.com/rstrouse/ESPSomfy-RTS"); SSDP.setManufacturer(0, "rstrouse"); SSDP.setManufacturerURL(0, "https://github.com/rstrouse"); SSDP.setURL(0, "/"); + SSDP.setActive(0, true); if(MDNS.begin(settings.hostname)) { Serial.printf("MDNS Responder Started: serverId=%s\n", settings.serverId); //MDNS.addService("http", "tcp", 80); @@ -204,7 +210,7 @@ void Network::setConnected(conn_types connType) { MDNS.addServiceTxt("espsomfy_rts", "tcp", "version", String(settings.fwVersion.name)); } if(settings.ssdpBroadcast) { - if(SSDP.begin()) Serial.println("SSDP Client Started..."); + SSDP.begin(); } else if(SSDP.isStarted) SSDP.end(); this->emitSockets(); diff --git a/SSDP.cpp b/SSDP.cpp index 5680939..7e45612 100644 --- a/SSDP.cpp +++ b/SSDP.cpp @@ -10,12 +10,15 @@ #define SSDP_URI_SIZE 2 #define SSDP_BUFFER_SIZE 64 #define SSDP_MULTICAST_ADDR 239, 255, 255, 250 +//#define DEBUG_SSDP Serial +//#define DEBUG_SSDP_PACKET Serial extern ConfigSettings settings; -static const char _ssdp_uuid_template[] PROGMEM = "C2496952-5610-47E6-A968-2FC1%02x%02x%02x%02x"; +static const char _ssdp_uuid_template[] PROGMEM = "C2496952-5610-47E6-A968-2FC1%02X%02X%02X%02X"; static const char _ssdp_serial_number_template[] PROGMEM = "ESP32-%02x%02x%02x"; -static const char _ssdp_usn_root_template[] PROGMEM = "%s::upnp::rootdevice"; -static const char _ssdp_usn_uuid_template[] PROGMEM = "%s::%s"; +static const char _ssdp_usn_root_template[] PROGMEM = "uuid:%s::upnp:rootdevice"; +static const char _ssdp_usn_uuid_template[] PROGMEM = "uuid:%s"; +static const char _ssdp_usn_urn_template[] PROGMEM = "uuid:%s::%s"; static const char _ssdp_response_template[] PROGMEM = "HTTP/1.1 200 OK\r\n" "EXT:\r\n"; @@ -26,14 +29,21 @@ static const char _ssdp_notify_template[] PROGMEM = static const char _ssdp_bye_template[] PROGMEM = "NOTIFY * HTTP/1.1\r\n" "HOST: 239.255.255.250:1900\r\n" - "NTS: ssdp:byebye\r\n"; + "NTS: ssdp:byebye\r\n" + "NT: %s\r\n" + "USN: %s\r\n" + "BOOTID.UPNP.ORG: %ul\r\n" + "CONFIGID.UPNP.ORG: %d\r\n" + "\r\n"; static const char _ssdp_packet_template[] PROGMEM = "%s" // _ssdp_response_template / _ssdp_notify_template "CACHE-CONTROL: max-age=%u\r\n" // _interval - "SERVER: Arduino/1.0 UPNP/1.1 %s/%s\r\n" // _modelName, _modelNumber + "SERVER: Arduino/1.0 UPnP/1.1 ESPSomfyRTS/2.0\r\n" "USN: %s\r\n" // _uuid "%s: %s\r\n" // "NT" or "ST", _deviceType "LOCATION: http://%u.%u.%u.%u:%u/%s\r\n" // WiFi.localIP(), _port, _schemaURL + "BOOTID.UPNP.ORG: %ul\r\n" + "CONFIGID.UPNP.ORG: %d\r\n" "\r\n"; static const char _ssdp_device_schema_template[] PROGMEM = "" @@ -46,7 +56,7 @@ static const char _ssdp_device_schema_template[] PROGMEM = "%s" "%s" "%s" - "Somfy RTS Controller" + "ESPSomfy RTS Controller" "%s" "%s" "" @@ -87,7 +97,7 @@ UPNPDeviceType::UPNPDeviceType(const char *deviceType) { this->setDeviceType(dev UPNPDeviceType::UPNPDeviceType(const char *deviceType, const char *uuid){ this->setDeviceType(deviceType); this->setUUID(uuid); } UPNPDeviceType::UPNPDeviceType(const char *deviceType, const char *uuid, const char*friendlyName) { this->setDeviceType(deviceType); this->setUUID(uuid); this->setName(friendlyName);} void UPNPDeviceType::setSchemaURL(const char *url) { strlcpy(schemaURL, url, sizeof(schemaURL)); } -void UPNPDeviceType::setDeviceType(const char *dtype) { strlcpy(deviceType, dtype, sizeof(deviceType)); } +void UPNPDeviceType::setDeviceType(const char *dtype) { strlcpy(this->deviceType, dtype, sizeof(deviceType)); } void UPNPDeviceType::setUUID(const char *id) { snprintf_P(uuid, sizeof(uuid), PSTR("uuid:%s"), id); } void UPNPDeviceType::setName(const char *name) { strlcpy(friendlyName, name, sizeof(friendlyName)); } void UPNPDeviceType::setURL(const char *url) { strlcpy(presentationURL, url, sizeof(presentationURL)); } @@ -99,26 +109,43 @@ void UPNPDeviceType::setModelURL(const char *url) { strlcpy(modelURL, url, sizeo void UPNPDeviceType::setManufacturer(const char *name) { strlcpy(manufacturer, name, sizeof(manufacturer)); } void UPNPDeviceType::setManufacturerURL(const char *url) { strlcpy(manufacturerURL, url, sizeof(manufacturerURL)); } //char * UPNPDeviceType::getUSN() { return this->getUSN(this->deviceType); } +char * UPNPDeviceType::getUSN(response_types_t responseType) { + switch(responseType) { + case response_types_t::root: + snprintf_P(this->m_usn, sizeof(this->m_usn) - 1, _ssdp_usn_root_template, this->uuid); + break; + case response_types_t::deviceType: + snprintf_P(this->m_usn, sizeof(this->m_usn) -1, _ssdp_usn_urn_template, this->uuid, this->deviceType); + break; + default: + snprintf_P(this->m_usn, sizeof(this->m_usn) - 1, _ssdp_usn_uuid_template, this->uuid); + break; + } + return this->m_usn; +} char * UPNPDeviceType::getUSN(const char *st) { - #ifdef DEBUG_SSDP - DEBUG_SSDP.print("GETUSN ST: "); - DEBUG_SSDP.println(st); - DEBUG_SSDP.print("GETUSN UUID: "); - DEBUG_SSDP.println(this->uuid); - DEBUG_SSDP.print("sizeof(this->m_usn)"); - DEBUG_SSDP.println(sizeof(this->m_usn)); - memset(this->m_usn, 0x00, sizeof(this->m_usn)); - #endif - if(strncmp("upnp:root", st, strlen(st)) == 0) { + //#ifdef DEBUG_SSDP + //DEBUG_SSDP.print("GETUSN ST: "); + //DEBUG_SSDP.println(st); + //DEBUG_SSDP.print("GETUSN UUID: "); + //DEBUG_SSDP.println(this->uuid); + //DEBUG_SSDP.print("sizeof(this->m_usn)"); + //DEBUG_SSDP.println(sizeof(this->m_usn)); + //#endif + if(strncmp("upnp:rootdevice", st, strlen(st)) == 0) { snprintf_P(this->m_usn, sizeof(this->m_usn) - 1, _ssdp_usn_root_template, this->uuid); } + else if(strncmp("uuid:", st, 5) == 0) + snprintf_P(this->m_usn, sizeof(this->m_usn) - 1, _ssdp_usn_uuid_template, this->uuid); + else if(strncmp("urn:", st, 4) == 0) + snprintf_P(this->m_usn, sizeof(this->m_usn) -1, _ssdp_usn_urn_template, this->uuid, this->deviceType); else { - snprintf_P(this->m_usn, sizeof(this->m_usn) - 1, _ssdp_usn_uuid_template, this->uuid, st); + snprintf_P(this->m_usn, sizeof(this->m_usn) - 1, _ssdp_usn_uuid_template, this->uuid); } - #ifdef DEBUG_SSDP - DEBUG_SSDP.print("RESUSN UUID: "); - DEBUG_SSDP.println(this->m_usn); - #endif + //#ifdef DEBUG_SSDP + //DEBUG_SSDP.print("RESUSN UUID: "); + //DEBUG_SSDP.println(this->m_usn); + //#endif return this->m_usn; } void UPNPDeviceType::setChipId(uint32_t chipId) { @@ -139,7 +166,7 @@ bool SSDPClass::begin() { for(int i = 0; i < SSDP_QUEUE_SIZE; i++) { this->sendQueue[i].waiting = false; } - this->end(); + if(this->_server.connected()) this->end(); //assert(NULL == _server); if(_server.connected()) { #ifdef DEBUG_SSDP @@ -148,33 +175,49 @@ bool SSDPClass::begin() { #endif return false; } + this->bootId = Timestamp::epoch(); + if(this->bootId < 1000) { + this->isStarted = false; + return false; + } + this->configId = (settings.fwVersion.major * 100) + (settings.fwVersion.minor * 10) + settings.fwVersion.build; _server.onPacket([](void * arg, AsyncUDPPacket& packet) { ((SSDPClass*)(arg))->_processRequest(packet); }, this); if(!_server.listenMulticast(IPAddress(SSDP_MULTICAST_ADDR), SSDP_PORT)) { #ifdef DEBUG_SSDP IPAddress mcast(SSDP_MULTICAST_ADDR); - DEBUG_SSDP.println(PSTR("Error starting SSDP listener on:")); + DEBUG_SSDP.println(PSTR("Error starting SSDP listener on: ")); DEBUG_SSDP.print(mcast); DEBUG_SSDP.print(":"); DEBUG_SSDP.println(SSDP_PORT); #endif return false; } + for(uint8_t i = 0; i < this->m_cdeviceTypes; i++) { + Serial.printf("SSDP: %s - %s\n", this->deviceTypes[i].deviceType, this->deviceTypes[i].isActive ? "true" : "false"); + } this->isStarted = true; this->_sendByeBye(); this->_sendNotify(); - Serial.println("Connected to SSDP"); + Serial.println("Connected to SSDP..."); return true; } void SSDPClass::end() { if(!this->_server || !this->_server.connected()) return; // server isn't connected nothing to do #ifdef DEBUG_SSDP - DEBUG_SSDP.printf(PSTR("SSDP end ... ")); + DEBUG_SSDP.printf(PSTR("SSDP end ...\n ")); #endif - this->_sendByeBye(); - //this->_stopTimer(); - this->_server.close(); + if(this->_server.connected()) { + this->_sendByeBye(); + this->_server.close(); + } this->isStarted = false; - Serial.println("Disconnected from SSDP"); + // Clear out the last notified so if the user starts us up again it will notify + // that we exist again. + for(uint8_t i = 0; i < this->m_cdeviceTypes; i++) { + this->deviceTypes[i].lastNotified = 0; + } + + Serial.println("Disconnected from SSDP..."); } UPNPDeviceType* SSDPClass::getDeviceType(uint8_t ndx) { if(ndx < this->m_cdeviceTypes) return &this->deviceTypes[ndx]; return nullptr; } UPNPDeviceType* SSDPClass::findDeviceByType(char *devType) { @@ -242,9 +285,6 @@ void SSDPClass::_parsePacket(ssdp_packet_t *pkt, AsyncUDPPacket &p) { buffer[0] = '\0'; while(p.available() > 0) { char c = p.read(); - //#ifdef DEBUG_SSDP_PACKET - //DEBUG_SSDP.print(c); - //#endif if(keys == KEY) { if(c == ':') { _trim(buffer); @@ -259,12 +299,8 @@ void SSDPClass::_parsePacket(ssdp_packet_t *pkt, AsyncUDPPacket &p) { else if(strcasecmp(buffer, "LOCATION") == 0) keys = LOCATION; else if(strcasecmp(buffer, "USN") == 0) keys = USN; else keys = HEAD; - //#ifdef DEBUG_SSDP_PACKET - //DEBUG_SSDP.printf("Found a key: %s ", buffer); - //#endif pos = 0; buffer[0] = '\0'; - } else if(c == '\r' || c == '\n') { // If we find a key that ends before a : then we need to bugger out. @@ -284,9 +320,6 @@ void SSDPClass::_parsePacket(ssdp_packet_t *pkt, AsyncUDPPacket &p) { // We are reading a value if(c == '\r' || c == '\n') { _trim(buffer); - #ifdef DEBUG_SSDP_PACKET - DEBUG_SSDP.println(buffer); - #endif switch(keys) { case HOST: if(strcasecmp(buffer, "239.255.255.250:1900") == 0) pkt->type = MULTICAST; @@ -324,9 +357,6 @@ void SSDPClass::_parsePacket(ssdp_packet_t *pkt, AsyncUDPPacket &p) { if(pkt->valid) { if(pkt->method == NONE) pkt->valid = false; if(pkt->method == SEARCH) { - #ifdef DEBUG_SSDP_PACKET - //this->_printPacket(pkt); - #endif if(strcmp(pkt->man, "ssdp:discover") != 0) pkt->valid = false; if(strlen(pkt->st) == 0) pkt->valid = false; else if(strcmp(pkt->st, "ssdp:all") != 0 && @@ -364,11 +394,9 @@ IPAddress SSDPClass::localIP() } return IPAddress(ip.ip.addr); } -void SSDPClass::_sendResponse(IPAddress addr, uint16_t port, UPNPDeviceType *d, char *st, bool sendUUID) { +void SSDPClass::_sendResponse(IPAddress addr, uint16_t port, UPNPDeviceType *d, const char *st, response_types_t responseType) { char buffer[1460]; IPAddress ip = this->localIP(); - //IPAddress ip = WiFi.localIP(); - char *pbuff = (char *)malloc(strlen_P(_ssdp_response_template)+1); if(!pbuff) { #ifdef DEBUG_SSDP @@ -378,41 +406,17 @@ void SSDPClass::_sendResponse(IPAddress addr, uint16_t port, UPNPDeviceType *d, } strcpy_P(pbuff, _ssdp_response_template); - #ifdef DEBUG_SSDP - //3FFC40B8 - DEBUG_SSDP.println("Before sprintf "); - DEBUG_SSDP.print("CACHE-CONTROL: max-age="); - DEBUG_SSDP.println(this->_interval); - DEBUG_SSDP.print("SERVER: Arduino/1.0 UPNP/1.1 "); - DEBUG_SSDP.print(d->modelName); - DEBUG_SSDP.print("/"); - DEBUG_SSDP.println(d->modelNumber); - DEBUG_SSDP.print("USN: "); - DEBUG_SSDP.println(d->getUSN(st)); - DEBUG_SSDP.print("ST: "); - DEBUG_SSDP.println((sendUUID) ? d->uuid : st); - DEBUG_SSDP.print("LOCATION: http://"); - DEBUG_SSDP.print(ip[0]); - DEBUG_SSDP.print(ip[1]); - DEBUG_SSDP.print(ip[2]); - DEBUG_SSDP.print(ip[3]); - DEBUG_SSDP.print(":"); - DEBUG_SSDP.print(this->_port); - DEBUG_SSDP.println(d->schemaURL); - #endif // Don't use ip.toString as this fragments the heap like no tomorrow. int len = snprintf_P(buffer, sizeof(buffer)-1, _ssdp_packet_template, pbuff, this->_interval, - d->modelName, d->modelNumber, - d->getUSN(st), - "ST", (sendUUID) ? d->uuid : st, - ip[0], ip[1], ip[2], ip[3], this->_port, d->schemaURL); - #ifdef DEBUG_SSDP - DEBUG_SSDP.println(buffer); - #endif - + d->getUSN(responseType), + "ST", st, + ip[0], ip[1], ip[2], ip[3], this->_port, d->schemaURL, + this->bootId, this->configId); + buffer[sizeof(buffer) - 1] = '\0'; + this->_sendResponse(addr, port, buffer); free(pbuff); /* static const char _ssdp_packet_template[] PROGMEM = @@ -423,10 +427,9 @@ static const char _ssdp_packet_template[] PROGMEM = "%s: %s\r\n" // "NT" or "ST", _deviceType "LOCATION: http://%u.%u.%u.%u:%u/%s\r\n" // WiFi.localIP(), _port, _schemaURL "\r\n"; -*/ + - buffer[sizeof(buffer) - 1] = '\0'; #ifdef DEBUG_SSDP DEBUG_SSDP.print("Sending Response to "); DEBUG_SSDP.print(IPAddress(addr)); @@ -436,32 +439,47 @@ static const char _ssdp_packet_template[] PROGMEM = #endif _server.writeTo((const uint8_t *)buffer, len, addr, port); + */ +} +void SSDPClass::_sendResponse(IPAddress addr, uint16_t port, const char *buff) { + #ifdef DEBUG_SSDP + DEBUG_SSDP.print("Sending Response to "); + DEBUG_SSDP.print(IPAddress(addr)); + DEBUG_SSDP.print(":"); + DEBUG_SSDP.println(port); + DEBUG_SSDP.println(buff); + #endif + + _server.writeTo((const uint8_t *)buff, strlen(buff), addr, port); } void SSDPClass::_sendNotify() { for(uint8_t i = 0; i < this->m_cdeviceTypes; i++) { UPNPDeviceType *dev = &this->deviceTypes[i]; + if(i == 0 && (strlen(dev->deviceType) == 0 || !dev->isActive)) Serial.printf("The device type is empty: %s\n", dev->isActive ? "true" : "false"); if(strlen(dev->deviceType) > 0 && dev->isActive) { - uint16_t elapsed = (millis() - dev->lastNotified); - if(!dev->lastNotified || (elapsed * 4) > (this->_interval * 1000)) { - #ifdef DEBUG_SSDP - DEBUG_SSDP.print(dev->deviceType); - DEBUG_SSDP.print(" Time since last notified: "); - DEBUG_SSDP.print(elapsed); - DEBUG_SSDP.print("msec "); - DEBUG_SSDP.print(this->_interval); - DEBUG_SSDP.println("msec"); - #endif - this->_sendNotify(dev, i == 0); - } - else { + unsigned long elapsed = (millis() - dev->lastNotified); + if(!dev->lastNotified || (elapsed * 5) > (this->_interval * 1000)) { #ifdef DEBUG_SSDP DEBUG_SSDP.print(dev->deviceType); DEBUG_SSDP.print(" Time since last notified: "); DEBUG_SSDP.print(elapsed/1000); - DEBUG_SSDP.print("msec "); + DEBUG_SSDP.print("sec "); DEBUG_SSDP.print(this->_interval); - DEBUG_SSDP.println("msec -- SKIPPING"); + DEBUG_SSDP.println("sec"); #endif + this->_sendNotify(dev, i == 0); + } + else { + /* + #ifdef DEBUG_SSDP + DEBUG_SSDP.print(dev->deviceType); + DEBUG_SSDP.print(" Time since last notified: "); + DEBUG_SSDP.print(elapsed/1000); + DEBUG_SSDP.print("sec "); + DEBUG_SSDP.print(this->_interval); + DEBUG_SSDP.println("sec -- SKIPPING"); + #endif + */ } } } @@ -469,9 +487,13 @@ void SSDPClass::_sendNotify() { void SSDPClass::_sendNotify(const char *msg) { //_server->append(msg, strlen(msg)); //_server->send(IPAddress(SSDP_MULTICAST_ADDR), SSDP_PORT); + #ifdef DEBUG_SSDP + DEBUG_SSDP.println("--------------- SEND NOTIFY PACKET ----------------"); + DEBUG_SSDP.println(msg); + #endif _server.writeTo((uint8_t *)msg, strlen(msg), IPAddress(SSDP_MULTICAST_ADDR), SSDP_PORT); } -void SSDPClass::_sendNotify(UPNPDeviceType *d) { this->_sendByeBye(d, strcmp(d->deviceType, this->deviceTypes[0].deviceType) == 0); } +void SSDPClass::_sendNotify(UPNPDeviceType *d) { this->_sendNotify(d, strcmp(d->deviceType, this->deviceTypes[0].deviceType) == 0); } void SSDPClass::_sendNotify(UPNPDeviceType *d, bool root) { char buffer[1460]; IPAddress ip = this->localIP(); @@ -492,60 +514,48 @@ void SSDPClass::_sendNotify(UPNPDeviceType *d, bool root) { #endif return; } - strcpy_P(pbuff, _ssdp_response_template); - - #ifdef DEBUG_SSDP - //3FFC40B8 - DEBUG_SSDP.print("Before Notify sprintf "); - DEBUG_SSDP.print((uintptr_t)d, HEX); - DEBUG_SSDP.println(" "); - #endif - + strcpy_P(pbuff, _ssdp_notify_template); if(root) { // Send 1 for root snprintf_P(buffer, sizeof(buffer), _ssdp_packet_template, pbuff, this->_interval, - d->modelName, d->modelNumber, - d->getUSN("upnp:rootdevice"), // USN + d->getUSN(response_types_t::root), // USN "NT", "upnp:rootdevice", - ip[0], ip[1], ip[2], ip[3], this->_port, d->schemaURL); - this->_sendNotify(buffer); - } - else { - // Send 1 for uuid - snprintf_P(buffer, sizeof(buffer), - _ssdp_packet_template, - pbuff, - this->_interval, - d->modelName, - d->modelNumber, - d->uuid, - "NT", d->uuid, - ip[0], ip[1], ip[2], ip[3], _port, d->schemaURL); - this->_sendNotify(buffer); - // Send 1 for deviceType - snprintf_P(buffer, sizeof(buffer), - _ssdp_packet_template, - pbuff, - this->_interval, - d->modelName, - d->modelNumber, - d->getUSN(d->deviceType), - "NT", d->deviceType, - ip[0], ip[1], ip[2], ip[3], _port, d->schemaURL); + ip[0], ip[1], ip[2], ip[3], this->_port, d->schemaURL, this->bootId, this->configId); this->_sendNotify(buffer); } + // Send 1 for uuid + snprintf_P(buffer, sizeof(buffer), + _ssdp_packet_template, + pbuff, + this->_interval, + d->getUSN(response_types_t::uuid), + "NT", d->getUSN(response_types_t::uuid), + ip[0], ip[1], ip[2], ip[3], _port, d->schemaURL, this->bootId, this->configId); + this->_sendNotify(buffer); + // Send 1 for deviceType + snprintf_P(buffer, sizeof(buffer), + _ssdp_packet_template, + pbuff, + this->_interval, + d->getUSN(response_types_t::deviceType), + "NT", d->deviceType, + ip[0], ip[1], ip[2], ip[3], _port, d->schemaURL, this->bootId, this->configId); + this->_sendNotify(buffer); d->lastNotified = millis(); } void SSDPClass::setActive(uint8_t ndx, bool isActive) { UPNPDeviceType *d = &this->deviceTypes[ndx]; - if(!isActive) this->_sendByeBye(d, ndx == 0); + if(!isActive) { + if(this->isStarted) this->_sendByeBye(d, ndx == 0); + d->isActive = false; + } else { d->isActive = true; d->lastNotified = 0; - this->_sendNotify(d, ndx == 0); + if(this->isStarted) this->_sendNotify(d, ndx == 0); } } void SSDPClass::_sendByeBye() { @@ -554,11 +564,9 @@ void SSDPClass::_sendByeBye() { if(strlen(dev->deviceType) > 0) { #ifdef DEBUG_SSDP DEBUG_SSDP.print(dev->deviceType); - //DEBUG_SSDP.print(" Time since last notified: "); - //DEBUG_SSDP.print(elapsed); - //DEBUG_SSDP.print("msec "); + DEBUG_SSDP.print(" "); DEBUG_SSDP.print(this->_interval); - DEBUG_SSDP.println("msec"); + DEBUG_SSDP.println("sec"); #endif this->_sendByeBye(dev, i == 0); } @@ -586,42 +594,24 @@ void SSDPClass::_sendByeBye(UPNPDeviceType *d, bool root) { if(root) { // Send 1 for root snprintf_P(buffer, sizeof(buffer)-1, - _ssdp_packet_template, - _ssdp_notify_template, - this->_interval, - d->modelName, d->modelNumber, - d->getUSN("upnp:rootdevice"), // USN - "NT", "upnp:rootdevice", - ip[0], ip[1], ip[2], ip[3], this->_port, d->schemaURL); + _ssdp_bye_template, + "upnp:rootdevice", d->getUSN(response_types_t::root), this->bootId, this->configId); this->_sendNotify(buffer); } - else { - // Send 1 for uuid - snprintf_P(buffer, sizeof(buffer)-1, - _ssdp_packet_template, - _ssdp_notify_template, - this->_interval, - d->modelName, - d->modelNumber, - d->uuid, - "NT", d->uuid, - ip[0], ip[1], ip[2], ip[3], this->_port, d->schemaURL); - this->_sendNotify(buffer); - // Send 1 for deviceType - snprintf_P(buffer, sizeof(buffer)-1, - _ssdp_packet_template, - _ssdp_notify_template, - this->_interval, - d->modelName, - d->modelNumber, - d->getUSN(d->deviceType), - "NT", d->deviceType, - ip[0], ip[1], ip[2], ip[3], this->_port, d->schemaURL); - this->_sendNotify(buffer); - } - d->isActive = false; + // Send 1 for uuid + snprintf_P(buffer, sizeof(buffer)-1, + _ssdp_bye_template, + d->getUSN(response_types_t::uuid), + d->getUSN(response_types_t::uuid), this->bootId, this->configId); + this->_sendNotify(buffer); + // Send 1 for deviceType + snprintf_P(buffer, sizeof(buffer)-1, + _ssdp_bye_template, + d->deviceType, + d->getUSN(response_types_t::deviceType), this->bootId, this->configId); + this->_sendNotify(buffer); } -void SSDPClass::_addToSendQueue(IPAddress addr, uint16_t port, UPNPDeviceType *d, char *st, uint8_t sec, bool sendUUID) { +void SSDPClass::_addToSendQueue(IPAddress addr, uint16_t port, UPNPDeviceType *d, const char *st, response_types_t responseType, uint8_t sec) { /* typedef struct ssdp_response_t { IPAddress address; @@ -636,9 +626,9 @@ void SSDPClass::_addToSendQueue(IPAddress addr, uint16_t port, UPNPDeviceType *d if(this->sendQueue[i].waiting) { // Check to see if this is a reply to the same place. ssdp_response_t *q = &this->sendQueue[i]; - if(q->address == addr && q->port == port && q->sendUUID == sendUUID) { + if(q->address == addr && q->port == port && q->responseType == responseType) { #ifdef DEBUG_SSDP - DEBUG_SSDP.printf("There is already a response to this query in slot #u\n", i); + DEBUG_SSDP.printf("There is already a response to this query in slot %u\n", i); #endif return; } @@ -655,8 +645,8 @@ void SSDPClass::_addToSendQueue(IPAddress addr, uint16_t port, UPNPDeviceType *d q->dev = d; q->sendTime = millis() + (random(0, sec - 1) * 1000L); q->address = addr; - q->port = _port; - q->sendUUID = sendUUID; + q->port = port; + q->responseType = responseType; strlcpy(q->st, st, sizeof(ssdp_response_t::st)-1); q->waiting = true; return; @@ -677,7 +667,7 @@ void SSDPClass::_sendQueuedResponses() { DEBUG_SSDP.print("Sending SSDP queued response "); DEBUG_SSDP.println(i); #endif - this->_sendResponse(q->address, q->port, q->dev, q->st, q->sendUUID); + this->_sendResponse(q->address, q->port, q->dev, q->st, q->responseType); q->waiting = false; return; } @@ -710,6 +700,7 @@ void SSDPClass::_printPacket(ssdp_packet_t *pkt) { } void SSDPClass::_processRequest(AsyncUDPPacket &p) { // This pending BS should probably be for unicast request only but we will play along for now. + if(!this->_server.connected()) return; if(p.length() == 0) { //this->_sendQueuedResponses(); return; @@ -719,21 +710,29 @@ void SSDPClass::_processRequest(AsyncUDPPacket &p) { if(pkt.valid && pkt.method == SEARCH) { // Check to see if we have anything to respond to from this packet. if(strcmp("ssdp:all", pkt.st) == 0) { + #ifdef DEBUG_SSDP + DEBUG_SSDP.println("--------------- ALL ---------------------"); + this->_printPacket(&pkt); + #endif for(uint8_t i = 0; i < this->m_cdeviceTypes; i++) { - if(strlen(this->deviceTypes[i].deviceType) > 0) - this->_addToSendQueue(p.remoteIP(), p.remotePort(), &this->deviceTypes[i], this->deviceTypes[i].deviceType, pkt.mx, false); + UPNPDeviceType *dev = &this->deviceTypes[i]; + if(strlen(this->deviceTypes[i].deviceType) > 0) { + this->_addToSendQueue(p.remoteIP(), p.remotePort(), dev, pkt.st, response_types_t::root, pkt.mx); + this->_addToSendQueue(p.remoteIP(), p.remotePort(), dev, pkt.st, response_types_t::uuid, pkt.mx); + this->_addToSendQueue(p.remoteIP(), p.remotePort(), dev, pkt.st, response_types_t::deviceType, pkt.mx); + } } } else if(strcmp("upnp:rootdevice", pkt.st) == 0) { UPNPDeviceType *dev = &this->deviceTypes[0]; #ifdef DEBUG_SSDP - this->_printPacket(&pkt); DEBUG_SSDP.println("--------------- ROOT ---------------------"); + this->_printPacket(&pkt); #endif if(pkt.type == MULTICAST) - this->_addToSendQueue(IPAddress(SSDP_MULTICAST_ADDR), SSDP_PORT, dev, pkt.st, pkt.mx, false); + this->_addToSendQueue(IPAddress(SSDP_MULTICAST_ADDR), SSDP_PORT, dev, pkt.st, response_types_t::root, pkt.mx); else - this->_sendResponse(p.remoteIP(), p.remotePort(), dev, pkt.st, false); + this->_sendResponse(p.remoteIP(), p.remotePort(), dev, pkt.st, response_types_t::root); } else { UPNPDeviceType *dev = nullptr; @@ -749,18 +748,18 @@ void SSDPClass::_processRequest(AsyncUDPPacket &p) { DEBUG_SSDP.println("-------------- ACCEPT --------------------"); #endif if(pkt.type == MULTICAST) - this->_addToSendQueue(IPAddress(SSDP_MULTICAST_ADDR), SSDP_PORT, dev, pkt.st, pkt.mx, useUUID); + this->_addToSendQueue(IPAddress(SSDP_MULTICAST_ADDR), SSDP_PORT, dev, pkt.st, useUUID ? response_types_t::uuid : response_types_t::root, pkt.mx); else { - this->_sendResponse(p.remoteIP(), p.remotePort(), dev, pkt.st, useUUID); + this->_sendResponse(p.remoteIP(), p.remotePort(), dev, pkt.st, useUUID ? response_types_t::uuid : response_types_t::root); } } } } - else if(pkt.method == SEARCH) { - } } void SSDPClass::loop() { this->_sendQueuedResponses(); + // We need to send the notify if required. + this->_sendNotify(); } void SSDPClass::schema(Print &client) { IPAddress ip = this->localIP(); @@ -812,7 +811,7 @@ void SSDPClass::schema(Print &client) { } client.print(F("\r\n")); #ifdef DEBUG_SSDP - DEBUG_SSDP.println("Sending Device.xml"); + DEBUG_SSDP.println("Sending upnp.xml"); #endif } /* @@ -836,7 +835,7 @@ void SSDPClass::setSchemaURL(uint8_t ndx, const char *url) { this->deviceTypes[ void SSDPClass::setUUID(uint8_t ndx, const char *uuid) { this->deviceTypes[ndx].setUUID(uuid); } void SSDPClass::setUUID(uint8_t ndx, const char *prefix, uint32_t id) { char svcuuid[50]; - sprintf(svcuuid, "%s%02x%02x%02x", + sprintf(svcuuid, "%s%02X%02X%02X", prefix, (uint16_t)((id >> 16) & 0xff), (uint16_t)((id >> 8) & 0xff), diff --git a/SSDP.h b/SSDP.h index 061c57a..596b1ec 100644 --- a/SSDP.h +++ b/SSDP.h @@ -7,14 +7,15 @@ #define SSDP_UUID_SIZE 42 #define SSDP_SCHEMA_URL_SIZE 64 #define SSDP_DEVICE_TYPE_SIZE 64 -#define SSDP_FRIENDLY_NAME_SIZE 64 #define SSDP_SERIAL_NUMBER_SIZE 37 #define SSDP_PRESENTATION_URL_SIZE 64 #define SSDP_MODEL_NAME_SIZE 64 #define SSDP_MODEL_URL_SIZE 64 #define SSDP_MODEL_VERSION_SIZE 32 #define SSDP_MANUFACTURER_SIZE 64 +#define SSDP_USN_SIZE 128 #define SSDP_MANUFACTURER_URL_SIZE 64 +#define SSDP_FRIENDLY_NAME_SIZE 64 #define SSDP_INTERVAL_SECONDS 1800 #define SSDP_MULTICAST_TTL 2 #define SSDP_HTTP_PORT 80 @@ -29,6 +30,11 @@ typedef enum { SEARCH, NOTIFY } ssdp_method_t; +typedef enum { + root, + uuid, + deviceType +} response_types_t; #define SSDP_FIELD_LEN 128 #define SSDP_LOCATION_LEN 256 @@ -53,7 +59,7 @@ struct ssdp_packet_t { }; class UPNPDeviceType { - char m_usn[SSDP_MANUFACTURER_URL_SIZE]; + char m_usn[SSDP_USN_SIZE]; public: UPNPDeviceType(); UPNPDeviceType(const char *deviceType); @@ -89,6 +95,7 @@ class UPNPDeviceType { void setManufacturerURL(const char *url); //char *getUSN(); char *getUSN(const char *st); + char *getUSN(response_types_t responseType); void setChipId(uint32_t chipId); }; struct ssdp_response_t { @@ -99,6 +106,7 @@ struct ssdp_response_t { bool sendUUID; unsigned long sendTime; char st[SSDP_DEVICE_TYPE_SIZE]; + response_types_t responseType; }; class SSDPClass { uint8_t m_cdeviceTypes = SSDP_CHILD_DEVICES + 1; @@ -118,7 +126,8 @@ class SSDPClass { void _sendByeBye(UPNPDeviceType *d, bool root); void _sendByeBye(const char *); void _sendQueuedResponses(); - void _sendResponse(IPAddress addr, uint16_t port, UPNPDeviceType *d, char *st, bool sendUUID); + void _sendResponse(IPAddress addr, uint16_t port, UPNPDeviceType *d, const char *st, response_types_t responseType); + void _sendResponse(IPAddress addr, uint16_t port, const char *msg); AsyncUDP _server; hw_timer_t* _timer = nullptr; uint16_t _port = SSDP_HTTP_PORT; @@ -128,7 +137,7 @@ class SSDPClass { void _parsePacket(ssdp_packet_t *pkt, AsyncUDPPacket &p); void _printPacket(ssdp_packet_t *pkt); bool _startsWith(const char* pre, const char* str); - void _addToSendQueue(IPAddress addr, uint16_t port, UPNPDeviceType *d, char *st, uint8_t sec, bool sendUUID); + void _addToSendQueue(IPAddress addr, uint16_t port, UPNPDeviceType *d, const char *st, response_types_t responseType, uint8_t sec); public: SSDPClass(); @@ -138,6 +147,8 @@ class SSDPClass { void loop(); void end(); bool isStarted = false; + unsigned long bootId = 0; + int configId = 0; IPAddress localIP(); UPNPDeviceType* getDeviceTypes(uint8_t ndx); UPNPDeviceType* getDeviceType(uint8_t ndx); diff --git a/Somfy.cpp b/Somfy.cpp index 7e81214..1734f4f 100644 --- a/Somfy.cpp +++ b/Somfy.cpp @@ -2702,6 +2702,7 @@ void SomfyShade::moveToTiltTarget(float target) { else if(target > this->currentTiltPos) cmd = somfy_commands::Down; if(target >= 0.0f && target <= 100.0f) { + // Only send a command if the lift is not moving. if(this->currentPos == this->target || this->tiltType == tilt_types::tiltmotor) { if(cmd != somfy_commands::My) { Serial.print("Moving Tilt to "); @@ -2712,12 +2713,12 @@ void SomfyShade::moveToTiltTarget(float target) { Serial.println(translateSomfyCommand(cmd)); SomfyRemote::sendCommand(cmd, this->tiltType == tilt_types::tiltmotor ? TILT_REPEATS : this->repeats); } - else - SomfyRemote::sendCommand(cmd, this->repeats); + // If the blind is currently moving then the command to stop it + // will occur on its own when the tilt target is set. } this->p_tiltTarget(target); } - this->settingTiltPos = true; + if(cmd != somfy_commands::My) this->settingTiltPos = true; } void SomfyShade::moveToTarget(float pos, float tilt) { somfy_commands cmd = somfy_commands::My; diff --git a/SomfyController.ino.esp32.bin b/SomfyController.ino.esp32.bin index 4e8e755..08cc8ef 100644 Binary files a/SomfyController.ino.esp32.bin and b/SomfyController.ino.esp32.bin differ diff --git a/SomfyController.ino.esp32s3.bin b/SomfyController.ino.esp32s3.bin index 153e8cd..742e2fc 100644 Binary files a/SomfyController.ino.esp32s3.bin and b/SomfyController.ino.esp32s3.bin differ diff --git a/SomfyController.littlefs.bin b/SomfyController.littlefs.bin index d596cef..f1aded8 100644 Binary files a/SomfyController.littlefs.bin and b/SomfyController.littlefs.bin differ diff --git a/Utils.cpp b/Utils.cpp index cc7dfbf..ece3c49 100644 --- a/Utils.cpp +++ b/Utils.cpp @@ -6,6 +6,13 @@ /********************************************************************* * Timestamp class members ********************************************************************/ +unsigned long Timestamp::epoch() { + struct tm tmNow; + time_t now; + if(!getLocalTime(&tmNow)) return 0; + time(&now); + return now; +} time_t Timestamp::now() { struct tm tmNow; getLocalTime(&tmNow); diff --git a/Utils.h b/Utils.h index 5cd6c92..d71ff5d 100644 --- a/Utils.h +++ b/Utils.h @@ -63,6 +63,7 @@ class Timestamp { static time_t mkUTCTime(struct tm *dt); static int calcTZOffset(time_t *dt); static time_t now(); + static unsigned long epoch(); }; // Sort an array template void sortArray(AnyType array[], size_t sizeOfArray); diff --git a/Web.cpp b/Web.cpp index 522cd0d..579bf8b 100644 --- a/Web.cpp +++ b/Web.cpp @@ -1260,7 +1260,7 @@ void Web::begin() { SomfyShade* shade = nullptr; if (method == HTTP_POST || method == HTTP_PUT) { Serial.println("Adding a shade"); - DynamicJsonDocument doc(512); + DynamicJsonDocument doc(1024); DeserializationError err = deserializeJson(doc, server.arg("plain")); if (err) { switch (err.code()) { @@ -1285,7 +1285,7 @@ void Web::begin() { Serial.println("Adding shade"); shade = somfy.addShade(obj); if (shade) { - DynamicJsonDocument sdoc(512); + DynamicJsonDocument sdoc(1024); JsonObject sobj = sdoc.to(); shade->toJSON(sobj); serializeJson(sdoc, g_content); @@ -1299,7 +1299,7 @@ void Web::begin() { } if (shade) { //Serial.println("Serializing shade"); - DynamicJsonDocument doc(256); + DynamicJsonDocument doc(1024); JsonObject obj = doc.to(); shade->toJSON(obj); serializeJson(doc, g_content); diff --git a/data/index.html b/data/index.html index 52b5245..bee6364 100644 --- a/data/index.html +++ b/data/index.html @@ -830,7 +830,7 @@

-
+ diff --git a/data/index.js b/data/index.js index 106a178..59312a3 100644 --- a/data/index.js +++ b/data/index.js @@ -2181,8 +2181,7 @@ class Somfy { _rooms.push(room); divCtl += `
${room.name}
`; } - if (rooms.length === 0) document.getElementById('divRoomSelector').style.display = 'none'; - else document.getElementById('divRoomSelector').style.display = ''; + document.getElementById('divRoomSelector').style.display = rooms.length === 0 ? 'none' : ''; document.getElementById('divRoomSelector-list').innerHTML = divCtl; document.getElementById('divRoomList').innerHTML = divCfg; document.getElementById('selShadeRoom').innerHTML = divOpts;