diff --git a/ConfigSettings.cpp b/ConfigSettings.cpp index f4ff676..1e0832d 100644 --- a/ConfigSettings.cpp +++ b/ConfigSettings.cpp @@ -85,6 +85,20 @@ bool appver_t::toJSON(JsonObject &obj) { obj["suffix"] = this->suffix; return true; } +void appver_t::toJSON(JsonResponse &json) { + json.addElem("name", this->name); + json.addElem("major", this->major); + json.addElem("minor", this->minor); + json.addElem("build", this->build); + json.addElem("suffix", this->suffix); +} +void appver_t::toJSON(JsonSockEvent *json) { + json->addElem("name", this->name); + json->addElem("major", this->major); + json->addElem("minor", this->minor); + json->addElem("build", this->build); + json->addElem("suffix", this->suffix); +} bool BaseSettings::load() { return true; } bool BaseSettings::loadFile(const char *filename) { @@ -235,6 +249,14 @@ bool ConfigSettings::toJSON(JsonObject &obj) { obj["checkForUpdate"] = this->checkForUpdate; return true; } +void ConfigSettings::toJSON(JsonResponse &json) { + json.addElem("ssdpBroadcast", this->ssdpBroadcast); + json.addElem("hostname", this->hostname); + json.addElem("connType", static_cast(this->connType)); + json.addElem("chipModel", this->chipModel); + json.addElem("checkForUpdate", this->checkForUpdate); +} + bool ConfigSettings::requiresAuth() { return this->Security.type != security_types::None; } bool ConfigSettings::fromJSON(JsonObject &obj) { if(obj.containsKey("ssdpBroadcast")) this->ssdpBroadcast = obj["ssdpBroadcast"]; @@ -286,6 +308,18 @@ bool MQTTSettings::begin() { this->load(); return true; } +void MQTTSettings::toJSON(JsonResponse &json) { + json.addElem("enabled", this->enabled); + json.addElem("pubDisco", this->pubDisco); + json.addElem("protocol", this->protocol); + json.addElem("hostname", this->hostname); + json.addElem("port", this->port); + json.addElem("username", this->username); + json.addElem("password", this->password); + json.addElem("rootTopic", this->rootTopic); + json.addElem("discoTopic", this->discoTopic); +} + bool MQTTSettings::toJSON(JsonObject &obj) { obj["enabled"] = this->enabled; obj["pubDisco"] = this->pubDisco; @@ -384,6 +418,11 @@ bool NTPSettings::fromJSON(JsonObject &obj) { this->parseValueString(obj, "posixZone", this->posixZone, sizeof(this->posixZone)); return true; } +void NTPSettings::toJSON(JsonResponse &json) { + json.addElem("ntpServer", this->ntpServer); + json.addElem("posixZone", this->posixZone); +} + bool NTPSettings::toJSON(JsonObject &obj) { obj["ntpServer"] = this->ntpServer; obj["posixZone"] = this->posixZone; @@ -420,6 +459,16 @@ bool IPSettings::toJSON(JsonObject &obj) { obj["dns2"] = this->dns2 == ipEmpty ? "" : this->dns2.toString(); return true; } +void IPSettings::toJSON(JsonResponse &json) { + IPAddress ipEmpty(0,0,0,0); + json.addElem("dhcp", this->dhcp); + json.addElem("ip", this->ip.toString().c_str()); + json.addElem("gateway", this->gateway.toString().c_str()); + json.addElem("subnet", this->subnet.toString().c_str()); + json.addElem("dns1", this->dns1.toString().c_str()); + json.addElem("dns2", this->dns2.toString().c_str()); +} + bool IPSettings::save() { pref.begin("IP"); pref.clear(); @@ -480,6 +529,14 @@ bool SecuritySettings::toJSON(JsonObject &obj) { obj["permissions"] = this->permissions; return true; } +void SecuritySettings::toJSON(JsonResponse &json) { + json.addElem("type", static_cast(this->type)); + json.addElem("username", this->username); + json.addElem("password", this->password); + json.addElem("pin", this->pin); + json.addElem("permissions", this->permissions); +} + bool SecuritySettings::save() { pref.begin("SEC"); pref.clear(); @@ -531,6 +588,12 @@ bool WifiSettings::toJSON(JsonObject &obj) { obj["roaming"] = this->roaming; return true; } +void WifiSettings::toJSON(JsonResponse &json) { + json.addElem("ssid", this->ssid); + json.addElem("passphrase", this->passphrase); + json.addElem("roaming", this->roaming); +} + bool WifiSettings::save() { pref.begin("WIFI"); pref.clear(); @@ -628,6 +691,16 @@ bool EthernetSettings::toJSON(JsonObject &obj) { obj["MDIOPin"] = this->MDIOPin; return true; } +void EthernetSettings::toJSON(JsonResponse &json) { + json.addElem("boardType", this->boardType); + json.addElem("phyAddress", this->phyAddress); + json.addElem("CLKMode", static_cast(this->CLKMode)); + json.addElem("phyType", static_cast(this->phyType)); + json.addElem("PWRPin", this->PWRPin); + json.addElem("MDCPin", this->MDCPin); + json.addElem("MDIOPin", this->MDIOPin); +} + bool EthernetSettings::usesPin(uint8_t pin) { if((this->CLKMode == 0 || this->CLKMode == 1) && pin == 0) return true; else if(this->CLKMode == 2 && pin == 16) return true; diff --git a/ConfigSettings.h b/ConfigSettings.h index 58c43fb..cc12f1f 100644 --- a/ConfigSettings.h +++ b/ConfigSettings.h @@ -2,7 +2,7 @@ #include #ifndef configsettings_h #define configsettings_h - +#include "WResp.h" #define FW_VERSION "v2.4.2" enum DeviceStatus { DS_OK = 0, @@ -26,6 +26,8 @@ struct appver_t { char suffix[4] = ""; void parse(const char *ver); bool toJSON(JsonObject &obj); + void toJSON(JsonResponse &json); + void toJSON(JsonSockEvent *json); int8_t compare(appver_t &ver); void copy(appver_t &ver); }; @@ -36,6 +38,7 @@ class BaseSettings { bool loadFile(const char* filename); bool fromJSON(JsonObject &obj); bool toJSON(JsonObject &obj); + void toJSON(JsonResponse &json); bool parseIPAddress(JsonObject &obj, const char *prop, IPAddress *); bool parseValueString(JsonObject &obj, const char *prop, char *dest, size_t size); int parseValueInt(JsonObject &obj, const char *prop, int defVal); @@ -50,6 +53,7 @@ class NTPSettings: BaseSettings { char posixZone[64] = ""; bool fromJSON(JsonObject &obj); bool toJSON(JsonObject &obj); + void toJSON(JsonResponse &json); bool apply(); bool begin(); bool save(); @@ -66,6 +70,7 @@ class WifiSettings: BaseSettings { bool begin(); bool fromJSON(JsonObject &obj); bool toJSON(JsonObject &obj); + void toJSON(JsonResponse &json); String mapEncryptionType(int type); bool ssidExists(const char *ssid); void printNetworks(); @@ -88,6 +93,7 @@ class EthernetSettings: BaseSettings { bool begin(); bool fromJSON(JsonObject &obj); bool toJSON(JsonObject &obj); + void toJSON(JsonResponse &json); bool load(); bool save(); void print(); @@ -105,6 +111,7 @@ class IPSettings: BaseSettings { bool begin(); bool fromJSON(JsonObject &obj); bool toJSON(JsonObject &obj); + void toJSON(JsonResponse &json); bool load(); bool save(); void print(); @@ -129,6 +136,7 @@ class SecuritySettings: BaseSettings { bool load(); void print(); bool toJSON(JsonObject &obj); + void toJSON(JsonResponse &json); bool fromJSON(JsonObject &obj); }; class MQTTSettings: BaseSettings { @@ -146,6 +154,7 @@ class MQTTSettings: BaseSettings { bool save(); bool load(); bool toJSON(JsonObject &obj); + void toJSON(JsonResponse &json); bool fromJSON(JsonObject &obj); }; enum class conn_types : byte { @@ -175,6 +184,7 @@ class ConfigSettings: BaseSettings { bool requiresAuth(); bool fromJSON(JsonObject &obj); bool toJSON(JsonObject &obj); + void toJSON(JsonResponse &json); bool begin(); bool save(); bool load(); diff --git a/GitOTA.cpp b/GitOTA.cpp index 9a0ec04..ffa7210 100644 --- a/GitOTA.cpp +++ b/GitOTA.cpp @@ -8,6 +8,8 @@ #include "Sockets.h" #include "Somfy.h" #include "Web.h" +#include "WResp.h" + extern ConfigSettings settings; @@ -79,6 +81,20 @@ bool GitRelease::toJSON(JsonObject &obj) { this->version.toJSON(ver); return true; } +void GitRelease::toJSON(JsonResponse &json) { + Timestamp ts; + json.addElem("id", this->id); + json.addElem("name", this->name); + json.addElem("date", ts.getISOTime(this->releaseDate)); + json.addElem("draft", this->draft); + json.addElem("preRelease", this->preRelease); + json.addElem("main", this->main); + json.addElem("hasFS", this->hasFS); + json.addElem("hwVersions", this->hwVersions); + json.beginObject("version"); + this->version.toJSON(json); + json.endObject(); +} #define ERR_CLIENT_OFFSET -50 int16_t GitRepo::getReleases(uint8_t num) { @@ -221,6 +237,22 @@ int16_t GitRepo::getReleases(uint8_t num) { settings.printAvailHeap(); return 0; } +void GitRepo::toJSON(JsonResponse &json) { + json.beginObject("fwVersion"); + settings.fwVersion.toJSON(json); + json.endObject(); + json.beginObject("appVersion"); + settings.appVersion.toJSON(json); + json.endObject(); + json.beginArray("releases"); + for(uint8_t i = 0; i < GIT_MAX_RELEASES + 1; i++) { + if(this->releases[i].id == 0) continue; + json.beginObject(); + this->releases[i].toJSON(json); + json.endObject(); + } + json.endArray(); +} bool GitRepo::toJSON(JsonObject &obj) { JsonObject fw = obj.createNestedObject("fwVersion"); settings.fwVersion.toJSON(fw); @@ -299,6 +331,24 @@ void GitUpdater::setCurrentRelease(GitRepo &repo) { } this->emitUpdateCheck(); } +void GitUpdater::toJSON(JsonResponse &json) { + json.addElem("available", this->updateAvailable); + json.addElem("status", this->status); + json.addElem("error", this->error); + json.addElem("cancelled", this->cancelled); + json.addElem("checkForUpdate", settings.checkForUpdate); + json.addElem("inetAvailable", this->inetAvailable); + json.beginObject("fwVersion"); + settings.fwVersion.toJSON(json); + json.endObject(); + json.beginObject("appVersion"); + settings.appVersion.toJSON(json); + json.endObject(); + json.beginObject("latest"); + this->latest.toJSON(json); + json.endObject(); +} + void GitUpdater::toJSON(JsonObject &obj) { obj["available"] = this->updateAvailable; obj["status"] = this->status; @@ -314,6 +364,26 @@ void GitUpdater::toJSON(JsonObject &obj) { this->latest.toJSON(latest); } void GitUpdater::emitUpdateCheck(uint8_t num) { + JsonSockEvent *json = sockEmit.beginEmit("fwStatus"); + json->beginObject(); + json->addElem("available", this->updateAvailable); + json->addElem("status", this->status); + json->addElem("error", this->error); + json->addElem("cancelled", this->cancelled); + json->addElem("checkForUpdate", settings.checkForUpdate); + json->addElem("inetAvailable", this->inetAvailable); + json->beginObject("fwVersion"); + settings.fwVersion.toJSON(json); + json->endObject(); + json->beginObject("appVersion"); + settings.appVersion.toJSON(json); + json->endObject(); + json->beginObject("latest"); + this->latest.toJSON(json); + json->endObject(); + json->endObject(); + sockEmit.endEmit(num); + /* ClientSocketEvent evt("fwStatus"); DynamicJsonDocument doc(512); JsonObject obj = doc.to(); @@ -322,6 +392,7 @@ void GitUpdater::emitUpdateCheck(uint8_t num) { sockEmit.sendToClients("fwStatus", doc); else sockEmit.sendToClient(num, "fwStatus", doc); + */ } int GitUpdater::checkInternet() { int err = 500; @@ -353,10 +424,22 @@ int GitUpdater::checkInternet() { } void GitUpdater::emitDownloadProgress(size_t total, size_t loaded, const char *evt) { this->emitDownloadProgress(255, total, loaded, evt); } void GitUpdater::emitDownloadProgress(uint8_t num, size_t total, size_t loaded, const char *evt) { + JsonSockEvent *json = sockEmit.beginEmit(evt); + json->beginObject(); + json->addElem("ver", this->targetRelease); + json->addElem("part", this->partition); + json->addElem("file", this->currentFile); + json->addElem("total", total); + json->addElem("loaded", loaded); + json->addElem("error", this->error); + json->endObject(); + sockEmit.endEmit(num); + /* char buf[420]; snprintf(buf, sizeof(buf), "{\"ver\":\"%s\",\"part\":%d,\"file\":\"%s\",\"total\":%d,\"loaded\":%d, \"error\":%d}", this->targetRelease, this->partition, this->currentFile, total, loaded, this->error); if(num >= 255) sockEmit.sendToClients(evt, buf); else sockEmit.sendToClient(num, evt, buf); + */ sockEmit.loop(); webServer.loop(); } diff --git a/GitOTA.h b/GitOTA.h index 8a4019b..7cbcb84 100644 --- a/GitOTA.h +++ b/GitOTA.h @@ -4,6 +4,7 @@ #include #include #include "ConfigSettings.h" +#include "WResp.h" #define GIT_MAX_RELEASES 5 #define GIT_STATUS_READY 0 @@ -28,13 +29,14 @@ class GitRelease { 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 { public: @@ -59,6 +61,7 @@ class GitUpdater { void setCurrentRelease(GitRepo &repo); void loop(); void toJSON(JsonObject &obj); + void toJSON(JsonResponse &json); bool recoverFilesystem(); int checkInternet(); void emitUpdateCheck(uint8_t num=255); diff --git a/Network.cpp b/Network.cpp index 2e01ce9..b973369 100644 --- a/Network.cpp +++ b/Network.cpp @@ -155,23 +155,58 @@ void Network::emitSockets() { void Network::emitSockets(uint8_t num) { char buf[128]; if(this->connType == conn_types::ethernet) { + JsonSockEvent *json = sockEmit.beginEmit("ethernet"); + json->beginObject(); + json->addElem("connected", this->connected()); + json->addElem("speed", ETH.linkSpeed()); + json->addElem("fullduplex", ETH.fullDuplex()); + json->endObject(); + sockEmit.endEmit(num); + /* snprintf(buf, sizeof(buf), "{\"connected\":%s,\"speed\":%d,\"fullduplex\":%s}", this->connected() ? "true" : "false", ETH.linkSpeed(), ETH.fullDuplex() ? "true" : "false"); if(num == 255) sockEmit.sendToClients("ethernet", buf); else sockEmit.sendToClient(num, "ethernet", buf); + */ } else { if(WiFi.status() == WL_CONNECTED) { + JsonSockEvent *json = sockEmit.beginEmit("wifiStrength"); + json->beginObject(); + json->addElem("ssid", WiFi.SSID().c_str()); + json->addElem("strength", WiFi.RSSI()); + json->addElem("channel", this->channel); + json->endObject(); + sockEmit.endEmit(num); + /* snprintf(buf, sizeof(buf), "{\"ssid\":\"%s\",\"strength\":%d,\"channel\":%d}", WiFi.SSID().c_str(), WiFi.RSSI(), this->channel); if(num == 255) sockEmit.sendToClients("wifiStrength", buf); else sockEmit.sendToClient(num, "wifiStrength", buf); + */ this->lastRSSI = WiFi.RSSI(); this->lastChannel = WiFi.channel(); } else { + JsonSockEvent *json = sockEmit.beginEmit("wifiStrength"); + json->beginObject(); + json->addElem("ssid", ""); + json->addElem("strength", -100); + json->addElem("channel", -1); + json->endObject(); + sockEmit.endEmit(num); + + json = sockEmit.beginEmit("ethernet"); + json->beginObject(); + json->addElem("connected", false); + json->addElem("speed", 0); + json->addElem("fullduplex", false); + json->endObject(); + sockEmit.endEmit(num); + /* + if(num == 255) { sockEmit.sendToClients("wifiStrength", "{\"ssid\":\"\", \"strength\":-100,\"channel\":-1}"); sockEmit.sendToClients("ethernet", "{\"connected\":false,\"speed\":0,\"fullduplex\":false}"); @@ -180,6 +215,7 @@ void Network::emitSockets(uint8_t num) { sockEmit.sendToClient(num, "wifiStrength", "{\"ssid\":\"\", \"strength\":-100,\"channel\":-1}"); sockEmit.sendToClient(num, "ethernet", "{\"connected\":false,\"speed\":0,\"fullduplex\":false}"); } + */ this->lastRSSI = -100; this->lastChannel = -1; } @@ -239,9 +275,18 @@ void Network::setConnected(conn_types connType) { settings.IP.dns1 = ETH.dnsIP(0); settings.IP.dns2 = ETH.dnsIP(1); } + JsonSockEvent *json = sockEmit.beginEmit("ethernet"); + json->beginObject(); + json->addElem("connected", this->connected()); + json->addElem("speed", ETH.linkSpeed()); + json->addElem("fullduplex", ETH.fullDuplex()); + json->endObject(); + sockEmit.endEmit(); + /* char buf[128]; snprintf(buf, sizeof(buf), "{\"connected\":true,\"speed\":%d,\"fullduplex\":%s}", ETH.linkSpeed(), ETH.fullDuplex() ? "true" : "false"); sockEmit.sendToClients("ethernet", buf); + */ } } else { @@ -364,7 +409,7 @@ bool Network::connectWired() { uint32_t wait = millis(); while(millis() - wait < 14000) { - if(ETH.linkUp()) { + if(ETH.linkUp() && ETH.localIP() != INADDR_NONE) { net.mac = ETH.macAddress(); net.setConnected(conn_types::ethernet); return true; @@ -660,7 +705,18 @@ 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", 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_STOP: diff --git a/Sockets.cpp b/Sockets.cpp index cde9249..49c1452 100644 --- a/Sockets.cpp +++ b/Sockets.cpp @@ -16,6 +16,9 @@ extern GitUpdater git; WebSocketsServer sockServer = WebSocketsServer(8080); +#define MAX_SOCK_RESPONSE 2048 +static char g_response[MAX_SOCK_RESPONSE]; + bool room_t::isJoined(uint8_t num) { for(uint8_t i = 0; i < sizeof(this->clients); i++) { if(this->clients[i] == num) return true; @@ -49,7 +52,9 @@ uint8_t room_t::activeClients() { /********************************************************************* * ClientSocketEvent class members ********************************************************************/ +/* void ClientSocketEvent::prepareMessage(const char *evt, const char *payload) { + if(strlen(payload) + 5 >= sizeof(this->msg)) Serial.printf("Socket buffer overflow %d > 2048\n", strlen(payload) + 5 + strlen(evt)); snprintf(this->msg, sizeof(this->msg), "42[%s,%s]", evt, payload); } void ClientSocketEvent::prepareMessage(const char *evt, JsonDocument &doc) { @@ -58,6 +63,7 @@ void ClientSocketEvent::prepareMessage(const char *evt, JsonDocument &doc) { serializeJson(doc, &this->msg[strlen(this->msg)], sizeof(this->msg) - strlen(this->msg) - 2); strcat(this->msg, "]"); } +*/ /********************************************************************* * SocketEmitter class members @@ -76,56 +82,113 @@ void SocketEmitter::loop() { sockServer.loop(); } /* -bool SocketEmitter::sendToClients(const char *evt, JsonObject &obj) { - serializeJson(obj, g_buffer, sizeof(g_buffer)); - return this->sendToClients(evt, g_buffer); -} -bool SocketEmitter::sendToClient(uint8_t num, const char *evt, JsonObject &obj) { - serializeJson(obj, g_buffer, sizeof(g_buffer)); - return this->sendToClient(num, evt, g_buffer); -} -*/ -ClientSocketEvent::ClientSocketEvent() {} +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); } +//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::appendJSONElem(const char *elem) { - this->msg[strlen(this->msg) - 1] = '\0'; // Trim off the ending bracket. - uint16_t len = strlen(this->msg); - if(len > 0) { - if(this->msg[strlen(this->msg) - 1] == '{') 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(":"); } - strcat(this->msg, "\""); - strcat(this->msg, elem); - strcat(this->msg, "\":"); - strcat(this->msg, "]"); + this->_nocomma = false; } -void ClientSocketEvent::appendJSON(const char *elem, const char *text, bool quoted) { - this->appendJSONElem(elem); - this->msg[strlen(this->msg) - 1] = '\0'; // Trim off the ending bracket. - if(quoted) strcat(this->msg, "\""); - strcat(this->msg, text); - if(quoted) strcat(this->msg, "\""); - strcat(this->msg, "]"); + +void ClientSocketEvent::addElem(const char *name, const char *val) { + if(!val) return; + this->appendElem(name); + this->_safecat(val, true); } -void ClientSocketEvent::appendJSON(const char *elem, const bool b) { this->appendJSON(elem, b ? "true" : "false", false); } -void ClientSocketEvent::appendJSON(const char *elem, const uint8_t val) { - char buff[5]; - sprintf(buff, "%d", val); - this->appendJSON(elem, buff, false); +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; +} +void SocketEmitter::endEmit(uint8_t num) { this->json.endEvent(num); } +void SocketEmitter::endEmitRoom(uint8_t room) { + 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->json.endEvent(r->clients[i]); + } + } +} 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]; @@ -164,7 +227,7 @@ bool SocketEmitter::sendToClients(const char *evt, JsonDocument &doc) { this->evt.prepareMessage(evt, doc); return sockServer.broadcastTXT(this->evt.msg); } - +*/ void SocketEmitter::end() { sockServer.close(); } void SocketEmitter::disconnect() { sockServer.disconnect(); } void SocketEmitter::wsEvent(uint8_t num, WStype_t type, uint8_t *payload, size_t length) { diff --git a/Sockets.h b/Sockets.h index 4d1fc06..9c7c0a9 100644 --- a/Sockets.h +++ b/Sockets.h @@ -1,4 +1,5 @@ #include +#include "WResp.h" #ifndef sockets_h #define sockets_h @@ -6,31 +7,70 @@ #define ROOM_EMIT_FRAME 0 struct room_t { - uint8_t clients[5] = {255, 255, 255, 255}; + uint8_t clients[5] = {255, 255, 255, 255, 255}; uint8_t activeClients(); bool isJoined(uint8_t num); 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]; + //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 { - ClientSocketEvent evt; - public: + JsonSockEvent json; + //ClientSocketEvent evt; room_t rooms[SOCK_MAX_ROOMS]; uint8_t activeClients(uint8_t room); void startup(); @@ -38,6 +78,10 @@ class SocketEmitter { void loop(); void end(); void disconnect(); + 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); @@ -45,6 +89,7 @@ class SocketEmitter { 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 dbc9ee5..a58d5ca 100644 --- a/Somfy.cpp +++ b/Somfy.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include "Utils.h" #include "ConfigSettings.h" #include "Somfy.h" @@ -1820,6 +1821,32 @@ float SomfyShade::p_myTiltPos(float pos) { void SomfyShade::emitState(const char *evt) { this->emitState(255, evt); } void SomfyShade::emitState(uint8_t num, const char *evt) { + JsonSockEvent *json = sockEmit.beginEmit(evt); + json->beginObject(); + json->addElem("shadeId", this->shadeId); + json->addElem("type", static_cast(this->shadeType)); + json->addElem("remoteAddress", this->getRemoteAddress()); + json->addElem("name", this->name); + json->addElem("direction", this->direction); + json->addElem("position", this->transformPosition(this->currentPos)); + json->addElem("target", this->transformPosition(this->target)); + json->addElem("myPos", this->transformPosition(this->myPos)); + json->addElem("tiltType", static_cast(this->tiltType)); + json->addElem("flipCommands", this->flipCommands); + json->addElem("flipPosition", this->flipPosition); + json->addElem("flags", this->flags); + json->addElem("sunSensor", this->hasSunSensor()); + json->addElem("light", this->hasLight()); + json->addElem("sortOrder", this->sortOrder); + if(this->tiltType != tilt_types::none) { + json->addElem("tiltDirection", this->tiltDirection); + json->addElem("tiltTarget", this->transformPosition(this->tiltTarget)); + json->addElem("tiltPosition", this->transformPosition(this->currentTiltPos)); + json->addElem("myTiltPos", this->transformPosition(this->myTiltPos)); + } + json->endObject(); + sockEmit.endEmit(num); + /* char buf[420]; if(this->tiltType != tilt_types::none) snprintf(buf, sizeof(buf), "{\"shadeId\":%d,\"type\":%u,\"remoteAddress\":%d,\"name\":\"%s\",\"direction\":%d,\"position\":%d,\"target\":%d,\"myPos\":%d,\"myTiltPos\":%d,\"tiltType\":%u,\"tiltDirection\":%d,\"tiltTarget\":%d,\"tiltPosition\":%d,\"flipCommands\":%s,\"flipPosition\":%s,\"flags\":%d,\"sunSensor\":%s,\"light\":%s,\"sortOrder\":%d}", @@ -1834,9 +1861,21 @@ void SomfyShade::emitState(uint8_t num, const char *evt) { static_cast(this->tiltType), this->flipCommands ? "true" : "false", this->flipPosition ? "true": "false", this->flags, this->hasSunSensor() ? "true" : "false", this->hasLight() ? "true" : "false", this->sortOrder); if(num >= 255) sockEmit.sendToClients(evt, buf); else sockEmit.sendToClient(num, evt, buf); + */ } void SomfyShade::emitCommand(somfy_commands cmd, const char *source, uint32_t sourceAddress, const char *evt) { this->emitCommand(255, cmd, source, sourceAddress, evt); } void SomfyShade::emitCommand(uint8_t num, somfy_commands cmd, const char *source, uint32_t sourceAddress, const char *evt) { + JsonSockEvent *json = sockEmit.beginEmit(evt); + json->beginObject(); + json->addElem("shadeId", this->shadeId); + json->addElem("remoteAddress", this->getRemoteAddress()); + json->addElem("cmd", translateSomfyCommand(cmd).c_str()); + json->addElem("source", source); + json->addElem("rcode", this->lastRollingCode); + json->addElem("sourceAddress", sourceAddress); + json->endObject(); + sockEmit.endEmit(num); + /* ClientSocketEvent e(evt); char buf[30]; snprintf(buf, sizeof(buf), "{\"shadeId\":%d", this->shadeId); @@ -1853,6 +1892,7 @@ void SomfyShade::emitCommand(uint8_t num, somfy_commands cmd, const char *source e.appendMessage(buf); if(num >= 255) sockEmit.sendToClients(&e); else sockEmit.sendToClient(num, &e); + */ if(mqtt.connected()) { this->publish("cmdSource", source); this->publish("cmdAddress", sourceAddress); @@ -1861,6 +1901,14 @@ void SomfyShade::emitCommand(uint8_t num, somfy_commands cmd, const char *source } void SomfyRoom::emitState(const char *evt) { this->emitState(255, evt); } void SomfyRoom::emitState(uint8_t num, const char *evt) { + JsonSockEvent *json = sockEmit.beginEmit(evt); + json->beginObject(); + json->addElem("roomId", this->roomId); + json->addElem("name", this->name); + json->addElem("sortOrder", this->sortOrder); + json->endObject(); + sockEmit.endEmit(num); + /* ClientSocketEvent e(evt); char buf[55]; uint8_t flags = 0; @@ -1872,10 +1920,28 @@ void SomfyRoom::emitState(uint8_t num, const char *evt) { e.appendMessage(buf); if(num >= 255) sockEmit.sendToClients(&e); else sockEmit.sendToClient(num, &e); + */ this->publish(); } void SomfyGroup::emitState(const char *evt) { this->emitState(255, evt); } void SomfyGroup::emitState(uint8_t num, const char *evt) { + JsonSockEvent *json = sockEmit.beginEmit(evt); + json->beginObject(); + json->addElem("groupId", this->groupId); + json->addElem("remoteAddress", this->getRemoteAddress()); + json->addElem("name", this->name); + json->addElem("sunSensor", this->hasSunSensor()); + json->beginArray("shades"); + for(uint8_t i = 0; i < SOMFY_MAX_GROUPED_SHADES; i++) { + if(this->linkedShades[i] != 255 && this->linkedShades[i] != 0) { + SomfyShade *shade = somfy.getShadeById(this->linkedShades[i]); + if(shade) json->addElem(this->linkedShades[i]); + } + } + json->endArray(); + json->endObject(); + sockEmit.endEmit(num); + /* ClientSocketEvent e(evt); char buf[55]; uint8_t flags = 0; @@ -1906,6 +1972,7 @@ void SomfyGroup::emitState(uint8_t num, const char *evt) { if(num >= 255) sockEmit.sendToClients(&e); else sockEmit.sendToClient(num, &e); + */ this->publish(); } int8_t SomfyShade::transformPosition(float fpos) { @@ -3110,6 +3177,7 @@ int8_t SomfyShade::fromJSON(JsonObject &obj) { } return err; } +/* bool SomfyShade::toJSONRef(JsonObject &obj) { obj["shadeId"] = this->getShadeId(); obj["roomId"] = this->roomId; @@ -3126,6 +3194,70 @@ bool SomfyShade::toJSONRef(JsonObject &obj) { SomfyRemote::toJSON(obj); return true; } +*/ +void SomfyShade::toJSONRef(JsonResponse &json) { + json.addElem("shadeId", this->getShadeId()); + json.addElem("roomId", this->roomId); + json.addElem("name", this->name); + json.addElem("remoteAddress", this->m_remoteAddress); + json.addElem("paired", this->paired); + json.addElem("shadeType", static_cast(this->shadeType)); + json.addElem("bitLength", this->bitLength); + json.addElem("proto", static_cast(this->proto)); + json.addElem("flags", this->flags); + json.addElem("sunSensor", this->hasSunSensor()); + json.addElem("hasLight", this->hasLight()); + json.addElem("repeats", this->repeats); + //SomfyRemote::toJSON(json); +} + +void SomfyShade::toJSON(JsonResponse &json) { + json.addElem("shadeId", this->getShadeId()); + json.addElem("roomId", this->roomId); + json.addElem("name", this->name); + json.addElem("remoteAddress", this->m_remoteAddress); + json.addElem("upTime", this->upTime); + json.addElem("downTime", this->downTime); + json.addElem("paired", this->paired); + json.addElem("lastRollingCode", this->lastRollingCode); + json.addElem("position", this->transformPosition(this->currentPos)); + json.addElem("tiltType", static_cast(this->tiltType)); + json.addElem("tiltPosition", this->transformPosition(this->currentTiltPos)); + json.addElem("tiltDirection", this->tiltDirection); + json.addElem("tiltTime", this->tiltTime); + json.addElem("stepSize", this->stepSize); + json.addElem("tiltTarget", this->transformPosition(this->tiltTarget)); + json.addElem("target", this->transformPosition(this->target)); + json.addElem("myPos", this->transformPosition(this->myPos)); + json.addElem("myTiltPos", this->transformPosition(this->myTiltPos)); + json.addElem("direction", this->direction); + json.addElem("shadeType", static_cast(this->shadeType)); + json.addElem("bitLength", this->bitLength); + json.addElem("proto", static_cast(this->proto)); + json.addElem("flags", this->flags); + json.addElem("flipCommands", this->flipCommands); + json.addElem("flipPosition", this->flipPosition); + json.addElem("inGroup", this->isInGroup()); + json.addElem("sunSensor", this->hasSunSensor()); + json.addElem("light", this->hasLight()); + json.addElem("repeats", this->repeats); + json.addElem("sortOrder", this->sortOrder); + json.addElem("gpioUp", this->gpioUp); + 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.beginArray("linkedRemotes"); + for(uint8_t i = 0; i < SOMFY_MAX_LINKED_REMOTES; i++) { + SomfyLinkedRemote &lremote = this->linkedRemotes[i]; + if(lremote.getRemoteAddress() != 0) { + json.beginObject(); + lremote.toJSON(json); + json.endObject(); + } + } + json.endArray(); +} + bool SomfyShade::toJSON(JsonObject &obj) { //Serial.print("Serializing Shade:"); //Serial.print(this->getShadeId()); @@ -3189,6 +3321,11 @@ bool SomfyRoom::toJSON(JsonObject &obj) { obj["sortOrder"] = this->sortOrder; return true; } +void SomfyRoom::toJSON(JsonResponse &json) { + json.addElem("roomId", this->roomId); + json.addElem("name", this->name); + json.addElem("sortOrder", this->sortOrder); +} bool SomfyGroup::fromJSON(JsonObject &obj) { if(obj.containsKey("name")) strlcpy(this->name, obj["name"], sizeof(this->name)); @@ -3211,6 +3348,51 @@ bool SomfyGroup::fromJSON(JsonObject &obj) { } return true; } +void SomfyGroup::toJSON(JsonResponse &json) { + this->updateFlags(); + json.addElem("groupId", this->getGroupId()); + json.addElem("roomId", this->roomId); + json.addElem("name", this->name); + json.addElem("remoteAddress", this->m_remoteAddress); + json.addElem("lastRollingCode", this->lastRollingCode); + json.addElem("bitLength", this->bitLength); + json.addElem("proto", static_cast(this->proto)); + json.addElem("sunSensor", this->hasSunSensor()); + json.addElem("flipCommands", this->flipCommands); + json.addElem("flags", this->flags); + json.addElem("repeats", this->repeats); + json.addElem("sortOrder", this->sortOrder); + json.beginArray("linkedShades"); + for(uint8_t i = 0; i < SOMFY_MAX_GROUPED_SHADES; i++) { + uint8_t shadeId = this->linkedShades[i]; + if(shadeId > 0 && shadeId < 255) { + SomfyShade *shade = somfy.getShadeById(shadeId); + if(shade) { + json.beginObject(); + shade->toJSONRef(json); + json.endObject(); + } + } + } + json.endArray(); +} +void SomfyGroup::toJSONRef(JsonResponse &json) { + this->updateFlags(); + json.addElem("groupId", this->getGroupId()); + json.addElem("roomId", this->roomId); + json.addElem("name", this->name); + json.addElem("remoteAddress", this->m_remoteAddress); + json.addElem("lastRollingCode", this->lastRollingCode); + json.addElem("bitLength", this->bitLength); + json.addElem("proto", static_cast(this->proto)); + json.addElem("sunSensor", this->hasSunSensor()); + json.addElem("flipCommands", this->flipCommands); + json.addElem("flags", this->flags); + json.addElem("repeats", this->repeats); + json.addElem("sortOrder", this->sortOrder); +} + +/* bool SomfyGroup::toJSON(JsonObject &obj) { this->updateFlags(); obj["groupId"] = this->getGroupId(); @@ -3239,6 +3421,11 @@ bool SomfyGroup::toJSON(JsonObject &obj) { } return true; } +*/ +void SomfyRemote::toJSON(JsonResponse &json) { + json.addElem("remoteAddress", this->getRemoteAddress()); + json.addElem("lastRollingCode", this->lastRollingCode); +} bool SomfyRemote::toJSON(JsonObject &obj) { //obj["remotePrefId"] = this->getRemotePrefId(); obj["remoteAddress"] = this->getRemoteAddress(); @@ -3807,6 +3994,28 @@ uint16_t SomfyRemote::setRollingCode(uint16_t code) { } return code; } +void SomfyShadeController::toJSONRooms(JsonResponse &json) { + for(uint8_t i = 0; i < SOMFY_MAX_ROOMS; i++) { + SomfyRoom *room = &this->rooms[i]; + if(room->roomId != 0) { + json.beginObject(); + room->toJSON(json); + json.endObject(); + } + } +} +void SomfyShadeController::toJSONShades(JsonResponse &json) { + for(uint8_t i = 0; i < SOMFY_MAX_SHADES; i++) { + SomfyShade &shade = this->shades[i]; + if(shade.getShadeId() != 255) { + json.beginObject(); + shade.toJSON(json); + json.endObject(); + } + } +} + +/* bool SomfyShadeController::toJSON(DynamicJsonDocument &doc) { doc["maxRooms"] = SOMFY_MAX_ROOMS; doc["maxShades"] = SOMFY_MAX_SHADES; @@ -3836,23 +4045,7 @@ bool SomfyShadeController::toJSON(JsonObject &obj) { this->toJSONGroups(arrGroups); return true; } -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; -} -bool SomfyShadeController::toJSONRooms(JsonArray &arr) { - for(uint8_t i = 0; i < SOMFY_MAX_ROOMS; i++) { - SomfyRoom &room = this->rooms[i]; - if(room.roomId != 0) { - JsonObject oroom = arr.createNestedObject(); - room.toJSON(oroom); - } - } - return true; -} bool SomfyShadeController::toJSONShades(JsonArray &arr) { for(uint8_t i = 0; i < SOMFY_MAX_SHADES; i++) { @@ -3874,6 +4067,29 @@ bool SomfyShadeController::toJSONGroups(JsonArray &arr) { } return true; } +*/ +void SomfyShadeController::toJSONGroups(JsonResponse &json) { + for(uint8_t i = 0; i < SOMFY_MAX_GROUPS; i++) { + SomfyGroup &group = this->groups[i]; + if(group.getGroupId() != 255) { + json.beginObject(); + group.toJSON(json); + json.endObject(); + } + } +} +void SomfyShadeController::toJSONRepeaters(JsonResponse &json) { + for(uint8_t i = 0; i < SOMFY_MAX_REPEATERS; i++) { + if(somfy.repeaters[i] != 0) json.addElem(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++) { @@ -4244,10 +4460,21 @@ void Transceiver::endFrequencyScan() { } } void Transceiver::emitFrequencyScan(uint8_t num) { + JsonSockEvent *json = sockEmit.beginEmit("frequencyScan"); + json->beginObject(); + json->addElem("scanning", rxmode == 3); + json->addElem("testFreq", currFreq); + json->addElem("testRSSI", currRSSI); + json->addElem("frequency", markFreq); + json->addElem("RSSI", markRSSI); + json->endObject(); + sockEmit.endEmit(num); + /* char buf[420]; snprintf(buf, sizeof(buf), "{\"scanning\":%s,\"testFreq\":%f,\"testRSSI\":%d,\"frequency\":%f,\"RSSI\":%d}", rxmode == 3 ? "true" : "false", currFreq, currRSSI, markFreq, markRSSI); if(num >= 255) sockEmit.sendToClients("frequencyScan", buf); else sockEmit.sendToClient(num, "frequencyScan", buf); + */ } bool Transceiver::receive(somfy_rx_t *rx) { // Check to see if there is anything in the buffer @@ -4262,6 +4489,27 @@ bool Transceiver::receive(somfy_rx_t *rx) { } void Transceiver::emitFrame(somfy_frame_t *frame, somfy_rx_t *rx) { if(sockEmit.activeClients(ROOM_EMIT_FRAME) > 0) { + JsonSockEvent *json = sockEmit.beginEmit("remoteFrame"); + json->beginObject(); + json->addElem("encKey", frame->encKey); + json->addElem("address", frame->remoteAddress); + json->addElem("rcode", frame->rollingCode); + json->addElem("command", translateSomfyCommand(frame->cmd).c_str()); + json->addElem("rssi", frame->rssi); + json->addElem("bits", rx->bit_length); + json->addElem("proto", static_cast(frame->proto)); + json->addElem("valid", frame->valid); + json->addElem("sync", frame->hwsync); + json->beginArray("pulses"); + if(rx) { + for(uint16_t i = 0; i < rx->pulseCount; i++) { + json->addElem(rx->pulses[i]); + } + } + json->endArray(); + json->endObject(); + sockEmit.endEmitRoom(ROOM_EMIT_FRAME); + /* ClientSocketEvent evt("remoteFrame"); char buf[30]; snprintf(buf, sizeof(buf), "{\"encKey\":%d,", frame->encKey); @@ -4291,6 +4539,7 @@ void Transceiver::emitFrame(somfy_frame_t *frame, somfy_rx_t *rx) { } evt.appendMessage("]}"); sockEmit.sendToRoom(ROOM_EMIT_FRAME, &evt); + */ } } void Transceiver::clearReceived(void) { @@ -4317,6 +4566,12 @@ void Transceiver::disableReceive(void) { interruptPin = 0; } +void Transceiver::toJSON(JsonResponse& json) { + json.beginObject("config"); + this->config.toJSON(json); + json.endObject(); +} + bool Transceiver::toJSON(JsonObject& obj) { //Serial.println("Setting Transceiver Json"); JsonObject objConfig = obj.createNestedObject("config"); @@ -4393,6 +4648,22 @@ void transceiver_config_t::fromJSON(JsonObject& obj) { */ Serial.printf("SCK:%u MISO:%u MOSI:%u CSN:%u RX:%u TX:%u\n", this->SCKPin, this->MISOPin, this->MOSIPin, this->CSNPin, this->RXPin, this->TXPin); } +void transceiver_config_t::toJSON(JsonResponse &json) { + json.addElem("type", this->type); + json.addElem("TXPin", this->TXPin); + json.addElem("RXPin", this->RXPin); + json.addElem("SCKPin", this->SCKPin); + json.addElem("MOSIPin", this->MOSIPin); + json.addElem("MISOPin", this->MISOPin); + json.addElem("CSNPin", this->CSNPin); + json.addElem("rxBandwidth", this->rxBandwidth); // float + json.addElem("frequency", this->frequency); // float + json.addElem("deviation", this->deviation); // float + json.addElem("txPower", this->txPower); + json.addElem("proto", static_cast(this->proto)); + json.addElem("enabled", this->enabled); + json.addElem("radioInit", this->radioInit); +} void transceiver_config_t::toJSON(JsonObject& obj) { obj["type"] = this->type; obj["TXPin"] = this->TXPin; diff --git a/Somfy.h b/Somfy.h index 414beec..444cea0 100644 --- a/Somfy.h +++ b/Somfy.h @@ -1,6 +1,7 @@ #ifndef SOMFY_H #define SOMFY_H #include "ConfigSettings.h" +#include "WResp.h" #define SOMFY_MAX_SHADES 32 #define SOMFY_MAX_GROUPS 16 @@ -197,6 +198,7 @@ class SomfyRoom { 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"); void publish(); @@ -227,6 +229,7 @@ class SomfyRemote { 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(); virtual uint16_t getNextRollingCode(); @@ -291,9 +294,12 @@ class SomfyShade : public SomfyRemote { SomfyLinkedRemote linkedRemotes[SOMFY_MAX_LINKED_REMOTES]; bool paired = false; int8_t validateJSON(JsonObject &obj); - bool toJSONRef(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] = ""; void setShadeId(uint8_t id) { shadeId = id; } uint8_t getShadeId() { return shadeId; } @@ -377,7 +383,10 @@ class SomfyGroup : public SomfyRemote { bool save(); void clear(); bool fromJSON(JsonObject &obj); - bool toJSON(JsonObject &obj); + //bool toJSON(JsonObject &obj); + void toJSON(JsonResponse &json); + void toJSONRef(JsonResponse &json); + bool linkShade(uint8_t shadeId); bool unlinkShade(uint8_t shadeId); bool hasShadeId(uint8_t shadeId); @@ -467,6 +476,7 @@ struct transceiver_config_t { */ void fromJSON(JsonObject& obj); void toJSON(JsonObject& obj); + void toJSON(JsonResponse& json); void save(); void load(); void apply(); @@ -481,6 +491,7 @@ class Transceiver { transceiver_config_t config; bool printBuffer = false; bool toJSON(JsonObject& obj); + void toJSON(JsonResponse& json); bool fromJSON(JsonObject& obj); bool save(); bool begin(); @@ -537,11 +548,15 @@ 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); + //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(); diff --git a/SomfyController.ino.esp32.bin b/SomfyController.ino.esp32.bin index c6f25f1..f481a75 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 ebd63b3..5341186 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 31604e8..0a9d30d 100644 Binary files a/SomfyController.littlefs.bin and b/SomfyController.littlefs.bin differ diff --git a/WResp.cpp b/WResp.cpp new file mode 100644 index 0000000..3956759 --- /dev/null +++ b/WResp.cpp @@ -0,0 +1,139 @@ +#include "WResp.h" +void JsonSockEvent::beginEvent(WebSocketsServer *server, const char *evt, char *buff, size_t buffSize) { + this->server = server; + this->buff = buff; + this->buffSize = buffSize; + this->_nocomma = true; + this->_closed = false; + snprintf(this->buff, buffSize, "42[%s,", evt); +} +void JsonSockEvent::closeEvent() { + if(!this->_closed) { + if(strlen(this->buff) < buffSize) strcat(this->buff, "]"); + else this->buff[buffSize - 1] = ']'; + } + this->_nocomma = true; + this->_closed = true; +} +void JsonSockEvent::endEvent(uint8_t num) { + this->closeEvent(); + if(num == 255) this->server->broadcastTXT(this->buff); + else this->server->sendTXT(num, this->buff); +} +void JsonSockEvent::_safecat(const char *val, bool escape) { + size_t len = strlen(val) + strlen(this->buff); + if(escape) len += 2; + if(len >= this->buffSize) { + Serial.printf("Socket exceeded buffer size %d - %d\n", this->buffSize, len); + Serial.println(this->buff); + return; + } + if(escape) strcat(this->buff, "\""); + strcat(this->buff, val); + if(escape) strcat(this->buff, "\""); +} +void JsonResponse::beginResponse(WebServer *server, char *buff, size_t buffSize) { + this->server = server; + this->buff = buff; + this->buffSize = buffSize; + this->buff[0] = 0x00; + this->_nocomma = true; + server->setContentLength(CONTENT_LENGTH_UNKNOWN); +} +void JsonResponse::endResponse() { + if(strlen(buff)) this->send(); + server->sendContent("", 0); +} +void JsonResponse::send() { + if(!this->_headersSent) server->send_P(200, "application/json", this->buff); + else server->sendContent(this->buff); + //Serial.printf("Sent %d bytes %d\n", strlen(this->buff), this->buffSize); + this->buff[0] = 0x00; + this->_headersSent = true; +} +void JsonResponse::_safecat(const char *val, bool escape) { + size_t len = strlen(val) + strlen(this->buff); + if(escape) len += 2; + if(len >= this->buffSize) { + this->send(); + } + if(escape) strcat(this->buff, "\""); + strcat(this->buff, val); + if(escape) strcat(this->buff, "\""); +} + +void JsonFormatter::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 JsonFormatter::endObject() { + //if(strlen(this->buff) + 1 > this->buffSize - 1) this->send(); + this->_safecat("}"); + this->_objects--; + this->_nocomma = false; +} +void JsonFormatter::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 JsonFormatter::endArray() { + //if(strlen(this->buff) + 1 > this->buffSize - 1) this->send(); + this->_safecat("]"); + this->_arrays--; + this->_nocomma = false; +} + +void JsonFormatter::appendElem(const char *name) { + if(!this->_nocomma) this->_safecat(","); + if(name && strlen(name) > 0) { + this->_safecat(name, true); + this->_safecat(":"); + } + this->_nocomma = false; +} + +void JsonFormatter::addElem(const char *name, const char *val) { + if(!val) return; + this->appendElem(name); + this->_safecat(val, true); +} +void JsonFormatter::addElem(const char *val) { this->addElem(nullptr, val); } +void JsonFormatter::addElem(float fval) { sprintf(this->_numbuff, "%.4f", fval); this->_appendNumber(nullptr); } +void JsonFormatter::addElem(int8_t nval) { sprintf(this->_numbuff, "%d", nval); this->_appendNumber(nullptr); } +void JsonFormatter::addElem(uint8_t nval) { sprintf(this->_numbuff, "%u", nval); this->_appendNumber(nullptr); } +void JsonFormatter::addElem(int16_t nval) { sprintf(this->_numbuff, "%d", nval); this->_appendNumber(nullptr); } +void JsonFormatter::addElem(uint16_t nval) { sprintf(this->_numbuff, "%u", nval); this->_appendNumber(nullptr); } +void JsonFormatter::addElem(int32_t nval) { sprintf(this->_numbuff, "%ld", (long)nval); this->_appendNumber(nullptr); } +void JsonFormatter::addElem(uint32_t nval) { sprintf(this->_numbuff, "%lu", (unsigned long)nval); this->_appendNumber(nullptr); } +void JsonFormatter::addElem(int64_t lval) { sprintf(this->_numbuff, "%lld", (long long)lval); this->_appendNumber(nullptr); } +void JsonFormatter::addElem(uint64_t lval) { sprintf(this->_numbuff, "%llu", (unsigned long long)lval); this->_appendNumber(nullptr); } +void JsonFormatter::addElem(bool bval) { strcpy(this->_numbuff, bval ? "true" : "false"); this->_appendNumber(nullptr); } + +void JsonFormatter::addElem(const char *name, float fval) { sprintf(this->_numbuff, "%.4f", fval); this->_appendNumber(name); } +void JsonFormatter::addElem(const char *name, int8_t nval) { sprintf(this->_numbuff, "%d", nval); this->_appendNumber(name); } +void JsonFormatter::addElem(const char *name, uint8_t nval) { sprintf(this->_numbuff, "%u", nval); this->_appendNumber(name); } +void JsonFormatter::addElem(const char *name, int16_t nval) { sprintf(this->_numbuff, "%d", nval); this->_appendNumber(name); } +void JsonFormatter::addElem(const char *name, uint16_t nval) { sprintf(this->_numbuff, "%u", nval); this->_appendNumber(name); } +void JsonFormatter::addElem(const char *name, int32_t nval) { sprintf(this->_numbuff, "%ld", (long)nval); this->_appendNumber(name); } +void JsonFormatter::addElem(const char *name, uint32_t nval) { sprintf(this->_numbuff, "%lu", (unsigned long)nval); this->_appendNumber(name); } +void JsonFormatter::addElem(const char *name, int64_t lval) { sprintf(this->_numbuff, "%lld", (long long)lval); this->_appendNumber(name); } +void JsonFormatter::addElem(const char *name, uint64_t lval) { sprintf(this->_numbuff, "%llu", (unsigned long long)lval); this->_appendNumber(name); } +void JsonFormatter::addElem(const char *name, bool bval) { strcpy(this->_numbuff, bval ? "true" : "false"); this->_appendNumber(name); } + +void JsonFormatter::_safecat(const char *val, bool escape) { + size_t len = strlen(val) + strlen(this->buff); + if(escape) len += 2; + if(len >= this->buffSize) { + return; + } + if(escape) strcat(this->buff, "\""); + strcat(this->buff, val); + if(escape) strcat(this->buff, "\""); +} +void JsonFormatter::_appendNumber(const char *name) { this->appendElem(name); this->_safecat(this->_numbuff); } diff --git a/WResp.h b/WResp.h new file mode 100644 index 0000000..a81a34d --- /dev/null +++ b/WResp.h @@ -0,0 +1,68 @@ +#include +#include +#include "Somfy.h" +#ifndef wresp_h +#define wresp_h + +class JsonFormatter { + protected: + char *buff; + size_t buffSize; + bool _headersSent = false; + uint8_t _objects = 0; + uint8_t _arrays = 0; + bool _nocomma = true; + char _numbuff[25] = {0}; + virtual void _safecat(const char *val, bool escape = false); + void _appendNumber(const char *name); + public: + 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 JsonResponse : public JsonFormatter { + protected: + void _safecat(const char *val, bool escape = false) override; + public: + WebServer *server; + void beginResponse(WebServer *server, char *buff, size_t buffSize); + void endResponse(); + void send(); +}; +class JsonSockEvent : public JsonFormatter { + protected: + bool _closed = false; + void _safecat(const char *val, bool escape = false) override; + public: + WebSocketsServer *server = nullptr; + void beginEvent(WebSocketsServer *server, const char *evt, char *buff, size_t buffSize); + void endEvent(uint8_t clientNum = 255); + void closeEvent(); +}; +#endif diff --git a/Web.cpp b/Web.cpp index 45b89e2..ccebcff 100644 --- a/Web.cpp +++ b/Web.cpp @@ -8,6 +8,7 @@ #include "Utils.h" #include "SSDP.h" #include "Somfy.h" +#include "WResp.h" #include "Web.h" #include "MQTT.h" #include "GitOTA.h" @@ -23,7 +24,7 @@ extern GitUpdater git; extern Network net; //#define WEB_MAX_RESPONSE 34768 -#define WEB_MAX_RESPONSE 8192 +#define WEB_MAX_RESPONSE 4096 static char g_content[WEB_MAX_RESPONSE]; @@ -226,42 +227,7 @@ void Web::handleStreamFile(WebServer &server, const char *filename, const char * server.streamFile(file, encoding); file.close(); } -void Web::chunkRoomsResponse(WebServer &server, const char * elem) { - uint8_t ndx = 0; - if(elem && strlen(elem) > 0) { - sprintf(g_content, "\"%s\"", elem); - server.sendContent(g_content); - } - for(uint8_t i = 0; i < SOMFY_MAX_ROOMS; i++) { - if(somfy.rooms[i].roomId != 0) { - DynamicJsonDocument doc(512); - JsonObject obj = doc.to(); - somfy.rooms[i].toJSON(obj); - strcpy(g_content, ndx++ != 0 ? "," : "["); - serializeJson(doc, &g_content[strlen(g_content)], sizeof(g_content) - strlen(g_content)); - server.sendContent(g_content); - } - } - server.sendContent(ndx == 0 ? "[]" : "]"); -} -void Web::chunkShadesResponse(WebServer &server, const char * elem) { - uint8_t ndx = 0; - if(elem && strlen(elem) > 0) { - sprintf(g_content, "\"%s\"", elem); - server.sendContent(g_content); - } - for(uint8_t i = 0; i < SOMFY_MAX_SHADES; i++) { - if(somfy.shades[i].getShadeId() != 255) { - DynamicJsonDocument doc(1024); - JsonObject obj = doc.to(); - somfy.shades[i].toJSON(obj); - strcpy(g_content, ndx++ != 0 ? "," : "["); - serializeJson(doc, &g_content[strlen(g_content)], sizeof(g_content) - strlen(g_content)); - server.sendContent(g_content); - } - } - server.sendContent(ndx == 0 ? "[]" : "]"); -} +/* void Web::chunkGroupResponse(WebServer &server, SomfyGroup * grp, const char *prefix) { grp->updateFlags(); snprintf(g_content, sizeof(g_content), "%s{\"groupId\":%d,\"roomId\":%d,\"name\":\"%s\",\"remoteAddress\":%d,\"lastRollingCode\":%d,\"bitLength\":%d,\"proto\":%d,\"sunSensor\":%s,\"flipCommands\":%s,\"flags\":%d,\"repeats\":%d,\"sortOrder\":%d,\"linkedShades\":[ ", @@ -296,75 +262,71 @@ void Web::chunkGroupsResponse(WebServer &server, const char * elem) { } server.sendContent(ndx == 0 ? "[]" : "]"); } +*/ void Web::handleController(WebServer &server) { webServer.sendCORSHeaders(server); if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } HTTPMethod method = server.method(); settings.printAvailHeap(); if (method == HTTP_POST || method == HTTP_GET) { - server.setContentLength(CONTENT_LENGTH_UNKNOWN); - // Alright lets chunk our response. - snprintf(g_content, sizeof(g_content), "{\"maxRooms\":%d,\"maxShades\":%d,\"maxGroups\":%d,\"maxGroupedShades\":%d,\"maxLinkedRemotes\":%d,\"startingAddress\":%d,\"transceiver\":", - SOMFY_MAX_ROOMS, SOMFY_MAX_SHADES, SOMFY_MAX_GROUPS, SOMFY_MAX_GROUPED_SHADES, SOMFY_MAX_LINKED_REMOTES, somfy.startingAddress); - server.send_P(200, _encoding_json, g_content); - { - DynamicJsonDocument doc(1024); - JsonObject trans = doc.to(); - somfy.transceiver.toJSON(trans); - serializeJson(doc, g_content); - server.sendContent(g_content); - } - { - DynamicJsonDocument doc(512); - JsonObject fw = doc.to(); - git.toJSON(fw); - server.sendContent(",\"version\":"); - serializeJson(doc, g_content); - server.sendContent(g_content); - } - server.sendContent(",\"rooms\":"); - this->chunkRoomsResponse(server); - server.sendContent(",\"shades\":"); - this->chunkShadesResponse(server); - server.sendContent(",\"groups\":"); - this->chunkGroupsResponse(server); - server.sendContent(",\"repeaters\":"); - { - DynamicJsonDocument doc(512); - JsonArray r = doc.to(); - somfy.toJSONRepeaters(r); - serializeJson(doc, g_content); - server.sendContent(g_content); - } - server.sendContent("}"); - server.sendContent("", 0); + JsonResponse resp; + resp.beginResponse(&server, g_content, sizeof(g_content)); + resp.beginObject(); + resp.addElem("maxRooms", SOMFY_MAX_ROOMS); + resp.addElem("maxShades", SOMFY_MAX_SHADES); + resp.addElem("maxGroups", SOMFY_MAX_GROUPS); + resp.addElem("maxGroupedShades", SOMFY_MAX_GROUPED_SHADES); + resp.addElem("maxLinkedRemotes", SOMFY_MAX_LINKED_REMOTES); + resp.addElem("startingAddress", somfy.startingAddress); + resp.beginObject("transceiver"); + somfy.transceiver.toJSON(resp); + resp.endObject(); + resp.beginObject("version"); + git.toJSON(resp); + resp.endObject(); + resp.beginArray("rooms"); + somfy.toJSONRooms(resp); + resp.endArray(); + resp.beginArray("shades"); + somfy.toJSONShades(resp); + resp.endArray(); + resp.beginArray("groups"); + somfy.toJSONGroups(resp); + resp.endArray(); + resp.beginArray("repeaters"); + somfy.toJSONRepeaters(resp); + resp.endArray(); + resp.endObject(); + resp.endResponse(); } else server.send(404, _encoding_text, _response_404); } void Web::handleLoginContext(WebServer &server) { webServer.sendCORSHeaders(server); if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - DynamicJsonDocument doc(512); - JsonObject obj = doc.to(); - obj["type"] = static_cast(settings.Security.type); - obj["permissions"] = settings.Security.permissions; - obj["serverId"] = settings.serverId; - obj["version"] = settings.fwVersion.name; - obj["model"] = "ESPSomfyRTS"; - obj["hostname"] = settings.hostname; - serializeJson(doc, g_content); - server.send(200, _encoding_json, g_content); + JsonResponse resp; + resp.beginResponse(&server, g_content, sizeof(g_content)); + resp.beginObject(); + resp.addElem("type", static_cast(settings.Security.type)); + resp.addElem("permissions", settings.Security.permissions); + resp.addElem("serverId", settings.serverId); + resp.addElem("version", settings.fwVersion.name); + resp.addElem("model", "ESPSomfyRTS"); + resp.addElem("hostname", settings.hostname); + resp.endObject(); + resp.endResponse(); } void Web::handleGetRepeaters(WebServer &server) { webServer.sendCORSHeaders(server); if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } HTTPMethod method = server.method(); if (method == HTTP_POST || method == HTTP_GET) { - DynamicJsonDocument doc(512); - JsonArray r = doc.to(); - somfy.toJSONRepeaters(r); - serializeJson(doc, g_content); - server.send(200, _encoding_json, g_content); + JsonResponse resp; + resp.beginResponse(&server, g_content, sizeof(g_content)); + resp.beginArray(); + somfy.toJSONRepeaters(resp); + resp.endArray(); + resp.endResponse(); } else server.send(404, _encoding_text, _response_404); } @@ -373,10 +335,12 @@ void Web::handleGetRooms(WebServer &server) { if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } HTTPMethod method = server.method(); if (method == HTTP_POST || method == HTTP_GET) { - server.setContentLength(CONTENT_LENGTH_UNKNOWN); - server.send_P(200, _encoding_json, " "); - this->chunkRoomsResponse(server); - server.sendContent("", 0); + JsonResponse resp; + resp.beginResponse(&server, g_content, sizeof(g_content)); + resp.beginArray(); + somfy.toJSONRooms(resp); + resp.endArray(); + resp.endResponse(); } else server.send(404, _encoding_text, _response_404); } @@ -385,10 +349,12 @@ void Web::handleGetShades(WebServer &server) { if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } HTTPMethod method = server.method(); if (method == HTTP_POST || method == HTTP_GET) { - server.setContentLength(CONTENT_LENGTH_UNKNOWN); - server.send_P(200, _encoding_json, " "); - this->chunkShadesResponse(server); - server.sendContent("", 0); + JsonResponse resp; + resp.beginResponse(&server, g_content, sizeof(g_content)); + resp.beginArray(); + somfy.toJSONShades(resp); + resp.endArray(); + resp.endResponse(); } else server.send(404, _encoding_text, _response_404); } @@ -397,10 +363,12 @@ void Web::handleGetGroups(WebServer &server) { if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } HTTPMethod method = server.method(); if (method == HTTP_POST || method == HTTP_GET) { - server.setContentLength(CONTENT_LENGTH_UNKNOWN); - server.send_P(200, _encoding_json, " "); - this->chunkGroupsResponse(server); - server.sendContent("", 0); + JsonResponse resp; + resp.beginResponse(&server, g_content, sizeof(g_content)); + resp.beginArray(); + somfy.toJSONGroups(resp); + resp.endArray(); + resp.endResponse(); } else server.send(404, _encoding_text, _response_404); } @@ -444,20 +412,21 @@ void Web::handleShadeCommand(WebServer& server) { else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No shade object supplied.\"}")); SomfyShade* shade = somfy.getShadeById(shadeId); if (shade) { - Serial.print("Received:"); - Serial.println(server.arg("plain")); - // Send the command to the shade. - if (target <= 100) - shade->moveToTarget(shade->transformPosition(target)); - else if (repeat > 0) - shade->sendCommand(command, repeat); - else - shade->sendCommand(command); - DynamicJsonDocument sdoc(512); - JsonObject sobj = sdoc.to(); - shade->toJSON(sobj); - serializeJson(sdoc, g_content); - server.send(200, _encoding_json, g_content); + Serial.print("Received:"); + Serial.println(server.arg("plain")); + // Send the command to the shade. + if (target <= 100) + shade->moveToTarget(shade->transformPosition(target)); + else if (repeat > 0) + shade->sendCommand(command, repeat); + else + shade->sendCommand(command); + JsonResponse resp; + resp.beginResponse(&server, g_content, sizeof(g_content)); + resp.beginArray(); + shade->toJSONRef(resp); + resp.endArray(); + resp.endResponse(); } else { server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade with the specified id not found.\"}")); @@ -497,8 +466,8 @@ void Web::handleRepeatCommand(WebServer& server) { if (obj.containsKey("repeat")) repeat = obj["repeat"].as(); } } - DynamicJsonDocument sdoc(512); - JsonObject sobj = sdoc.to(); + //DynamicJsonDocument sdoc(512); + //JsonObject sobj = sdoc.to(); if(shadeId != 255) { SomfyShade *shade = somfy.getShadeById(shadeId); if(!shade) { @@ -513,9 +482,12 @@ void Web::handleRepeatCommand(WebServer& server) { else { shade->repeatFrame(repeat >= 0 ? repeat : shade->repeats); } - shade->toJSON(sobj); - serializeJson(sdoc, g_content); - server.send(200, _encoding_json, g_content); + JsonResponse resp; + resp.beginResponse(&server, g_content, sizeof(g_content)); + resp.beginArray(); + shade->toJSONRef(resp); + resp.endArray(); + resp.endResponse(); } else if(groupId != 255) { SomfyGroup * group = somfy.getGroupById(groupId); @@ -529,9 +501,16 @@ void Web::handleRepeatCommand(WebServer& server) { } else group->repeatFrame(repeat >= 0 ? repeat : group->repeats); - group->toJSON(sobj); - serializeJson(sdoc, g_content); - server.send(200, _encoding_json, g_content); + JsonResponse resp; + resp.beginResponse(&server, g_content, sizeof(g_content)); + resp.beginObject(); + group->toJSONRef(resp); + resp.endObject(); + resp.endResponse(); + + //group->toJSON(sobj); + //serializeJson(sdoc, g_content); + //server.send(200, _encoding_json, g_content); } } else { @@ -578,10 +557,12 @@ void Web::handleGroupCommand(WebServer &server) { // Send the command to the group. if(repeat > 0) group->sendCommand(command, repeat); else group->sendCommand(command); - server.setContentLength(CONTENT_LENGTH_UNKNOWN); - server.send_P(200, _encoding_json, " "); - this->chunkGroupResponse(server, group); - server.sendContent("", 0); + JsonResponse resp; + resp.beginResponse(&server, g_content, sizeof(g_content)); + resp.beginObject(); + group->toJSONRef(resp); + resp.endObject(); + resp.endResponse(); } else { server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Group with the specified id not found.\"}")); @@ -634,11 +615,12 @@ void Web::handleTiltCommand(WebServer &server) { shade->moveToTiltTarget(shade->transformPosition(target)); else shade->sendTiltCommand(command); - DynamicJsonDocument sdoc(512); - JsonObject sobj = sdoc.to(); - shade->toJSON(sobj); - serializeJson(sdoc, g_content); - server.send(200, _encoding_json, g_content); + JsonResponse resp; + resp.beginResponse(&server, g_content, sizeof(g_content)); + resp.beginObject(); + shade->toJSONRef(resp); + resp.endObject(); + resp.endResponse(); } else { server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade with the specified id not found.\"}")); @@ -656,11 +638,12 @@ void Web::handleRoom(WebServer &server) { int roomId = atoi(server.arg("roomId").c_str()); SomfyRoom* room = somfy.getRoomById(roomId); if (room) { - DynamicJsonDocument doc(512); - JsonObject obj = doc.to(); - room->toJSON(obj); - serializeJson(doc, g_content); - server.send(200, _encoding_json, g_content); + JsonResponse resp; + resp.beginResponse(&server, g_content, sizeof(g_content)); + resp.beginObject(); + room->toJSON(resp); + resp.endObject(); + resp.endResponse(); } else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Room Id not found.\"}")); } @@ -686,11 +669,12 @@ void Web::handleRoom(WebServer &server) { uint8_t err = room->fromJSON(obj); if(err == 0) { room->save(); - DynamicJsonDocument sdoc(2048); - JsonObject sobj = sdoc.to(); - room->toJSON(sobj); - serializeJson(sdoc, g_content); - server.send(200, _encoding_json, g_content); + JsonResponse resp; + resp.beginResponse(&server, g_content, sizeof(g_content)); + resp.beginObject(); + room->toJSON(resp); + resp.endObject(); + resp.endResponse(); } else { snprintf(g_content, sizeof(g_content), "{\"status\":\"DATA\",\"desc\":\"Data Error.\", \"code\":%d}", err); @@ -716,11 +700,12 @@ void Web::handleShade(WebServer &server) { int shadeId = atoi(server.arg("shadeId").c_str()); SomfyShade* shade = somfy.getShadeById(shadeId); if (shade) { - DynamicJsonDocument doc(2048); - JsonObject obj = doc.to(); - shade->toJSON(obj); - serializeJson(doc, g_content); - server.send(200, _encoding_json, g_content); + JsonResponse resp; + resp.beginResponse(&server, g_content, sizeof(g_content)); + resp.beginObject(); + shade->toJSON(resp); + resp.endObject(); + resp.endResponse(); } else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade Id not found.\"}")); } @@ -746,11 +731,12 @@ void Web::handleShade(WebServer &server) { uint8_t err = shade->fromJSON(obj); if(err == 0) { shade->save(); - DynamicJsonDocument sdoc(2048); - JsonObject sobj = sdoc.to(); - shade->toJSON(sobj); - serializeJson(sdoc, g_content); - server.send(200, _encoding_json, g_content); + JsonResponse resp; + resp.beginResponse(&server, g_content, sizeof(g_content)); + resp.beginObject(); + shade->toJSON(resp); + resp.endObject(); + resp.endResponse(); } else { snprintf(g_content, sizeof(g_content), "{\"status\":\"DATA\",\"desc\":\"Data Error.\", \"code\":%d}", err); @@ -776,10 +762,12 @@ void Web::handleGroup(WebServer &server) { int groupId = atoi(server.arg("groupId").c_str()); SomfyGroup* group = somfy.getGroupById(groupId); if (group) { - server.setContentLength(CONTENT_LENGTH_UNKNOWN); - server.send_P(200, _encoding_json, " "); - this->chunkGroupResponse(server, group); - server.sendContent("", 0); + JsonResponse resp; + resp.beginResponse(&server, g_content, sizeof(g_content)); + resp.beginObject(); + group->toJSON(resp); + resp.endObject(); + resp.endResponse(); } else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Group Id not found.\"}")); } @@ -804,11 +792,12 @@ void Web::handleGroup(WebServer &server) { if (group) { group->fromJSON(obj); group->save(); - DynamicJsonDocument sdoc(2048); - JsonObject sobj = sdoc.to(); - group->toJSON(sobj); - serializeJson(sdoc, g_content); - server.send(200, _encoding_json, g_content); + JsonResponse resp; + resp.beginResponse(&server, g_content, sizeof(g_content)); + resp.beginObject(); + group->toJSON(resp); + resp.endObject(); + resp.endResponse(); } else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Group Id not found.\"}")); } @@ -824,32 +813,34 @@ void Web::handleDiscovery(WebServer &server) { HTTPMethod method = apiServer.method(); if (method == HTTP_POST || method == HTTP_GET) { Serial.println("Discovery Requested"); - server.setContentLength(CONTENT_LENGTH_UNKNOWN); - // Alright lets chunk our response. char connType[10] = "Unknown"; if(net.connType == conn_types::ethernet) strcpy(connType, "Ethernet"); else if(net.connType == conn_types::wifi) strcpy(connType, "Wifi"); - snprintf(g_content, sizeof(g_content), "{\"serverId\":\"%s\",\"version\":\"%s\",\"latest\":\"%s\",\"model\":\"%s\",\"hostname\":\"%s\",\"authType\":%d,\"permissions\":%d,\"chipModel\":\"%s\",\"connType\":\"%s\",\"checkForUpdate\":%s", - settings.serverId, settings.fwVersion.name, git.latest.name, "ESPSomfyRTS", settings.hostname, static_cast(settings.Security.type), settings.Security.permissions, settings.chipModel, connType, settings.checkForUpdate ? "true" : "false"); - server.send_P(200, _encoding_json, g_content); - /* - if(net.connType == conn_types::ethernet) { - snprintf(g_content, sizeof(g_content), ",\"ethernet\":{\"connected\":true,\"speed\":%d,\"fullduplex\":%s}", ETH.linkSpeed(), ETH.fullDuplex() ? "true" : "false"); - server.sendContent(g_content); - } - else { - snprintf(g_content, sizeof(g_content), ",\"wifi\":{\"ssid\":\"%s\",\"strength\":%d,\"channel\":%d}", WiFi.SSID().c_str(), WiFi.RSSI(), WiFi.channel()); - server.sendContent(g_content); - } - */ - server.sendContent(",\"rooms\":"); - this->chunkRoomsResponse(server); - server.sendContent(",\"shades\":"); - this->chunkShadesResponse(server); - server.sendContent(",\"groups\":"); - this->chunkGroupsResponse(server); - server.sendContent("}"); - server.sendContent("", 0); + + JsonResponse resp; + resp.beginResponse(&server, g_content, sizeof(g_content)); + resp.beginObject(); + resp.addElem("serverId", settings.serverId); + resp.addElem("version", settings.fwVersion.name); + resp.addElem("latest", git.latest.name); + resp.addElem("model", "ESPSomfyRTS"); + resp.addElem("hostname", settings.hostname); + resp.addElem("authType", static_cast(settings.Security.type)); + resp.addElem("permissions", settings.Security.permissions); + resp.addElem("chipModel", settings.chipModel); + resp.addElem("connType", connType); + resp.addElem("checkForUpdate", settings.checkForUpdate); + resp.beginArray("rooms"); + somfy.toJSONRooms(resp); + resp.endArray(); + resp.beginArray("shades"); + somfy.toJSONShades(resp); + resp.endArray(); + resp.beginArray("groups"); + somfy.toJSONGroups(resp); + resp.endArray(); + resp.endObject(); + resp.endResponse(); net.needsBroadcast = true; } else @@ -916,11 +907,12 @@ void Web::handleSetPositions(WebServer &server) { if(pos >= 0) shade->target = shade->currentPos = pos; if(tiltPos >= 0 && shade->tiltType != tilt_types::none) shade->tiltTarget = shade->currentTiltPos = tiltPos; shade->emitState(); - DynamicJsonDocument sdoc(2048); - JsonObject sobj = sdoc.to(); - shade->toJSON(sobj); - serializeJson(sdoc, g_content); - server.send(200, _encoding_json, g_content); + JsonResponse resp; + resp.beginResponse(&server, g_content, sizeof(g_content)); + resp.beginObject(); + shade->toJSON(resp); + resp.endObject(); + resp.endResponse(); } else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"An invalid shadeId was provided\"}")); @@ -968,11 +960,12 @@ void Web::handleSetSensor(WebServer &server) { if(shade) { shade->sendSensorCommand(windy, sunny, repeat >= 0 ? (uint8_t)repeat : shade->repeats); shade->emitState(); - DynamicJsonDocument sdoc(2048); - JsonObject sobj = sdoc.to(); - shade->toJSON(sobj); - serializeJson(sdoc, g_content); - server.send(200, _encoding_json, g_content); + JsonResponse resp; + resp.beginResponse(&server, g_content, sizeof(g_content)); + resp.beginObject(); + shade->toJSON(resp); + resp.endObject(); + resp.endResponse(); } else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"An invalid shadeId was provided\"}")); @@ -983,11 +976,12 @@ void Web::handleSetSensor(WebServer &server) { if(group) { group->sendSensorCommand(windy, sunny, repeat >= 0 ? (uint8_t)repeat : group->repeats); group->emitState(); - DynamicJsonDocument sdoc(2048); - JsonObject sobj = sdoc.to(); - group->toJSON(sobj); - serializeJson(sdoc, g_content); - server.send(200, _encoding_json, g_content); + JsonResponse resp; + resp.beginResponse(&server, g_content, sizeof(g_content)); + resp.beginObject(); + group->toJSON(resp); + resp.endObject(); + resp.endResponse(); } else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"An invalid groupId was provided\"}")); @@ -1018,11 +1012,12 @@ void Web::handleDownloadFirmware(WebServer &server) { } } if(rel) { - DynamicJsonDocument sdoc(1024); - JsonObject sobj = sdoc.to(); - rel->toJSON(sobj); - serializeJson(sdoc, g_content); - server.send(200, _encoding_json, g_content); + JsonResponse resp; + resp.beginResponse(&server, g_content, sizeof(g_content)); + resp.beginObject(); + rel->toJSON(resp); + resp.endObject(); + resp.endResponse(); strcpy(git.targetRelease, rel->name); git.status = GIT_AWAITING_UPDATE; } @@ -1124,25 +1119,31 @@ void Web::begin() { GitRepo repo; repo.getReleases(); git.setCurrentRelease(repo); - DynamicJsonDocument doc(2048); - JsonObject obj = doc.to(); - repo.toJSON(obj); - serializeJson(doc, g_content); - server.send(200, _encoding_json, g_content); + JsonResponse resp; + resp.beginResponse(&server, g_content, sizeof(g_content)); + resp.beginObject(); + repo.toJSON(resp); + resp.endObject(); + resp.endResponse(); }); server.on("/downloadFirmware", []() { webServer.handleDownloadFirmware(server); }); server.on("/cancelFirmware", []() { webServer.sendCORSHeaders(server); if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - DynamicJsonDocument sdoc(512); - JsonObject sobj = sdoc.to(); + // If we are currently downloading the filesystem we cannot cancel. if(!git.lockFS) { git.status = GIT_UPDATE_CANCELLING; - git.toJSON(sobj); + JsonResponse resp; + resp.beginResponse(&server, g_content, sizeof(g_content)); + resp.beginObject(); + git.toJSON(resp); + resp.endObject(); + resp.endResponse(); git.cancelled = true; } - serializeJson(sdoc, g_content); - server.send(200, _encoding_json, g_content); + else { + server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Cannot cancel during filesystem update.\"}")); + } }); server.on("/backup", []() { webServer.handleBackup(server, true); }); server.on("/restore", HTTP_POST, []() { @@ -1208,39 +1209,40 @@ void Web::begin() { server.on("/getNextRoom", []() { webServer.sendCORSHeaders(server); if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - StaticJsonDocument<256> doc; - uint8_t roomId = somfy.getNextRoomId(); - JsonObject obj = doc.to(); - obj["roomId"] = roomId; - serializeJson(doc, g_content); - server.send(200, _encoding_json, g_content); + JsonResponse resp; + resp.beginResponse(&server, g_content, sizeof(g_content)); + resp.beginObject(); + resp.addElem("roomId", somfy.getNextRoomId()); + resp.endObject(); + resp.endResponse(); }); server.on("/getNextShade", []() { webServer.sendCORSHeaders(server); if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - StaticJsonDocument<256> doc; uint8_t shadeId = somfy.getNextShadeId(); - JsonObject obj = doc.to(); - obj["shadeId"] = shadeId; - obj["remoteAddress"] = somfy.getNextRemoteAddress(shadeId); - obj["bitLength"] = somfy.transceiver.config.type; - obj["stepSize"] = 100; - obj["proto"] = static_cast(somfy.transceiver.config.proto); - serializeJson(doc, g_content); - server.send(200, _encoding_json, g_content); + JsonResponse resp; + resp.beginResponse(&server, g_content, sizeof(g_content)); + resp.beginObject(); + resp.addElem("shadeId", shadeId); + resp.addElem("remoteAddress", somfy.getNextRemoteAddress(shadeId)); + resp.addElem("bitLength", somfy.transceiver.config.type); + resp.addElem("stepSize", 100); + resp.addElem("proto", static_cast(somfy.transceiver.config.proto)); + resp.endObject(); + resp.endResponse(); }); server.on("/getNextGroup", []() { webServer.sendCORSHeaders(server); - StaticJsonDocument<256> doc; - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } uint8_t groupId = somfy.getNextGroupId(); - JsonObject obj = doc.to(); - obj["groupId"] = groupId; - obj["remoteAddress"] = somfy.getNextRemoteAddress(groupId); - obj["bitLength"] = somfy.transceiver.config.type; - obj["proto"] = static_cast(somfy.transceiver.config.proto); - serializeJson(doc, g_content); - server.send(200, _encoding_json, g_content); + JsonResponse resp; + resp.beginResponse(&server, g_content, sizeof(g_content)); + resp.beginObject(); + resp.addElem("groupId", groupId); + resp.addElem("remoteAddress", somfy.getNextRemoteAddress(groupId)); + resp.addElem("bitLength", somfy.transceiver.config.type); + resp.addElem("proto", static_cast(somfy.transceiver.config.proto)); + resp.endObject(); + resp.endResponse(); }); server.on("/addRoom", []() { if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } @@ -1259,30 +1261,25 @@ void Web::begin() { Serial.println("Counting rooms"); if (somfy.roomCount() > SOMFY_MAX_ROOMS) { server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Maximum number of rooms exceeded.\"}")); + return; } else { Serial.println("Adding room"); room = somfy.addRoom(obj); - if (room) { - DynamicJsonDocument sdoc(512); - JsonObject sobj = sdoc.to(); - room->toJSON(sobj); - serializeJson(sdoc, g_content); - server.send(200, _encoding_json, g_content); - } - else { + if (!room) { server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Error adding room.\"}")); + return; } } } } if (room) { - DynamicJsonDocument doc(256); - JsonObject obj = doc.to(); - room->toJSON(obj); - serializeJson(doc, g_content); - Serial.println(g_content); - server.send(200, _encoding_json, g_content); + JsonResponse resp; + resp.beginResponse(&server, g_content, sizeof(g_content)); + resp.beginObject(); + room->toJSON(resp); + resp.endObject(); + resp.endResponse(); } else { server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Error saving Somfy Room.\"}")); @@ -1305,31 +1302,26 @@ void Web::begin() { Serial.println("Counting shades"); if (somfy.shadeCount() > SOMFY_MAX_SHADES) { server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Maximum number of shades exceeded.\"}")); + return; } else { Serial.println("Adding shade"); shade = somfy.addShade(obj); - if (shade) { - DynamicJsonDocument sdoc(1024); - JsonObject sobj = sdoc.to(); - shade->toJSON(sobj); - serializeJson(sdoc, g_content); - server.send(200, _encoding_json, g_content); - } - else { + if (!shade) { server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Error adding shade.\"}")); + return; } } } } if (shade) { //Serial.println("Serializing shade"); - DynamicJsonDocument doc(1024); - JsonObject obj = doc.to(); - shade->toJSON(obj); - serializeJson(doc, g_content); - //Serial.println(g_content); - server.send(200, _encoding_json, g_content); + JsonResponse resp; + resp.beginResponse(&server, g_content, sizeof(g_content)); + resp.beginObject(); + shade->toJSON(resp); + resp.endObject(); + resp.endResponse(); } else { server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Error saving Somfy Shade.\"}")); @@ -1352,30 +1344,25 @@ void Web::begin() { Serial.println("Counting shades"); if (somfy.groupCount() > SOMFY_MAX_GROUPS) { server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Maximum number of groups exceeded.\"}")); + return; } else { Serial.println("Adding group"); group = somfy.addGroup(obj); - if (group) { - DynamicJsonDocument sdoc(512); - JsonObject sobj = sdoc.to(); - group->toJSON(sobj); - serializeJson(sdoc, g_content); - server.send(200, _encoding_json, g_content); - } - else { + if (!group) { server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Error adding group.\"}")); + return; } } } } if (group) { - DynamicJsonDocument doc(256); - JsonObject obj = doc.to(); - group->toJSON(obj); - serializeJson(doc, g_content); - Serial.println(g_content); - server.send(200, _encoding_json, g_content); + JsonResponse resp; + resp.beginResponse(&server, g_content, sizeof(g_content)); + resp.beginObject(); + group->toJSON(resp); + resp.endObject(); + resp.endResponse(); } else { server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Error saving Somfy Group.\"}")); @@ -1390,10 +1377,11 @@ void Web::begin() { int groupId = atoi(server.arg("groupId").c_str()); SomfyGroup* group = somfy.getGroupById(groupId); if (group) { - DynamicJsonDocument doc(8192); - JsonObject obj = doc.to(); - group->toJSON(obj); - JsonArray arr = obj.createNestedArray("availShades"); + JsonResponse resp; + resp.beginResponse(&server, g_content, sizeof(g_content)); + resp.beginObject(); + group->toJSON(resp); + resp.beginArray("availShades"); for(uint8_t i = 0; i < SOMFY_MAX_SHADES; i++) { SomfyShade *shade = &somfy.shades[i]; if(shade->getShadeId() != 255) { @@ -1405,13 +1393,15 @@ void Web::begin() { } } if(!isLinked) { - JsonObject s = arr.createNestedObject(); - shade->toJSONRef(s); + resp.beginObject(); + shade->toJSONRef(resp); + resp.endObject(); } } } - serializeJson(doc, g_content); - server.send(200, _encoding_json, g_content); + resp.endArray(); + resp.endObject(); + resp.endResponse(); } else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Group Id not found.\"}")); } @@ -1442,11 +1432,12 @@ void Web::begin() { if (room) { room->fromJSON(obj); room->save(); - DynamicJsonDocument sdoc(512); - JsonObject sobj = sdoc.to(); - room->toJSON(sobj); - serializeJson(sdoc, g_content); - server.send(200, _encoding_json, g_content); + JsonResponse resp; + resp.beginResponse(&server, g_content, sizeof(g_content)); + resp.beginObject(); + room->toJSON(resp); + resp.endObject(); + resp.endResponse(); } else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Room Id not found.\"}")); } @@ -1479,11 +1470,12 @@ void Web::begin() { int8_t err = shade->fromJSON(obj); if(err == 0) { shade->save(); - DynamicJsonDocument sdoc(1024); - JsonObject sobj = sdoc.to(); - shade->toJSON(sobj); - serializeJson(sdoc, g_content); - server.send(200, _encoding_json, g_content); + JsonResponse resp; + resp.beginResponse(&server, g_content, sizeof(g_content)); + resp.beginObject(); + shade->toJSON(resp); + resp.endObject(); + resp.endResponse(); } else { snprintf(g_content, sizeof(g_content), "{\"status\":\"DATA\",\"desc\":\"Data Error.\", \"code\":%d}", err); @@ -1519,11 +1511,12 @@ void Web::begin() { if (group) { group->fromJSON(obj); group->save(); - DynamicJsonDocument sdoc(512); - JsonObject sobj = sdoc.to(); - group->toJSON(sobj); - serializeJson(sdoc, g_content); - server.send(200, _encoding_json, g_content); + JsonResponse resp; + resp.beginResponse(&server, g_content, sizeof(g_content)); + resp.beginObject(); + group->toJSON(resp); + resp.endObject(); + resp.endResponse(); } else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Group Id not found.\"}")); } @@ -1569,11 +1562,12 @@ void Web::begin() { if(shade->tiltType == tilt_types::none) tilt = -1; if(pos >= 0 && pos <= 100) shade->setMyPosition(shade->transformPosition(pos), shade->transformPosition(tilt)); - DynamicJsonDocument sdoc(512); - JsonObject sobj = sdoc.to(); - shade->toJSON(sobj); - serializeJson(sdoc, g_content); - server.send(200, _encoding_json, g_content); + JsonResponse resp; + resp.beginResponse(&server, g_content, sizeof(g_content)); + resp.beginObject(); + shade->toJSONRef(resp); + resp.endObject(); + resp.endResponse(); } else { server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade with the specified id not found.\"}")); @@ -1614,11 +1608,12 @@ void Web::begin() { } else { shade->setRollingCode(rollingCode); - DynamicJsonDocument doc(512); - JsonObject obj = doc.to(); - shade->toJSON(obj); - serializeJson(doc, g_content); - server.send(200, _encoding_json, g_content); + JsonResponse resp; + resp.beginResponse(&server, g_content, sizeof(g_content)); + resp.beginObject(); + shade->toJSON(resp); + resp.endObject(); + resp.endResponse(); } } }); @@ -1652,11 +1647,12 @@ void Web::begin() { else { shade->paired = paired; shade->save(); - DynamicJsonDocument doc(1024); - JsonObject obj = doc.to(); - shade->toJSON(obj); - serializeJson(doc, g_content); - server.send(200, _encoding_json, g_content); + JsonResponse resp; + resp.beginResponse(&server, g_content, sizeof(g_content)); + resp.beginObject(); + shade->toJSON(resp); + resp.endObject(); + resp.endResponse(); } }); server.on("/unpairShade", []() { @@ -1692,11 +1688,12 @@ void Web::begin() { shade->sendCommand(somfy_commands::Prog, 1); shade->paired = false; shade->save(); - DynamicJsonDocument doc(1024); - JsonObject obj = doc.to(); - shade->toJSON(obj); - serializeJson(doc, g_content); - server.send(200, _encoding_json, g_content); + JsonResponse resp; + resp.beginResponse(&server, g_content, sizeof(g_content)); + resp.beginObject(); + shade->toJSON(resp); + resp.endObject(); + resp.endResponse(); } } }); @@ -1727,11 +1724,12 @@ void Web::begin() { server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No repeater address was supplied.\"}")); else { somfy.linkRepeater(address); - DynamicJsonDocument doc(512); - JsonArray r = doc.to(); - somfy.toJSONRepeaters(r); - serializeJson(doc, g_content); - server.send(200, _encoding_json, g_content); + JsonResponse resp; + resp.beginResponse(&server, g_content, sizeof(g_content)); + resp.beginArray(); + somfy.toJSONRepeaters(resp); + resp.endArray(); + resp.endResponse(); } } }); @@ -1762,11 +1760,12 @@ void Web::begin() { server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No repeater address was supplied.\"}")); else { somfy.unlinkRepeater(address); - DynamicJsonDocument doc(512); - JsonArray r = doc.to(); - somfy.toJSONRepeaters(r); - serializeJson(doc, g_content); - server.send(200, _encoding_json, g_content); + JsonResponse resp; + resp.beginResponse(&server, g_content, sizeof(g_content)); + resp.beginArray(); + somfy.toJSONRepeaters(resp); + resp.endArray(); + resp.endResponse(); } } }); @@ -1795,11 +1794,12 @@ void Web::begin() { else { server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Remote address not provided.\"}")); } - DynamicJsonDocument sdoc(2048); - JsonObject sobj = sdoc.to(); - shade->toJSON(sobj); - serializeJson(sdoc, g_content); - server.send(200, _encoding_json, g_content); + JsonResponse resp; + resp.beginResponse(&server, g_content, sizeof(g_content)); + resp.beginObject(); + shade->toJSON(resp); + resp.endObject(); + resp.endResponse(); } else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade Id not found.\"}")); } @@ -1835,11 +1835,12 @@ void Web::begin() { else { server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Remote address not provided.\"}")); } - DynamicJsonDocument sdoc(2048); - JsonObject sobj = sdoc.to(); - shade->toJSON(sobj); - serializeJson(sdoc, g_content); - server.send(200, _encoding_json, g_content); + JsonResponse resp; + resp.beginResponse(&server, g_content, sizeof(g_content)); + resp.beginObject(); + shade->toJSON(resp); + resp.endObject(); + resp.endResponse(); } else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade Id not found.\"}")); } @@ -1885,11 +1886,12 @@ void Web::begin() { return; } group->linkShade(shadeId); - DynamicJsonDocument sdoc(2048); - JsonObject sobj = sdoc.to(); - group->toJSON(sobj); - serializeJson(sdoc, g_content); - server.send(200, _encoding_json, g_content); + JsonResponse resp; + resp.beginResponse(&server, g_content, sizeof(g_content)); + resp.beginObject(); + group->toJSON(resp); + resp.endObject(); + resp.endResponse(); } } else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No linking object supplied.\"}")); @@ -1940,11 +1942,12 @@ void Web::begin() { return; } group->unlinkShade(shadeId); - DynamicJsonDocument sdoc(2048); - JsonObject sobj = sdoc.to(); - group->toJSON(sobj); - serializeJson(sdoc, g_content); - server.send(200, _encoding_json, g_content); + JsonResponse resp; + resp.beginResponse(&server, g_content, sizeof(g_content)); + resp.beginObject(); + group->toJSON(resp); + resp.endObject(); + resp.endResponse(); } } else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No unlinking object supplied.\"}")); @@ -2180,21 +2183,28 @@ void Web::begin() { Serial.print(n); Serial.println(" networks"); // Ok we need to chunk this response as well. - server.setContentLength(CONTENT_LENGTH_UNKNOWN); - // Alright lets chunk our response to this because we cannot allocate all that memory. - snprintf(g_content, sizeof(g_content), "{\"connected\":{\"name\":\"%s\",\"passphrase\":\"%s\",\"strength\":%d,\"channel\":%d},\"accessPoints\":[", - settings.WIFI.ssid, settings.WIFI.passphrase, WiFi.RSSI(), WiFi.channel()); - server.send_P(200, _encoding_json, g_content); - bool bFirst = true; + JsonResponse resp; + resp.beginResponse(&server, g_content, sizeof(g_content)); + resp.beginObject(); + resp.beginObject("connected"); + resp.addElem("name", settings.WIFI.ssid); + resp.addElem("passphrase", settings.WIFI.passphrase); + resp.addElem("strength", WiFi.RSSI()); + resp.addElem("channel", WiFi.channel()); + resp.endObject(); + resp.beginArray("accessPoints"); for(int i = 0; i < n; ++i) { if(WiFi.SSID(i).length() == 0 || WiFi.RSSI(i) < -95) continue; // Ignore hidden and weak networks that we cannot connect to anyway. - snprintf(g_content, sizeof(g_content), "%s{\"name\":\"%s\",\"channel\":%d,\"encryption\":\"%s\",\"strength\":%d,\"macAddress\":\"%s\"}", - bFirst ? "" : ",", WiFi.SSID(i).c_str(), WiFi.channel(i), settings.WIFI.mapEncryptionType(WiFi.encryptionType(i)).c_str(), WiFi.RSSI(i), WiFi.BSSIDstr(i).c_str()); - server.sendContent(g_content); - bFirst = false; + resp.beginObject(); + resp.addElem("name", WiFi.SSID(i).c_str()); + resp.addElem("channel", WiFi.channel(i)); + resp.addElem("strength", WiFi.RSSI(i)); + resp.addElem("macAddress", WiFi.BSSIDstr(i).c_str()); + resp.endObject(); } - server.sendContent("]}"); - server.sendContent("", 0); + resp.endArray(); + resp.endObject(); + resp.endResponse(); }); server.on("/reboot", []() { webServer.handleReboot(server);}); server.on("/saveSecurity", []() { @@ -2253,12 +2263,12 @@ void Web::begin() { if (method == HTTP_POST || method == HTTP_PUT) { somfy.transceiver.fromJSON(obj); somfy.transceiver.save(); - DynamicJsonDocument sdoc(1024); - JsonObject sobj = sdoc.to(); - somfy.transceiver.toJSON(sobj); - serializeJson(sdoc, g_content); - server.send(200, _encoding_json, g_content); - //server.send(200, "application/json", "{\"status\":\"OK\",\"desc\":\"Successfully saved radio\"}"); + JsonResponse resp; + resp.beginResponse(&server, g_content, sizeof(g_content)); + resp.beginObject(); + somfy.transceiver.toJSON(resp); + resp.endObject(); + resp.endResponse(); } else { server.send(201, "application/json", "{\"status\":\"ERROR\",\"desc\":\"Invalid HTTP Method: \"}"); @@ -2267,11 +2277,12 @@ void Web::begin() { }); server.on("/getRadio", []() { webServer.sendCORSHeaders(server); - DynamicJsonDocument doc(1024); - JsonObject obj = doc.to(); - somfy.transceiver.toJSON(obj); - serializeJson(doc, g_content); - server.send(200, _encoding_json, g_content); + JsonResponse resp; + resp.beginResponse(&server, g_content, sizeof(g_content)); + resp.beginObject(); + somfy.transceiver.toJSON(resp); + resp.endObject(); + resp.endResponse(); }); server.on("/sendRemoteCommand", []() { webServer.sendCORSHeaders(server); @@ -2467,6 +2478,15 @@ void Web::begin() { }); server.on("/modulesettings", []() { webServer.sendCORSHeaders(server); + JsonResponse resp; + resp.beginResponse(&server, g_content, sizeof(g_content)); + resp.beginObject(); + resp.addElem("fwVersion", settings.fwVersion.name); + settings.toJSON(resp); + settings.NTP.toJSON(resp); + resp.endObject(); + resp.endResponse(); + /* DynamicJsonDocument doc(512); JsonObject obj = doc.to(); doc["fwVersion"] = settings.fwVersion.name; @@ -2476,9 +2496,27 @@ void Web::begin() { settings.NTP.toJSON(obj); serializeJson(doc, g_content); server.send(200, _encoding_json, g_content); + */ }); server.on("/networksettings", []() { webServer.sendCORSHeaders(server); + JsonResponse resp; + resp.beginResponse(&server, g_content, sizeof(g_content)); + resp.beginObject(); + resp.addElem("fwVersion", settings.fwVersion.name); + resp.beginObject("ethernet"); + settings.Ethernet.toJSON(resp); + resp.endObject(); + resp.beginObject("wifi"); + settings.WIFI.toJSON(resp); + resp.endObject(); + resp.beginObject("ip"); + settings.IP.toJSON(resp); + resp.endObject(); + resp.endObject(); + resp.endResponse(); + + /* DynamicJsonDocument doc(2048); JsonObject obj = doc.to(); doc["fwVersion"] = settings.fwVersion.name; @@ -2491,6 +2529,7 @@ void Web::begin() { settings.IP.toJSON(ip); serializeJson(doc, g_content); server.send(200, _encoding_json, g_content); + */ }); server.on("/connectmqtt", []() { if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } @@ -2510,12 +2549,19 @@ void Web::begin() { mqtt.disconnect(); settings.MQTT.fromJSON(obj); settings.MQTT.save(); - + JsonResponse resp; + resp.beginResponse(&server, g_content, sizeof(g_content)); + resp.beginObject(); + settings.MQTT.toJSON(resp); + resp.endObject(); + resp.endResponse(); + /* DynamicJsonDocument sdoc(1024); JsonObject sobj = sdoc.to(); settings.MQTT.toJSON(sobj); serializeJson(sdoc, g_content); server.send(200, _encoding_json, g_content); + */ } else { server.send(201, "application/json", "{\"status\":\"ERROR\",\"desc\":\"Invalid HTTP Method: \"}"); @@ -2524,11 +2570,20 @@ void Web::begin() { }); server.on("/mqttsettings", []() { webServer.sendCORSHeaders(server); + JsonResponse resp; + resp.beginResponse(&server, g_content, sizeof(g_content)); + resp.beginObject(); + settings.MQTT.toJSON(resp); + resp.endObject(); + resp.endResponse(); + + /* DynamicJsonDocument doc(1024); JsonObject obj = doc.to(); settings.MQTT.toJSON(obj); serializeJson(doc, g_content); server.send(200, _encoding_json, g_content); + */ }); server.on("/roomSortOrder", []() { if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } @@ -2626,20 +2681,36 @@ void Web::begin() { server.on("/beginFrequencyScan", []() { webServer.sendCORSHeaders(server); somfy.transceiver.beginFrequencyScan(); + JsonResponse resp; + resp.beginResponse(&server, g_content, sizeof(g_content)); + resp.beginObject(); + somfy.transceiver.toJSON(resp); + resp.endObject(); + resp.endResponse(); + /* DynamicJsonDocument doc(1024); JsonObject obj = doc.to(); somfy.transceiver.toJSON(obj); serializeJson(doc, g_content); server.send(200, _encoding_json, g_content); + */ }); server.on("/endFrequencyScan", []() { webServer.sendCORSHeaders(server); somfy.transceiver.endFrequencyScan(); + JsonResponse resp; + resp.beginResponse(&server, g_content, sizeof(g_content)); + resp.beginObject(); + somfy.transceiver.toJSON(resp); + resp.endObject(); + resp.endResponse(); + /* DynamicJsonDocument doc(1024); JsonObject obj = doc.to(); somfy.transceiver.toJSON(obj); serializeJson(doc, g_content); server.send(200, _encoding_json, g_content); + */ }); server.on("/recoverFilesystem", [] () { if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } diff --git a/Web.h b/Web.h index ba1fd3b..3339295 100644 --- a/Web.h +++ b/Web.h @@ -42,10 +42,9 @@ class Web { bool createAPIPasswordToken(const IPAddress ipAddress, const char *username, const char *password, char *token); bool isAuthenticated(WebServer &server, bool cfg = false); - void chunkRoomsResponse(WebServer &server, const char *elem = nullptr); - void chunkShadesResponse(WebServer &server, const char *elem = nullptr); - void chunkGroupsResponse(WebServer &server, const char *elem = nullptr); - void chunkGroupResponse(WebServer &server, SomfyGroup *, const char *prefix = nullptr); + //void chunkRoomsResponse(WebServer &server, const char *elem = nullptr); + //void chunkShadesResponse(WebServer &server, const char *elem = nullptr); + //void chunkGroupsResponse(WebServer &server, const char *elem = nullptr); + //void chunkGroupResponse(WebServer &server, SomfyGroup *, const char *prefix = nullptr); }; - #endif diff --git a/data/index.html b/data/index.html index dd93e0b..4869b58 100644 --- a/data/index.html +++ b/data/index.html @@ -3,11 +3,11 @@ - - - + + + - +
diff --git a/data/index.js b/data/index.js index aaff4cb..cfd383e 100644 --- a/data/index.js +++ b/data/index.js @@ -1,5 +1,6 @@ //var hst = '192.168.1.208'; -var hst = '192.168.1.152'; +//var hst = '192.168.1.152'; +var hst = '192.168.1.159'; var _rooms = [{ roomId: 0, name: 'Home' }]; var errors = [ @@ -562,6 +563,8 @@ async function initSockets() { await wifi.loadNetwork(); await somfy.loadSomfy(); await mqtt.loadMQTT(); + if (ui.isConfigOpen()) socket.send('join:0'); + //await general.init(); //await somfy.init(); //await mqtt.init(); @@ -1264,7 +1267,7 @@ var security = new Security(); class General { initialized = false; - appVersion = 'v2.4.1'; + appVersion = 'v2.4.2'; reloadApp = false; init() { if (this.initialized) return; @@ -2582,12 +2585,15 @@ class Somfy { divCtl += `${group.name}`; divCtl += `
`; if (typeof group.linkedShades !== 'undefined') { + divCtl += `${group.linkedShades.length}`; + /* for (let j = 0; j < group.linkedShades.length; j++) { divCtl += ''; if (j !== 0) divCtl += ', '; divCtl += group.linkedShades[j].name; divCtl += ''; } + */ } divCtl += '
'; divCtl += `
`; diff --git a/data/main.css b/data/main.css index e3fe079..68c7ed7 100644 --- a/data/main.css +++ b/data/main.css @@ -651,7 +651,10 @@ div.wait-overlay > .lds-roller { } .somfyGroupCtl .groupctl-shades { font-size:12px; - margin-top:-3px; + margin-top:0px; + } + .somfyGroupCtl .groupctl-shades > label { + margin-right:3px; } .somfyGroupCtl .groupctl-name, .somfyShadeCtl .shadectl-name {