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: env:
ARDUINO_BOARD_MANAGER_ADDITIONAL_URLS: "https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json" ARDUINO_BOARD_MANAGER_ADDITIONAL_URLS: "https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json"
ARDUINO_CLI_VERSION: "0.x" ARDUINO_CLI_VERSION: "0.x"
ARDUINO_ESP32_VERSION: "2.0.14" ARDUINO_ESP32_VERSION: "2.0.15"
ARDUINO_JSON_VERSION: "6.21.5" ARDUINO_JSON_VERSION: "6.21.5"
ESPTOOL_VERSION: "4.7" ESPTOOL_VERSION: "4.7"
LITTLEFS_VERSION: "v2.5.1" LITTLEFS_VERSION: "v2.5.1"

View file

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

View file

@ -3,7 +3,7 @@
#ifndef configsettings_h #ifndef configsettings_h
#define configsettings_h #define configsettings_h
#include "WResp.h" #include "WResp.h"
#define FW_VERSION "v2.4.2" #define FW_VERSION "v2.4.3"
enum DeviceStatus { enum DeviceStatus {
DS_OK = 0, DS_OK = 0,
DS_ERROR = 1, DS_ERROR = 1,
@ -161,7 +161,8 @@ enum class conn_types : byte {
unset = 0x00, unset = 0x00,
wifi = 0x01, wifi = 0x01,
ethernet = 0x02, ethernet = 0x02,
ethernetpref = 0x03 ethernetpref = 0x03,
ap = 0x04
}; };
class ConfigSettings: BaseSettings { class ConfigSettings: BaseSettings {
public: 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) { void GitRelease::toJSON(JsonResponse &json) {
Timestamp ts; Timestamp ts;
char buff[20]; char buff[20];
@ -228,7 +214,7 @@ int16_t GitRepo::getReleases(uint8_t num) {
} }
else { else {
https.end(); https.end();
//sclient.stop(); sclient.stop();
return httpCode; return httpCode;
} }
} }
@ -254,19 +240,6 @@ void GitRepo::toJSON(JsonResponse &json) {
} }
json.endArray(); 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 UPDATE_ERR_OFFSET 20
#define ERR_DOWNLOAD_HTTP -40 #define ERR_DOWNLOAD_HTTP -40
#define ERR_DOWNLOAD_BUFFER -41 #define ERR_DOWNLOAD_BUFFER -41
@ -274,11 +247,8 @@ bool GitRepo::toJSON(JsonObject &obj) {
void GitUpdater::loop() { void GitUpdater::loop() {
if(this->status == GIT_STATUS_READY) { if(this->status == GIT_STATUS_READY) {
//if(this->lastCheck == 0)
//this->lastCheck = millis();
//else
if(settings.checkForUpdate && 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->lastCheck + 86400000 < millis() || this->lastCheck == 0) && !rebootDelay.reboot) { // 1 day
this->checkForUpdate(); this->checkForUpdate();
} }
@ -302,6 +272,7 @@ void GitUpdater::loop() {
void GitUpdater::checkForUpdate() { void GitUpdater::checkForUpdate() {
if(this->status != 0) return; // If we are already checking. if(this->status != 0) return; // If we are already checking.
Serial.println("Check github for updates..."); Serial.println("Check github for updates...");
this->status = GIT_STATUS_CHECK; this->status = GIT_STATUS_CHECK;
settings.printAvailHeap(); settings.printAvailHeap();
this->lastCheck = millis(); this->lastCheck = millis();
@ -349,21 +320,6 @@ void GitUpdater::toJSON(JsonResponse &json) {
this->latest.toJSON(json); this->latest.toJSON(json);
json.endObject(); 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) { void GitUpdater::emitUpdateCheck(uint8_t num) {
JsonSockEvent *json = sockEmit.beginEmit("fwStatus"); JsonSockEvent *json = sockEmit.beginEmit("fwStatus");
json->beginObject(); json->beginObject();
@ -384,16 +340,6 @@ void GitUpdater::emitUpdateCheck(uint8_t num) {
json->endObject(); json->endObject();
json->endObject(); json->endObject();
sockEmit.endEmit(num); 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 GitUpdater::checkInternet() {
int err = 500; int err = 500;

View file

@ -28,14 +28,12 @@ class GitRelease {
appver_t version; appver_t version;
void setReleaseProperty(const char *key, const char *val); void setReleaseProperty(const char *key, const char *val);
void setAssetProperty(const char *key, const char *val); void setAssetProperty(const char *key, const char *val);
bool toJSON(JsonObject &obj);
void toJSON(JsonResponse &json); void toJSON(JsonResponse &json);
}; };
class GitRepo { class GitRepo {
public: public:
int16_t getReleases(uint8_t num = GIT_MAX_RELEASES); int16_t getReleases(uint8_t num = GIT_MAX_RELEASES);
GitRelease releases[GIT_MAX_RELEASES + 1]; GitRelease releases[GIT_MAX_RELEASES + 1];
bool toJSON(JsonObject &obj);
void toJSON(JsonResponse &json); void toJSON(JsonResponse &json);
}; };
class GitUpdater { class GitUpdater {
@ -60,7 +58,6 @@ class GitUpdater {
void setFirmwareFile(); void setFirmwareFile();
void setCurrentRelease(GitRepo &repo); void setCurrentRelease(GitRepo &repo);
void loop(); void loop();
void toJSON(JsonObject &obj);
void toJSON(JsonResponse &json); void toJSON(JsonResponse &json);
bool recoverFilesystem(); bool recoverFilesystem();
int checkInternet(); 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); snprintf(g_content, sizeof(g_content), "%u", val);
return this->publish(topic, g_content, retain); 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) { bool MQTTClass::unpublish(const char *topic) {
if(mqttClient.connected()) { if(mqttClient.connected()) {
char top[128]; char top[128];

3
MQTT.h
View file

@ -17,9 +17,6 @@ class MQTTClass {
void reset(); void reset();
bool unpublish(const char *topic); bool unpublish(const char *topic);
bool publish(const char *topic, const char *payload, bool retain = false); 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, uint8_t val, bool retain = false);
bool publish(const char *topic, int8_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); bool publish(const char *topic, uint32_t val, bool retain = false);

View file

@ -15,6 +15,8 @@ extern SocketEmitter sockEmit;
extern MQTTClass mqtt; extern MQTTClass mqtt;
extern rebootDelay_t rebootDelay; extern rebootDelay_t rebootDelay;
extern Network net; extern Network net;
extern SomfyShadeController somfy;
static bool _apScanning = false; static bool _apScanning = false;
static uint32_t _lastMaxHeap = 0; static uint32_t _lastMaxHeap = 0;
@ -40,39 +42,26 @@ bool Network::setup() {
WiFi.mode(WIFI_STA); WiFi.mode(WIFI_STA);
settings.WIFI.printNetworks(); settings.WIFI.printNetworks();
} }
if(!this->connect()) this->openSoftAP(); //if(!this->connect()) this->openSoftAP();
return true; return true;
} }
void Network::loop() { void Network::loop() {
this->connect();
if(!this->connected() || this->connecting()) return;
if(millis() - this->lastEmit > 1500) { if(millis() - this->lastEmit > 1500) {
this->lastEmit = millis(); this->lastEmit = millis();
if(!this->softAPOpened) { if(!this->softAPOpened) {
while(!this->connect()) { if(this->connected()) {
// If we lost our connection this->emitSockets();
connectRetries++; this->lastEmit = millis();
if(connectRetries > 100) {
if(!this->connected()) this->openSoftAP();
break;
}
sockEmit.loop();
} }
connectRetries = 0;
} }
this->emitSockets();
this->lastEmit = millis();
if(!this->connected()) return;
} }
sockEmit.loop(); sockEmit.loop();
if(this->connected() && millis() - this->lastMDNS > 60000) { 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 // 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 // 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(this->connType == conn_types::wifi && settings.WIFI.roaming && !this->softAPOpened) {
// If we are not already scanning then we need to start a passive scan // If we are not already scanning then we need to start a passive scan
// and only respond if there is a better connection. // and only respond if there is a better connection.
@ -237,6 +226,7 @@ void Network::setConnected(conn_types connType) {
WiFi.softAPdisconnect(true); WiFi.softAPdisconnect(true);
WiFi.mode(WIFI_STA); WiFi.mode(WIFI_STA);
} }
this->_connecting = false;
this->ssid = WiFi.SSID(); this->ssid = WiFi.SSID();
this->mac = WiFi.BSSIDstr(); this->mac = WiFi.BSSIDstr();
this->strength = WiFi.RSSI(); this->strength = WiFi.RSSI();
@ -248,6 +238,7 @@ void Network::setConnected(conn_types connType) {
WiFi.softAPdisconnect(true); WiFi.softAPdisconnect(true);
WiFi.mode(WIFI_OFF); WiFi.mode(WIFI_OFF);
} }
this->_connecting = false;
this->wifiFallback = false; this->wifiFallback = false;
} }
sockEmit.begin(); sockEmit.begin();
@ -365,8 +356,8 @@ void Network::setConnected(conn_types connType) {
this->needsBroadcast = true; this->needsBroadcast = true;
} }
bool Network::connectWired() { bool Network::connectWired() {
//if(this->connType == conn_types::ethernet && ETH.linkUp()) {
if(ETH.linkUp()) { if(ETH.linkUp()) {
// If the ethernet link is re-established then we need to shut down wifi.
if(WiFi.status() == WL_CONNECTED) { if(WiFi.status() == WL_CONNECTED) {
sockEmit.end(); sockEmit.end();
WiFi.disconnect(true); WiFi.disconnect(true);
@ -386,7 +377,11 @@ bool Network::connectWired() {
} }
else else
Serial.println("Connecting to Wired Ethernet"); Serial.println("Connecting to Wired Ethernet");
this->connectAttempts++; this->connectAttempts++;
this->_connecting = true;
this->connTarget = conn_types::ethernet;
this->connType = conn_types::unset;
if(!this->ethStarted) { if(!this->ethStarted) {
this->ethStarted = true; this->ethStarted = true;
WiFi.mode(WIFI_OFF); WiFi.mode(WIFI_OFF);
@ -415,23 +410,10 @@ bool Network::connectWired() {
} }
else else
ETH.config(INADDR_NONE, INADDR_NONE, INADDR_NONE, INADDR_NONE); ETH.config(INADDR_NONE, INADDR_NONE, INADDR_NONE, INADDR_NONE);
uint32_t wait = millis();
while(millis() - wait < 14000) {
if(ETH.linkUp() && ETH.localIP() != INADDR_NONE) {
net.mac = ETH.macAddress();
net.setConnected(conn_types::ethernet);
return true;
}
delay(500);
}
if(settings.connType == conn_types::ethernetpref) {
this->wifiFallback = true;
return connectWiFi();
}
} }
} }
return false; this->connectStart = millis();
return true;
} }
void Network::updateHostname() { void Network::updateHostname() {
if(settings.hostname[0] != '\0' && this->connected()) { if(settings.hostname[0] != '\0' && this->connected()) {
@ -467,11 +449,12 @@ bool Network::connectWiFi() {
} }
else Serial.println("Connecting to AP"); else Serial.println("Connecting to AP");
this->connectAttempts++; this->connectAttempts++;
this->connectStart = millis(); this->_connecting = true;
this->connTarget = conn_types::wifi;
this->connType = conn_types::unset;
WiFi.setSleep(false); WiFi.setSleep(false);
WiFi.mode(WIFI_MODE_NULL); WiFi.mode(WIFI_MODE_NULL);
//WiFi.onEvent(this->networkEvent);
if(!settings.IP.dhcp) { if(!settings.IP.dhcp) {
if(!WiFi.config(settings.IP.ip, settings.IP.gateway, settings.IP.subnet, settings.IP.dns1, settings.IP.dns2)) 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); WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE, INADDR_NONE);
@ -494,80 +477,35 @@ bool Network::connectWiFi() {
} }
else else
WiFi.begin(settings.WIFI.ssid, settings.WIFI.passphrase); WiFi.begin(settings.WIFI.ssid, settings.WIFI.passphrase);
delay(100); this->connectStart = millis();
int retries = 0;
while(retries < 100) {
switch(WiFi.status()) {
case WL_SCAN_COMPLETED:
Serial.println("Status: Scan Completed");
break;
case WL_CONNECT_FAILED:
if(this->connectAttempts == 1) Serial.println();
Serial.println("WiFi Module connection failed");
return false;
case WL_DISCONNECTED:
break;
case WL_IDLE_STATUS:
Serial.print("*");
break;
case WL_CONNECTED:
//WiFi.hostname(settings.hostname);
this->ssid = WiFi.SSID();
this->mac = WiFi.BSSIDstr();
this->strength = WiFi.RSSI();
this->channel = WiFi.channel();
this->setConnected(conn_types::wifi);
WiFi.setSleep(false);
return true;
case WL_NO_SHIELD:
Serial.println("Connection failed - WiFi module not found");
return false;
case WL_NO_SSID_AVAIL:
Serial.print(" Connection failed the SSID ");
Serial.print(settings.WIFI.ssid);
Serial.println(" could not be found");
return false;
default:
break;
}
delay(500);
if(connectAttempts == 1) Serial.print("*");
retries++;
}
if(this->connectAttempts != 1) {
int st = this->getStrengthBySSID(settings.WIFI.ssid);
Serial.print("(");
Serial.print(st);
Serial.print("dBm) ");
Serial.println("Failed");
//if(disconnected > 0 && st == -100) settings.WIFI.PrintNetworks();
disconnected++;
}
} }
return false; return true;
} }
bool Network::connect() { bool Network::connect() {
if(settings.connType == conn_types::unset) return true; if(this->connecting()) {
else if(settings.connType == conn_types::ethernet || (settings.connType == conn_types::ethernetpref)) { if(this->connType == conn_types::unset) {
bool bConnected = this->connectWired(); // If we reached our timeout for the connection then we need to open the soft ap.
if(!bConnected && settings.connType == conn_types::ethernetpref && settings.WIFI.ssid[0] != '\0') { if(millis() > this->connectStart + CONNECT_TIMEOUT) {
bConnected = this->connectWiFi(); if(this->connTarget == conn_types::ethernet && settings.connType == conn_types::ethernetpref && settings.WIFI.ssid[0] != '\0')
this->wifiFallback = true; this->connectWiFi();
else {
Serial.println("Fell into timeout");
this->openSoftAP();
}
}
} }
return bConnected; else
this->setConnected(this->connTarget);
} }
return this->connectWiFi(); else if(settings.connType == conn_types::ethernet || settings.connType == conn_types::ethernetpref)
this->connectWired();
else if(settings.connType == conn_types::wifi && strlen(settings.WIFI.ssid) > 0)
this->connectWiFi();
else
this->openSoftAP();
return true;
} }
/* DEPRECATED 03-02-24
int Network::getStrengthByMac(const char *macAddr) {
int n = WiFi.scanNetworks(true);
for(int i = 0; i < n; i++) {
if (WiFi.BSSIDstr(i).compareTo(macAddr) == 0)
return WiFi.RSSI(i);
}
return -100;
}
*/
uint32_t Network::getChipId() { uint32_t Network::getChipId() {
uint32_t chipId = 0; uint32_t chipId = 0;
uint64_t mac = ESP.getEfuseMac(); uint64_t mac = ESP.getEfuseMac();
@ -645,6 +583,7 @@ bool Network::openSoftAP() {
while (!this->connected()) while (!this->connected())
{ {
int clients = WiFi.softAPgetStationNum(); int clients = WiFi.softAPgetStationNum();
somfy.loop();
webServer.loop(); webServer.loop();
if(millis() - this->lastEmit > 1500) { if(millis() - this->lastEmit > 1500) {
//if(this->connect()) {} //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 // 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. // 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) && (strlen(settings.WIFI.ssid) > 0 || settings.connType == conn_types::ethernet || settings.connType == conn_types::ethernetpref) &&
millis() - startTime > 3 * 60000) { millis() - startTime > 3 * 60000) {
Serial.println(); Serial.println();
Serial.println("Stopping AP Mode"); Serial.println("Stopping AP Mode");
WiFi.softAPdisconnect(true); WiFi.softAPdisconnect(true);
return false; return false;
} }
if(c == 100) { if(c == 100) {
Serial.println(); Serial.println();
c = 0; c = 0;
@ -682,30 +629,41 @@ bool Network::openSoftAP() {
return true; return true;
} }
bool Network::connected() { 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::wifi) return WiFi.status() == WL_CONNECTED;
else if(this->connType == conn_types::ethernet) return ETH.linkUp(); else if(this->connType == conn_types::ethernet) return ETH.linkUp();
else return this->connType != conn_types::unset; else return this->connType != conn_types::unset;
return false; return false;
} }
bool Network::connecting() {
return this->_connecting;
}
void Network::networkEvent(WiFiEvent_t event) { void Network::networkEvent(WiFiEvent_t event) {
switch(event) { switch(event) {
case ARDUINO_EVENT_ETH_START: case ARDUINO_EVENT_WIFI_READY: Serial.println("WiFi interface ready"); break;
Serial.println("Ethernet Started"); 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; 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: case ARDUINO_EVENT_ETH_GOT_IP:
// If the Wifi is connected then drop that connection // If the Wifi is connected then drop that connection
if(WiFi.status() == WL_CONNECTED) WiFi.disconnect(true); if(WiFi.status() == WL_CONNECTED) WiFi.disconnect(true);
Serial.print("Got Ethernet IP "); Serial.print("Got Ethernet IP ");
Serial.println(ETH.localIP()); Serial.println(ETH.localIP());
net.connType = conn_types::ethernet;
break; 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: case ARDUINO_EVENT_ETH_CONNECTED:
Serial.print("Ethernet Connected "); Serial.print("Ethernet Connected ");
// We don't want to call setConnected if we do not have an IP address yet // We don't want to call setConnected if we do not have an IP address yet
@ -714,37 +672,25 @@ void Network::networkEvent(WiFiEvent_t event) {
break; break;
case ARDUINO_EVENT_ETH_DISCONNECTED: case ARDUINO_EVENT_ETH_DISCONNECTED:
Serial.println("Ethernet 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; net.connType = conn_types::unset;
break; break;
case ARDUINO_EVENT_ETH_START:
Serial.println("Ethernet Started");
net.ethStarted = true;
break;
case ARDUINO_EVENT_ETH_STOP: case ARDUINO_EVENT_ETH_STOP:
Serial.println("Ethernet Stopped"); Serial.println("Ethernet Stopped");
net.connType = conn_types::unset; net.connType = conn_types::unset;
net.ethStarted = false;
break;
case ARDUINO_EVENT_WIFI_AP_START:
Serial.println("WiFi AP Started");
net.softAPOpened = true;
break; break;
case ARDUINO_EVENT_WIFI_AP_STOP: case ARDUINO_EVENT_WIFI_AP_STOP:
Serial.println("WiFi AP Stopped"); Serial.println("WiFi AP Stopped");
net.softAPOpened = false; net.softAPOpened = false;
break; break;
case ARDUINO_EVENT_WIFI_AP_START:
Serial.println("WiFi AP Started");
net.softAPOpened = true;
break;
case ARDUINO_EVENT_WIFI_STA_START:
if(settings.hostname[0] != '\0') WiFi.setHostname(settings.hostname);
break;
case ARDUINO_EVENT_WIFI_STA_CONNECTED:
break;
default: default:
if(event > ARDUINO_EVENT_ETH_START) if(event > ARDUINO_EVENT_ETH_START)
Serial.printf("Unknown Ethernet Event %d\n", event); Serial.printf("Unknown Ethernet Event %d\n", event);

View file

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

View file

@ -82,96 +82,6 @@ void SocketEmitter::loop() {
this->initClients(); this->initClients();
sockServer.loop(); 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) { JsonSockEvent *SocketEmitter::beginEmit(const char *evt) {
this->json.beginEvent(&sockServer, evt, g_response, sizeof(g_response)); this->json.beginEvent(&sockServer, evt, g_response, sizeof(g_response));
return &this->json; return &this->json;
@ -189,46 +99,6 @@ uint8_t SocketEmitter::activeClients(uint8_t room) {
if(room < SOCK_MAX_ROOMS) return this->rooms[room].activeClients(); if(room < SOCK_MAX_ROOMS) return this->rooms[room].activeClients();
return 0; 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() { void SocketEmitter::initClients() {
for(uint8_t i = 0; i < sizeof(this->newClients); i++) { for(uint8_t i = 0; i < sizeof(this->newClients); i++) {
uint8_t num = this->newClients[i]; uint8_t num = this->newClients[i];

View file

@ -13,60 +13,6 @@ struct room_t {
bool join(uint8_t num); bool join(uint8_t num);
bool leave(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 { class SocketEmitter {
protected: protected:
uint8_t newclients = 0; uint8_t newclients = 0;
@ -86,15 +32,6 @@ class SocketEmitter {
JsonSockEvent * beginEmit(const char *evt); JsonSockEvent * beginEmit(const char *evt);
void endEmit(uint8_t num = 255); void endEmit(uint8_t num = 255);
void endEmitRoom(uint8_t num); 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); static void wsEvent(uint8_t num, WStype_t type, uint8_t *payload, size_t length);
}; };
#endif #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("Flag")) return somfy_commands::Flag;
else if (string.equalsIgnoreCase("Sensor")) return somfy_commands::Sensor; else if (string.equalsIgnoreCase("Sensor")) return somfy_commands::Sensor;
else if (string.equalsIgnoreCase("Toggle")) return somfy_commands::Toggle; 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("mud") || string.startsWith("MUD")) return somfy_commands::MyUpDown;
else if (string.startsWith("md") || string.startsWith("MD")) return somfy_commands::MyDown; else if (string.startsWith("md") || string.startsWith("MD")) return somfy_commands::MyDown;
else if (string.startsWith("ud") || string.startsWith("UD")) return somfy_commands::UpDown; else if (string.startsWith("ud") || string.startsWith("UD")) return somfy_commands::UpDown;
@ -104,6 +106,8 @@ String translateSomfyCommand(const somfy_commands cmd) {
return "Sensor"; return "Sensor";
case somfy_commands::Toggle: case somfy_commands::Toggle:
return "Toggle"; return "Toggle";
case somfy_commands::Fav:
return "Favorite";
default: default:
return "Unknown(" + String((uint8_t)cmd) + ")"; 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->remoteAddress = (decoded[6] + (decoded[5] << 8) + (decoded[4] << 16));
this->valid = this->checksum == checksum && this->remoteAddress > 0 && this->remoteAddress < 16777215; 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->cmd != somfy_commands::Sensor && this->valid) this->valid = (this->rollingCode > 0);
if (this->valid) { if (this->valid) {
uint8_t csc80 = 0;
uint8_t cs80 = 0;
// Check for valid command. // Check for valid command.
switch (this->cmd) { switch (this->cmd) {
//case somfy_commands::Unknown0: //case somfy_commands::Unknown0:
@ -177,7 +185,24 @@ void somfy_frame_t::decodeFrame(byte* frame) {
case somfy_commands::StepUp: case somfy_commands::StepUp:
case somfy_commands::StepDown: case somfy_commands::StepDown:
case somfy_commands::Toggle: case somfy_commands::Toggle:
case somfy_commands::Fav:
// These must be 80 bit commands // 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; break;
default: default:
this->valid = false; this->valid = false;
@ -644,8 +669,10 @@ void SomfyRoom::clear() {
void SomfyGroup::clear() { void SomfyGroup::clear() {
this->setGroupId(255); this->setGroupId(255);
this->setRemoteAddress(0); this->setRemoteAddress(0);
this->repeats = 1; this->repeats = 0;
memset(&this->linkedShades[0], 0x00, sizeof(this->linkedShades)); this->roomId = 0;
this->name[0] = 0x00;
memset(&this->linkedShades, 0x00, sizeof(this->linkedShades));
} }
bool SomfyShade::linkRemote(uint32_t address, uint16_t rollingCode) { bool SomfyShade::linkRemote(uint32_t address, uint16_t rollingCode) {
// Check to see if the remote is already linked. If it is // 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; 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; 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::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; } 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::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 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() { void SomfyGroup::updateFlags() {
uint8_t oldFlags = this->flags; uint8_t oldFlags = this->flags;
this->flags = 0; this->flags = 0;
@ -2772,7 +2802,12 @@ void SomfyShade::moveToMyPosition() {
if(this->tiltType != tilt_types::tiltonly && this->myPos >= 0.0f && this->myPos <= 100.0f) this->p_target(this->myPos); if(this->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); if(this->myTiltPos >= 0.0f && this->myTiltPos <= 100.0f) this->p_tiltTarget(this->myTiltPos);
this->settingPos = false; this->settingPos = false;
SomfyRemote::sendCommand(somfy_commands::My, this->repeats); if(this->simMy()) {
Serial.print("Moving to simulated favorite\n");
this->moveToTarget(this->myPos, this->myTiltPos);
}
else
SomfyRemote::sendCommand(somfy_commands::My, this->repeats);
} }
void SomfyShade::sendCommand(somfy_commands cmd) { this->sendCommand(cmd, this->repeats); } void SomfyShade::sendCommand(somfy_commands cmd) { this->sendCommand(cmd, this->repeats); }
void SomfyShade::sendCommand(somfy_commands cmd, uint8_t repeat) { void SomfyShade::sendCommand(somfy_commands cmd, uint8_t repeat) {
@ -3098,6 +3133,7 @@ int8_t SomfyShade::fromJSON(JsonObject &obj) {
if(obj.containsKey("bitLength")) this->bitLength = obj["bitLength"]; if(obj.containsKey("bitLength")) this->bitLength = obj["bitLength"];
if(obj.containsKey("proto")) this->proto = static_cast<radio_proto>(obj["proto"].as<uint8_t>()); 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("sunSensor")) this->setSunSensor(obj["sunSensor"]);
if(obj.containsKey("simMy")) this->setSimMy(obj["simMy"]);
if(obj.containsKey("light")) this->setLight(obj["light"]); if(obj.containsKey("light")) this->setLight(obj["light"]);
if(obj.containsKey("gpioFlags")) this->gpioFlags = obj["gpioFlags"]; if(obj.containsKey("gpioFlags")) this->gpioFlags = obj["gpioFlags"];
if(obj.containsKey("gpioLLTrigger")) { if(obj.containsKey("gpioLLTrigger")) {
@ -3180,24 +3216,6 @@ int8_t SomfyShade::fromJSON(JsonObject &obj) {
} }
return err; 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) { void SomfyShade::toJSONRef(JsonResponse &json) {
json.addElem("shadeId", this->getShadeId()); json.addElem("shadeId", this->getShadeId());
json.addElem("roomId", this->roomId); json.addElem("roomId", this->roomId);
@ -3249,6 +3267,7 @@ void SomfyShade::toJSON(JsonResponse &json) {
json.addElem("gpioDown", this->gpioDown); json.addElem("gpioDown", this->gpioDown);
json.addElem("gpioMy", this->gpioMy); json.addElem("gpioMy", this->gpioMy);
json.addElem("gpioLLTrigger", ((this->gpioFlags & (uint8_t)gpio_flags_t::LowLevelTrigger) == 0) ? false : true); json.addElem("gpioLLTrigger", ((this->gpioFlags & (uint8_t)gpio_flags_t::LowLevelTrigger) == 0) ? false : true);
json.addElem("simMy", this->simMy());
json.beginArray("linkedRemotes"); json.beginArray("linkedRemotes");
for(uint8_t i = 0; i < SOMFY_MAX_LINKED_REMOTES; i++) { for(uint8_t i = 0; i < SOMFY_MAX_LINKED_REMOTES; i++) {
SomfyLinkedRemote &lremote = this->linkedRemotes[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]); 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() { void SomfyShadeController::loop() {
this->transceiver.loop(); this->transceiver.loop();
for(uint8_t i = 0; i < SOMFY_MAX_SHADES; i++) { 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 RTWProto = 0xF, // RTW Protocol
// Command extensions for 80 bit frames // Command extensions for 80 bit frames
StepUp = 0x8B, StepUp = 0x8B,
Fav = 0x90,
}; };
enum class group_types : byte { enum class group_types : byte {
channel = 0x00 channel = 0x00
@ -155,7 +156,8 @@ enum class somfy_flags_t : byte {
Light = 0x08, Light = 0x08,
Windy = 0x10, Windy = 0x10,
Sunny = 0x20, Sunny = 0x20,
Lighted = 0x40 Lighted = 0x40,
SimMy = 0x80
}; };
enum class gpio_flags_t : byte { enum class gpio_flags_t : byte {
LowLevelTrigger = 0x01 LowLevelTrigger = 0x01
@ -181,6 +183,7 @@ struct somfy_frame_t {
uint32_t await = 0; uint32_t await = 0;
uint8_t bitLength = 56; uint8_t bitLength = 56;
uint16_t pulseCount = 0; uint16_t pulseCount = 0;
uint8_t stepSize = 0;
void print(); void print();
void encodeFrame(byte *frame); void encodeFrame(byte *frame);
void decodeFrame(byte* frame); void decodeFrame(byte* frame);
@ -197,7 +200,6 @@ class SomfyRoom {
void clear(); void clear();
bool save(); bool save();
bool fromJSON(JsonObject &obj); bool fromJSON(JsonObject &obj);
//bool toJSON(JsonObject &obj);
void toJSON(JsonResponse &json); void toJSON(JsonResponse &json);
void emitState(const char *evt = "roomState"); void emitState(const char *evt = "roomState");
void emitState(uint8_t num, const char *evt = "roomState"); void emitState(uint8_t num, const char *evt = "roomState");
@ -228,7 +230,6 @@ class SomfyRemote {
uint8_t repeats = 1; uint8_t repeats = 1;
virtual bool isLastCommand(somfy_commands cmd); virtual bool isLastCommand(somfy_commands cmd);
char *getRemotePrefId() {return m_remotePrefId;} char *getRemotePrefId() {return m_remotePrefId;}
//virtual bool toJSON(JsonObject &obj);
virtual void toJSON(JsonResponse &json); virtual void toJSON(JsonResponse &json);
virtual void setRemoteAddress(uint32_t address); virtual void setRemoteAddress(uint32_t address);
virtual uint32_t getRemoteAddress(); virtual uint32_t getRemoteAddress();
@ -236,8 +237,10 @@ class SomfyRemote {
virtual uint16_t setRollingCode(uint16_t code); virtual uint16_t setRollingCode(uint16_t code);
bool hasSunSensor(); bool hasSunSensor();
bool hasLight(); bool hasLight();
bool simMy();
void setSunSensor(bool bHasSensor); void setSunSensor(bool bHasSensor);
void setLight(bool bHasLight); void setLight(bool bHasLight);
void setSimMy(bool bSimMy);
virtual void sendCommand(somfy_commands cmd); virtual void sendCommand(somfy_commands cmd);
virtual void sendCommand(somfy_commands cmd, uint8_t repeat); virtual void sendCommand(somfy_commands cmd, uint8_t repeat);
void sendSensorCommand(int8_t isWindy, int8_t isSunny, 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 #ifdef USE_NVS
void load(); void load();
#endif #endif
//somfy_tx_queue_t txQueue;
float currentPos = 0.0f; float currentPos = 0.0f;
float currentTiltPos = 0.0f; float currentTiltPos = 0.0f;
//uint16_t movement = 0;
int8_t lastMovement = 0; int8_t lastMovement = 0;
int8_t direction = 0; // 0 = stopped, 1=down, -1=up. int8_t direction = 0; // 0 = stopped, 1=down, -1=up.
int8_t tiltDirection = 0; // 0=stopped, 1=clockwise, -1=counter clockwise 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]; SomfyLinkedRemote linkedRemotes[SOMFY_MAX_LINKED_REMOTES];
bool paired = false; bool paired = false;
int8_t validateJSON(JsonObject &obj); int8_t validateJSON(JsonObject &obj);
//bool toJSONRef(JsonObject &obj);
void toJSONRef(JsonResponse &json); void toJSONRef(JsonResponse &json);
int8_t fromJSON(JsonObject &obj); int8_t fromJSON(JsonObject &obj);
//bool toJSON(JsonObject &obj) override;
void toJSON(JsonResponse &json) override; void toJSON(JsonResponse &json) override;
char name[21] = ""; char name[21] = "";
@ -548,16 +547,10 @@ class SomfyShadeController {
SomfyGroup groups[SOMFY_MAX_GROUPS]; SomfyGroup groups[SOMFY_MAX_GROUPS];
bool linkRepeater(uint32_t address); bool linkRepeater(uint32_t address);
bool unlinkRepeater(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 toJSONShades(JsonResponse &json);
void toJSONRooms(JsonResponse &json); void toJSONRooms(JsonResponse &json);
void toJSONGroups(JsonResponse &json); void toJSONGroups(JsonResponse &json);
void toJSONRepeaters(JsonResponse &json); void toJSONRepeaters(JsonResponse &json);
bool toJSONRepeaters(JsonArray &arr);
uint8_t repeaterCount(); uint8_t repeaterCount();
uint8_t roomCount(); uint8_t roomCount();
uint8_t shadeCount(); 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-title" content="ESPSomfy RTS App">
<meta name="apple-mobile-web-app-status-bar-style" content="black"> <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="main.css?v=2.4.3b" type="text/css" />
<link rel="stylesheet" href="widgets.css?v=2.4.2r" 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.2r" 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" /> <link rel="icon" type="image/png" href="favicon.png" />
<!-- iPad retina icon --> <!-- 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> <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> </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;"> <div classs="field-group" style="display:inline-block;">
<label for="selRepeatCommnds" style="cursor:pointer;color:#00bcd4;margin-right:4px;">Repeat Commands</label> <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;"> <select id="selRepeatCommands" data-bind="repeats" data-datatype="int" style="width:127px;">

View file

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