Groups #350, #354, Ethernet #348

This commit is contained in:
Robert Strouse 2024-05-03 16:39:46 -07:00
parent 82c867d2eb
commit 2feb420551
19 changed files with 163 additions and 461 deletions

View file

@ -7,7 +7,7 @@ on:
env:
ARDUINO_BOARD_MANAGER_ADDITIONAL_URLS: "https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json"
ARDUINO_CLI_VERSION: "0.x"
ARDUINO_ESP32_VERSION: "2.0.14"
ARDUINO_ESP32_VERSION: "2.0.15"
ARDUINO_JSON_VERSION: "6.21.5"
ESPTOOL_VERSION: "4.7"
LITTLEFS_VERSION: "v2.5.1"

View file

@ -7,10 +7,10 @@
extern Preferences pref;
#define SHADE_HDR_VER 22
#define SHADE_HDR_VER 23
#define SHADE_HDR_SIZE 76
#define SHADE_REC_SIZE 276
#define GROUP_REC_SIZE 194
#define GROUP_REC_SIZE 200
#define TRANS_REC_SIZE 74
#define ROOM_REC_SIZE 29
#define REPEATER_REC_SIZE 77
@ -728,12 +728,14 @@ bool ShadeConfigFile::readGroupRecord(SomfyGroup *group) {
this->readString(group->name, sizeof(group->name));
group->proto = static_cast<radio_proto>(this->readUInt8(0));
group->bitLength = this->readUInt8(56);
if(this->header.version >= 23) group->lastRollingCode = this->readUInt16(0);
if(group->getRemoteAddress() != 0) {
uint16_t rc = pref.getUShort(group->getRemotePrefId(), 0);
group->lastRollingCode = max(rc, group->lastRollingCode);
if(rc < group->lastRollingCode) pref.putUShort(group->getRemotePrefId(), group->lastRollingCode);
}
uint8_t lsd = 0;
memset(group->linkedShades, 0x00, sizeof(group->linkedShades));
for(uint8_t j = 0; j < SOMFY_MAX_GROUPED_SHADES; j++) {
uint8_t shadeId = this->readUInt8(0);
// Do this to eliminate gaps.
@ -925,6 +927,7 @@ bool ShadeConfigFile::writeGroupRecord(SomfyGroup *group) {
this->writeString(group->name, sizeof(group->name));
this->writeUInt8(static_cast<uint8_t>(group->proto));
this->writeUInt8(group->bitLength);
this->writeUInt16(group->lastRollingCode);
for(uint8_t j = 0; j < SOMFY_MAX_GROUPED_SHADES; j++) {
this->writeUInt8(group->linkedShades[j]);
}

View file

@ -3,7 +3,7 @@
#ifndef configsettings_h
#define configsettings_h
#include "WResp.h"
#define FW_VERSION "v2.4.2"
#define FW_VERSION "v2.4.3"
enum DeviceStatus {
DS_OK = 0,
DS_ERROR = 1,
@ -161,7 +161,8 @@ enum class conn_types : byte {
unset = 0x00,
wifi = 0x01,
ethernet = 0x02,
ethernetpref = 0x03
ethernetpref = 0x03,
ap = 0x04
};
class ConfigSettings: BaseSettings {
public:

View file

@ -68,20 +68,6 @@ void GitRelease::setAssetProperty(const char *key, const char *val) {
}
}
}
bool GitRelease::toJSON(JsonObject &obj) {
Timestamp ts;
obj["id"] = this->id;
obj["name"] = this->name;
obj["date"] = ts.getISOTime(this->releaseDate);
obj["draft"] = this->draft;
obj["preRelease"] = this->preRelease;
obj["main"] = this->main;
obj["hasFS"] = this->hasFS;
obj["hwVersions"] = this->hwVersions;
JsonObject ver = obj.createNestedObject("version");
this->version.toJSON(ver);
return true;
}
void GitRelease::toJSON(JsonResponse &json) {
Timestamp ts;
char buff[20];
@ -228,7 +214,7 @@ int16_t GitRepo::getReleases(uint8_t num) {
}
else {
https.end();
//sclient.stop();
sclient.stop();
return httpCode;
}
}
@ -254,19 +240,6 @@ void GitRepo::toJSON(JsonResponse &json) {
}
json.endArray();
}
bool GitRepo::toJSON(JsonObject &obj) {
JsonObject fw = obj.createNestedObject("fwVersion");
settings.fwVersion.toJSON(fw);
JsonObject app = obj.createNestedObject("appVersion");
settings.appVersion.toJSON(app);
JsonArray arr = obj.createNestedArray("releases");
for(uint8_t i = 0; i < GIT_MAX_RELEASES + 1; i++) {
if(this->releases[i].id == 0) continue;
JsonObject o = arr.createNestedObject();
this->releases[i].toJSON(o);
}
return true;
}
#define UPDATE_ERR_OFFSET 20
#define ERR_DOWNLOAD_HTTP -40
#define ERR_DOWNLOAD_BUFFER -41
@ -274,11 +247,8 @@ bool GitRepo::toJSON(JsonObject &obj) {
void GitUpdater::loop() {
if(this->status == GIT_STATUS_READY) {
//if(this->lastCheck == 0)
//this->lastCheck = millis();
//else
if(settings.checkForUpdate &&
//(this->lastCheck + 14400000 < millis() || this->lastCheck == 0) && !rebootDelay.reboot) { // 4 hours
(millis() > 60000) && // Wait a minute before checking after boot.
(this->lastCheck + 86400000 < millis() || this->lastCheck == 0) && !rebootDelay.reboot) { // 1 day
this->checkForUpdate();
}
@ -302,6 +272,7 @@ void GitUpdater::loop() {
void GitUpdater::checkForUpdate() {
if(this->status != 0) return; // If we are already checking.
Serial.println("Check github for updates...");
this->status = GIT_STATUS_CHECK;
settings.printAvailHeap();
this->lastCheck = millis();
@ -349,21 +320,6 @@ void GitUpdater::toJSON(JsonResponse &json) {
this->latest.toJSON(json);
json.endObject();
}
void GitUpdater::toJSON(JsonObject &obj) {
obj["available"] = this->updateAvailable;
obj["status"] = this->status;
obj["error"] = this->error;
obj["cancelled"] = this->cancelled;
obj["checkForUpdate"] = settings.checkForUpdate;
obj["inetAvailable"] = this->inetAvailable;
JsonObject fw = obj.createNestedObject("fwVersion");
settings.fwVersion.toJSON(fw);
JsonObject app = obj.createNestedObject("appVersion");
settings.appVersion.toJSON(app);
JsonObject latest = obj.createNestedObject("latest");
this->latest.toJSON(latest);
}
void GitUpdater::emitUpdateCheck(uint8_t num) {
JsonSockEvent *json = sockEmit.beginEmit("fwStatus");
json->beginObject();
@ -384,16 +340,6 @@ void GitUpdater::emitUpdateCheck(uint8_t num) {
json->endObject();
json->endObject();
sockEmit.endEmit(num);
/*
ClientSocketEvent evt("fwStatus");
DynamicJsonDocument doc(512);
JsonObject obj = doc.to<JsonObject>();
this->toJSON(obj);
if(num == 255)
sockEmit.sendToClients("fwStatus", doc);
else
sockEmit.sendToClient(num, "fwStatus", doc);
*/
}
int GitUpdater::checkInternet() {
int err = 500;

View file

@ -28,14 +28,12 @@ class GitRelease {
appver_t version;
void setReleaseProperty(const char *key, const char *val);
void setAssetProperty(const char *key, const char *val);
bool toJSON(JsonObject &obj);
void toJSON(JsonResponse &json);
};
class GitRepo {
public:
int16_t getReleases(uint8_t num = GIT_MAX_RELEASES);
GitRelease releases[GIT_MAX_RELEASES + 1];
bool toJSON(JsonObject &obj);
void toJSON(JsonResponse &json);
};
class GitUpdater {
@ -60,7 +58,6 @@ class GitUpdater {
void setFirmwareFile();
void setCurrentRelease(GitRepo &repo);
void loop();
void toJSON(JsonObject &obj);
void toJSON(JsonResponse &json);
bool recoverFilesystem();
int checkInternet();

View file

@ -299,18 +299,6 @@ bool MQTTClass::publish(const char *topic, uint32_t val, bool retain) {
snprintf(g_content, sizeof(g_content), "%u", val);
return this->publish(topic, g_content, retain);
}
bool MQTTClass::publish(const char *topic, JsonDocument &doc, bool retain) {
serializeJson(doc, g_content, sizeof(g_content));
return this->publish(topic, g_content, retain);
}
bool MQTTClass::publish(const char *topic, JsonArray &arr, bool retain) {
serializeJson(arr, g_content, sizeof(g_content));
return this->publish(topic, g_content, retain);
}
bool MQTTClass::publish(const char *topic, JsonObject &obj, bool retain) {
serializeJson(obj, g_content, sizeof(g_content));
return this->publish(topic, g_content, retain);
}
bool MQTTClass::unpublish(const char *topic) {
if(mqttClient.connected()) {
char top[128];

3
MQTT.h
View file

@ -17,9 +17,6 @@ class MQTTClass {
void reset();
bool unpublish(const char *topic);
bool publish(const char *topic, const char *payload, bool retain = false);
bool publish(const char *topic, JsonDocument &doc, bool retain = false);
bool publish(const char *topic, JsonArray &arr, bool retain = false);
bool publish(const char *topic, JsonObject &obj, bool retain = false);
bool publish(const char *topic, uint8_t val, bool retain = false);
bool publish(const char *topic, int8_t val, bool retain = false);
bool publish(const char *topic, uint32_t val, bool retain = false);

View file

@ -15,6 +15,8 @@ extern SocketEmitter sockEmit;
extern MQTTClass mqtt;
extern rebootDelay_t rebootDelay;
extern Network net;
extern SomfyShadeController somfy;
static bool _apScanning = false;
static uint32_t _lastMaxHeap = 0;
@ -40,39 +42,26 @@ bool Network::setup() {
WiFi.mode(WIFI_STA);
settings.WIFI.printNetworks();
}
if(!this->connect()) this->openSoftAP();
//if(!this->connect()) this->openSoftAP();
return true;
}
void Network::loop() {
this->connect();
if(!this->connected() || this->connecting()) return;
if(millis() - this->lastEmit > 1500) {
this->lastEmit = millis();
if(!this->softAPOpened) {
while(!this->connect()) {
// If we lost our connection
connectRetries++;
if(connectRetries > 100) {
if(!this->connected()) this->openSoftAP();
break;
}
sockEmit.loop();
}
connectRetries = 0;
}
if(this->connected()) {
this->emitSockets();
this->lastEmit = millis();
if(!this->connected()) return;
}
}
}
sockEmit.loop();
if(this->connected() && millis() - this->lastMDNS > 60000) {
// We are doing this every 60 seconds because of the BS related to
// the MDNS library. The original library required manual updates
// to the MDNS or it would lose its hostname after 2 minutes.
//if(this->lastMDNS != 0) MDNS.setInstanceName(settings.hostname);
// Every 60 seconds we are going to look at wifi connectivity
// to get around the roaming issues with ESP32. We will try to do this in an async manner. If
// there is a channel that is better we will stop the radio and reconnect
// there is a channel that is better we will stop the wifi radio and reconnect
if(this->connType == conn_types::wifi && settings.WIFI.roaming && !this->softAPOpened) {
// If we are not already scanning then we need to start a passive scan
// and only respond if there is a better connection.
@ -237,6 +226,7 @@ void Network::setConnected(conn_types connType) {
WiFi.softAPdisconnect(true);
WiFi.mode(WIFI_STA);
}
this->_connecting = false;
this->ssid = WiFi.SSID();
this->mac = WiFi.BSSIDstr();
this->strength = WiFi.RSSI();
@ -248,6 +238,7 @@ void Network::setConnected(conn_types connType) {
WiFi.softAPdisconnect(true);
WiFi.mode(WIFI_OFF);
}
this->_connecting = false;
this->wifiFallback = false;
}
sockEmit.begin();
@ -365,8 +356,8 @@ void Network::setConnected(conn_types connType) {
this->needsBroadcast = true;
}
bool Network::connectWired() {
//if(this->connType == conn_types::ethernet && ETH.linkUp()) {
if(ETH.linkUp()) {
// If the ethernet link is re-established then we need to shut down wifi.
if(WiFi.status() == WL_CONNECTED) {
sockEmit.end();
WiFi.disconnect(true);
@ -386,7 +377,11 @@ bool Network::connectWired() {
}
else
Serial.println("Connecting to Wired Ethernet");
this->connectAttempts++;
this->_connecting = true;
this->connTarget = conn_types::ethernet;
this->connType = conn_types::unset;
if(!this->ethStarted) {
this->ethStarted = true;
WiFi.mode(WIFI_OFF);
@ -415,24 +410,11 @@ bool Network::connectWired() {
}
else
ETH.config(INADDR_NONE, INADDR_NONE, INADDR_NONE, INADDR_NONE);
uint32_t wait = millis();
while(millis() - wait < 14000) {
if(ETH.linkUp() && ETH.localIP() != INADDR_NONE) {
net.mac = ETH.macAddress();
net.setConnected(conn_types::ethernet);
}
}
this->connectStart = millis();
return true;
}
delay(500);
}
if(settings.connType == conn_types::ethernetpref) {
this->wifiFallback = true;
return connectWiFi();
}
}
}
return false;
}
void Network::updateHostname() {
if(settings.hostname[0] != '\0' && this->connected()) {
if(this->connType == conn_types::ethernet &&
@ -467,11 +449,12 @@ bool Network::connectWiFi() {
}
else Serial.println("Connecting to AP");
this->connectAttempts++;
this->connectStart = millis();
this->_connecting = true;
this->connTarget = conn_types::wifi;
this->connType = conn_types::unset;
WiFi.setSleep(false);
WiFi.mode(WIFI_MODE_NULL);
//WiFi.onEvent(this->networkEvent);
if(!settings.IP.dhcp) {
if(!WiFi.config(settings.IP.ip, settings.IP.gateway, settings.IP.subnet, settings.IP.dns1, settings.IP.dns2))
WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE, INADDR_NONE);
@ -494,80 +477,35 @@ bool Network::connectWiFi() {
}
else
WiFi.begin(settings.WIFI.ssid, settings.WIFI.passphrase);
delay(100);
int retries = 0;
while(retries < 100) {
switch(WiFi.status()) {
case WL_SCAN_COMPLETED:
Serial.println("Status: Scan Completed");
break;
case WL_CONNECT_FAILED:
if(this->connectAttempts == 1) Serial.println();
Serial.println("WiFi Module connection failed");
return false;
case WL_DISCONNECTED:
break;
case WL_IDLE_STATUS:
Serial.print("*");
break;
case WL_CONNECTED:
//WiFi.hostname(settings.hostname);
this->ssid = WiFi.SSID();
this->mac = WiFi.BSSIDstr();
this->strength = WiFi.RSSI();
this->channel = WiFi.channel();
this->setConnected(conn_types::wifi);
WiFi.setSleep(false);
this->connectStart = millis();
}
return true;
case WL_NO_SHIELD:
Serial.println("Connection failed - WiFi module not found");
return false;
case WL_NO_SSID_AVAIL:
Serial.print(" Connection failed the SSID ");
Serial.print(settings.WIFI.ssid);
Serial.println(" could not be found");
return false;
default:
break;
}
delay(500);
if(connectAttempts == 1) Serial.print("*");
retries++;
}
if(this->connectAttempts != 1) {
int st = this->getStrengthBySSID(settings.WIFI.ssid);
Serial.print("(");
Serial.print(st);
Serial.print("dBm) ");
Serial.println("Failed");
//if(disconnected > 0 && st == -100) settings.WIFI.PrintNetworks();
disconnected++;
}
}
return false;
}
bool Network::connect() {
if(settings.connType == conn_types::unset) return true;
else if(settings.connType == conn_types::ethernet || (settings.connType == conn_types::ethernetpref)) {
bool bConnected = this->connectWired();
if(!bConnected && settings.connType == conn_types::ethernetpref && settings.WIFI.ssid[0] != '\0') {
bConnected = this->connectWiFi();
this->wifiFallback = true;
if(this->connecting()) {
if(this->connType == conn_types::unset) {
// If we reached our timeout for the connection then we need to open the soft ap.
if(millis() > this->connectStart + CONNECT_TIMEOUT) {
if(this->connTarget == conn_types::ethernet && settings.connType == conn_types::ethernetpref && settings.WIFI.ssid[0] != '\0')
this->connectWiFi();
else {
Serial.println("Fell into timeout");
this->openSoftAP();
}
return bConnected;
}
return this->connectWiFi();
}
/* DEPRECATED 03-02-24
int Network::getStrengthByMac(const char *macAddr) {
int n = WiFi.scanNetworks(true);
for(int i = 0; i < n; i++) {
if (WiFi.BSSIDstr(i).compareTo(macAddr) == 0)
return WiFi.RSSI(i);
else
this->setConnected(this->connTarget);
}
return -100;
else if(settings.connType == conn_types::ethernet || settings.connType == conn_types::ethernetpref)
this->connectWired();
else if(settings.connType == conn_types::wifi && strlen(settings.WIFI.ssid) > 0)
this->connectWiFi();
else
this->openSoftAP();
return true;
}
*/
uint32_t Network::getChipId() {
uint32_t chipId = 0;
uint64_t mac = ESP.getEfuseMac();
@ -645,6 +583,7 @@ bool Network::openSoftAP() {
while (!this->connected())
{
int clients = WiFi.softAPgetStationNum();
somfy.loop();
webServer.loop();
if(millis() - this->lastEmit > 1500) {
//if(this->connect()) {}
@ -665,14 +604,22 @@ bool Network::openSoftAP() {
// If no clients have connected in 3 minutes from starting this server reboot this pig. This will
// force a reboot cycle until we have some response. That is unless the SSID has been cleared.
if(clients == 0 &&
if(clients == 0 && this->connType != conn_types::unset) {
Serial.println();
Serial.println("Connection Established Stopping AP Mode");
this->setConnected(this->connType);
return false;
}
else if(clients == 0 &&
(strlen(settings.WIFI.ssid) > 0 || settings.connType == conn_types::ethernet || settings.connType == conn_types::ethernetpref) &&
millis() - startTime > 3 * 60000) {
Serial.println();
Serial.println("Stopping AP Mode");
WiFi.softAPdisconnect(true);
return false;
}
if(c == 100) {
Serial.println();
c = 0;
@ -682,30 +629,41 @@ bool Network::openSoftAP() {
return true;
}
bool Network::connected() {
if(this->connType == conn_types::unset) return false;
if(this->connecting()) return false;
else if(this->connType == conn_types::unset) return false;
else if(this->connType == conn_types::wifi) return WiFi.status() == WL_CONNECTED;
else if(this->connType == conn_types::ethernet) return ETH.linkUp();
else return this->connType != conn_types::unset;
return false;
}
bool Network::connecting() {
return this->_connecting;
}
void Network::networkEvent(WiFiEvent_t event) {
switch(event) {
case ARDUINO_EVENT_ETH_START:
Serial.println("Ethernet Started");
case ARDUINO_EVENT_WIFI_READY: Serial.println("WiFi interface ready"); break;
case ARDUINO_EVENT_WIFI_SCAN_DONE: Serial.println("Completed scan for access points"); break;
case ARDUINO_EVENT_WIFI_STA_START:
Serial.println("WiFi station mode started");
if(settings.hostname[0] != '\0') WiFi.setHostname(settings.hostname);
break;
case ARDUINO_EVENT_WIFI_STA_STOP: Serial.println("WiFi clients stopped"); break;
case ARDUINO_EVENT_WIFI_STA_CONNECTED: Serial.println("Connected to access point"); break;
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED: Serial.println("Disconnected from WiFi access point"); break;
case ARDUINO_EVENT_WIFI_STA_AUTHMODE_CHANGE: Serial.println("Authentication mode of access point has changed"); break;
case ARDUINO_EVENT_WIFI_STA_GOT_IP:
Serial.print("Got WiFi IP: ");
Serial.println(WiFi.localIP());
net.connType = conn_types::wifi;
break;
case ARDUINO_EVENT_WIFI_STA_LOST_IP: Serial.println("Lost IP address and IP address is reset to 0"); break;
case ARDUINO_EVENT_ETH_GOT_IP:
// If the Wifi is connected then drop that connection
if(WiFi.status() == WL_CONNECTED) WiFi.disconnect(true);
Serial.print("Got Ethernet IP ");
Serial.println(ETH.localIP());
net.connType = conn_types::ethernet;
break;
/*
case ARDUINO_EVENT_ETH_LOST_IP:
Serial.println("Ethernet Lost IP");
sockEmit.sendToClients("ethernet", "{\"connected\":false, \"speed\":0,\"fullduplex\":false}");
net.connType = conn_types::unset;
break;
*/
case ARDUINO_EVENT_ETH_CONNECTED:
Serial.print("Ethernet Connected ");
// We don't want to call setConnected if we do not have an IP address yet
@ -714,36 +672,24 @@ void Network::networkEvent(WiFiEvent_t event) {
break;
case ARDUINO_EVENT_ETH_DISCONNECTED:
Serial.println("Ethernet Disconnected");
{
JsonSockEvent *json = sockEmit.beginEmit("ethernet");
json->beginObject();
json->addElem("connected", false);
json->addElem("speed", (uint8_t)0);
json->addElem("fullduplex", false);
json->endObject();
sockEmit.endEmit();
}
/*
sockEmit.sendToClients("ethernet", "{\"connected\":false,\"speed\":0,\"fullduplex\":false}");
*/
net.connType = conn_types::unset;
break;
case ARDUINO_EVENT_ETH_START:
Serial.println("Ethernet Started");
net.ethStarted = true;
break;
case ARDUINO_EVENT_ETH_STOP:
Serial.println("Ethernet Stopped");
net.connType = conn_types::unset;
break;
case ARDUINO_EVENT_WIFI_AP_STOP:
Serial.println("WiFi AP Stopped");
net.softAPOpened = false;
net.ethStarted = false;
break;
case ARDUINO_EVENT_WIFI_AP_START:
Serial.println("WiFi AP Started");
net.softAPOpened = true;
break;
case ARDUINO_EVENT_WIFI_STA_START:
if(settings.hostname[0] != '\0') WiFi.setHostname(settings.hostname);
break;
case ARDUINO_EVENT_WIFI_STA_CONNECTED:
case ARDUINO_EVENT_WIFI_AP_STOP:
Serial.println("WiFi AP Stopped");
net.softAPOpened = false;
break;
default:
if(event > ARDUINO_EVENT_ETH_START)

View file

@ -2,6 +2,8 @@
#ifndef Network_h
#define Network_h
#define CONNECT_TIMEOUT 20000
class Network {
protected:
unsigned long lastEmit = 0;
@ -9,21 +11,24 @@ class Network {
int lastRSSI = 0;
int lastChannel = 0;
int linkSpeed = 0;
bool ethStarted = false;
bool _connecting = false;
public:
bool ethStarted = false;
bool wifiFallback = false;
bool softAPOpened = false;
bool needsBroadcast = true;
conn_types connType = conn_types::unset;
conn_types connTarget = conn_types::unset;
bool connected();
bool connecting();
String ssid;
String mac;
int channel;
int strength;
int disconnected = 0;
int connectAttempts = 0;
long connectStart = 0;
long connectTime = 0;
uint32_t connectStart = 0;
uint32_t connectTime = 0;
bool openSoftAP();
bool connect();
bool connectWiFi();

View file

@ -82,96 +82,6 @@ void SocketEmitter::loop() {
this->initClients();
sockServer.loop();
}
/*
ClientSocketEvent::ClientSocketEvent() { this->msg[0] = 0x00; }
ClientSocketEvent::ClientSocketEvent(const char *evt) { snprintf(this->msg, sizeof(this->msg), "42[%s,]", evt); }
//ClientSocketEvent::ClientSocketEvent(const char *evt, const char *payload) { snprintf(this->msg, sizeof(this->msg), "42[%s,%s]", evt, payload); }
void ClientSocketEvent::beginEmit(const char *evt) { snprintf(this->msg, sizeof(this->msg), "42[%s,", evt); }
void ClientSocketEvent::endEmit() { this->_safecat("]"); }
void ClientSocketEvent::appendMessage(const char *text) {
uint16_t len = strlen(this->msg);
this->msg[len - 1] = '\0';
strcat(this->msg, text);
strcat(this->msg, "]");
}
void ClientSocketEvent::beginObject(const char *name) {
if(name && strlen(name) > 0) this->appendElem(name);
else if(!this->_nocomma) this->_safecat(",");
this->_safecat("{");
this->_objects++;
this->_nocomma = true;
}
void ClientSocketEvent::endObject() {
//if(strlen(this->buff) + 1 > this->buffSize - 1) this->send();
this->_safecat("}");
this->_objects--;
this->_nocomma = false;
}
void ClientSocketEvent::beginArray(const char *name) {
if(name && strlen(name) > 0) this->appendElem(name);
else if(!this->_nocomma) this->_safecat(",");
this->_safecat("[");
this->_arrays++;
this->_nocomma = true;
}
void ClientSocketEvent::endArray() {
//if(strlen(this->buff) + 1 > this->buffSize - 1) this->send();
this->_safecat("]");
this->_arrays--;
this->_nocomma = false;
}
void ClientSocketEvent::appendElem(const char *name) {
if(!this->_nocomma) this->_safecat(",");
if(name && strlen(name) > 0) {
this->_safecat(name, true);
this->_safecat(":");
}
this->_nocomma = false;
}
void ClientSocketEvent::addElem(const char *name, const char *val) {
if(!val) return;
this->appendElem(name);
this->_safecat(val, true);
}
void ClientSocketEvent::addElem(const char *val) { this->addElem(nullptr, val); }
void ClientSocketEvent::addElem(float fval) { sprintf(this->_numbuff, "%.4f", fval); this->_appendNumber(nullptr); }
void ClientSocketEvent::addElem(int8_t nval) { sprintf(this->_numbuff, "%d", nval); this->_appendNumber(nullptr); }
void ClientSocketEvent::addElem(uint8_t nval) { sprintf(this->_numbuff, "%u", nval); this->_appendNumber(nullptr); }
void ClientSocketEvent::addElem(int16_t nval) { sprintf(this->_numbuff, "%d", nval); this->_appendNumber(nullptr); }
void ClientSocketEvent::addElem(uint16_t nval) { sprintf(this->_numbuff, "%u", nval); this->_appendNumber(nullptr); }
void ClientSocketEvent::addElem(int32_t nval) { sprintf(this->_numbuff, "%ld", (long)nval); this->_appendNumber(nullptr); }
void ClientSocketEvent::addElem(uint32_t nval) { sprintf(this->_numbuff, "%lu", (unsigned long)nval); this->_appendNumber(nullptr); }
void ClientSocketEvent::addElem(int64_t lval) { sprintf(this->_numbuff, "%lld", (long long)lval); this->_appendNumber(nullptr); }
void ClientSocketEvent::addElem(uint64_t lval) { sprintf(this->_numbuff, "%llu", (unsigned long long)lval); this->_appendNumber(nullptr); }
void ClientSocketEvent::addElem(bool bval) { strcpy(this->_numbuff, bval ? "true" : "false"); this->_appendNumber(nullptr); }
void ClientSocketEvent::addElem(const char *name, float fval) { sprintf(this->_numbuff, "%.4f", fval); this->_appendNumber(name); }
void ClientSocketEvent::addElem(const char *name, int8_t nval) { sprintf(this->_numbuff, "%d", nval); this->_appendNumber(name); }
void ClientSocketEvent::addElem(const char *name, uint8_t nval) { sprintf(this->_numbuff, "%u", nval); this->_appendNumber(name); }
void ClientSocketEvent::addElem(const char *name, int16_t nval) { sprintf(this->_numbuff, "%d", nval); this->_appendNumber(name); }
void ClientSocketEvent::addElem(const char *name, uint16_t nval) { sprintf(this->_numbuff, "%u", nval); this->_appendNumber(name); }
void ClientSocketEvent::addElem(const char *name, int32_t nval) { sprintf(this->_numbuff, "%ld", (long)nval); this->_appendNumber(name); }
void ClientSocketEvent::addElem(const char *name, uint32_t nval) { sprintf(this->_numbuff, "%lu", (unsigned long)nval); this->_appendNumber(name); }
void ClientSocketEvent::addElem(const char *name, int64_t lval) { sprintf(this->_numbuff, "%lld", (long long)lval); this->_appendNumber(name); }
void ClientSocketEvent::addElem(const char *name, uint64_t lval) { sprintf(this->_numbuff, "%llu", (unsigned long long)lval); this->_appendNumber(name); }
void ClientSocketEvent::addElem(const char *name, bool bval) { strcpy(this->_numbuff, bval ? "true" : "false"); this->_appendNumber(name); }
void ClientSocketEvent::_safecat(const char *val, bool escape) {
size_t len = strlen(val) + strlen(this->msg);
if(escape) len += 2;
if(len >= sizeof(this->msg)) {
//this->send();
}
if(escape) strcat(this->msg, "\"");
strcat(this->msg, val);
if(escape) strcat(this->msg, "\"");
}
void ClientSocketEvent::_appendNumber(const char *name) { this->appendElem(name); this->_safecat(this->_numbuff); }
*/
JsonSockEvent *SocketEmitter::beginEmit(const char *evt) {
this->json.beginEvent(&sockServer, evt, g_response, sizeof(g_response));
return &this->json;
@ -189,46 +99,6 @@ uint8_t SocketEmitter::activeClients(uint8_t room) {
if(room < SOCK_MAX_ROOMS) return this->rooms[room].activeClients();
return 0;
}
/*
bool SocketEmitter::sendToRoom(uint8_t room, ClientSocketEvent *evt) {
if(room < SOCK_MAX_ROOMS) {
room_t *r = &this->rooms[room];
for(uint8_t i = 0; i < sizeof(r->clients); i++) {
if(r->clients[i] != 255) this->sendToClient(r->clients[i], evt);
}
return true;
}
return false;
}
bool SocketEmitter::sendToClients(ClientSocketEvent *evt) {
if(evt->msg[strlen(evt->msg) - 1] != ']') strcat(evt->msg, "]");
return sockServer.broadcastTXT(evt->msg);
}
bool SocketEmitter::sendToClient(uint8_t num, ClientSocketEvent *evt) {
if(evt->msg[strlen(evt->msg) - 1] != ']') strcat(evt->msg, "]");
return sockServer.sendTXT(num, evt->msg);
}
bool SocketEmitter::sendToClients(const char *evt, const char *payload) {
if(settings.status == DS_FWUPDATE) return true;
this->evt.prepareMessage(evt, payload);
return sockServer.broadcastTXT(this->evt.msg);
}
bool SocketEmitter::sendToClient(uint8_t num, const char *evt, const char *payload) {
if(settings.status == DS_FWUPDATE) return true;
this->evt.prepareMessage(evt, payload);
return sockServer.sendTXT(num, this->evt.msg);
}
bool SocketEmitter::sendToClient(uint8_t num, const char *evt, JsonDocument &doc) {
if(settings.status == DS_FWUPDATE) return true;
this->evt.prepareMessage(evt, doc);
return sockServer.sendTXT(num, this->evt.msg);
}
bool SocketEmitter::sendToClients(const char *evt, JsonDocument &doc) {
if(settings.status == DS_FWUPDATE) return true;
this->evt.prepareMessage(evt, doc);
return sockServer.broadcastTXT(this->evt.msg);
}
*/
void SocketEmitter::initClients() {
for(uint8_t i = 0; i < sizeof(this->newClients); i++) {
uint8_t num = this->newClients[i];

View file

@ -13,60 +13,6 @@ struct room_t {
bool join(uint8_t num);
bool leave(uint8_t num);
};
/*
class ClientSocketEvent {
private:
uint8_t _objects = 0;
uint8_t _arrays = 0;
bool _nocomma = true;
char _numbuff[25] = {0};
protected:
void _safecat(const char *val, bool escape = false);
void _appendNumber(const char *name);
public:
ClientSocketEvent();
ClientSocketEvent(const char *evt);
//ClientSocketEvent(const char *evt, const char *data);
char msg[2048] = "";
void beginEmit(const char *evt);
void endEmit();
void prepareMessage(const char *evt, const char *data);
void prepareMessage(const char *evt, JsonDocument &doc);
void appendMessage(const char *text);
void appendElement(const char *elem, const char *val);
void beginObject(const char *name = nullptr);
void endObject();
void beginArray(const char *name = nullptr);
void endArray();
void appendElem(const char *name = nullptr);
void addElem(const char* val);
void addElem(float fval);
void addElem(int8_t nval);
void addElem(uint8_t nval);
void addElem(int16_t nval);
void addElem(uint16_t nval);
void addElem(int32_t nval);
void addElem(uint32_t nval);
void addElem(int64_t lval);
void addElem(uint64_t lval);
void addElem(bool bval);
void addElem(const char* name, float fval);
void addElem(const char* name, int8_t nval);
void addElem(const char* name, uint8_t nval);
void addElem(const char* name, int16_t nval);
void addElem(const char* name, uint16_t nval);
void addElem(const char* name, int32_t nval);
void addElem(const char* name, uint32_t nval);
void addElem(const char* name, int64_t lval);
void addElem(const char* name, uint64_t lval);
void addElem(const char* name, bool bval);
void addElem(const char *name, const char *val);
};
*/
class SocketEmitter {
protected:
uint8_t newclients = 0;
@ -86,15 +32,6 @@ class SocketEmitter {
JsonSockEvent * beginEmit(const char *evt);
void endEmit(uint8_t num = 255);
void endEmitRoom(uint8_t num);
/*
bool sendToRoom(uint8_t room, ClientSocketEvent *evt);
bool sendToClients(ClientSocketEvent *evt);
bool sendToClient(uint8_t num, ClientSocketEvent *evt);
bool sendToClients(const char *evt, const char *data);
bool sendToClient(uint8_t num, const char *evt, const char *data);
bool sendToClients(const char *evt, JsonDocument &doc);
bool sendToClient(uint8_t num, const char *evt, JsonDocument &doc);
*/
static void wsEvent(uint8_t num, WStype_t type, uint8_t *payload, size_t length);
};
#endif

View file

@ -57,6 +57,8 @@ somfy_commands translateSomfyCommand(const String& string) {
else if (string.equalsIgnoreCase("Flag")) return somfy_commands::Flag;
else if (string.equalsIgnoreCase("Sensor")) return somfy_commands::Sensor;
else if (string.equalsIgnoreCase("Toggle")) return somfy_commands::Toggle;
else if (string.equalsIgnoreCase("Favorite")) return somfy_commands::Fav;
else if (string.startsWith("fav") || string.startsWith("FAV")) return somfy_commands::Fav;
else if (string.startsWith("mud") || string.startsWith("MUD")) return somfy_commands::MyUpDown;
else if (string.startsWith("md") || string.startsWith("MD")) return somfy_commands::MyDown;
else if (string.startsWith("ud") || string.startsWith("UD")) return somfy_commands::UpDown;
@ -104,6 +106,8 @@ String translateSomfyCommand(const somfy_commands cmd) {
return "Sensor";
case somfy_commands::Toggle:
return "Toggle";
case somfy_commands::Fav:
return "Favorite";
default:
return "Unknown(" + String((uint8_t)cmd) + ")";
}
@ -154,7 +158,11 @@ void somfy_frame_t::decodeFrame(byte* frame) {
this->remoteAddress = (decoded[6] + (decoded[5] << 8) + (decoded[4] << 16));
this->valid = this->checksum == checksum && this->remoteAddress > 0 && this->remoteAddress < 16777215;
if (this->cmd != somfy_commands::Sensor && this->valid) this->valid = (this->rollingCode > 0);
if (this->valid) {
uint8_t csc80 = 0;
uint8_t cs80 = 0;
// Check for valid command.
switch (this->cmd) {
//case somfy_commands::Unknown0:
@ -177,7 +185,24 @@ void somfy_frame_t::decodeFrame(byte* frame) {
case somfy_commands::StepUp:
case somfy_commands::StepDown:
case somfy_commands::Toggle:
case somfy_commands::Fav:
// These must be 80 bit commands
csc80 = (decoded[9] & 0x0F);
cs80 = (((decoded[7] & 0xF0) >> 4) ^ ((decoded[8] & 0xF0) >> 4));
cs80 ^= ((decoded[9] & 0xF0) >> 4);
cs80 ^= (decoded[7] & 0x0F);
cs80 ^= (decoded[8] & 0x0F);
//cs80 = (((decoded[7] & 0xF0) >> 4) ^ ((decoded[8] & 0xF0) >> 4) ^ ((decoded[9] & 0xF0) >> 4) ^ (decoded[7] & 0x0F) ^ (decoded[8] & 0x0F));
if(csc80 != cs80) this->valid = false;
/*
uint8_t ai = ((decoded[7] & 0xF0) >> 4);
uint8_t aj = ((decoded[8] & 0xF0) >> 4);
uint8_t ak = ((decoded[9] & 0xF0) >> 4);
uint8_t al = (decoded[7] & 0x0F);
uint8_t am = (decoded[8] & 0x0F);
uint8_t an = (decoded[9] & 0x0F);
cs80 = ai ^ aj ^ ak ^ al ^ am;
*/
break;
default:
this->valid = false;
@ -644,8 +669,10 @@ void SomfyRoom::clear() {
void SomfyGroup::clear() {
this->setGroupId(255);
this->setRemoteAddress(0);
this->repeats = 1;
memset(&this->linkedShades[0], 0x00, sizeof(this->linkedShades));
this->repeats = 0;
this->roomId = 0;
this->name[0] = 0x00;
memset(&this->linkedShades, 0x00, sizeof(this->linkedShades));
}
bool SomfyShade::linkRemote(uint32_t address, uint16_t rollingCode) {
// Check to see if the remote is already linked. If it is
@ -830,10 +857,13 @@ bool SomfyShade::isAtTarget() {
else if(this->tiltType == tilt_types::none) return fabs(this->currentPos - this->target) < epsilon;
return fabs(this->currentPos - this->target) < epsilon && fabs(this->currentTiltPos - this->tiltTarget) < epsilon;
}
bool SomfyRemote::simMy() { return (this->flags & static_cast<uint8_t>(somfy_flags_t::SimMy)) > 0; }
void SomfyRemote::setSimMy(bool bSimMy) { bSimMy ? this->flags |= static_cast<uint8_t>(somfy_flags_t::SimMy) : this->flags &= ~(static_cast<uint8_t>(somfy_flags_t::SimMy)); }
bool SomfyRemote::hasSunSensor() { return (this->flags & static_cast<uint8_t>(somfy_flags_t::SunSensor)) > 0;}
bool SomfyRemote::hasLight() { return (this->flags & static_cast<uint8_t>(somfy_flags_t::Light)) > 0; }
void SomfyRemote::setSunSensor(bool bHasSensor ) { bHasSensor ? this->flags |= static_cast<uint8_t>(somfy_flags_t::SunSensor) : this->flags &= ~(static_cast<uint8_t>(somfy_flags_t::SunSensor)); }
void SomfyRemote::setLight(bool bHasLight ) { bHasLight ? this->flags |= static_cast<uint8_t>(somfy_flags_t::Light) : this->flags &= ~(static_cast<uint8_t>(somfy_flags_t::Light)); }
void SomfyGroup::updateFlags() {
uint8_t oldFlags = this->flags;
this->flags = 0;
@ -2772,6 +2802,11 @@ void SomfyShade::moveToMyPosition() {
if(this->tiltType != tilt_types::tiltonly && this->myPos >= 0.0f && this->myPos <= 100.0f) this->p_target(this->myPos);
if(this->myTiltPos >= 0.0f && this->myTiltPos <= 100.0f) this->p_tiltTarget(this->myTiltPos);
this->settingPos = false;
if(this->simMy()) {
Serial.print("Moving to simulated favorite\n");
this->moveToTarget(this->myPos, this->myTiltPos);
}
else
SomfyRemote::sendCommand(somfy_commands::My, this->repeats);
}
void SomfyShade::sendCommand(somfy_commands cmd) { this->sendCommand(cmd, this->repeats); }
@ -3098,6 +3133,7 @@ int8_t SomfyShade::fromJSON(JsonObject &obj) {
if(obj.containsKey("bitLength")) this->bitLength = obj["bitLength"];
if(obj.containsKey("proto")) this->proto = static_cast<radio_proto>(obj["proto"].as<uint8_t>());
if(obj.containsKey("sunSensor")) this->setSunSensor(obj["sunSensor"]);
if(obj.containsKey("simMy")) this->setSimMy(obj["simMy"]);
if(obj.containsKey("light")) this->setLight(obj["light"]);
if(obj.containsKey("gpioFlags")) this->gpioFlags = obj["gpioFlags"];
if(obj.containsKey("gpioLLTrigger")) {
@ -3180,24 +3216,6 @@ int8_t SomfyShade::fromJSON(JsonObject &obj) {
}
return err;
}
/*
bool SomfyShade::toJSONRef(JsonObject &obj) {
obj["shadeId"] = this->getShadeId();
obj["roomId"] = this->roomId;
obj["name"] = this->name;
obj["remoteAddress"] = this->m_remoteAddress;
obj["paired"] = this->paired;
obj["shadeType"] = static_cast<uint8_t>(this->shadeType);
obj["bitLength"] = this->bitLength;
obj["proto"] = static_cast<uint8_t>(this->proto);
obj["flags"] = this->flags;
obj["sunSensor"] = this->hasSunSensor();
obj["hasLight"] = this->hasLight();
obj["repeats"] = this->repeats;
SomfyRemote::toJSON(obj);
return true;
}
*/
void SomfyShade::toJSONRef(JsonResponse &json) {
json.addElem("shadeId", this->getShadeId());
json.addElem("roomId", this->roomId);
@ -3249,6 +3267,7 @@ void SomfyShade::toJSON(JsonResponse &json) {
json.addElem("gpioDown", this->gpioDown);
json.addElem("gpioMy", this->gpioMy);
json.addElem("gpioLLTrigger", ((this->gpioFlags & (uint8_t)gpio_flags_t::LowLevelTrigger) == 0) ? false : true);
json.addElem("simMy", this->simMy());
json.beginArray("linkedRemotes");
for(uint8_t i = 0; i < SOMFY_MAX_LINKED_REMOTES; i++) {
SomfyLinkedRemote &lremote = this->linkedRemotes[i];
@ -4093,13 +4112,6 @@ void SomfyShadeController::toJSONRepeaters(JsonResponse &json) {
if(somfy.repeaters[i] != 0) json.addElem((uint8_t)somfy.repeaters[i]);
}
}
bool SomfyShadeController::toJSONRepeaters(JsonArray &arr) {
for(uint8_t i = 0; i < SOMFY_MAX_REPEATERS; i++) {
if(somfy.repeaters[i] != 0) arr.add(somfy.repeaters[i]);
}
return true;
}
void SomfyShadeController::loop() {
this->transceiver.loop();
for(uint8_t i = 0; i < SOMFY_MAX_SHADES; i++) {

19
Somfy.h
View file

@ -47,6 +47,7 @@ enum class somfy_commands : byte {
RTWProto = 0xF, // RTW Protocol
// Command extensions for 80 bit frames
StepUp = 0x8B,
Fav = 0x90,
};
enum class group_types : byte {
channel = 0x00
@ -155,7 +156,8 @@ enum class somfy_flags_t : byte {
Light = 0x08,
Windy = 0x10,
Sunny = 0x20,
Lighted = 0x40
Lighted = 0x40,
SimMy = 0x80
};
enum class gpio_flags_t : byte {
LowLevelTrigger = 0x01
@ -181,6 +183,7 @@ struct somfy_frame_t {
uint32_t await = 0;
uint8_t bitLength = 56;
uint16_t pulseCount = 0;
uint8_t stepSize = 0;
void print();
void encodeFrame(byte *frame);
void decodeFrame(byte* frame);
@ -197,7 +200,6 @@ class SomfyRoom {
void clear();
bool save();
bool fromJSON(JsonObject &obj);
//bool toJSON(JsonObject &obj);
void toJSON(JsonResponse &json);
void emitState(const char *evt = "roomState");
void emitState(uint8_t num, const char *evt = "roomState");
@ -228,7 +230,6 @@ class SomfyRemote {
uint8_t repeats = 1;
virtual bool isLastCommand(somfy_commands cmd);
char *getRemotePrefId() {return m_remotePrefId;}
//virtual bool toJSON(JsonObject &obj);
virtual void toJSON(JsonResponse &json);
virtual void setRemoteAddress(uint32_t address);
virtual uint32_t getRemoteAddress();
@ -236,8 +237,10 @@ class SomfyRemote {
virtual uint16_t setRollingCode(uint16_t code);
bool hasSunSensor();
bool hasLight();
bool simMy();
void setSunSensor(bool bHasSensor);
void setLight(bool bHasLight);
void setSimMy(bool bSimMy);
virtual void sendCommand(somfy_commands cmd);
virtual void sendCommand(somfy_commands cmd, uint8_t repeat);
void sendSensorCommand(int8_t isWindy, int8_t isSunny, uint8_t repeat);
@ -280,10 +283,8 @@ class SomfyShade : public SomfyRemote {
#ifdef USE_NVS
void load();
#endif
//somfy_tx_queue_t txQueue;
float currentPos = 0.0f;
float currentTiltPos = 0.0f;
//uint16_t movement = 0;
int8_t lastMovement = 0;
int8_t direction = 0; // 0 = stopped, 1=down, -1=up.
int8_t tiltDirection = 0; // 0=stopped, 1=clockwise, -1=counter clockwise
@ -294,10 +295,8 @@ class SomfyShade : public SomfyRemote {
SomfyLinkedRemote linkedRemotes[SOMFY_MAX_LINKED_REMOTES];
bool paired = false;
int8_t validateJSON(JsonObject &obj);
//bool toJSONRef(JsonObject &obj);
void toJSONRef(JsonResponse &json);
int8_t fromJSON(JsonObject &obj);
//bool toJSON(JsonObject &obj) override;
void toJSON(JsonResponse &json) override;
char name[21] = "";
@ -548,16 +547,10 @@ class SomfyShadeController {
SomfyGroup groups[SOMFY_MAX_GROUPS];
bool linkRepeater(uint32_t address);
bool unlinkRepeater(uint32_t address);
//bool toJSON(DynamicJsonDocument &doc);
//bool toJSON(JsonObject &obj);
//bool toJSONRooms(JsonArray &arr);
//bool toJSONShades(JsonArray &arr);
//bool toJSONGroups(JsonArray &arr);
void toJSONShades(JsonResponse &json);
void toJSONRooms(JsonResponse &json);
void toJSONGroups(JsonResponse &json);
void toJSONRepeaters(JsonResponse &json);
bool toJSONRepeaters(JsonArray &arr);
uint8_t repeaterCount();
uint8_t roomCount();
uint8_t shadeCount();

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -1 +1 @@
2.4.1
2.4.3

View file

@ -8,9 +8,9 @@
<meta name="apple-mobile-web-app-title" content="ESPSomfy RTS App">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<link rel="stylesheet" href="main.css?v=2.4.2r" type="text/css" />
<link rel="stylesheet" href="widgets.css?v=2.4.2r" type="text/css" />
<link rel="stylesheet" href="icons.css?v=2.4.2r" type="text/css" />
<link rel="stylesheet" href="main.css?v=2.4.3b" type="text/css" />
<link rel="stylesheet" href="widgets.css?v=2.4.3b" type="text/css" />
<link rel="stylesheet" href="icons.css?v=2.4.3b" type="text/css" />
<link rel="icon" type="image/png" href="favicon.png" />
<!-- iPad retina icon -->
@ -609,6 +609,13 @@
<label for="cbFlipPosition" style="display:block;font-size:1em;margin-top:0px;margin-left:7px;display:inline-block;">Invert Position (expressed in % of open)</label>
</div>
</div>
<div style="margin-top:-10px;" id="divSimMy">
<div class="field-group">
<input id="cbSimMy" name="simMy" data-bind="simMy" type="checkbox" style="" />
<label for="cbSimMy" style="display:block;font-size:1em;margin-top:0px;margin-left:7px;display:inline-block;">Simulate Favorite Position</label>
</div>
</div>
<div classs="field-group" style="display:inline-block;">
<label for="selRepeatCommnds" style="cursor:pointer;color:#00bcd4;margin-right:4px;">Repeat Commands</label>
<select id="selRepeatCommands" data-bind="repeats" data-datatype="int" style="width:127px;">

View file

@ -1270,7 +1270,7 @@ var security = new Security();
class General {
initialized = false;
appVersion = 'v2.4.2';
appVersion = 'v2.4.3';
reloadApp = false;
init() {
if (this.initialized) return;