diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index d10e4dc..ab28e7c 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -7,7 +7,7 @@ on: env: ARDUINO_BOARD_MANAGER_ADDITIONAL_URLS: "https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json" ARDUINO_CLI_VERSION: "0.x" - ARDUINO_ESP32_VERSION: "2.0.14" + ARDUINO_ESP32_VERSION: "2.0.15" ARDUINO_JSON_VERSION: "6.21.5" ESPTOOL_VERSION: "4.7" LITTLEFS_VERSION: "v2.5.1" diff --git a/ConfigFile.cpp b/ConfigFile.cpp index 9be2730..ca0e93f 100644 --- a/ConfigFile.cpp +++ b/ConfigFile.cpp @@ -7,10 +7,10 @@ extern Preferences pref; -#define SHADE_HDR_VER 22 +#define SHADE_HDR_VER 23 #define SHADE_HDR_SIZE 76 #define SHADE_REC_SIZE 276 -#define GROUP_REC_SIZE 194 +#define GROUP_REC_SIZE 200 #define TRANS_REC_SIZE 74 #define ROOM_REC_SIZE 29 #define REPEATER_REC_SIZE 77 @@ -728,12 +728,14 @@ bool ShadeConfigFile::readGroupRecord(SomfyGroup *group) { this->readString(group->name, sizeof(group->name)); group->proto = static_cast(this->readUInt8(0)); group->bitLength = this->readUInt8(56); + if(this->header.version >= 23) group->lastRollingCode = this->readUInt16(0); if(group->getRemoteAddress() != 0) { uint16_t rc = pref.getUShort(group->getRemotePrefId(), 0); group->lastRollingCode = max(rc, group->lastRollingCode); if(rc < group->lastRollingCode) pref.putUShort(group->getRemotePrefId(), group->lastRollingCode); } uint8_t lsd = 0; + memset(group->linkedShades, 0x00, sizeof(group->linkedShades)); for(uint8_t j = 0; j < SOMFY_MAX_GROUPED_SHADES; j++) { uint8_t shadeId = this->readUInt8(0); // Do this to eliminate gaps. @@ -925,6 +927,7 @@ bool ShadeConfigFile::writeGroupRecord(SomfyGroup *group) { this->writeString(group->name, sizeof(group->name)); this->writeUInt8(static_cast(group->proto)); this->writeUInt8(group->bitLength); + this->writeUInt16(group->lastRollingCode); for(uint8_t j = 0; j < SOMFY_MAX_GROUPED_SHADES; j++) { this->writeUInt8(group->linkedShades[j]); } diff --git a/ConfigSettings.h b/ConfigSettings.h index cc12f1f..d5af040 100644 --- a/ConfigSettings.h +++ b/ConfigSettings.h @@ -3,7 +3,7 @@ #ifndef configsettings_h #define configsettings_h #include "WResp.h" -#define FW_VERSION "v2.4.2" +#define FW_VERSION "v2.4.3" enum DeviceStatus { DS_OK = 0, DS_ERROR = 1, @@ -161,7 +161,8 @@ enum class conn_types : byte { unset = 0x00, wifi = 0x01, ethernet = 0x02, - ethernetpref = 0x03 + ethernetpref = 0x03, + ap = 0x04 }; class ConfigSettings: BaseSettings { public: diff --git a/GitOTA.cpp b/GitOTA.cpp index e0ceb85..66a71ec 100644 --- a/GitOTA.cpp +++ b/GitOTA.cpp @@ -68,20 +68,6 @@ void GitRelease::setAssetProperty(const char *key, const char *val) { } } } -bool GitRelease::toJSON(JsonObject &obj) { - Timestamp ts; - obj["id"] = this->id; - obj["name"] = this->name; - obj["date"] = ts.getISOTime(this->releaseDate); - obj["draft"] = this->draft; - obj["preRelease"] = this->preRelease; - obj["main"] = this->main; - obj["hasFS"] = this->hasFS; - obj["hwVersions"] = this->hwVersions; - JsonObject ver = obj.createNestedObject("version"); - this->version.toJSON(ver); - return true; -} void GitRelease::toJSON(JsonResponse &json) { Timestamp ts; char buff[20]; @@ -228,7 +214,7 @@ int16_t GitRepo::getReleases(uint8_t num) { } else { https.end(); - //sclient.stop(); + sclient.stop(); return httpCode; } } @@ -254,19 +240,6 @@ void GitRepo::toJSON(JsonResponse &json) { } json.endArray(); } -bool GitRepo::toJSON(JsonObject &obj) { - JsonObject fw = obj.createNestedObject("fwVersion"); - settings.fwVersion.toJSON(fw); - JsonObject app = obj.createNestedObject("appVersion"); - settings.appVersion.toJSON(app); - JsonArray arr = obj.createNestedArray("releases"); - for(uint8_t i = 0; i < GIT_MAX_RELEASES + 1; i++) { - if(this->releases[i].id == 0) continue; - JsonObject o = arr.createNestedObject(); - this->releases[i].toJSON(o); - } - return true; -} #define UPDATE_ERR_OFFSET 20 #define ERR_DOWNLOAD_HTTP -40 #define ERR_DOWNLOAD_BUFFER -41 @@ -274,11 +247,8 @@ bool GitRepo::toJSON(JsonObject &obj) { void GitUpdater::loop() { if(this->status == GIT_STATUS_READY) { - //if(this->lastCheck == 0) - //this->lastCheck = millis(); - //else if(settings.checkForUpdate && - //(this->lastCheck + 14400000 < millis() || this->lastCheck == 0) && !rebootDelay.reboot) { // 4 hours + (millis() > 60000) && // Wait a minute before checking after boot. (this->lastCheck + 86400000 < millis() || this->lastCheck == 0) && !rebootDelay.reboot) { // 1 day this->checkForUpdate(); } @@ -302,6 +272,7 @@ void GitUpdater::loop() { void GitUpdater::checkForUpdate() { if(this->status != 0) return; // If we are already checking. Serial.println("Check github for updates..."); + this->status = GIT_STATUS_CHECK; settings.printAvailHeap(); this->lastCheck = millis(); @@ -349,21 +320,6 @@ void GitUpdater::toJSON(JsonResponse &json) { this->latest.toJSON(json); json.endObject(); } - -void GitUpdater::toJSON(JsonObject &obj) { - obj["available"] = this->updateAvailable; - obj["status"] = this->status; - obj["error"] = this->error; - obj["cancelled"] = this->cancelled; - obj["checkForUpdate"] = settings.checkForUpdate; - obj["inetAvailable"] = this->inetAvailable; - JsonObject fw = obj.createNestedObject("fwVersion"); - settings.fwVersion.toJSON(fw); - JsonObject app = obj.createNestedObject("appVersion"); - settings.appVersion.toJSON(app); - JsonObject latest = obj.createNestedObject("latest"); - this->latest.toJSON(latest); -} void GitUpdater::emitUpdateCheck(uint8_t num) { JsonSockEvent *json = sockEmit.beginEmit("fwStatus"); json->beginObject(); @@ -384,16 +340,6 @@ void GitUpdater::emitUpdateCheck(uint8_t num) { json->endObject(); json->endObject(); sockEmit.endEmit(num); - /* - ClientSocketEvent evt("fwStatus"); - DynamicJsonDocument doc(512); - JsonObject obj = doc.to(); - this->toJSON(obj); - if(num == 255) - sockEmit.sendToClients("fwStatus", doc); - else - sockEmit.sendToClient(num, "fwStatus", doc); - */ } int GitUpdater::checkInternet() { int err = 500; diff --git a/GitOTA.h b/GitOTA.h index 7cbcb84..f79aaea 100644 --- a/GitOTA.h +++ b/GitOTA.h @@ -28,14 +28,12 @@ class GitRelease { appver_t version; void setReleaseProperty(const char *key, const char *val); void setAssetProperty(const char *key, const char *val); - bool toJSON(JsonObject &obj); void toJSON(JsonResponse &json); }; class GitRepo { public: int16_t getReleases(uint8_t num = GIT_MAX_RELEASES); GitRelease releases[GIT_MAX_RELEASES + 1]; - bool toJSON(JsonObject &obj); void toJSON(JsonResponse &json); }; class GitUpdater { @@ -60,7 +58,6 @@ class GitUpdater { void setFirmwareFile(); void setCurrentRelease(GitRepo &repo); void loop(); - void toJSON(JsonObject &obj); void toJSON(JsonResponse &json); bool recoverFilesystem(); int checkInternet(); diff --git a/MQTT.cpp b/MQTT.cpp index 65eb63d..6ade1da 100644 --- a/MQTT.cpp +++ b/MQTT.cpp @@ -299,18 +299,6 @@ bool MQTTClass::publish(const char *topic, uint32_t val, bool retain) { snprintf(g_content, sizeof(g_content), "%u", val); return this->publish(topic, g_content, retain); } -bool MQTTClass::publish(const char *topic, JsonDocument &doc, bool retain) { - serializeJson(doc, g_content, sizeof(g_content)); - return this->publish(topic, g_content, retain); -} -bool MQTTClass::publish(const char *topic, JsonArray &arr, bool retain) { - serializeJson(arr, g_content, sizeof(g_content)); - return this->publish(topic, g_content, retain); -} -bool MQTTClass::publish(const char *topic, JsonObject &obj, bool retain) { - serializeJson(obj, g_content, sizeof(g_content)); - return this->publish(topic, g_content, retain); -} bool MQTTClass::unpublish(const char *topic) { if(mqttClient.connected()) { char top[128]; diff --git a/MQTT.h b/MQTT.h index 073c943..fbc4839 100644 --- a/MQTT.h +++ b/MQTT.h @@ -17,9 +17,6 @@ class MQTTClass { void reset(); bool unpublish(const char *topic); bool publish(const char *topic, const char *payload, bool retain = false); - bool publish(const char *topic, JsonDocument &doc, bool retain = false); - bool publish(const char *topic, JsonArray &arr, bool retain = false); - bool publish(const char *topic, JsonObject &obj, bool retain = false); bool publish(const char *topic, uint8_t val, bool retain = false); bool publish(const char *topic, int8_t val, bool retain = false); bool publish(const char *topic, uint32_t val, bool retain = false); diff --git a/Network.cpp b/Network.cpp index 45c7d74..54459b6 100644 --- a/Network.cpp +++ b/Network.cpp @@ -15,6 +15,8 @@ extern SocketEmitter sockEmit; extern MQTTClass mqtt; extern rebootDelay_t rebootDelay; extern Network net; +extern SomfyShadeController somfy; + static bool _apScanning = false; static uint32_t _lastMaxHeap = 0; @@ -40,39 +42,26 @@ bool Network::setup() { WiFi.mode(WIFI_STA); settings.WIFI.printNetworks(); } - if(!this->connect()) this->openSoftAP(); + //if(!this->connect()) this->openSoftAP(); return true; } void Network::loop() { + this->connect(); + if(!this->connected() || this->connecting()) return; if(millis() - this->lastEmit > 1500) { this->lastEmit = millis(); if(!this->softAPOpened) { - while(!this->connect()) { - // If we lost our connection - connectRetries++; - if(connectRetries > 100) { - if(!this->connected()) this->openSoftAP(); - break; - } - sockEmit.loop(); + if(this->connected()) { + this->emitSockets(); + this->lastEmit = millis(); } - connectRetries = 0; } - this->emitSockets(); - this->lastEmit = millis(); - if(!this->connected()) return; } sockEmit.loop(); if(this->connected() && millis() - this->lastMDNS > 60000) { - // We are doing this every 60 seconds because of the BS related to - // the MDNS library. The original library required manual updates - // to the MDNS or it would lose its hostname after 2 minutes. - //if(this->lastMDNS != 0) MDNS.setInstanceName(settings.hostname); - - // Every 60 seconds we are going to look at wifi connectivity // to get around the roaming issues with ESP32. We will try to do this in an async manner. If - // there is a channel that is better we will stop the radio and reconnect + // there is a channel that is better we will stop the wifi radio and reconnect if(this->connType == conn_types::wifi && settings.WIFI.roaming && !this->softAPOpened) { // If we are not already scanning then we need to start a passive scan // and only respond if there is a better connection. @@ -237,6 +226,7 @@ void Network::setConnected(conn_types connType) { WiFi.softAPdisconnect(true); WiFi.mode(WIFI_STA); } + this->_connecting = false; this->ssid = WiFi.SSID(); this->mac = WiFi.BSSIDstr(); this->strength = WiFi.RSSI(); @@ -248,6 +238,7 @@ void Network::setConnected(conn_types connType) { WiFi.softAPdisconnect(true); WiFi.mode(WIFI_OFF); } + this->_connecting = false; this->wifiFallback = false; } sockEmit.begin(); @@ -365,8 +356,8 @@ void Network::setConnected(conn_types connType) { this->needsBroadcast = true; } bool Network::connectWired() { - //if(this->connType == conn_types::ethernet && ETH.linkUp()) { if(ETH.linkUp()) { + // If the ethernet link is re-established then we need to shut down wifi. if(WiFi.status() == WL_CONNECTED) { sockEmit.end(); WiFi.disconnect(true); @@ -386,7 +377,11 @@ bool Network::connectWired() { } else Serial.println("Connecting to Wired Ethernet"); + this->connectAttempts++; + this->_connecting = true; + this->connTarget = conn_types::ethernet; + this->connType = conn_types::unset; if(!this->ethStarted) { this->ethStarted = true; WiFi.mode(WIFI_OFF); @@ -415,23 +410,10 @@ bool Network::connectWired() { } else ETH.config(INADDR_NONE, INADDR_NONE, INADDR_NONE, INADDR_NONE); - - uint32_t wait = millis(); - while(millis() - wait < 14000) { - if(ETH.linkUp() && ETH.localIP() != INADDR_NONE) { - net.mac = ETH.macAddress(); - net.setConnected(conn_types::ethernet); - return true; - } - delay(500); - } - if(settings.connType == conn_types::ethernetpref) { - this->wifiFallback = true; - return connectWiFi(); - } } } - return false; + this->connectStart = millis(); + return true; } void Network::updateHostname() { if(settings.hostname[0] != '\0' && this->connected()) { @@ -467,11 +449,12 @@ bool Network::connectWiFi() { } else Serial.println("Connecting to AP"); this->connectAttempts++; - this->connectStart = millis(); + this->_connecting = true; + this->connTarget = conn_types::wifi; + this->connType = conn_types::unset; + WiFi.setSleep(false); WiFi.mode(WIFI_MODE_NULL); - //WiFi.onEvent(this->networkEvent); - if(!settings.IP.dhcp) { if(!WiFi.config(settings.IP.ip, settings.IP.gateway, settings.IP.subnet, settings.IP.dns1, settings.IP.dns2)) WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE, INADDR_NONE); @@ -494,80 +477,35 @@ bool Network::connectWiFi() { } else WiFi.begin(settings.WIFI.ssid, settings.WIFI.passphrase); - delay(100); - int retries = 0; - while(retries < 100) { - switch(WiFi.status()) { - case WL_SCAN_COMPLETED: - Serial.println("Status: Scan Completed"); - break; - case WL_CONNECT_FAILED: - if(this->connectAttempts == 1) Serial.println(); - Serial.println("WiFi Module connection failed"); - return false; - case WL_DISCONNECTED: - break; - case WL_IDLE_STATUS: - Serial.print("*"); - break; - case WL_CONNECTED: - //WiFi.hostname(settings.hostname); - this->ssid = WiFi.SSID(); - this->mac = WiFi.BSSIDstr(); - this->strength = WiFi.RSSI(); - this->channel = WiFi.channel(); - this->setConnected(conn_types::wifi); - WiFi.setSleep(false); - return true; - case WL_NO_SHIELD: - Serial.println("Connection failed - WiFi module not found"); - return false; - case WL_NO_SSID_AVAIL: - Serial.print(" Connection failed the SSID "); - Serial.print(settings.WIFI.ssid); - Serial.println(" could not be found"); - return false; - default: - break; - } - delay(500); - if(connectAttempts == 1) Serial.print("*"); - retries++; - } - if(this->connectAttempts != 1) { - int st = this->getStrengthBySSID(settings.WIFI.ssid); - Serial.print("("); - Serial.print(st); - Serial.print("dBm) "); - Serial.println("Failed"); - //if(disconnected > 0 && st == -100) settings.WIFI.PrintNetworks(); - disconnected++; - } + this->connectStart = millis(); } - return false; + return true; } bool Network::connect() { - if(settings.connType == conn_types::unset) return true; - else if(settings.connType == conn_types::ethernet || (settings.connType == conn_types::ethernetpref)) { - bool bConnected = this->connectWired(); - if(!bConnected && settings.connType == conn_types::ethernetpref && settings.WIFI.ssid[0] != '\0') { - bConnected = this->connectWiFi(); - this->wifiFallback = true; + if(this->connecting()) { + if(this->connType == conn_types::unset) { + // If we reached our timeout for the connection then we need to open the soft ap. + if(millis() > this->connectStart + CONNECT_TIMEOUT) { + if(this->connTarget == conn_types::ethernet && settings.connType == conn_types::ethernetpref && settings.WIFI.ssid[0] != '\0') + this->connectWiFi(); + else { + Serial.println("Fell into timeout"); + this->openSoftAP(); + + } + } } - return bConnected; + else + this->setConnected(this->connTarget); } - return this->connectWiFi(); + else if(settings.connType == conn_types::ethernet || settings.connType == conn_types::ethernetpref) + this->connectWired(); + else if(settings.connType == conn_types::wifi && strlen(settings.WIFI.ssid) > 0) + this->connectWiFi(); + else + this->openSoftAP(); + return true; } -/* DEPRECATED 03-02-24 -int Network::getStrengthByMac(const char *macAddr) { - int n = WiFi.scanNetworks(true); - for(int i = 0; i < n; i++) { - if (WiFi.BSSIDstr(i).compareTo(macAddr) == 0) - return WiFi.RSSI(i); - } - return -100; -} -*/ uint32_t Network::getChipId() { uint32_t chipId = 0; uint64_t mac = ESP.getEfuseMac(); @@ -645,6 +583,7 @@ bool Network::openSoftAP() { while (!this->connected()) { int clients = WiFi.softAPgetStationNum(); + somfy.loop(); webServer.loop(); if(millis() - this->lastEmit > 1500) { //if(this->connect()) {} @@ -665,14 +604,22 @@ bool Network::openSoftAP() { // If no clients have connected in 3 minutes from starting this server reboot this pig. This will // force a reboot cycle until we have some response. That is unless the SSID has been cleared. - if(clients == 0 && + if(clients == 0 && this->connType != conn_types::unset) { + Serial.println(); + Serial.println("Connection Established Stopping AP Mode"); + this->setConnected(this->connType); + return false; + } + else if(clients == 0 && (strlen(settings.WIFI.ssid) > 0 || settings.connType == conn_types::ethernet || settings.connType == conn_types::ethernetpref) && millis() - startTime > 3 * 60000) { Serial.println(); Serial.println("Stopping AP Mode"); WiFi.softAPdisconnect(true); + return false; } + if(c == 100) { Serial.println(); c = 0; @@ -682,30 +629,41 @@ bool Network::openSoftAP() { return true; } bool Network::connected() { - if(this->connType == conn_types::unset) return false; + if(this->connecting()) return false; + else if(this->connType == conn_types::unset) return false; else if(this->connType == conn_types::wifi) return WiFi.status() == WL_CONNECTED; else if(this->connType == conn_types::ethernet) return ETH.linkUp(); else return this->connType != conn_types::unset; return false; } +bool Network::connecting() { + return this->_connecting; +} void Network::networkEvent(WiFiEvent_t event) { switch(event) { - case ARDUINO_EVENT_ETH_START: - Serial.println("Ethernet Started"); + case ARDUINO_EVENT_WIFI_READY: Serial.println("WiFi interface ready"); break; + case ARDUINO_EVENT_WIFI_SCAN_DONE: Serial.println("Completed scan for access points"); break; + case ARDUINO_EVENT_WIFI_STA_START: + Serial.println("WiFi station mode started"); + if(settings.hostname[0] != '\0') WiFi.setHostname(settings.hostname); break; + case ARDUINO_EVENT_WIFI_STA_STOP: Serial.println("WiFi clients stopped"); break; + case ARDUINO_EVENT_WIFI_STA_CONNECTED: Serial.println("Connected to access point"); break; + case ARDUINO_EVENT_WIFI_STA_DISCONNECTED: Serial.println("Disconnected from WiFi access point"); break; + case ARDUINO_EVENT_WIFI_STA_AUTHMODE_CHANGE: Serial.println("Authentication mode of access point has changed"); break; + case ARDUINO_EVENT_WIFI_STA_GOT_IP: + Serial.print("Got WiFi IP: "); + Serial.println(WiFi.localIP()); + net.connType = conn_types::wifi; + break; + case ARDUINO_EVENT_WIFI_STA_LOST_IP: Serial.println("Lost IP address and IP address is reset to 0"); break; case ARDUINO_EVENT_ETH_GOT_IP: // If the Wifi is connected then drop that connection if(WiFi.status() == WL_CONNECTED) WiFi.disconnect(true); Serial.print("Got Ethernet IP "); Serial.println(ETH.localIP()); + net.connType = conn_types::ethernet; break; -/* - case ARDUINO_EVENT_ETH_LOST_IP: - Serial.println("Ethernet Lost IP"); - sockEmit.sendToClients("ethernet", "{\"connected\":false, \"speed\":0,\"fullduplex\":false}"); - net.connType = conn_types::unset; - break; -*/ case ARDUINO_EVENT_ETH_CONNECTED: Serial.print("Ethernet Connected "); // We don't want to call setConnected if we do not have an IP address yet @@ -714,37 +672,25 @@ void Network::networkEvent(WiFiEvent_t event) { break; case ARDUINO_EVENT_ETH_DISCONNECTED: Serial.println("Ethernet Disconnected"); - { - JsonSockEvent *json = sockEmit.beginEmit("ethernet"); - json->beginObject(); - json->addElem("connected", false); - json->addElem("speed", (uint8_t)0); - json->addElem("fullduplex", false); - json->endObject(); - sockEmit.endEmit(); - } - /* - sockEmit.sendToClients("ethernet", "{\"connected\":false,\"speed\":0,\"fullduplex\":false}"); - */ net.connType = conn_types::unset; break; + case ARDUINO_EVENT_ETH_START: + Serial.println("Ethernet Started"); + net.ethStarted = true; + break; case ARDUINO_EVENT_ETH_STOP: Serial.println("Ethernet Stopped"); net.connType = conn_types::unset; + net.ethStarted = false; + break; + case ARDUINO_EVENT_WIFI_AP_START: + Serial.println("WiFi AP Started"); + net.softAPOpened = true; break; case ARDUINO_EVENT_WIFI_AP_STOP: Serial.println("WiFi AP Stopped"); net.softAPOpened = false; break; - case ARDUINO_EVENT_WIFI_AP_START: - Serial.println("WiFi AP Started"); - net.softAPOpened = true; - break; - case ARDUINO_EVENT_WIFI_STA_START: - if(settings.hostname[0] != '\0') WiFi.setHostname(settings.hostname); - break; - case ARDUINO_EVENT_WIFI_STA_CONNECTED: - break; default: if(event > ARDUINO_EVENT_ETH_START) Serial.printf("Unknown Ethernet Event %d\n", event); diff --git a/Network.h b/Network.h index 5794433..9025b73 100644 --- a/Network.h +++ b/Network.h @@ -2,6 +2,8 @@ #ifndef Network_h #define Network_h + +#define CONNECT_TIMEOUT 20000 class Network { protected: unsigned long lastEmit = 0; @@ -9,21 +11,24 @@ class Network { int lastRSSI = 0; int lastChannel = 0; int linkSpeed = 0; - bool ethStarted = false; + bool _connecting = false; public: + bool ethStarted = false; bool wifiFallback = false; bool softAPOpened = false; bool needsBroadcast = true; conn_types connType = conn_types::unset; + conn_types connTarget = conn_types::unset; bool connected(); + bool connecting(); String ssid; String mac; int channel; int strength; int disconnected = 0; int connectAttempts = 0; - long connectStart = 0; - long connectTime = 0; + uint32_t connectStart = 0; + uint32_t connectTime = 0; bool openSoftAP(); bool connect(); bool connectWiFi(); diff --git a/Sockets.cpp b/Sockets.cpp index 66b4279..97c26e0 100644 --- a/Sockets.cpp +++ b/Sockets.cpp @@ -82,96 +82,6 @@ void SocketEmitter::loop() { this->initClients(); sockServer.loop(); } -/* -ClientSocketEvent::ClientSocketEvent() { this->msg[0] = 0x00; } -ClientSocketEvent::ClientSocketEvent(const char *evt) { snprintf(this->msg, sizeof(this->msg), "42[%s,]", evt); } -//ClientSocketEvent::ClientSocketEvent(const char *evt, const char *payload) { snprintf(this->msg, sizeof(this->msg), "42[%s,%s]", evt, payload); } -void ClientSocketEvent::beginEmit(const char *evt) { snprintf(this->msg, sizeof(this->msg), "42[%s,", evt); } -void ClientSocketEvent::endEmit() { this->_safecat("]"); } -void ClientSocketEvent::appendMessage(const char *text) { - uint16_t len = strlen(this->msg); - this->msg[len - 1] = '\0'; - strcat(this->msg, text); - strcat(this->msg, "]"); -} -void ClientSocketEvent::beginObject(const char *name) { - if(name && strlen(name) > 0) this->appendElem(name); - else if(!this->_nocomma) this->_safecat(","); - this->_safecat("{"); - this->_objects++; - this->_nocomma = true; -} -void ClientSocketEvent::endObject() { - //if(strlen(this->buff) + 1 > this->buffSize - 1) this->send(); - this->_safecat("}"); - this->_objects--; - this->_nocomma = false; -} -void ClientSocketEvent::beginArray(const char *name) { - if(name && strlen(name) > 0) this->appendElem(name); - else if(!this->_nocomma) this->_safecat(","); - this->_safecat("["); - this->_arrays++; - this->_nocomma = true; -} -void ClientSocketEvent::endArray() { - //if(strlen(this->buff) + 1 > this->buffSize - 1) this->send(); - this->_safecat("]"); - this->_arrays--; - this->_nocomma = false; -} - - -void ClientSocketEvent::appendElem(const char *name) { - if(!this->_nocomma) this->_safecat(","); - if(name && strlen(name) > 0) { - this->_safecat(name, true); - this->_safecat(":"); - } - this->_nocomma = false; -} - -void ClientSocketEvent::addElem(const char *name, const char *val) { - if(!val) return; - this->appendElem(name); - this->_safecat(val, true); -} -void ClientSocketEvent::addElem(const char *val) { this->addElem(nullptr, val); } -void ClientSocketEvent::addElem(float fval) { sprintf(this->_numbuff, "%.4f", fval); this->_appendNumber(nullptr); } -void ClientSocketEvent::addElem(int8_t nval) { sprintf(this->_numbuff, "%d", nval); this->_appendNumber(nullptr); } -void ClientSocketEvent::addElem(uint8_t nval) { sprintf(this->_numbuff, "%u", nval); this->_appendNumber(nullptr); } -void ClientSocketEvent::addElem(int16_t nval) { sprintf(this->_numbuff, "%d", nval); this->_appendNumber(nullptr); } -void ClientSocketEvent::addElem(uint16_t nval) { sprintf(this->_numbuff, "%u", nval); this->_appendNumber(nullptr); } -void ClientSocketEvent::addElem(int32_t nval) { sprintf(this->_numbuff, "%ld", (long)nval); this->_appendNumber(nullptr); } -void ClientSocketEvent::addElem(uint32_t nval) { sprintf(this->_numbuff, "%lu", (unsigned long)nval); this->_appendNumber(nullptr); } -void ClientSocketEvent::addElem(int64_t lval) { sprintf(this->_numbuff, "%lld", (long long)lval); this->_appendNumber(nullptr); } -void ClientSocketEvent::addElem(uint64_t lval) { sprintf(this->_numbuff, "%llu", (unsigned long long)lval); this->_appendNumber(nullptr); } -void ClientSocketEvent::addElem(bool bval) { strcpy(this->_numbuff, bval ? "true" : "false"); this->_appendNumber(nullptr); } - -void ClientSocketEvent::addElem(const char *name, float fval) { sprintf(this->_numbuff, "%.4f", fval); this->_appendNumber(name); } -void ClientSocketEvent::addElem(const char *name, int8_t nval) { sprintf(this->_numbuff, "%d", nval); this->_appendNumber(name); } -void ClientSocketEvent::addElem(const char *name, uint8_t nval) { sprintf(this->_numbuff, "%u", nval); this->_appendNumber(name); } -void ClientSocketEvent::addElem(const char *name, int16_t nval) { sprintf(this->_numbuff, "%d", nval); this->_appendNumber(name); } -void ClientSocketEvent::addElem(const char *name, uint16_t nval) { sprintf(this->_numbuff, "%u", nval); this->_appendNumber(name); } -void ClientSocketEvent::addElem(const char *name, int32_t nval) { sprintf(this->_numbuff, "%ld", (long)nval); this->_appendNumber(name); } -void ClientSocketEvent::addElem(const char *name, uint32_t nval) { sprintf(this->_numbuff, "%lu", (unsigned long)nval); this->_appendNumber(name); } -void ClientSocketEvent::addElem(const char *name, int64_t lval) { sprintf(this->_numbuff, "%lld", (long long)lval); this->_appendNumber(name); } -void ClientSocketEvent::addElem(const char *name, uint64_t lval) { sprintf(this->_numbuff, "%llu", (unsigned long long)lval); this->_appendNumber(name); } -void ClientSocketEvent::addElem(const char *name, bool bval) { strcpy(this->_numbuff, bval ? "true" : "false"); this->_appendNumber(name); } - -void ClientSocketEvent::_safecat(const char *val, bool escape) { - size_t len = strlen(val) + strlen(this->msg); - if(escape) len += 2; - if(len >= sizeof(this->msg)) { - //this->send(); - } - if(escape) strcat(this->msg, "\""); - strcat(this->msg, val); - if(escape) strcat(this->msg, "\""); -} -void ClientSocketEvent::_appendNumber(const char *name) { this->appendElem(name); this->_safecat(this->_numbuff); } -*/ - JsonSockEvent *SocketEmitter::beginEmit(const char *evt) { this->json.beginEvent(&sockServer, evt, g_response, sizeof(g_response)); return &this->json; @@ -189,46 +99,6 @@ uint8_t SocketEmitter::activeClients(uint8_t room) { if(room < SOCK_MAX_ROOMS) return this->rooms[room].activeClients(); return 0; } -/* -bool SocketEmitter::sendToRoom(uint8_t room, ClientSocketEvent *evt) { - if(room < SOCK_MAX_ROOMS) { - room_t *r = &this->rooms[room]; - for(uint8_t i = 0; i < sizeof(r->clients); i++) { - if(r->clients[i] != 255) this->sendToClient(r->clients[i], evt); - } - return true; - } - return false; -} -bool SocketEmitter::sendToClients(ClientSocketEvent *evt) { - if(evt->msg[strlen(evt->msg) - 1] != ']') strcat(evt->msg, "]"); - return sockServer.broadcastTXT(evt->msg); -} -bool SocketEmitter::sendToClient(uint8_t num, ClientSocketEvent *evt) { - if(evt->msg[strlen(evt->msg) - 1] != ']') strcat(evt->msg, "]"); - return sockServer.sendTXT(num, evt->msg); -} -bool SocketEmitter::sendToClients(const char *evt, const char *payload) { - if(settings.status == DS_FWUPDATE) return true; - this->evt.prepareMessage(evt, payload); - return sockServer.broadcastTXT(this->evt.msg); -} -bool SocketEmitter::sendToClient(uint8_t num, const char *evt, const char *payload) { - if(settings.status == DS_FWUPDATE) return true; - this->evt.prepareMessage(evt, payload); - return sockServer.sendTXT(num, this->evt.msg); -} -bool SocketEmitter::sendToClient(uint8_t num, const char *evt, JsonDocument &doc) { - if(settings.status == DS_FWUPDATE) return true; - this->evt.prepareMessage(evt, doc); - return sockServer.sendTXT(num, this->evt.msg); -} -bool SocketEmitter::sendToClients(const char *evt, JsonDocument &doc) { - if(settings.status == DS_FWUPDATE) return true; - this->evt.prepareMessage(evt, doc); - return sockServer.broadcastTXT(this->evt.msg); -} -*/ void SocketEmitter::initClients() { for(uint8_t i = 0; i < sizeof(this->newClients); i++) { uint8_t num = this->newClients[i]; diff --git a/Sockets.h b/Sockets.h index f5336c3..f6acf46 100644 --- a/Sockets.h +++ b/Sockets.h @@ -13,60 +13,6 @@ struct room_t { bool join(uint8_t num); bool leave(uint8_t num); }; -/* -class ClientSocketEvent { - private: - uint8_t _objects = 0; - uint8_t _arrays = 0; - bool _nocomma = true; - char _numbuff[25] = {0}; - protected: - void _safecat(const char *val, bool escape = false); - void _appendNumber(const char *name); - public: - ClientSocketEvent(); - ClientSocketEvent(const char *evt); - //ClientSocketEvent(const char *evt, const char *data); - char msg[2048] = ""; - void beginEmit(const char *evt); - void endEmit(); - - void prepareMessage(const char *evt, const char *data); - void prepareMessage(const char *evt, JsonDocument &doc); - void appendMessage(const char *text); - void appendElement(const char *elem, const char *val); - - void beginObject(const char *name = nullptr); - void endObject(); - void beginArray(const char *name = nullptr); - void endArray(); - - void appendElem(const char *name = nullptr); - void addElem(const char* val); - void addElem(float fval); - void addElem(int8_t nval); - void addElem(uint8_t nval); - void addElem(int16_t nval); - void addElem(uint16_t nval); - void addElem(int32_t nval); - void addElem(uint32_t nval); - void addElem(int64_t lval); - void addElem(uint64_t lval); - void addElem(bool bval); - - void addElem(const char* name, float fval); - void addElem(const char* name, int8_t nval); - void addElem(const char* name, uint8_t nval); - void addElem(const char* name, int16_t nval); - void addElem(const char* name, uint16_t nval); - void addElem(const char* name, int32_t nval); - void addElem(const char* name, uint32_t nval); - void addElem(const char* name, int64_t lval); - void addElem(const char* name, uint64_t lval); - void addElem(const char* name, bool bval); - void addElem(const char *name, const char *val); -}; -*/ class SocketEmitter { protected: uint8_t newclients = 0; @@ -86,15 +32,6 @@ class SocketEmitter { JsonSockEvent * beginEmit(const char *evt); void endEmit(uint8_t num = 255); void endEmitRoom(uint8_t num); - /* - bool sendToRoom(uint8_t room, ClientSocketEvent *evt); - bool sendToClients(ClientSocketEvent *evt); - bool sendToClient(uint8_t num, ClientSocketEvent *evt); - bool sendToClients(const char *evt, const char *data); - bool sendToClient(uint8_t num, const char *evt, const char *data); - bool sendToClients(const char *evt, JsonDocument &doc); - bool sendToClient(uint8_t num, const char *evt, JsonDocument &doc); - */ static void wsEvent(uint8_t num, WStype_t type, uint8_t *payload, size_t length); }; #endif diff --git a/Somfy.cpp b/Somfy.cpp index c929d8c..3b1b9e2 100644 --- a/Somfy.cpp +++ b/Somfy.cpp @@ -57,6 +57,8 @@ somfy_commands translateSomfyCommand(const String& string) { else if (string.equalsIgnoreCase("Flag")) return somfy_commands::Flag; else if (string.equalsIgnoreCase("Sensor")) return somfy_commands::Sensor; else if (string.equalsIgnoreCase("Toggle")) return somfy_commands::Toggle; + else if (string.equalsIgnoreCase("Favorite")) return somfy_commands::Fav; + else if (string.startsWith("fav") || string.startsWith("FAV")) return somfy_commands::Fav; else if (string.startsWith("mud") || string.startsWith("MUD")) return somfy_commands::MyUpDown; else if (string.startsWith("md") || string.startsWith("MD")) return somfy_commands::MyDown; else if (string.startsWith("ud") || string.startsWith("UD")) return somfy_commands::UpDown; @@ -104,6 +106,8 @@ String translateSomfyCommand(const somfy_commands cmd) { return "Sensor"; case somfy_commands::Toggle: return "Toggle"; + case somfy_commands::Fav: + return "Favorite"; default: return "Unknown(" + String((uint8_t)cmd) + ")"; } @@ -154,7 +158,11 @@ void somfy_frame_t::decodeFrame(byte* frame) { this->remoteAddress = (decoded[6] + (decoded[5] << 8) + (decoded[4] << 16)); this->valid = this->checksum == checksum && this->remoteAddress > 0 && this->remoteAddress < 16777215; if (this->cmd != somfy_commands::Sensor && this->valid) this->valid = (this->rollingCode > 0); + if (this->valid) { + uint8_t csc80 = 0; + uint8_t cs80 = 0; + // Check for valid command. switch (this->cmd) { //case somfy_commands::Unknown0: @@ -177,7 +185,24 @@ void somfy_frame_t::decodeFrame(byte* frame) { case somfy_commands::StepUp: case somfy_commands::StepDown: case somfy_commands::Toggle: + case somfy_commands::Fav: // These must be 80 bit commands + csc80 = (decoded[9] & 0x0F); + cs80 = (((decoded[7] & 0xF0) >> 4) ^ ((decoded[8] & 0xF0) >> 4)); + cs80 ^= ((decoded[9] & 0xF0) >> 4); + cs80 ^= (decoded[7] & 0x0F); + cs80 ^= (decoded[8] & 0x0F); + //cs80 = (((decoded[7] & 0xF0) >> 4) ^ ((decoded[8] & 0xF0) >> 4) ^ ((decoded[9] & 0xF0) >> 4) ^ (decoded[7] & 0x0F) ^ (decoded[8] & 0x0F)); + if(csc80 != cs80) this->valid = false; + /* + uint8_t ai = ((decoded[7] & 0xF0) >> 4); + uint8_t aj = ((decoded[8] & 0xF0) >> 4); + uint8_t ak = ((decoded[9] & 0xF0) >> 4); + uint8_t al = (decoded[7] & 0x0F); + uint8_t am = (decoded[8] & 0x0F); + uint8_t an = (decoded[9] & 0x0F); + cs80 = ai ^ aj ^ ak ^ al ^ am; + */ break; default: this->valid = false; @@ -644,8 +669,10 @@ void SomfyRoom::clear() { void SomfyGroup::clear() { this->setGroupId(255); this->setRemoteAddress(0); - this->repeats = 1; - memset(&this->linkedShades[0], 0x00, sizeof(this->linkedShades)); + this->repeats = 0; + this->roomId = 0; + this->name[0] = 0x00; + memset(&this->linkedShades, 0x00, sizeof(this->linkedShades)); } bool SomfyShade::linkRemote(uint32_t address, uint16_t rollingCode) { // Check to see if the remote is already linked. If it is @@ -830,10 +857,13 @@ bool SomfyShade::isAtTarget() { else if(this->tiltType == tilt_types::none) return fabs(this->currentPos - this->target) < epsilon; return fabs(this->currentPos - this->target) < epsilon && fabs(this->currentTiltPos - this->tiltTarget) < epsilon; } +bool SomfyRemote::simMy() { return (this->flags & static_cast(somfy_flags_t::SimMy)) > 0; } +void SomfyRemote::setSimMy(bool bSimMy) { bSimMy ? this->flags |= static_cast(somfy_flags_t::SimMy) : this->flags &= ~(static_cast(somfy_flags_t::SimMy)); } bool SomfyRemote::hasSunSensor() { return (this->flags & static_cast(somfy_flags_t::SunSensor)) > 0;} bool SomfyRemote::hasLight() { return (this->flags & static_cast(somfy_flags_t::Light)) > 0; } void SomfyRemote::setSunSensor(bool bHasSensor ) { bHasSensor ? this->flags |= static_cast(somfy_flags_t::SunSensor) : this->flags &= ~(static_cast(somfy_flags_t::SunSensor)); } void SomfyRemote::setLight(bool bHasLight ) { bHasLight ? this->flags |= static_cast(somfy_flags_t::Light) : this->flags &= ~(static_cast(somfy_flags_t::Light)); } + void SomfyGroup::updateFlags() { uint8_t oldFlags = this->flags; this->flags = 0; @@ -2772,7 +2802,12 @@ void SomfyShade::moveToMyPosition() { if(this->tiltType != tilt_types::tiltonly && this->myPos >= 0.0f && this->myPos <= 100.0f) this->p_target(this->myPos); if(this->myTiltPos >= 0.0f && this->myTiltPos <= 100.0f) this->p_tiltTarget(this->myTiltPos); this->settingPos = false; - SomfyRemote::sendCommand(somfy_commands::My, this->repeats); + if(this->simMy()) { + Serial.print("Moving to simulated favorite\n"); + this->moveToTarget(this->myPos, this->myTiltPos); + } + else + SomfyRemote::sendCommand(somfy_commands::My, this->repeats); } void SomfyShade::sendCommand(somfy_commands cmd) { this->sendCommand(cmd, this->repeats); } void SomfyShade::sendCommand(somfy_commands cmd, uint8_t repeat) { @@ -3098,6 +3133,7 @@ int8_t SomfyShade::fromJSON(JsonObject &obj) { if(obj.containsKey("bitLength")) this->bitLength = obj["bitLength"]; if(obj.containsKey("proto")) this->proto = static_cast(obj["proto"].as()); if(obj.containsKey("sunSensor")) this->setSunSensor(obj["sunSensor"]); + if(obj.containsKey("simMy")) this->setSimMy(obj["simMy"]); if(obj.containsKey("light")) this->setLight(obj["light"]); if(obj.containsKey("gpioFlags")) this->gpioFlags = obj["gpioFlags"]; if(obj.containsKey("gpioLLTrigger")) { @@ -3180,24 +3216,6 @@ int8_t SomfyShade::fromJSON(JsonObject &obj) { } return err; } -/* -bool SomfyShade::toJSONRef(JsonObject &obj) { - obj["shadeId"] = this->getShadeId(); - obj["roomId"] = this->roomId; - obj["name"] = this->name; - obj["remoteAddress"] = this->m_remoteAddress; - obj["paired"] = this->paired; - obj["shadeType"] = static_cast(this->shadeType); - obj["bitLength"] = this->bitLength; - obj["proto"] = static_cast(this->proto); - obj["flags"] = this->flags; - obj["sunSensor"] = this->hasSunSensor(); - obj["hasLight"] = this->hasLight(); - obj["repeats"] = this->repeats; - SomfyRemote::toJSON(obj); - return true; -} -*/ void SomfyShade::toJSONRef(JsonResponse &json) { json.addElem("shadeId", this->getShadeId()); json.addElem("roomId", this->roomId); @@ -3249,6 +3267,7 @@ void SomfyShade::toJSON(JsonResponse &json) { json.addElem("gpioDown", this->gpioDown); json.addElem("gpioMy", this->gpioMy); json.addElem("gpioLLTrigger", ((this->gpioFlags & (uint8_t)gpio_flags_t::LowLevelTrigger) == 0) ? false : true); + json.addElem("simMy", this->simMy()); json.beginArray("linkedRemotes"); for(uint8_t i = 0; i < SOMFY_MAX_LINKED_REMOTES; i++) { SomfyLinkedRemote &lremote = this->linkedRemotes[i]; @@ -4093,13 +4112,6 @@ void SomfyShadeController::toJSONRepeaters(JsonResponse &json) { if(somfy.repeaters[i] != 0) json.addElem((uint8_t)somfy.repeaters[i]); } } -bool SomfyShadeController::toJSONRepeaters(JsonArray &arr) { - for(uint8_t i = 0; i < SOMFY_MAX_REPEATERS; i++) { - if(somfy.repeaters[i] != 0) arr.add(somfy.repeaters[i]); - } - return true; -} - void SomfyShadeController::loop() { this->transceiver.loop(); for(uint8_t i = 0; i < SOMFY_MAX_SHADES; i++) { diff --git a/Somfy.h b/Somfy.h index 410c4c5..25047e4 100644 --- a/Somfy.h +++ b/Somfy.h @@ -47,6 +47,7 @@ enum class somfy_commands : byte { RTWProto = 0xF, // RTW Protocol // Command extensions for 80 bit frames StepUp = 0x8B, + Fav = 0x90, }; enum class group_types : byte { channel = 0x00 @@ -155,7 +156,8 @@ enum class somfy_flags_t : byte { Light = 0x08, Windy = 0x10, Sunny = 0x20, - Lighted = 0x40 + Lighted = 0x40, + SimMy = 0x80 }; enum class gpio_flags_t : byte { LowLevelTrigger = 0x01 @@ -181,6 +183,7 @@ struct somfy_frame_t { uint32_t await = 0; uint8_t bitLength = 56; uint16_t pulseCount = 0; + uint8_t stepSize = 0; void print(); void encodeFrame(byte *frame); void decodeFrame(byte* frame); @@ -197,7 +200,6 @@ class SomfyRoom { void clear(); bool save(); bool fromJSON(JsonObject &obj); - //bool toJSON(JsonObject &obj); void toJSON(JsonResponse &json); void emitState(const char *evt = "roomState"); void emitState(uint8_t num, const char *evt = "roomState"); @@ -228,7 +230,6 @@ class SomfyRemote { uint8_t repeats = 1; virtual bool isLastCommand(somfy_commands cmd); char *getRemotePrefId() {return m_remotePrefId;} - //virtual bool toJSON(JsonObject &obj); virtual void toJSON(JsonResponse &json); virtual void setRemoteAddress(uint32_t address); virtual uint32_t getRemoteAddress(); @@ -236,8 +237,10 @@ class SomfyRemote { virtual uint16_t setRollingCode(uint16_t code); bool hasSunSensor(); bool hasLight(); + bool simMy(); void setSunSensor(bool bHasSensor); void setLight(bool bHasLight); + void setSimMy(bool bSimMy); virtual void sendCommand(somfy_commands cmd); virtual void sendCommand(somfy_commands cmd, uint8_t repeat); void sendSensorCommand(int8_t isWindy, int8_t isSunny, uint8_t repeat); @@ -280,10 +283,8 @@ class SomfyShade : public SomfyRemote { #ifdef USE_NVS void load(); #endif - //somfy_tx_queue_t txQueue; float currentPos = 0.0f; float currentTiltPos = 0.0f; - //uint16_t movement = 0; int8_t lastMovement = 0; int8_t direction = 0; // 0 = stopped, 1=down, -1=up. int8_t tiltDirection = 0; // 0=stopped, 1=clockwise, -1=counter clockwise @@ -294,10 +295,8 @@ class SomfyShade : public SomfyRemote { SomfyLinkedRemote linkedRemotes[SOMFY_MAX_LINKED_REMOTES]; bool paired = false; int8_t validateJSON(JsonObject &obj); - //bool toJSONRef(JsonObject &obj); void toJSONRef(JsonResponse &json); int8_t fromJSON(JsonObject &obj); - //bool toJSON(JsonObject &obj) override; void toJSON(JsonResponse &json) override; char name[21] = ""; @@ -548,16 +547,10 @@ class SomfyShadeController { SomfyGroup groups[SOMFY_MAX_GROUPS]; bool linkRepeater(uint32_t address); bool unlinkRepeater(uint32_t address); - //bool toJSON(DynamicJsonDocument &doc); - //bool toJSON(JsonObject &obj); - //bool toJSONRooms(JsonArray &arr); - //bool toJSONShades(JsonArray &arr); - //bool toJSONGroups(JsonArray &arr); void toJSONShades(JsonResponse &json); void toJSONRooms(JsonResponse &json); void toJSONGroups(JsonResponse &json); void toJSONRepeaters(JsonResponse &json); - bool toJSONRepeaters(JsonArray &arr); uint8_t repeaterCount(); uint8_t roomCount(); uint8_t shadeCount(); diff --git a/SomfyController.ino.esp32.bin b/SomfyController.ino.esp32.bin index 2df4380..ba757ee 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 53e2c5b..9ba9049 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 55ccfdb..8777b84 100644 Binary files a/SomfyController.littlefs.bin and b/SomfyController.littlefs.bin differ diff --git a/data/appversion b/data/appversion index 58073ef..6550da6 100644 --- a/data/appversion +++ b/data/appversion @@ -1 +1 @@ -2.4.1 \ No newline at end of file +2.4.3 \ No newline at end of file diff --git a/data/index.html b/data/index.html index 0817c4c..a56a24b 100644 --- a/data/index.html +++ b/data/index.html @@ -8,9 +8,9 @@ - - - + + + @@ -609,6 +609,13 @@ +
+
+ + +
+
+