diff --git a/data/index.js b/data-src/index.js
similarity index 99%
rename from data/index.js
rename to data-src/index.js
index 2a70854..d2400e8 100644
--- a/data/index.js
+++ b/data-src/index.js
@@ -1270,7 +1270,7 @@ var security = new Security();
class General {
initialized = false;
- appVersion = 'v2.4.7';
+ appVersion = 'v2.4.8';
reloadApp = false;
init() {
if (this.initialized) return;
@@ -4335,7 +4335,7 @@ class Firmware {
if (typeof overlay !== 'undefined') overlay.remove();
reject({ htmlError: status, service: 'GET /backup' });
};
- xhr.open('GET', baseUrl.length > 0 ? `${baseUrl}/backup` : '/backup', true);
+ xhr.open('GET', baseUrl.length > 0 ? `${baseUrl}/backup?attach=true` : '/backup?attach=true', true);
xhr.send();
});
}
@@ -4385,6 +4385,20 @@ class Firmware {
if (sp) sp.innerHTML = mem.max.fmt('#,##0');
sp = document.getElementById('spanMinMemory');
if (sp) sp.innerHTML = mem.min.fmt('#,##0');
+ sp = document.getElementById('spanUptime');
+ if (sp) {
+ let t = Math.floor(mem.uptime / 1000);
+ let d = Math.floor(t / 86400); t %= 86400;
+ let h = Math.floor(t / 3600); t %= 3600;
+ let m = Math.floor(t / 60);
+ let s = t % 60;
+ let parts = [];
+ if (d > 0) parts.push(d + 'd');
+ if (h > 0) parts.push(h + 'h');
+ if (m > 0) parts.push(m + 'm');
+ if (s > 0 || parts.length === 0) parts.push(s + 's');
+ sp.innerHTML = parts.join(' ');
+ }
}
diff --git a/data/login.html b/data-src/login.html
similarity index 100%
rename from data/login.html
rename to data-src/login.html
diff --git a/data/main.css b/data-src/main.css
similarity index 100%
rename from data/main.css
rename to data-src/main.css
diff --git a/data/widgets.css b/data-src/widgets.css
similarity index 100%
rename from data/widgets.css
rename to data-src/widgets.css
diff --git a/data/appversion b/data/appversion
deleted file mode 100644
index 48a6b50..0000000
--- a/data/appversion
+++ /dev/null
@@ -1 +0,0 @@
-2.4.7
\ No newline at end of file
diff --git a/esp32_3MB.csv b/esp32_3MB.csv
new file mode 100644
index 0000000..9457d66
--- /dev/null
+++ b/esp32_3MB.csv
@@ -0,0 +1,6 @@
+# Name, Type, SubType, Offset, Size
+nvs, data, nvs, 0x9000, 0x5000
+otadata, data, ota, 0xE000, 0x2000
+app0, app, ota_0, 0x10000, 0x180000
+app1, app, ota_1, 0x190000, 0x180000
+spiffs, data, spiffs, 0x310000, 0x0F0000
diff --git a/include/README b/include/README
new file mode 100644
index 0000000..49819c0
--- /dev/null
+++ b/include/README
@@ -0,0 +1,37 @@
+
+This directory is intended for project header files.
+
+A header file is a file containing C declarations and macro definitions
+to be shared between several project source files. You request the use of a
+header file in your project source file (C, C++, etc) located in `src` folder
+by including it, with the C preprocessing directive `#include'.
+
+```src/main.c
+
+#include "header.h"
+
+int main (void)
+{
+ ...
+}
+```
+
+Including a header file produces the same results as copying the header file
+into each source file that needs it. Such copying would be time-consuming
+and error-prone. With a header file, the related declarations appear
+in only one place. If they need to be changed, they can be changed in one
+place, and programs that include the header file will automatically use the
+new version when next recompiled. The header file eliminates the labor of
+finding and changing all the copies as well as the risk that a failure to
+find one copy will result in inconsistencies within a program.
+
+In C, the convention is to give header files names that end with `.h'.
+
+Read more about using header files in official GCC documentation:
+
+* Include Syntax
+* Include Operation
+* Once-Only Headers
+* Computed Includes
+
+https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
diff --git a/lib/README b/lib/README
new file mode 100644
index 0000000..9379397
--- /dev/null
+++ b/lib/README
@@ -0,0 +1,46 @@
+
+This directory is intended for project specific (private) libraries.
+PlatformIO will compile them to static libraries and link into the executable file.
+
+The source code of each library should be placed in a separate directory
+("lib/your_library_name/[Code]").
+
+For example, see the structure of the following example libraries `Foo` and `Bar`:
+
+|--lib
+| |
+| |--Bar
+| | |--docs
+| | |--examples
+| | |--src
+| | |- Bar.c
+| | |- Bar.h
+| | |- library.json (optional. for custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
+| |
+| |--Foo
+| | |- Foo.c
+| | |- Foo.h
+| |
+| |- README --> THIS FILE
+|
+|- platformio.ini
+|--src
+ |- main.c
+
+Example contents of `src/main.c` using Foo and Bar:
+```
+#include
+#include
+
+int main (void)
+{
+ ...
+}
+
+```
+
+The PlatformIO Library Dependency Finder will find automatically dependent
+libraries by scanning project source files.
+
+More information about PlatformIO Library Dependency Finder
+- https://docs.platformio.org/page/librarymanager/ldf.html
diff --git a/minify.py b/minify.py
new file mode 100644
index 0000000..a5c581c
--- /dev/null
+++ b/minify.py
@@ -0,0 +1,290 @@
+"""
+PlatformIO pre-build script: minify & gzip web assets.
+
+Copies files from data-src/ → data/
+ • HTML – whitespace & comment removal, then gzip
+ • CSS – whitespace & comment removal, then gzip
+ • JS – whitespace & comment removal, then gzip
+ • PNG – copied as-is (optipng if available)
+ • Other – copied as-is
+
+Usage in platformio.ini
+-----------------------
+ extra_scripts = pre:minify.py
+
+Directory layout
+----------------
+ project/
+ ├── data-src/ ← original, human-readable assets
+ │ ├── index.html
+ │ ├── css/
+ │ ├── js/
+ │ └── img/
+ ├── data/ ← auto-generated (gitignore this!)
+ ├── minify.py
+ └── platformio.ini
+
+The script runs automatically before LittleFS / SPIFFS image is built.
+It also runs once at the start of every build so the data/ dir is always fresh.
+
+Dependencies: none (pure Python ≥ 3.7).
+Optional: optipng (for PNG optimisation)
+"""
+
+Import("env") # PlatformIO macro – gives us the build environment
+
+import gzip
+import os
+import re
+import shutil
+import subprocess
+import sys
+
+# ──────────────────────────────────────────────
+# Config
+# ──────────────────────────────────────────────
+SRC_DIR_NAME = "data-src"
+DST_DIR_NAME = "data"
+
+# Extensions that will be minified + gzipped
+MINIFY_AND_GZIP = {".html", ".htm", ".css", ".js", ".json", ".svg", ".xml"}
+
+# Extensions that optipng can optimise
+PNG_EXTENSIONS = {".png"}
+
+# Everything else is copied verbatim
+# ──────────────────────────────────────────────
+
+
+def _project_dir():
+ return env.subst("$PROJECT_DIR")
+
+
+def _src_dir():
+ return os.path.join(_project_dir(), SRC_DIR_NAME)
+
+
+def _dst_dir():
+ return os.path.join(_project_dir(), DST_DIR_NAME)
+
+
+# ──────────────────────────────────────────────
+# Minifiers (pure Python, no npm needed)
+# ──────────────────────────────────────────────
+def minify_html(text: str) -> str:
+ """Simple but effective HTML minifier."""
+ # Remove HTML comments (but keep IE conditional comments)
+ text = re.sub(r"", "", text, flags=re.DOTALL)
+ # Collapse whitespace between tags
+ text = re.sub(r">\s+<", "> <", text)
+ # Collapse runs of whitespace into a single space
+ text = re.sub(r"\s{2,}", " ", text)
+ # Remove whitespace around = in attributes
+ text = re.sub(r'\s*=\s*', '=', text)
+ # Strip leading/trailing whitespace per line, rejoin
+ lines = [line.strip() for line in text.splitlines() if line.strip()]
+ return " ".join(lines)
+
+
+def minify_css(text: str) -> str:
+ """Remove comments, collapse whitespace in CSS."""
+ # Remove comments
+ text = re.sub(r"/\*.*?\*/", "", text, flags=re.DOTALL)
+ # Remove whitespace around : ; { } ,
+ text = re.sub(r"\s*([:{};,])\s*", r"\1", text)
+ # Collapse remaining whitespace
+ text = re.sub(r"\s{2,}", " ", text)
+ # Strip leading/trailing
+ return text.strip()
+
+
+# def minify_js(text: str) -> str:
+# """
+# Light JS minifier – removes comments and collapses whitespace.
+# For heavy minification install terser and the script will use it
+# automatically (see _try_terser below).
+# """
+# # Remove single-line comments (careful with URLs – :// )
+# text = re.sub(r"(? str:
+ """Compact JSON by removing unnecessary whitespace."""
+ import json
+ try:
+ data = json.loads(text)
+ return json.dumps(data, separators=(",", ":"), ensure_ascii=False)
+ except json.JSONDecodeError:
+ return text
+
+
+def minify_svg(text: str) -> str:
+ """Minimal SVG minifier – comments + whitespace."""
+ text = re.sub(r"", "", text, flags=re.DOTALL)
+ text = re.sub(r">\s+<", "><", text)
+ text = re.sub(r"\s{2,}", " ", text)
+ return text.strip()
+
+
+MINIFIERS = {
+ # ".html": minify_html,
+ # ".htm": minify_html,
+ # ".css": minify_css,
+ # ".js": minify_js,
+ # ".json": minify_json,
+ # ".svg": minify_svg,
+ # ".xml": minify_svg, # same approach works for generic XML
+}
+
+
+# ──────────────────────────────────────────────
+# Optional external tools (used when available)
+# ──────────────────────────────────────────────
+def _has_command(cmd: str) -> bool:
+ return shutil.which(cmd) is not None
+
+
+def _try_terser(src_path: str, dst_path: str) -> bool:
+ """Use terser for JS if installed (npm i -g terser)."""
+ if not _has_command("terser"):
+ return False
+ try:
+ subprocess.run(
+ ["terser", src_path, "-o", dst_path, "--compress", "--mangle"],
+ check=True, capture_output=True,
+ )
+ return True
+ except subprocess.CalledProcessError:
+ return False
+
+
+def _try_optipng(path: str) -> None:
+ """Optimise PNG in-place if optipng is available."""
+ if _has_command("optipng"):
+ try:
+ subprocess.run(
+ ["optipng", "-quiet", "-o2", path],
+ check=True, capture_output=True,
+ )
+ except subprocess.CalledProcessError:
+ pass
+
+
+# ──────────────────────────────────────────────
+# Core logic
+# ──────────────────────────────────────────────
+def process_file(src_path: str, dst_path: str) -> dict:
+ """
+ Process a single file: minify, gzip or copy.
+ Returns a small stats dict.
+ """
+ ext = os.path.splitext(src_path)[1].lower()
+ original_size = os.path.getsize(src_path)
+ stats = {"src": src_path, "original": original_size, "final": 0, "action": "copy"}
+
+ os.makedirs(os.path.dirname(dst_path), exist_ok=True)
+
+ # ── Text assets: minify + gzip ──────────
+ if ext in MINIFY_AND_GZIP:
+ # Try terser for JS first
+ if ext == ".js" and _try_terser(src_path, dst_path + ".tmp"):
+ with open(dst_path + ".tmp", "rb") as f:
+ minified = f.read()
+ os.remove(dst_path + ".tmp")
+ stats["action"] = "terser+gzip"
+ else:
+ with open(src_path, "r", encoding="utf-8", errors="ignore") as f:
+ content = f.read()
+
+ minifier = MINIFIERS.get(ext)
+ if minifier:
+ content = minifier(content)
+ stats["action"] = "minify+gzip"
+ else:
+ stats["action"] = "gzip"
+
+ minified = content.encode("utf-8")
+
+ # Write gzipped version
+ gz_path = dst_path + ".gz"
+ with gzip.open(gz_path, "wb", compresslevel=9) as gz:
+ gz.write(minified)
+
+ stats["final"] = os.path.getsize(gz_path)
+ return stats
+
+ # ── PNG: copy + optional optipng ────────
+ if ext in PNG_EXTENSIONS:
+ shutil.copy2(src_path, dst_path)
+ _try_optipng(dst_path)
+ stats["final"] = os.path.getsize(dst_path)
+ stats["action"] = "optipng" if _has_command("optipng") else "copy"
+ return stats
+
+ # ── Everything else: plain copy ─────────
+ shutil.copy2(src_path, dst_path)
+ stats["final"] = os.path.getsize(dst_path)
+ return stats
+
+
+def minify_all():
+ src_dir = _src_dir()
+ dst_dir = _dst_dir()
+
+ if not os.path.isdir(src_dir):
+ print(f"[minify] WARNING: '{SRC_DIR_NAME}/' not found – skipping.")
+ return
+
+ print(f"[minify] {SRC_DIR_NAME}/ → {DST_DIR_NAME}/")
+
+ # Clean destination
+ if os.path.exists(dst_dir):
+ shutil.rmtree(dst_dir)
+ os.makedirs(dst_dir, exist_ok=True)
+
+ total_original = 0
+ total_final = 0
+ file_count = 0
+
+ for root, dirs, files in os.walk(src_dir):
+ for fname in sorted(files):
+ # Skip hidden files and editor temp files
+ if fname.startswith(".") or fname.endswith("~"):
+ continue
+
+ src_path = os.path.join(root, fname)
+ rel_path = os.path.relpath(src_path, src_dir)
+ dst_path = os.path.join(dst_dir, rel_path)
+
+ stats = process_file(src_path, dst_path)
+
+ pct = (1 - stats["final"] / stats["original"]) * 100 if stats["original"] > 0 else 0
+ print(
+ f" {rel_path:<40s} "
+ f"{stats['original']:>8,d} → {stats['final']:>8,d} B "
+ f"({pct:+.0f}%) [{stats['action']}]"
+ )
+
+ total_original += stats["original"]
+ total_final += stats["final"]
+ file_count += 1
+
+ print(f"[minify] {file_count} files processed")
+ print(
+ f"[minify] Total: {total_original:,d} → {total_final:,d} bytes "
+ f"(saved {total_original - total_final:,d} bytes, "
+ f"{(1 - total_final / total_original) * 100:.0f}%)"
+ )
+
+
+# ──────────────────────────────────────────────
+# PlatformIO hooks
+# ──────────────────────────────────────────────
+# Run at the start of every build
+minify_all()
\ No newline at end of file
diff --git a/platformio.ini b/platformio.ini
new file mode 100644
index 0000000..3cbb462
--- /dev/null
+++ b/platformio.ini
@@ -0,0 +1,66 @@
+; PlatformIO Project Configuration File
+;
+; Build options: build flags, source filter
+; Upload options: custom upload port, speed and extra flags
+; Library options: dependencies, extra library storages
+; Advanced options: extra scripting
+;
+; Please visit documentation for the other options and examples
+; https://docs.platformio.org/page/projectconf.html
+
+[platformio]
+default_envs = esp32devdbg
+
+[env]
+platform = espressif32 @ 5.4.0
+framework = arduino
+lib_deps =
+ bblanchon/ArduinoJson@^7.2.2
+ lsatan/SmartRC-CC1101-Driver-Lib@^2.5.7
+ knolleary/PubSubClient@^2.8
+ esp32async/ESPAsyncWebServer@^3.10.3
+ esp32async/AsyncTCP@^3.4.10
+extra_scripts =
+ pre:minify.py
+ post:archive_elf.py
+
+board_build.filesystem = littlefs
+build_flags =
+ -DCORE_DEBUG_LEVEL=1
+ -DCONFIG_ESP_COREDUMP_ENABLE_TO_FLASH=1
+ -DCONFIG_ESP_COREDUMP_DATA_FORMAT_ELF=1
+ -DCONFIG_ESP_COREDUMP_CHECKSUM_CRC32=1
+ -DCONFIG_ESP_TASK_WDT_PANIC=1
+ -DCONFIG_ESP_COREDUMP_DECODE_INFO=1
+monitor_speed = 115200
+monitor_filters =
+ time
+ esp32_exception_decoder
+ log2file
+
+[env:esp32dev]
+board = esp32dev
+board_build.partitions = esp32_3MB.csv
+
+[env:esp32devdbg]
+board = esp32dev
+build_type = debug
+board_build.partitions = esp32_3MB.csv
+
+[env:esp32c3]
+board = esp32-c3-devkitm-1
+board_build.partitions = esp32_3MB.csv
+
+[env:esp32s3]
+board = esp32-s3-devkitc-1
+
+[env:esp32c6]
+platform = https://github.com/mnowak32/platform-espressif32.git#boards/seeed_xiao_esp32c6
+platform_packages =
+ framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#3.0.4
+ framework-arduinoespressif32-libs @ https://github.com/espressif/arduino-esp32/releases/download/3.0.4/esp32-arduino-libs-3.0.4.zip
+board = seeed_xiao_esp32c6
+board_build.partitions = esp32_3MB.csv
+build_flags =
+ ${env.build_flags}
+ -I${platformio.packages_dir}/framework-arduinoespressif32/libraries/Network/src
diff --git a/ConfigFile.cpp b/src/ConfigFile.cpp
similarity index 90%
rename from ConfigFile.cpp
rename to src/ConfigFile.cpp
index e515bed..d496632 100644
--- a/ConfigFile.cpp
+++ b/src/ConfigFile.cpp
@@ -1,10 +1,13 @@
#include
#include
#include
+#include "esp_log.h"
#include "ConfigFile.h"
#include "Utils.h"
#include "ConfigSettings.h"
+static const char *TAG = "ConfigFile";
+
extern Preferences pref;
#define SHADE_HDR_VER 24
@@ -22,10 +25,15 @@ bool ConfigFile::begin(const char* filename, bool readOnly) {
this->_opened = true;
return true;
}
+bool ConfigFile::beginRAM(String *buf) {
+ _ramBuf = buf;
+ _opened = true;
+ return true;
+}
void ConfigFile::end() {
if(this->isOpen()) {
- if(!this->readOnly) this->file.flush();
- this->file.close();
+ if(_ramBuf) { _ramBuf = nullptr; }
+ else { if(!this->readOnly) this->file.flush(); this->file.close(); }
}
this->_opened = false;
}
@@ -63,7 +71,7 @@ bool ConfigFile::writeHeader(const config_header_t &hdr) {
bool ConfigFile::readHeader() {
if(!this->isOpen()) return false;
//if(this->file.position() != 0) this->file.seek(0, SeekSet);
- Serial.printf("Reading header at %u\n", this->file.position());
+ ESP_LOGD(TAG, "Reading header at %u", this->file.position());
this->header.version = this->readUInt8(this->header.version);
this->header.length = this->readUInt8(0);
if(this->header.version >= 19) {
@@ -88,7 +96,7 @@ bool ConfigFile::readHeader() {
this->header.transRecordSize = this->readUInt16(this->header.transRecordSize);
this->readString(this->header.serverId, sizeof(this->header.serverId));
}
- Serial.printf("version:%u len:%u roomSize:%u roomRecs:%u shadeSize:%u shadeRecs:%u groupSize:%u groupRecs: %u pos:%d\n", this->header.version, this->header.length, this->header.roomRecordSize, this->header.roomRecords, this->header.shadeRecordSize, this->header.shadeRecords, this->header.groupRecordSize, this->header.groupRecords, this->file.position());
+ ESP_LOGD(TAG, "version:%u len:%u roomSize:%u roomRecs:%u shadeSize:%u shadeRecs:%u groupSize:%u groupRecs: %u pos:%d", this->header.version, this->header.length, this->header.roomRecordSize, this->header.roomRecords, this->header.shadeRecordSize, this->header.shadeRecords, this->header.groupRecordSize, this->header.groupRecords, this->file.position());
return true;
}
/*
@@ -187,10 +195,16 @@ bool ConfigFile::readVarString(char *buff, size_t len) {
bool ConfigFile::writeString(const char *val, size_t len, const char tok) {
if(!this->isOpen()) return false;
int slen = strlen(val);
+ if(_ramBuf) {
+ if(slen > 0) _ramBuf->concat(val);
+ while(slen < (int)len - 1) { _ramBuf->concat(' '); slen++; }
+ if(tok != CFG_TOK_NONE) _ramBuf->concat(tok);
+ return true;
+ }
if(slen > 0)
if(this->file.write((uint8_t *)val, slen) != slen) return false;
// Now we need to pad the end of the string so that it is of a fixed length.
- while(slen < len - 1) {
+ while(slen < (int)len - 1) {
this->file.write(' ');
slen++;
}
@@ -202,6 +216,13 @@ bool ConfigFile::writeString(const char *val, size_t len, const char tok) {
bool ConfigFile::writeVarString(const char *val, const char tok) {
if(!this->isOpen()) return false;
int slen = strlen(val);
+ if(_ramBuf) {
+ _ramBuf->concat((char)CFG_TOK_QUOTE);
+ if(slen > 0) _ramBuf->concat(val);
+ _ramBuf->concat((char)CFG_TOK_QUOTE);
+ if(tok != CFG_TOK_NONE) _ramBuf->concat(tok);
+ return true;
+ }
this->writeChar(CFG_TOK_QUOTE);
if(slen > 0) if(this->file.write((uint8_t *)val, slen) != slen) return false;
this->writeChar(CFG_TOK_QUOTE);
@@ -210,6 +231,7 @@ bool ConfigFile::writeVarString(const char *val, const char tok) {
}
bool ConfigFile::writeChar(const char val) {
if(!this->isOpen()) return false;
+ if(_ramBuf) { _ramBuf->concat(val); return true; }
if(this->file.write(static_cast(val)) == 1) return true;
return false;
}
@@ -384,39 +406,34 @@ bool ShadeConfigFile::backup(SomfyShadeController *s) {
bool ShadeConfigFile::validate() {
this->readHeader();
if(this->header.version < 1) {
- Serial.print("Invalid Header Version:");
- Serial.println(this->header.version);
+ ESP_LOGE(TAG, "Invalid Header Version:%u", this->header.version);
return false;
}
if(this->header.shadeRecordSize < 100) {
- Serial.print("Invalid Shade Record Size:");
- Serial.println(this->header.shadeRecordSize);
+ ESP_LOGE(TAG, "Invalid Shade Record Size:%u", this->header.shadeRecordSize);
return false;
}
/*
if(this->header.shadeRecords != SOMFY_MAX_SHADES) {
- Serial.print("Invalid Shade Record Count:");
- Serial.println(this->header.shadeRecords);
+ ESP_LOGE(TAG, "Invalid Shade Record Count:%u", this->header.shadeRecords);
return false;
}
*/
if(this->header.version > 10) {
if(this->header.groupRecordSize < 100) {
- Serial.print("Invalid Group Record Size:");
- Serial.println(this->header.groupRecordSize);
+ ESP_LOGE(TAG, "Invalid Group Record Size:%u", this->header.groupRecordSize);
return false;
}
/*
if(this->header.groupRecords != SOMFY_MAX_GROUPS) {
- Serial.print("Invalid Group Record Count:");
- Serial.println(this->header.groupRecords);
+ ESP_LOGE(TAG, "Invalid Group Record Count:%u", this->header.groupRecords);
return false;
}
*/
}
if(this->file.position() != this->header.length) {
- Serial.printf("File not positioned at %u end of header: %d\n", this->header.length, this->file.position());
+ ESP_LOGE(TAG, "File not positioned at %u end of header: %d", this->header.length, this->file.position());
return false;
}
@@ -433,7 +450,7 @@ bool ShadeConfigFile::validate() {
fsize += (this->header.repeaterRecordSize * this->header.repeaterRecords);
}
if(this->file.size() != fsize) {
- Serial.printf("File size is not correct should be %d and got %d\n", fsize, this->file.size());
+ ESP_LOGE(TAG, "File size is not correct should be %d and got %d", fsize, this->file.size());
}
// Next check to see if the records match the header length.
uint8_t recs = 0;
@@ -442,11 +459,11 @@ bool ShadeConfigFile::validate() {
while(recs < this->header.roomRecords) {
uint32_t pos = this->file.position();
if(!this->seekChar(CFG_REC_END)) {
- Serial.printf("Failed to find the room record end %d\n", recs);
+ ESP_LOGE(TAG, "Failed to find the room record end %d", recs);
return false;
}
if(this->file.position() - pos != this->header.roomRecordSize) {
- Serial.printf("Room record length is %d and should be %d\n", this->file.position() - pos, this->header.roomRecordSize);
+ ESP_LOGE(TAG, "Room record length is %d and should be %d", this->file.position() - pos, this->header.roomRecordSize);
return false;
}
recs++;
@@ -456,11 +473,11 @@ bool ShadeConfigFile::validate() {
while(recs < this->header.shadeRecords) {
uint32_t pos = this->file.position();
if(!this->seekChar(CFG_REC_END)) {
- Serial.printf("Failed to find the shade record end %d\n", recs);
+ ESP_LOGE(TAG, "Failed to find the shade record end %d", recs);
return false;
}
if(this->file.position() - pos != this->header.shadeRecordSize) {
- Serial.printf("Shade record length is %d and should be %d\n", this->file.position() - pos, this->header.shadeRecordSize);
+ ESP_LOGE(TAG, "Shade record length is %d and should be %d", this->file.position() - pos, this->header.shadeRecordSize);
return false;
}
recs++;
@@ -470,12 +487,12 @@ bool ShadeConfigFile::validate() {
while(recs < this->header.groupRecords) {
uint32_t pos = this->file.position();
if(!this->seekChar(CFG_REC_END)) {
- Serial.printf("Failed to find the group record end %d\n", recs);
+ ESP_LOGE(TAG, "Failed to find the group record end %d", recs);
return false;
}
recs++;
if(this->file.position() - pos != this->header.groupRecordSize) {
- Serial.printf("Group record length is %d and should be %d\n", this->file.position() - pos, this->header.groupRecordSize);
+ ESP_LOGE(TAG, "Group record length is %d and should be %d", this->file.position() - pos, this->header.groupRecordSize);
return false;
}
}
@@ -485,7 +502,7 @@ bool ShadeConfigFile::validate() {
while(recs < this->header.repeaterRecords) {
//uint32_t pos = this->file.position();
if(!this->seekChar(CFG_REC_END)) {
- Serial.printf("Failed to find the repeater record end %d\n", recs);
+ ESP_LOGE(TAG, "Failed to find the repeater record end %d", recs);
}
recs++;
@@ -515,30 +532,27 @@ bool ShadeConfigFile::restore(SomfyShadeController *s, const char *filename, res
bool ShadeConfigFile::restoreFile(SomfyShadeController *s, const char *filename, restore_options_t &opts) {
bool opened = false;
if(!this->isOpen()) {
- Serial.println("Opening shade restore file");
+ ESP_LOGI(TAG, "Opening shade restore file");
this->begin(filename, true);
opened = true;
}
if(!this->validate()) {
- Serial.println("Shade restore file invalid!");
+ ESP_LOGE(TAG, "Shade restore file invalid!");
if(opened) this->end();
return false;
}
if(opts.shades) {
- Serial.println("Restoring Rooms...");
+ ESP_LOGI(TAG, "Restoring Rooms...");
for(uint8_t i = 0; i < this->header.roomRecords; i++) {
this->readRoomRecord(&s->rooms[i]);
- if(i > 0) Serial.print(",");
- Serial.print(s->rooms[i].roomId);
+ ESP_LOGD(TAG, "Room %u", s->rooms[i].roomId);
}
- Serial.println("Restoring Shades...");
+ ESP_LOGI(TAG, "Restoring Shades...");
// We should be valid so start reading.
for(uint8_t i = 0; i < this->header.shadeRecords; i++) {
this->readShadeRecord(&s->shades[i]);
- if(i > 0) Serial.print(",");
- Serial.print(s->shades[i].getShadeId());
+ ESP_LOGD(TAG, "Shade %u", s->shades[i].getShadeId());
}
- Serial.println("");
if(this->header.shadeRecords < SOMFY_MAX_SHADES) {
uint8_t ndx = this->header.shadeRecords;
// Clear out any positions that are not in the shade file.
@@ -546,13 +560,11 @@ bool ShadeConfigFile::restoreFile(SomfyShadeController *s, const char *filename,
((SomfyShade *)&s->shades[ndx++])->clear();
}
}
- Serial.println("Restoring Groups...");
+ ESP_LOGI(TAG, "Restoring Groups...");
for(uint8_t i = 0; i < this->header.groupRecords; i++) {
- if(i > 0) Serial.print(",");
- Serial.print(s->groups[i].getGroupId());
+ ESP_LOGD(TAG, "Group %u", s->groups[i].getGroupId());
this->readGroupRecord(&s->groups[i]);
}
- Serial.println("");
if(this->header.groupRecords < SOMFY_MAX_GROUPS) {
uint8_t ndx = this->header.groupRecords;
// Clear out any positions that are not in the shade file.
@@ -562,14 +574,14 @@ bool ShadeConfigFile::restoreFile(SomfyShadeController *s, const char *filename,
}
}
else {
- Serial.println("Shade data ignored");
+ ESP_LOGI(TAG, "Shade data ignored");
// FF past the shades and groups.
this->file.seek(this->file.position()
+ (this->header.shadeRecords * this->header.shadeRecordSize)
+ (this->header.groupRecords * this->header.groupRecordSize), SeekSet); // Start at the beginning of the file after the header.
}
if(opts.repeaters) {
- Serial.println("Restoring Repeaters...");
+ ESP_LOGI(TAG, "Restoring Repeaters...");
if(this->header.repeaterRecords > 0) {
memset(s->repeaters, 0x00, sizeof(uint32_t) * SOMFY_MAX_REPEATERS);
for(uint8_t i = 0; i < this->header.repeaterRecords; i++) {
@@ -604,7 +616,9 @@ bool ShadeConfigFile::restoreFile(SomfyShadeController *s, const char *filename,
if(opts.network) {
settings.IP.save();
settings.WIFI.save();
+#ifndef CONFIG_IDF_TARGET_ESP32C6
settings.Ethernet.save();
+#endif
}
if(opts.mqtt) settings.MQTT.save();
return true;
@@ -613,7 +627,7 @@ bool ShadeConfigFile::readNetRecord(restore_options_t &opts) {
if(this->header.netRecordSize > 0) {
uint32_t startPos = this->file.position();
if(opts.network) {
- Serial.println("Reading network settings from file...");
+ ESP_LOGD(TAG, "Reading network settings from file...");
settings.connType = static_cast(this->readUInt8(static_cast(conn_types_t::unset)));
settings.IP.dhcp = this->readBool(true);
char ip[24];
@@ -659,7 +673,8 @@ bool ShadeConfigFile::readNetRecord(restore_options_t &opts) {
// the ethernet phy settings.
if(opts.network) {
if(strncmp(settings.serverId, this->header.serverId, sizeof(settings.serverId)) == 0) {
- Serial.println("Restoring Ethernet adapter settings");
+ ESP_LOGI(TAG, "Restoring Ethernet adapter settings");
+#ifndef CONFIG_IDF_TARGET_ESP32C6
settings.Ethernet.boardType = this->readUInt8(1);
settings.Ethernet.phyType = static_cast(this->readUInt8(0));
settings.Ethernet.CLKMode = static_cast(this->readUInt8(0));
@@ -667,10 +682,11 @@ bool ShadeConfigFile::readNetRecord(restore_options_t &opts) {
settings.Ethernet.PWRPin = this->readInt8(1);
settings.Ethernet.MDCPin = this->readInt8(16);
settings.Ethernet.MDIOPin = this->readInt8(23);
+#endif
}
}
if(this->file.position() != startPos + this->header.netRecordSize) {
- Serial.println("Reading to end of network record");
+ ESP_LOGD(TAG, "Reading to end of network record");
this->seekChar(CFG_REC_END);
}
}
@@ -679,7 +695,7 @@ bool ShadeConfigFile::readNetRecord(restore_options_t &opts) {
bool ShadeConfigFile::readTransRecord(transceiver_config_t &cfg) {
if(this->header.transRecordSize > 0) {
uint32_t startPos = this->file.position();
- Serial.println("Reading Transceiver settings from file...");
+ ESP_LOGD(TAG, "Reading Transceiver settings from file...");
cfg.enabled = this->readBool(false);
cfg.proto = static_cast(this->readUInt8(0));
cfg.type = this->readUInt8(56);
@@ -694,7 +710,7 @@ bool ShadeConfigFile::readTransRecord(transceiver_config_t &cfg) {
cfg.deviation = this->readFloat(cfg.deviation);
cfg.txPower = this->readInt8(cfg.txPower);
if(this->file.position() != startPos + this->header.transRecordSize) {
- Serial.println("Reading to end of transceiver record");
+ ESP_LOGD(TAG, "Reading to end of transceiver record");
this->seekChar(CFG_REC_END);
}
@@ -704,7 +720,7 @@ bool ShadeConfigFile::readTransRecord(transceiver_config_t &cfg) {
bool ShadeConfigFile::readSettingsRecord() {
if(this->header.settingsRecordSize > 0) {
uint32_t startPos = this->file.position();
- Serial.println("Reading settings from file...");
+ ESP_LOGD(TAG, "Reading settings from file...");
char ver[24];
this->readVarString(ver, sizeof(ver));
this->readVarString(settings.hostname, sizeof(settings.hostname));
@@ -713,7 +729,7 @@ bool ShadeConfigFile::readSettingsRecord() {
settings.ssdpBroadcast = this->readBool(false);
if(this->header.version >= 20) settings.checkForUpdate = this->readBool(true);
if(this->file.position() != startPos + this->header.settingsRecordSize) {
- Serial.println("Reading to end of settings record");
+ ESP_LOGD(TAG, "Reading to end of settings record");
this->seekChar(CFG_REC_END);
}
}
@@ -753,7 +769,7 @@ bool ShadeConfigFile::readGroupRecord(SomfyGroup *group) {
pref.end();
if(this->file.position() != startPos + this->header.groupRecordSize) {
- Serial.println("Reading to end of group record");
+ ESP_LOGD(TAG, "Reading to end of group record");
this->seekChar(CFG_REC_END);
}
return true;
@@ -765,7 +781,7 @@ bool ShadeConfigFile::readRepeaterRecord(SomfyShadeController *s) {
s->linkRepeater(this->readUInt32(0));
}
if(this->file.position() != startPos + this->header.repeaterRecordSize) {
- Serial.println("Reading to end of repeater record");
+ ESP_LOGD(TAG, "Reading to end of repeater record");
this->seekChar(CFG_REC_END);
}
return true;
@@ -776,7 +792,7 @@ bool ShadeConfigFile::readRoomRecord(SomfyRoom *room) {
this->readString(room->name, sizeof(room->name));
room->sortOrder = this->readUInt8(room->roomId - 1);
if(this->file.position() != startPos + this->header.roomRecordSize) {
- Serial.println("Reading to end of room record");
+ ESP_LOGD(TAG, "Reading to end of room record");
this->seekChar(CFG_REC_END);
}
return true;
@@ -862,7 +878,7 @@ bool ShadeConfigFile::readShadeRecord(SomfyShade *shade) {
pinMode(shade->gpioMy, OUTPUT);
if(this->header.version >= 19) shade->roomId = this->readUInt8(0);
if(this->file.position() != startPos + this->header.shadeRecordSize) {
- Serial.println("Reading to end of shade record");
+ ESP_LOGD(TAG, "Reading to end of shade record");
this->seekChar(CFG_REC_END);
}
return true;
@@ -870,12 +886,12 @@ bool ShadeConfigFile::readShadeRecord(SomfyShade *shade) {
bool ShadeConfigFile::loadFile(SomfyShadeController *s, const char *filename) {
bool opened = false;
if(!this->isOpen()) {
- Serial.println("Opening shade config file");
+ ESP_LOGI(TAG, "Opening shade config file");
this->begin(filename, true);
opened = true;
}
if(!this->validate()) {
- Serial.println("Shade config file invalid!");
+ ESP_LOGE(TAG, "Shade config file invalid!");
if(opened) this->end();
return false;
}
@@ -917,7 +933,7 @@ bool ShadeConfigFile::loadFile(SomfyShadeController *s, const char *filename) {
this->readRepeaterRecord(s);
}
if(opened) {
- Serial.println("Closing shade config file");
+ ESP_LOGI(TAG, "Closing shade config file");
this->end();
}
return true;
@@ -1023,6 +1039,7 @@ bool ShadeConfigFile::writeNetRecord() {
this->writeBool(settings.MQTT.pubDisco);
this->writeVarString(settings.MQTT.rootTopic);
this->writeVarString(settings.MQTT.discoTopic);
+#ifndef CONFIG_IDF_TARGET_ESP32C6
this->writeUInt8(settings.Ethernet.boardType);
this->writeUInt8(static_cast(settings.Ethernet.phyType));
this->writeUInt8(static_cast(settings.Ethernet.CLKMode));
@@ -1030,6 +1047,9 @@ bool ShadeConfigFile::writeNetRecord() {
this->writeInt8(settings.Ethernet.PWRPin);
this->writeInt8(settings.Ethernet.MDCPin);
this->writeInt8(settings.Ethernet.MDIOPin, CFG_REC_END);
+#else
+ this->writeUInt8(0, CFG_REC_END);
+#endif
return true;
}
bool ShadeConfigFile::writeTransRecord(transceiver_config_t &cfg) {
diff --git a/ConfigFile.h b/src/ConfigFile.h
similarity index 98%
rename from ConfigFile.h
rename to src/ConfigFile.h
index 5ad9281..011e7a9 100644
--- a/ConfigFile.h
+++ b/src/ConfigFile.h
@@ -31,12 +31,14 @@ struct config_header_t {
class ConfigFile {
protected:
File file;
+ String *_ramBuf = nullptr;
bool readOnly = false;
bool begin(const char *filename, bool readOnly = false);
uint32_t startRecPos = 0;
bool _opened = false;
public:
config_header_t header;
+ bool beginRAM(String *buf);
void end();
bool isOpen();
bool seekRecordByIndex(uint16_t ndx);
diff --git a/ConfigSettings.cpp b/src/ConfigSettings.cpp
similarity index 73%
rename from ConfigSettings.cpp
rename to src/ConfigSettings.cpp
index 5deec5e..cb9a6a9 100644
--- a/ConfigSettings.cpp
+++ b/src/ConfigSettings.cpp
@@ -6,16 +6,19 @@
#include "ConfigSettings.h"
#include "Utils.h"
#include "esp_chip_info.h"
+#include "esp_log.h"
+
+static const char *TAG = "Config";
Preferences pref;
void restore_options_t::fromJSON(JsonObject &obj) {
- if(obj.containsKey("shades")) this->shades = obj["shades"];
- if(obj.containsKey("settings")) this->settings = obj["settings"];
- if(obj.containsKey("network")) this->network = obj["network"];
- if(obj.containsKey("transceiver")) this->transceiver = obj["transceiver"];
- if(obj.containsKey("repeaters")) this->repeaters = obj["repeaters"];
- if(obj.containsKey("mqtt")) this->mqtt = obj["mqtt"];
+ if(!obj["shades"].isNull()) this->shades = obj["shades"];
+ if(!obj["settings"].isNull()) this->settings = obj["settings"];
+ if(!obj["network"].isNull()) this->network = obj["network"];
+ if(!obj["transceiver"].isNull()) this->transceiver = obj["transceiver"];
+ if(!obj["repeaters"].isNull()) this->repeaters = obj["repeaters"];
+ if(!obj["mqtt"].isNull()) this->mqtt = obj["mqtt"];
}
int8_t appver_t::compare(appver_t &ver) {
if(this->major == ver.major && this->minor == ver.minor && this->build == ver.build) return 0;
@@ -85,13 +88,6 @@ bool appver_t::toJSON(JsonObject &obj) {
obj["suffix"] = this->suffix;
return true;
}
-void appver_t::toJSON(JsonResponse &json) {
- json.addElem("name", this->name);
- json.addElem("major", this->major);
- json.addElem("minor", this->minor);
- json.addElem("build", this->build);
- json.addElem("suffix", this->suffix);
-}
void appver_t::toJSON(JsonSockEvent *json) {
json->addElem("name", this->name);
json->addElem("major", this->major);
@@ -111,7 +107,7 @@ bool BaseSettings::loadFile(const char *filename) {
char c = file.read();
data += c;
}
- DynamicJsonDocument doc(filesize);
+ JsonDocument doc;
deserializeJson(doc, data);
JsonObject obj = doc.as();
this->fromJSON(obj);
@@ -121,7 +117,7 @@ bool BaseSettings::loadFile(const char *filename) {
}
bool BaseSettings::saveFile(const char *filename) {
File file = LittleFS.open(filename, "w");
- DynamicJsonDocument doc(2048);
+ JsonDocument doc;
JsonObject obj = doc.as();
this->toJSON(obj);
serializeJson(doc, file);
@@ -129,11 +125,11 @@ bool BaseSettings::saveFile(const char *filename) {
return true;
}
bool BaseSettings::parseValueString(JsonObject &obj, const char *prop, char *pdest, size_t size) {
- if(obj.containsKey(prop)) strlcpy(pdest, obj[prop], size);
+ if(!obj[prop].isNull()) strlcpy(pdest, obj[prop], size);
return true;
}
bool BaseSettings::parseIPAddress(JsonObject &obj, const char *prop, IPAddress *pdest) {
- if(obj.containsKey(prop)) {
+ if(!obj[prop].isNull()) {
char buff[16];
strlcpy(buff, obj[prop], sizeof(buff));
pdest->fromString(buff);
@@ -141,11 +137,11 @@ bool BaseSettings::parseIPAddress(JsonObject &obj, const char *prop, IPAddress *
return true;
}
int BaseSettings::parseValueInt(JsonObject &obj, const char *prop, int defVal) {
- if(obj.containsKey(prop)) return obj[prop];
+ if(!obj[prop].isNull()) return obj[prop];
return defVal;
}
double BaseSettings::parseValueDouble(JsonObject &obj, const char *prop, double defVal) {
- if(obj.containsKey(prop)) return obj[prop];
+ if(!obj[prop].isNull()) return obj[prop];
return defVal;
}
bool ConfigSettings::begin() {
@@ -159,26 +155,19 @@ bool ConfigSettings::begin() {
case esp_chip_model_t::CHIP_ESP32S3:
strcpy(this->chipModel, "s3");
break;
- case esp_chip_model_t::CHIP_ESP32S2:
- strcpy(this->chipModel, "s2");
- break;
case esp_chip_model_t::CHIP_ESP32C3:
strcpy(this->chipModel, "c3");
break;
-// case esp_chip_model_t::CHIP_ESP32C2:
-// strcpy(this->chipModel, "c2");
-// break;
-// case esp_chip_model_t::CHIP_ESP32C6:
-// strcpy(this->chipModel, "c6");
-// break;
- case esp_chip_model_t::CHIP_ESP32H2:
- strcpy(this->chipModel, "h2");
+#ifdef CHIP_ESP32C6
+ case esp_chip_model_t::CHIP_ESP32C6:
+ strcpy(this->chipModel, "c6");
break;
+#endif
default:
sprintf(this->chipModel, "UNK%d", static_cast(ci.model));
break;
}
- Serial.printf("Chip Model ESP32-%s\n", this->chipModel);
+ ESP_LOGD(TAG, "Chip Model ESP32-%s", this->chipModel);
this->fwVersion.parse(FW_VERSION);
uint64_t mac = ESP.getEfuseMac();
for(int i=0; i<17; i=i+8) {
@@ -192,7 +181,9 @@ bool ConfigSettings::begin() {
this->Security.begin();
this->IP.begin();
this->WIFI.begin();
+#ifndef CONFIG_IDF_TARGET_ESP32C6
this->Ethernet.begin();
+#endif
this->NTP.begin();
this->MQTT.begin();
this->print();
@@ -249,28 +240,22 @@ bool ConfigSettings::toJSON(JsonObject &obj) {
obj["checkForUpdate"] = this->checkForUpdate;
return true;
}
-void ConfigSettings::toJSON(JsonResponse &json) {
- json.addElem("ssdpBroadcast", this->ssdpBroadcast);
- json.addElem("hostname", this->hostname);
- json.addElem("connType", static_cast(this->connType));
- json.addElem("chipModel", this->chipModel);
- json.addElem("checkForUpdate", this->checkForUpdate);
-}
-
bool ConfigSettings::requiresAuth() { return this->Security.type != security_types::None; }
bool ConfigSettings::fromJSON(JsonObject &obj) {
- if(obj.containsKey("ssdpBroadcast")) this->ssdpBroadcast = obj["ssdpBroadcast"];
- if(obj.containsKey("hostname")) this->parseValueString(obj, "hostname", this->hostname, sizeof(this->hostname));
- if(obj.containsKey("connType")) this->connType = static_cast(obj["connType"].as());
- if(obj.containsKey("checkForUpdate")) this->checkForUpdate = obj["checkForUpdate"];
+ if(!obj["ssdpBroadcast"].isNull()) this->ssdpBroadcast = obj["ssdpBroadcast"];
+ if(!obj["hostname"].isNull()) this->parseValueString(obj, "hostname", this->hostname, sizeof(this->hostname));
+ if(!obj["connType"].isNull()) this->connType = static_cast(obj["connType"].as());
+ if(!obj["checkForUpdate"].isNull()) this->checkForUpdate = obj["checkForUpdate"];
return true;
}
void ConfigSettings::print() {
this->Security.print();
- Serial.printf("Connection Type: %u\n", (unsigned int) this->connType);
+ ESP_LOGD(TAG, "Connection Type: %u", (unsigned int) this->connType);
this->NTP.print();
if(this->connType == conn_types_t::wifi || this->connType == conn_types_t::unset) this->WIFI.print();
+#ifndef CONFIG_IDF_TARGET_ESP32C6
if(this->connType == conn_types_t::ethernet || this->connType == conn_types_t::ethernetpref) this->Ethernet.print();
+#endif
}
void ConfigSettings::emitSockets() {}
void ConfigSettings::emitSockets(uint8_t num) {}
@@ -308,18 +293,6 @@ bool MQTTSettings::begin() {
this->load();
return true;
}
-void MQTTSettings::toJSON(JsonResponse &json) {
- json.addElem("enabled", this->enabled);
- json.addElem("pubDisco", this->pubDisco);
- json.addElem("protocol", this->protocol);
- json.addElem("hostname", this->hostname);
- json.addElem("port", (uint32_t)this->port);
- json.addElem("username", this->username);
- json.addElem("password", this->password);
- json.addElem("rootTopic", this->rootTopic);
- json.addElem("discoTopic", this->discoTopic);
-}
-
bool MQTTSettings::toJSON(JsonObject &obj) {
obj["enabled"] = this->enabled;
obj["pubDisco"] = this->pubDisco;
@@ -333,15 +306,15 @@ bool MQTTSettings::toJSON(JsonObject &obj) {
return true;
}
bool MQTTSettings::fromJSON(JsonObject &obj) {
- if(obj.containsKey("enabled")) this->enabled = obj["enabled"];
- if(obj.containsKey("pubDisco")) this->pubDisco = obj["pubDisco"];
+ if(!obj["enabled"].isNull()) this->enabled = obj["enabled"];
+ if(!obj["pubDisco"].isNull()) this->pubDisco = obj["pubDisco"];
this->parseValueString(obj, "protocol", this->protocol, sizeof(this->protocol));
this->parseValueString(obj, "hostname", this->hostname, sizeof(this->hostname));
this->parseValueString(obj, "username", this->username, sizeof(this->username));
this->parseValueString(obj, "password", this->password, sizeof(this->password));
this->parseValueString(obj, "rootTopic", this->rootTopic, sizeof(this->rootTopic));
this->parseValueString(obj, "discoTopic", this->discoTopic, sizeof(this->discoTopic));
- if(obj.containsKey("port")) this->port = obj["port"];
+ if(!obj["port"].isNull()) this->port = obj["port"];
return true;
}
bool MQTTSettings::save() {
@@ -373,7 +346,7 @@ bool MQTTSettings::load() {
pref.end();
return true;
}
-bool ConfigSettings::toJSON(DynamicJsonDocument &doc) {
+bool ConfigSettings::toJSON(JsonDocument &doc) {
doc["fwVersion"] = this->fwVersion.name;
JsonObject objWIFI = doc.createNestedObject("WIFI");
this->WIFI.toJSON(objWIFI);
@@ -408,21 +381,13 @@ bool NTPSettings::load() {
return true;
}
void NTPSettings::print() {
- Serial.println("NTP Settings ");
- Serial.print(this->ntpServer);
- Serial.print(" TZ:");
- Serial.println(this->posixZone);
+ ESP_LOGD(TAG, "NTP Settings %s TZ:%s", this->ntpServer, this->posixZone);
}
bool NTPSettings::fromJSON(JsonObject &obj) {
this->parseValueString(obj, "ntpServer", this->ntpServer, sizeof(this->ntpServer));
this->parseValueString(obj, "posixZone", this->posixZone, sizeof(this->posixZone));
return true;
}
-void NTPSettings::toJSON(JsonResponse &json) {
- json.addElem("ntpServer", this->ntpServer);
- json.addElem("posixZone", this->posixZone);
-}
-
bool NTPSettings::toJSON(JsonObject &obj) {
obj["ntpServer"] = this->ntpServer;
obj["posixZone"] = this->posixZone;
@@ -441,7 +406,7 @@ bool IPSettings::begin() {
return true;
}
bool IPSettings::fromJSON(JsonObject &obj) {
- if(obj.containsKey("dhcp")) this->dhcp = obj["dhcp"];
+ if(!obj["dhcp"].isNull()) this->dhcp = obj["dhcp"];
this->parseIPAddress(obj, "ip", &this->ip);
this->parseIPAddress(obj, "gateway", &this->gateway);
this->parseIPAddress(obj, "subnet", &this->subnet);
@@ -459,16 +424,6 @@ bool IPSettings::toJSON(JsonObject &obj) {
obj["dns2"] = this->dns2 == ipEmpty ? "" : this->dns2.toString();
return true;
}
-void IPSettings::toJSON(JsonResponse &json) {
- IPAddress ipEmpty(0,0,0,0);
- json.addElem("dhcp", this->dhcp);
- json.addElem("ip", this->ip.toString().c_str());
- json.addElem("gateway", this->gateway.toString().c_str());
- json.addElem("subnet", this->subnet.toString().c_str());
- json.addElem("dns1", this->dns1.toString().c_str());
- json.addElem("dns2", this->dns2.toString().c_str());
-}
-
bool IPSettings::save() {
pref.begin("IP");
pref.clear();
@@ -505,7 +460,7 @@ bool IPSettings::load() {
pref.getString("dns2", buff, sizeof(buff));
this->dns2.fromString(buff);
}
- Serial.printf("Preference IP Free Entries: %d\n", pref.freeEntries());
+ ESP_LOGD(TAG, "Preference IP Free Entries: %d", pref.freeEntries());
pref.end();
return true;
}
@@ -514,11 +469,11 @@ bool SecuritySettings::begin() {
return true;
}
bool SecuritySettings::fromJSON(JsonObject &obj) {
- if(obj.containsKey("type")) this->type = static_cast(obj["type"].as());
+ if(!obj["type"].isNull()) this->type = static_cast(obj["type"].as());
this->parseValueString(obj, "username", this->username, sizeof(this->username));
this->parseValueString(obj, "password", this->password, sizeof(this->password));
this->parseValueString(obj, "pin", this->pin, sizeof(this->pin));
- if(obj.containsKey("permissions")) this->permissions = obj["permissions"];
+ if(!obj["permissions"].isNull()) this->permissions = obj["permissions"];
return true;
}
bool SecuritySettings::toJSON(JsonObject &obj) {
@@ -529,14 +484,6 @@ bool SecuritySettings::toJSON(JsonObject &obj) {
obj["permissions"] = this->permissions;
return true;
}
-void SecuritySettings::toJSON(JsonResponse &json) {
- json.addElem("type", static_cast(this->type));
- json.addElem("username", this->username);
- json.addElem("password", this->password);
- json.addElem("pin", this->pin);
- json.addElem("permissions", this->permissions);
-}
-
bool SecuritySettings::save() {
pref.begin("SEC");
pref.clear();
@@ -559,16 +506,8 @@ bool SecuritySettings::load() {
return true;
}
void SecuritySettings::print() {
- Serial.print("SECURITY Type:");
- Serial.print(static_cast(this->type));
- Serial.print(" Username:[");
- Serial.print(this->username);
- Serial.print("] Password:[");
- Serial.print(this->password);
- Serial.print("] Pin:[");
- Serial.print(this->pin);
- Serial.print("] Permissions:");
- Serial.println(this->permissions);
+ ESP_LOGD(TAG, "SECURITY Type:%u Username:[%s] Password:[%s] Pin:[%s] Permissions:%u",
+ static_cast(this->type), this->username, this->password, this->pin, this->permissions);
}
WifiSettings::WifiSettings() {}
@@ -579,8 +518,8 @@ bool WifiSettings::begin() {
bool WifiSettings::fromJSON(JsonObject &obj) {
this->parseValueString(obj, "ssid", this->ssid, sizeof(this->ssid));
this->parseValueString(obj, "passphrase", this->passphrase, sizeof(this->passphrase));
- if(obj.containsKey("roaming")) this->roaming = obj["roaming"];
- if(obj.containsKey("hidden")) this->hidden = obj["hidden"];
+ if(!obj["roaming"].isNull()) this->roaming = obj["roaming"];
+ if(!obj["hidden"].isNull()) this->hidden = obj["hidden"];
return true;
}
bool WifiSettings::toJSON(JsonObject &obj) {
@@ -590,13 +529,6 @@ bool WifiSettings::toJSON(JsonObject &obj) {
obj["hidden"] = this->hidden;
return true;
}
-void WifiSettings::toJSON(JsonResponse &json) {
- json.addElem("ssid", this->ssid);
- json.addElem("passphrase", this->passphrase);
- json.addElem("roaming", this->roaming);
- json.addElem("hidden", this->hidden);
-}
-
bool WifiSettings::save() {
pref.begin("WIFI");
pref.clear();
@@ -636,34 +568,17 @@ String WifiSettings::mapEncryptionType(int type) {
return "Unknown";
}
void WifiSettings::print() {
- Serial.println("WIFI Settings");
- Serial.print(" SSID: [");
- Serial.print(this->ssid);
- Serial.print("] PassPhrase: [");
- Serial.print(this->passphrase);
- Serial.println("]");
+ ESP_LOGD(TAG, "WIFI Settings SSID: [%s] PassPhrase: [%s]", this->ssid, this->passphrase);
}
void WifiSettings::printNetworks() {
int n = WiFi.scanNetworks(false, false);
- Serial.print("Scanned ");
- Serial.print(n);
- Serial.println(" Networks...");
+ ESP_LOGI(TAG, "Scanned %d Networks...", n);
String network;
for(int i = 0; i < n; i++) {
- if(WiFi.SSID(i).compareTo(this->ssid) == 0) Serial.print("*");
- else Serial.print(" ");
- Serial.print(i);
- Serial.print(": ");
- Serial.print(WiFi.SSID(i));
- Serial.print(" (");
- Serial.print(WiFi.RSSI(i));
- Serial.print("dBm) CH:");
- Serial.print(WiFi.channel(i));
- Serial.print(" MAC:");
- Serial.print(WiFi.BSSIDstr(i));
- Serial.println();
+ ESP_LOGI(TAG, "%s%d: %s (%ddBm) CH:%d MAC:%s",
+ (WiFi.SSID(i).compareTo(this->ssid) == 0) ? "*" : " ",
+ i, WiFi.SSID(i).c_str(), WiFi.RSSI(i), WiFi.channel(i), WiFi.BSSIDstr(i).c_str());
}
-
}
bool WifiSettings::ssidExists(const char *ssid) {
int n = WiFi.scanNetworks(false, true);
@@ -672,19 +587,20 @@ bool WifiSettings::ssidExists(const char *ssid) {
}
return false;
}
+#ifndef CONFIG_IDF_TARGET_ESP32C6
EthernetSettings::EthernetSettings() {}
bool EthernetSettings::begin() {
this->load();
return true;
}
bool EthernetSettings::fromJSON(JsonObject &obj) {
- if(obj.containsKey("boardType")) this->boardType = obj["boardType"];
- if(obj.containsKey("phyAddress")) this->phyAddress = obj["phyAddress"];
- if(obj.containsKey("CLKMode")) this->CLKMode = static_cast(obj["CLKMode"]);
- if(obj.containsKey("phyType")) this->phyType = static_cast(obj["phyType"]);
- if(obj.containsKey("PWRPin")) this->PWRPin = obj["PWRPin"];
- if(obj.containsKey("MDCPin")) this->MDCPin = obj["MDCPin"];
- if(obj.containsKey("MDIOPin")) this->MDIOPin = obj["MDIOPin"];
+ if(!obj["boardType"].isNull()) this->boardType = obj["boardType"];
+ if(!obj["phyAddress"].isNull()) this->phyAddress = obj["phyAddress"];
+ if(!obj["CLKMode"].isNull()) this->CLKMode = static_cast(obj["CLKMode"]);
+ if(!obj["phyType"].isNull()) this->phyType = static_cast(obj["phyType"]);
+ if(!obj["PWRPin"].isNull()) this->PWRPin = obj["PWRPin"];
+ if(!obj["MDCPin"].isNull()) this->MDCPin = obj["MDCPin"];
+ if(!obj["MDIOPin"].isNull()) this->MDIOPin = obj["MDIOPin"];
return true;
}
bool EthernetSettings::toJSON(JsonObject &obj) {
@@ -697,16 +613,6 @@ bool EthernetSettings::toJSON(JsonObject &obj) {
obj["MDIOPin"] = this->MDIOPin;
return true;
}
-void EthernetSettings::toJSON(JsonResponse &json) {
- json.addElem("boardType", this->boardType);
- json.addElem("phyAddress", this->phyAddress);
- json.addElem("CLKMode", static_cast(this->CLKMode));
- json.addElem("phyType", static_cast(this->phyType));
- json.addElem("PWRPin", this->PWRPin);
- json.addElem("MDCPin", this->MDCPin);
- json.addElem("MDIOPin", this->MDIOPin);
-}
-
bool EthernetSettings::usesPin(uint8_t pin) {
if((this->CLKMode == 0 || this->CLKMode == 1) && pin == 0) return true;
else if(this->CLKMode == 2 && pin == 16) return true;
@@ -742,14 +648,12 @@ bool EthernetSettings::load() {
return true;
}
void EthernetSettings::print() {
- Serial.println("Ethernet Settings");
- Serial.printf("Board:%d PHYType:%d CLK:%d ADDR:%d PWR:%d MDC:%d MDIO:%d\n", this->boardType, this->phyType, this->CLKMode, this->phyAddress, this->PWRPin, this->MDCPin, this->MDIOPin);
+ ESP_LOGD(TAG, "Ethernet Settings Board:%d PHYType:%d CLK:%d ADDR:%d PWR:%d MDC:%d MDIO:%d",
+ this->boardType, this->phyType, this->CLKMode, this->phyAddress, this->PWRPin, this->MDCPin, this->MDIOPin);
}
+#endif // CONFIG_IDF_TARGET_ESP32C6
void ConfigSettings::printAvailHeap() {
- Serial.print("Max Heap: ");
- Serial.println(ESP.getMaxAllocHeap());
- Serial.print("Free Heap: ");
- Serial.println(ESP.getFreeHeap());
- Serial.print("Min Heap: ");
- Serial.println(ESP.getMinFreeHeap());
+ ESP_LOGD(TAG, "Max Heap: %u", (unsigned int)ESP.getMaxAllocHeap());
+ ESP_LOGD(TAG, "Free Heap: %u", (unsigned int)ESP.getFreeHeap());
+ ESP_LOGD(TAG, "Min Heap: %u", (unsigned int)ESP.getMinFreeHeap());
}
diff --git a/ConfigSettings.h b/src/ConfigSettings.h
similarity index 92%
rename from ConfigSettings.h
rename to src/ConfigSettings.h
index 350db96..bdd62a7 100644
--- a/ConfigSettings.h
+++ b/src/ConfigSettings.h
@@ -1,9 +1,11 @@
#include
+#ifndef CONFIG_IDF_TARGET_ESP32C6
#include
+#endif
#ifndef configsettings_h
#define configsettings_h
#include "WResp.h"
-#define FW_VERSION "v2.4.7"
+#define FW_VERSION "v2.4.8"
enum class conn_types_t : byte {
unset = 0x00,
wifi = 0x01,
@@ -34,7 +36,7 @@ struct appver_t {
char suffix[4] = "";
void parse(const char *ver);
bool toJSON(JsonObject &obj);
- void toJSON(JsonResponse &json);
+
void toJSON(JsonSockEvent *json);
int8_t compare(appver_t &ver);
void copy(appver_t &ver);
@@ -46,7 +48,7 @@ class BaseSettings {
bool loadFile(const char* filename);
bool fromJSON(JsonObject &obj);
bool toJSON(JsonObject &obj);
- void toJSON(JsonResponse &json);
+
bool parseIPAddress(JsonObject &obj, const char *prop, IPAddress *);
bool parseValueString(JsonObject &obj, const char *prop, char *dest, size_t size);
int parseValueInt(JsonObject &obj, const char *prop, int defVal);
@@ -61,7 +63,7 @@ class NTPSettings: BaseSettings {
char posixZone[64] = "";
bool fromJSON(JsonObject &obj);
bool toJSON(JsonObject &obj);
- void toJSON(JsonResponse &json);
+
bool apply();
bool begin();
bool save();
@@ -79,7 +81,7 @@ class WifiSettings: BaseSettings {
bool begin();
bool fromJSON(JsonObject &obj);
bool toJSON(JsonObject &obj);
- void toJSON(JsonResponse &json);
+
String mapEncryptionType(int type);
bool ssidExists(const char *ssid);
void printNetworks();
@@ -88,6 +90,7 @@ class WifiSettings: BaseSettings {
void print();
};
+#ifndef CONFIG_IDF_TARGET_ESP32C6
class EthernetSettings: BaseSettings {
public:
EthernetSettings();
@@ -98,16 +101,17 @@ class EthernetSettings: BaseSettings {
int8_t PWRPin = ETH_PHY_POWER;
int8_t MDCPin = ETH_PHY_MDC;
int8_t MDIOPin = ETH_PHY_MDIO;
-
+
bool begin();
bool fromJSON(JsonObject &obj);
bool toJSON(JsonObject &obj);
- void toJSON(JsonResponse &json);
+
bool load();
bool save();
void print();
bool usesPin(uint8_t pin);
};
+#endif
class IPSettings: BaseSettings {
public:
IPSettings();
@@ -120,7 +124,7 @@ class IPSettings: BaseSettings {
bool begin();
bool fromJSON(JsonObject &obj);
bool toJSON(JsonObject &obj);
- void toJSON(JsonResponse &json);
+
bool load();
bool save();
void print();
@@ -145,7 +149,7 @@ class SecuritySettings: BaseSettings {
bool load();
void print();
bool toJSON(JsonObject &obj);
- void toJSON(JsonResponse &json);
+
bool fromJSON(JsonObject &obj);
};
class MQTTSettings: BaseSettings {
@@ -163,7 +167,7 @@ class MQTTSettings: BaseSettings {
bool save();
bool load();
bool toJSON(JsonObject &obj);
- void toJSON(JsonResponse &json);
+
bool fromJSON(JsonObject &obj);
};
class ConfigSettings: BaseSettings {
@@ -180,21 +184,23 @@ class ConfigSettings: BaseSettings {
uint8_t status;
IPSettings IP;
WifiSettings WIFI;
+#ifndef CONFIG_IDF_TARGET_ESP32C6
EthernetSettings Ethernet;
+#endif
NTPSettings NTP;
MQTTSettings MQTT;
SecuritySettings Security;
bool requiresAuth();
bool fromJSON(JsonObject &obj);
bool toJSON(JsonObject &obj);
- void toJSON(JsonResponse &json);
+
bool begin();
bool save();
bool load();
void print();
void emitSockets();
void emitSockets(uint8_t num);
- bool toJSON(DynamicJsonDocument &doc);
+ bool toJSON(JsonDocument &doc);
uint16_t calcSettingsRecSize();
uint16_t calcNetRecSize();
bool getAppVersion();
diff --git a/Network.cpp b/src/ESPNetwork.cpp
similarity index 79%
rename from Network.cpp
rename to src/ESPNetwork.cpp
index ad8f2e1..bf4f5a0 100644
--- a/Network.cpp
+++ b/src/ESPNetwork.cpp
@@ -1,21 +1,26 @@
+#ifndef CONFIG_IDF_TARGET_ESP32C6
#include
+#endif
#include
#include
#include
+#include "esp_log.h"
#include "ConfigSettings.h"
-#include "Network.h"
+#include "ESPNetwork.h"
#include "Web.h"
#include "Sockets.h"
#include "Utils.h"
#include "SSDP.h"
#include "MQTT.h"
+static const char *TAG = "Network";
+
extern ConfigSettings settings;
extern Web webServer;
extern SocketEmitter sockEmit;
extern MQTTClass mqtt;
extern rebootDelay_t rebootDelay;
-extern Network net;
+extern ESPNetwork net;
extern SomfyShadeController somfy;
static unsigned long _lastHeapEmit = 0;
@@ -24,13 +29,13 @@ static bool _apScanning = false;
static uint32_t _lastMaxHeap = 0;
static uint32_t _lastHeap = 0;
int connectRetries = 0;
-void Network::end() {
+void ESPNetwork::end() {
SSDP.end();
mqtt.end();
sockEmit.end();
delay(100);
}
-bool Network::setup() {
+bool ESPNetwork::setup() {
WiFi.setScanMethod(WIFI_ALL_CHANNEL_SCAN);
WiFi.setSortMethod(WIFI_CONNECT_AP_BY_SIGNAL);
WiFi.persistent(false);
@@ -41,29 +46,30 @@ bool Network::setup() {
if(settings.connType == conn_types_t::wifi || settings.connType == conn_types_t::unset) {
WiFi.persistent(false);
if(settings.hostname[0] != '\0') WiFi.setHostname(settings.hostname);
- Serial.print("WiFi Mode: ");
- Serial.println(WiFi.getMode());
+ ESP_LOGI(TAG, "WiFi Mode: %d", WiFi.getMode());
WiFi.mode(WIFI_STA);
}
sockEmit.begin();
return true;
}
-conn_types_t Network::preferredConnType() {
+conn_types_t ESPNetwork::preferredConnType() {
switch(settings.connType) {
case conn_types_t::wifi:
return settings.WIFI.ssid[0] != '\0' ? conn_types_t::wifi : conn_types_t::ap;
case conn_types_t::unset:
case conn_types_t::ap:
return conn_types_t::ap;
+#ifndef CONFIG_IDF_TARGET_ESP32C6
case conn_types_t::ethernetpref:
return settings.WIFI.ssid[0] != '\0' && (!ETH.linkUp() && this->ethStarted) ? conn_types_t::wifi : conn_types_t::ethernet;
case conn_types_t::ethernet:
return ETH.linkUp() || !this->ethStarted ? conn_types_t::ethernet : conn_types_t::ap;
+#endif
default:
return settings.connType;
}
}
-void Network::loop() {
+void ESPNetwork::loop() {
// ORDER OF OPERATIONS:
// ----------------------------------------------
// 1. If we are in the middle of a connection process we need to simply bail after the connect method. The
@@ -87,7 +93,7 @@ void Network::loop() {
(this->connected() && !settings.WIFI.roaming) || // We are already connected and should not be roaming.
(this->softAPOpened && WiFi.softAPgetStationNum() != 0) || // The Soft AP is open and a user is connected.
(ctype != conn_types_t::wifi)) { // The Ethernet link is up so we should ignore this scan.
- Serial.println("Cancelling WiFi STA Scan...");
+ ESP_LOGI(TAG, "Cancelling WiFi STA Scan...");
_apScanning = false;
WiFi.scanDelete();
}
@@ -99,11 +105,11 @@ void Network::loop() {
if(this->getStrongestAP(settings.WIFI.ssid, bssid, &channel)) {
if(!WiFi.BSSID() || memcmp(bssid, WiFi.BSSID(), sizeof(bssid)) != 0) {
if(!this->connected()) {
- Serial.printf("Connecting to AP %02X:%02X:%02X:%02X:%02X:%02X CH: %d\n", bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5], channel);
+ ESP_LOGD(TAG, "Connecting to AP %02X:%02X:%02X:%02X:%02X:%02X CH: %d", bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5], channel);
this->connectWiFi(bssid, channel);
}
else {
- Serial.printf("Found stronger AP %02X:%02X:%02X:%02X:%02X:%02X CH: %d\n", bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5], channel);
+ ESP_LOGD(TAG, "Found stronger AP %02X:%02X:%02X:%02X:%02X:%02X CH: %d", bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5], channel);
this->changeAP(bssid, channel);
}
}
@@ -154,7 +160,7 @@ void Network::loop() {
}
else if(!settings.ssdpBroadcast && SSDP.isStarted) SSDP.end();
}
-bool Network::changeAP(const uint8_t *bssid, const int32_t channel) {
+bool ESPNetwork::changeAP(const uint8_t *bssid, const int32_t channel) {
esp_task_wdt_reset(); // Make sure we do not reboot here.
if(SSDP.isStarted) SSDP.end();
mqtt.disconnect();
@@ -167,7 +173,7 @@ bool Network::changeAP(const uint8_t *bssid, const int32_t channel) {
this->connectStart = millis();
return false;
}
-void Network::emitSockets() {
+void ESPNetwork::emitSockets() {
this->emitHeap();
if(this->needsBroadcast ||
(this->connType == conn_types_t::wifi && (abs(abs(WiFi.RSSI()) - abs(this->lastRSSI)) > 1 || WiFi.channel() != this->lastChannel))) {
@@ -176,7 +182,8 @@ void Network::emitSockets() {
this->needsBroadcast = false;
}
}
-void Network::emitSockets(uint8_t num) {
+void ESPNetwork::emitSockets(uint8_t num) {
+#ifndef CONFIG_IDF_TARGET_ESP32C6
if(this->connType == conn_types_t::ethernet) {
JsonSockEvent *json = sockEmit.beginEmit("ethernet");
json->beginObject();
@@ -186,7 +193,9 @@ void Network::emitSockets(uint8_t num) {
json->endObject();
sockEmit.endEmit(num);
}
- else {
+ else
+#endif
+ {
if(WiFi.status() == WL_CONNECTED) {
JsonSockEvent *json = sockEmit.beginEmit("wifiStrength");
json->beginObject();
@@ -220,12 +229,12 @@ void Network::emitSockets(uint8_t num) {
}
this->emitHeap(num);
}
-void Network::setConnected(conn_types_t connType) {
+void ESPNetwork::setConnected(conn_types_t connType) {
esp_task_wdt_reset();
this->connType = connType;
this->connectTime = millis();
connectRetries = 0;
- Serial.println("Setting connected...");
+ ESP_LOGI(TAG, "Setting connected...");
if(this->connType == conn_types_t::wifi) {
if(this->softAPOpened && WiFi.softAPgetStationNum() == 0) {
WiFi.softAPdisconnect(true);
@@ -238,9 +247,10 @@ void Network::setConnected(conn_types_t connType) {
this->channel = WiFi.channel();
this->connectAttempts++;
}
+#ifndef CONFIG_IDF_TARGET_ESP32C6
else if(this->connType == conn_types_t::ethernet) {
if(this->softAPOpened) {
- Serial.println("Disonnecting from SoftAP");
+ ESP_LOGI(TAG, "Disconnecting from SoftAP");
WiFi.softAPdisconnect(true);
WiFi.mode(WIFI_OFF);
}
@@ -248,18 +258,14 @@ void Network::setConnected(conn_types_t connType) {
this->_connecting = false;
this->wifiFallback = false;
}
+#endif // CONFIG_IDF_TARGET_ESP32C6
// NET: Begin this in the startup.
//sockEmit.begin();
esp_task_wdt_reset();
-
+
if(this->connectAttempts == 1) {
- Serial.println();
if(this->connType == conn_types_t::wifi) {
- Serial.print("Successfully Connected to WiFi!!!!");
- Serial.print(WiFi.localIP());
- Serial.print(" (");
- Serial.print(this->strength);
- Serial.println("dbm)");
+ ESP_LOGI(TAG, "Successfully Connected to WiFi!!!! %s (%ddbm)", WiFi.localIP().toString().c_str(), this->strength);
if(settings.IP.dhcp) {
settings.IP.ip = WiFi.localIP();
settings.IP.subnet = WiFi.subnetMask();
@@ -268,15 +274,9 @@ void Network::setConnected(conn_types_t connType) {
settings.IP.dns2 = WiFi.dnsIP(1);
}
}
+#ifndef CONFIG_IDF_TARGET_ESP32C6
else {
- Serial.print("Successfully Connected to Ethernet!!! ");
- Serial.print(ETH.localIP());
- if(ETH.fullDuplex()) {
- Serial.print(" FULL DUPLEX");
- }
- Serial.print(" ");
- Serial.print(ETH.linkSpeed());
- Serial.println("Mbps");
+ ESP_LOGI(TAG, "Successfully Connected to Ethernet!!! %s%s %dMbps", ETH.localIP().toString().c_str(), ETH.fullDuplex() ? " FULL DUPLEX" : "", ETH.linkSpeed());
if(settings.IP.dhcp) {
settings.IP.ip = ETH.localIP();
settings.IP.subnet = ETH.subnetMask();
@@ -294,34 +294,17 @@ void Network::setConnected(conn_types_t connType) {
sockEmit.endEmit();
esp_task_wdt_reset();
}
+#endif
}
else {
- Serial.println();
- Serial.print("Reconnected after ");
- Serial.print(1.0 * (millis() - this->connectStart)/1000);
- Serial.print("sec IP: ");
if(this->connType == conn_types_t::wifi) {
- Serial.print(WiFi.localIP());
- Serial.print(" ");
- Serial.print(this->mac);
- Serial.print(" CH:");
- Serial.print(this->channel);
- Serial.print(" (");
- Serial.print(this->strength);
- Serial.print(" dBm)");
+ ESP_LOGI(TAG, "Reconnected after %.3fsec IP: %s %s CH:%d (%d dBm) Disconnected %d times", 1.0 * (millis() - this->connectStart)/1000, WiFi.localIP().toString().c_str(), this->mac.c_str(), this->channel, this->strength, this->connectAttempts - 1);
}
+#ifndef CONFIG_IDF_TARGET_ESP32C6
else {
- Serial.print(ETH.localIP());
- if(ETH.fullDuplex()) {
- Serial.print(" FULL DUPLEX");
- }
- Serial.print(" ");
- Serial.print(ETH.linkSpeed());
- Serial.print("Mbps");
+ ESP_LOGI(TAG, "Reconnected after %.3fsec IP: %s%s %dMbps Disconnected %d times", 1.0 * (millis() - this->connectStart)/1000, ETH.localIP().toString().c_str(), ETH.fullDuplex() ? " FULL DUPLEX" : "", ETH.linkSpeed(), this->connectAttempts - 1);
}
- Serial.print(" Disconnected ");
- Serial.print(this->connectAttempts - 1);
- Serial.println(" times");
+#endif
}
SSDP.setHTTPPort(80);
SSDP.setSchemaURL(0, "upnp.xml");
@@ -345,7 +328,7 @@ void Network::setConnected(conn_types_t connType) {
SSDP.setActive(0, true);
esp_task_wdt_reset();
if(MDNS.begin(settings.hostname)) {
- Serial.printf("MDNS Responder Started: serverId=%s\n", settings.serverId);
+ ESP_LOGI(TAG, "MDNS Responder Started: serverId=%s", settings.serverId);
MDNS.addService("http", "tcp", 80);
//MDNS.addServiceTxt("http", "tcp", "board", "ESP32");
//MDNS.addServiceTxt("http", "tcp", "model", "ESPSomfyRTS");
@@ -365,7 +348,8 @@ void Network::setConnected(conn_types_t connType) {
settings.printAvailHeap();
this->needsBroadcast = true;
}
-bool Network::connectWired() {
+#ifndef CONFIG_IDF_TARGET_ESP32C6
+bool ESPNetwork::connectWired() {
if(ETH.linkUp()) {
// If the ethernet link is re-established then we need to shut down wifi.
if(WiFi.status() == WL_CONNECTED) {
@@ -382,11 +366,10 @@ bool Network::connectWired() {
return this->connectWiFi();
}
if(this->connectAttempts > 0) {
- Serial.printf("Ethernet Connection Lost... %d Reconnecting ", this->connectAttempts);
- Serial.println(this->mac);
+ ESP_LOGW(TAG, "Ethernet Connection Lost... %d Reconnecting %s", this->connectAttempts, this->mac.c_str());
}
else
- Serial.println("Connecting to Wired Ethernet");
+ ESP_LOGI(TAG, "Connecting to Wired Ethernet");
this->_connecting = true;
this->connTarget = conn_types_t::ethernet;
this->connType = conn_types_t::unset;
@@ -398,10 +381,9 @@ bool Network::connectWired() {
ETH.setHostname(settings.hostname);
else
ETH.setHostname("ESPSomfy-RTS");
- Serial.print("Set hostname to:");
- Serial.println(ETH.getHostname());
- if(!ETH.begin(settings.Ethernet.phyAddress, settings.Ethernet.PWRPin, settings.Ethernet.MDCPin, settings.Ethernet.MDIOPin, settings.Ethernet.phyType, settings.Ethernet.CLKMode)) {
- Serial.println("Ethernet Begin failed");
+ ESP_LOGD(TAG, "Set hostname to: %s", ETH.getHostname());
+ if(!ETH.begin(settings.Ethernet.phyAddress, settings.Ethernet.PWRPin, settings.Ethernet.MDCPin, settings.Ethernet.MDIOPin, settings.Ethernet.phyType, settings.Ethernet.CLKMode)) {
+ ESP_LOGE(TAG, "Ethernet Begin failed");
this->ethStarted = false;
if(settings.connType == conn_types_t::ethernetpref) {
this->wifiFallback = true;
@@ -412,7 +394,7 @@ bool Network::connectWired() {
else {
if(!settings.IP.dhcp) {
if(!ETH.config(settings.IP.ip, settings.IP.gateway, settings.IP.subnet, settings.IP.dns1, settings.IP.dns2)) {
- Serial.println("Unable to configure static IP address....");
+ ESP_LOGE(TAG, "Unable to configure static IP address....");
ETH.config(INADDR_NONE, INADDR_NONE, INADDR_NONE, INADDR_NONE);
}
}
@@ -423,24 +405,28 @@ bool Network::connectWired() {
this->connectStart = millis();
return true;
}
-void Network::updateHostname() {
+#endif // CONFIG_IDF_TARGET_ESP32C6
+void ESPNetwork::updateHostname() {
if(settings.hostname[0] != '\0' && this->connected()) {
+#ifndef CONFIG_IDF_TARGET_ESP32C6
if(this->connType == conn_types_t::ethernet &&
strcmp(settings.hostname, ETH.getHostname()) != 0) {
- Serial.printf("Updating host name to %s...\n", settings.hostname);
+ ESP_LOGD(TAG, "Updating host name to %s...", settings.hostname);
ETH.setHostname(settings.hostname);
- MDNS.setInstanceName(settings.hostname);
+ MDNS.setInstanceName(settings.hostname);
SSDP.setName(0, settings.hostname);
}
- else if(strcmp(settings.hostname, WiFi.getHostname()) != 0) {
- Serial.printf("Updating host name to %s...\n", settings.hostname);
+ else
+#endif
+ if(strcmp(settings.hostname, WiFi.getHostname()) != 0) {
+ ESP_LOGD(TAG, "Updating host name to %s...", settings.hostname);
WiFi.setHostname(settings.hostname);
MDNS.setInstanceName(settings.hostname);
SSDP.setName(0, settings.hostname);
}
}
}
-bool Network::connectWiFi(const uint8_t *bssid, const int32_t channel) {
+bool ESPNetwork::connectWiFi(const uint8_t *bssid, const int32_t channel) {
if(this->softAPOpened && WiFi.softAPgetStationNum() > 0) {
// There is a client connected to the soft AP. We do not want to close out the connection. While both the
// Soft AP and a wifi connection can coexist on ESP32 the performance is abysmal.
@@ -467,7 +453,7 @@ bool Network::connectWiFi(const uint8_t *bssid, const int32_t channel) {
}
this->connTarget = conn_types_t::wifi;
this->connType = conn_types_t::unset;
- Serial.println("WiFi begin...");
+ ESP_LOGI(TAG, "WiFi begin...");
this->_connecting = true;
WiFi.begin(settings.WIFI.ssid, settings.WIFI.passphrase, channel, bssid);
this->connectStart = millis();
@@ -484,26 +470,19 @@ bool Network::connectWiFi(const uint8_t *bssid, const int32_t channel) {
this->connTarget = conn_types_t::wifi;
this->connType = conn_types_t::unset;
if(this->connectAttempts > 0) {
- Serial.print("Connection Lost...");
- Serial.print(this->mac);
- Serial.print(" CH:");
- Serial.print(this->channel);
- Serial.print(" (");
- Serial.print(this->strength);
- Serial.println("dbm) ");
+ ESP_LOGW(TAG, "Connection Lost... %s CH:%d (%ddbm)", this->mac.c_str(), this->channel, this->strength);
}
- else Serial.println("Connecting to AP");
+ else ESP_LOGI(TAG, "Connecting to AP");
delay(100);
// There is also another method simply called hostname() but this is legacy for esp8266.
if(settings.hostname[0] != '\0') WiFi.setHostname(settings.hostname);
- Serial.print("Set hostname to:");
- Serial.println(WiFi.getHostname());
+ ESP_LOGD(TAG, "Set hostname to: %s", WiFi.getHostname());
WiFi.setScanMethod(WIFI_ALL_CHANNEL_SCAN);
WiFi.setSortMethod(WIFI_CONNECT_AP_BY_SIGNAL);
uint8_t _bssid[6];
int32_t _channel = 0;
if(!settings.WIFI.hidden && this->getStrongestAP(settings.WIFI.ssid, _bssid, &_channel)) {
- Serial.printf("Found strongest AP %02X:%02X:%02X:%02X:%02X:%02X CH:%d\n", _bssid[0], _bssid[1], _bssid[2], _bssid[3], _bssid[4], _bssid[5], _channel);
+ ESP_LOGD(TAG, "Found strongest AP %02X:%02X:%02X:%02X:%02X:%02X CH:%d", _bssid[0], _bssid[1], _bssid[2], _bssid[3], _bssid[4], _bssid[5], _channel);
WiFi.begin(settings.WIFI.ssid, settings.WIFI.passphrase, _channel, _bssid);
}
else
@@ -513,15 +492,18 @@ bool Network::connectWiFi(const uint8_t *bssid, const int32_t channel) {
this->connectStart = millis();
return true;
}
-bool Network::connect(conn_types_t ctype) {
+bool ESPNetwork::connect(conn_types_t ctype) {
esp_task_wdt_reset();
if(this->connecting()) return true;
if(this->disconnectTime == 0) this->disconnectTime = millis();
+#ifndef CONFIG_IDF_TARGET_ESP32C6
if(ctype == conn_types_t::ethernet && this->connType != conn_types_t::ethernet) {
// Here we need to call the connect to ethernet.
this->connectWired();
}
- else if(ctype == conn_types_t::ap || (!this->connected() && millis() > this->disconnectTime + CONNECT_TIMEOUT)) {
+ else
+#endif
+ if(ctype == conn_types_t::ap || (!this->connected() && millis() > this->disconnectTime + CONNECT_TIMEOUT)) {
if(!this->softAPOpened && !this->openingSoftAP) {
this->disconnectTime = millis();
this->openSoftAP();
@@ -538,7 +520,7 @@ bool Network::connect(conn_types_t ctype) {
return true;
}
-uint32_t Network::getChipId() {
+uint32_t ESPNetwork::getChipId() {
uint32_t chipId = 0;
uint64_t mac = ESP.getEfuseMac();
for(int i=0; i<17; i=i+8) {
@@ -546,7 +528,7 @@ uint32_t Network::getChipId() {
}
return chipId;
}
-bool Network::getStrongestAP(const char *ssid, uint8_t *bssid, int32_t *channel) {
+bool ESPNetwork::getStrongestAP(const char *ssid, uint8_t *bssid, int32_t *channel) {
// The new AP must be at least 10dbm greater.
int32_t strength = this->connected() ? WiFi.RSSI() + 10 : -127;
int32_t chan = -1;
@@ -567,64 +549,64 @@ bool Network::getStrongestAP(const char *ssid, uint8_t *bssid, int32_t *channel)
WiFi.scanDelete();
return chan > 0;
}
-bool Network::openSoftAP() {
+bool ESPNetwork::openSoftAP() {
if(this->softAPOpened || this->openingSoftAP) return true;
if(this->connected()) WiFi.disconnect(false);
this->openingSoftAP = true;
- Serial.println();
- Serial.println("Turning the HotSpot On");
+ ESP_LOGI(TAG, "Turning the HotSpot On");
esp_task_wdt_reset(); // Make sure we do not reboot here.
WiFi.softAP(strlen(settings.hostname) > 0 ? settings.hostname : "ESPSomfy RTS", "");
delay(200);
return true;
}
-bool Network::connected() {
+bool ESPNetwork::connected() {
if(this->connecting()) return false;
else if(this->connType == conn_types_t::unset) return false;
else if(this->connType == conn_types_t::wifi) return WiFi.status() == WL_CONNECTED;
+#ifndef CONFIG_IDF_TARGET_ESP32C6
else if(this->connType == conn_types_t::ethernet) return ETH.linkUp();
+#endif
else return this->connType != conn_types_t::unset;
return false;
}
-bool Network::connecting() {
+bool ESPNetwork::connecting() {
if(this->_connecting && millis() > this->connectStart + CONNECT_TIMEOUT) this->_connecting = false;
return this->_connecting;
}
-void Network::clearConnecting() { this->_connecting = false; }
-void Network::networkEvent(WiFiEvent_t event) {
+void ESPNetwork::clearConnecting() { this->_connecting = false; }
+void ESPNetwork::networkEvent(WiFiEvent_t event) {
switch(event) {
- case ARDUINO_EVENT_WIFI_READY: Serial.println("(evt) WiFi interface ready"); break;
+ case ARDUINO_EVENT_WIFI_READY: ESP_LOGI(TAG, "(evt) WiFi interface ready"); break;
case ARDUINO_EVENT_WIFI_SCAN_DONE:
- Serial.printf("(evt) Completed scan for access points (%d)\n", WiFi.scanComplete());
+ ESP_LOGI(TAG, "(evt) Completed scan for access points (%d)", WiFi.scanComplete());
//Serial.println("(evt) Completed scan for access points");
net.lastWifiScan = millis();
break;
case ARDUINO_EVENT_WIFI_STA_START:
- Serial.println("WiFi station mode started");
+ ESP_LOGI(TAG, "WiFi station mode started");
if(settings.hostname[0] != '\0') WiFi.setHostname(settings.hostname);
break;
- case ARDUINO_EVENT_WIFI_STA_STOP: Serial.println("(evt) WiFi clients stopped"); break;
- case ARDUINO_EVENT_WIFI_STA_CONNECTED: Serial.println("(evt) Connected to WiFi STA access point"); break;
+ case ARDUINO_EVENT_WIFI_STA_STOP: ESP_LOGI(TAG, "(evt) WiFi clients stopped"); break;
+ case ARDUINO_EVENT_WIFI_STA_CONNECTED: ESP_LOGI(TAG, "(evt) Connected to WiFi STA access point"); break;
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
- Serial.printf("(evt) Disconnected from WiFi STA access point. Connecting: %d\n", net.connecting());
+ ESP_LOGI(TAG, "(evt) Disconnected from WiFi STA access point. Connecting: %d", net.connecting());
net.connType = conn_types_t::unset;
net.disconnectTime = millis();
net.clearConnecting();
break;
- case ARDUINO_EVENT_WIFI_STA_AUTHMODE_CHANGE: Serial.println("(evt) Authentication mode of STA access point has changed"); break;
+ case ARDUINO_EVENT_WIFI_STA_AUTHMODE_CHANGE: ESP_LOGI(TAG, "(evt) Authentication mode of STA access point has changed"); break;
case ARDUINO_EVENT_WIFI_STA_GOT_IP:
- Serial.print("(evt) Got WiFi STA IP: ");
- Serial.println(WiFi.localIP());
+ ESP_LOGI(TAG, "(evt) Got WiFi STA IP: %s", WiFi.localIP().toString().c_str());
net.connType = conn_types_t::wifi;
net.connectTime = millis();
net.setConnected(conn_types_t::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_WIFI_STA_LOST_IP: ESP_LOGW(TAG, "Lost IP address and IP address is reset to 0"); break;
+#ifndef CONFIG_IDF_TARGET_ESP32C6
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());
+ ESP_LOGI(TAG, "Got Ethernet IP %s", ETH.localIP().toString().c_str());
net.connectTime = millis();
net.connType = conn_types_t::ethernet;
if(settings.IP.dhcp) {
@@ -633,44 +615,46 @@ void Network::networkEvent(WiFiEvent_t event) {
settings.IP.gateway = ETH.gatewayIP();
settings.IP.dns1 = ETH.dnsIP(0);
settings.IP.dns2 = ETH.dnsIP(1);
- }
+ }
net.setConnected(conn_types_t::ethernet);
break;
case ARDUINO_EVENT_ETH_CONNECTED:
- Serial.print("(evt) Ethernet Connected ");
+ ESP_LOGI(TAG, "(evt) Ethernet Connected");
break;
case ARDUINO_EVENT_ETH_DISCONNECTED:
- Serial.println("(evt) Ethernet Disconnected");
+ ESP_LOGI(TAG, "(evt) Ethernet Disconnected");
net.connType = conn_types_t::unset;
net.disconnectTime = millis();
net.clearConnecting();
break;
- case ARDUINO_EVENT_ETH_START:
- Serial.println("(evt) Ethernet Started");
+ case ARDUINO_EVENT_ETH_START:
+ ESP_LOGI(TAG, "(evt) Ethernet Started");
net.ethStarted = true;
break;
case ARDUINO_EVENT_ETH_STOP:
- Serial.println("(evt) Ethernet Stopped");
+ ESP_LOGI(TAG, "(evt) Ethernet Stopped");
net.connType = conn_types_t::unset;
net.ethStarted = false;
break;
+#endif
case ARDUINO_EVENT_WIFI_AP_START:
- Serial.print("(evt) WiFi SoftAP Started IP:");
- Serial.println(WiFi.softAPIP());
+ ESP_LOGI(TAG, "(evt) WiFi SoftAP Started IP: %s", WiFi.softAPIP().toString().c_str());
net.openingSoftAP = false;
net.softAPOpened = true;
break;
case ARDUINO_EVENT_WIFI_AP_STOP:
- if(!net.openingSoftAP) Serial.println("(evt) WiFi SoftAP Stopped");
+ if(!net.openingSoftAP) ESP_LOGI(TAG, "(evt) WiFi SoftAP Stopped");
net.softAPOpened = false;
break;
default:
+#ifndef CONFIG_IDF_TARGET_ESP32C6
if(event > ARDUINO_EVENT_ETH_START)
- Serial.printf("(evt) Unknown Ethernet Event %d\n", event);
+ ESP_LOGW(TAG, "(evt) Unknown Ethernet Event %d", event);
+#endif
break;
}
}
-void Network::emitHeap(uint8_t num) {
+void ESPNetwork::emitHeap(uint8_t num) {
bool bEmit = false;
bool bTimeEmit = millis() - _lastHeapEmit > 15000;
bool bRoomEmit = false;
@@ -691,6 +675,7 @@ void Network::emitHeap(uint8_t num) {
json->addElem("free", freeHeap);
json->addElem("min", minHeap);
json->addElem("total", ESP.getHeapSize());
+ json->addElem("uptime", (uint64_t)millis());
json->endObject();
if(num == 255 && bTimeEmit && bValEmit) {
sockEmit.endEmit(num);
diff --git a/Network.h b/src/ESPNetwork.h
similarity index 95%
rename from Network.h
rename to src/ESPNetwork.h
index 864966c..338a282 100644
--- a/Network.h
+++ b/src/ESPNetwork.h
@@ -1,13 +1,14 @@
#include
+#include
-#ifndef Network_h
-#define Network_h
+#ifndef ESPNetwork_h
+#define ESPNetwork_h
//enum class conn_types_t : byte;
#define CONNECT_TIMEOUT 20000
#define SSID_SCAN_INTERVAL 60000
-class Network {
+class ESPNetwork {
protected:
unsigned long lastEmit = 0;
unsigned long lastMDNS = 0;
diff --git a/GitOTA.cpp b/src/GitOTA.cpp
similarity index 83%
rename from GitOTA.cpp
rename to src/GitOTA.cpp
index 49ea3c5..68de7ec 100644
--- a/GitOTA.cpp
+++ b/src/GitOTA.cpp
@@ -3,6 +3,8 @@
#include
#include
#include
+#include
+#include "esp_log.h"
#include "ConfigSettings.h"
#include "GitOTA.h"
#include "Utils.h"
@@ -10,7 +12,7 @@
#include "Somfy.h"
#include "Web.h"
#include "WResp.h"
-#include "Network.h"
+#include "ESPNetwork.h"
@@ -20,9 +22,9 @@ extern SocketEmitter sockEmit;
extern SomfyShadeController somfy;
extern rebootDelay_t rebootDelay;
extern Web webServer;
-extern Network net;
-
+extern ESPNetwork net;
+static const char *TAG = "OTA";
#define MAX_BUFF_SIZE 4096
void GitRelease::setReleaseProperty(const char *key, const char *val) {
@@ -72,22 +74,6 @@ void GitRelease::setAssetProperty(const char *key, const char *val) {
}
}
}
-void GitRelease::toJSON(JsonResponse &json) {
- Timestamp ts;
- char buff[20];
- sprintf(buff, "%llu", this->id);
- json.addElem("id", buff);
- json.addElem("name", this->name);
- json.addElem("date", ts.getISOTime(this->releaseDate));
- json.addElem("draft", this->draft);
- json.addElem("preRelease", this->preRelease);
- json.addElem("main", this->main);
- json.addElem("hasFS", this->hasFS);
- json.addElem("hwVersions", this->hwVersions);
- json.beginObject("version");
- this->version.toJSON(json);
- json.endObject();
-}
#define ERR_CLIENT_OFFSET -50
int16_t GitRepo::getReleases(uint8_t num) {
@@ -111,10 +97,10 @@ int16_t GitRepo::getReleases(uint8_t num) {
if(https.begin(sclient, url)) {
esp_task_wdt_reset();
int httpCode = https.GET();
- Serial.printf("[HTTPS] GET... code: %d\n", httpCode);
+ ESP_LOGD(TAG, "[HTTPS] GET... code: %d", httpCode);
if(httpCode > 0) {
int len = https.getSize();
- Serial.printf("[HTTPS] GET... code: %d - %d\n", httpCode, len);
+ ESP_LOGD(TAG, "[HTTPS] GET... code: %d - %d", httpCode, len);
if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
WiFiClient *stream = https.getStreamPtr();
uint8_t buff[128] = {0};
@@ -230,22 +216,6 @@ int16_t GitRepo::getReleases(uint8_t num) {
settings.printAvailHeap();
return 0;
}
-void GitRepo::toJSON(JsonResponse &json) {
- json.beginObject("fwVersion");
- settings.fwVersion.toJSON(json);
- json.endObject();
- json.beginObject("appVersion");
- settings.appVersion.toJSON(json);
- json.endObject();
- json.beginArray("releases");
- for(uint8_t i = 0; i < GIT_MAX_RELEASES + 1; i++) {
- if(this->releases[i].id == 0) continue;
- json.beginObject();
- this->releases[i].toJSON(json);
- json.endObject();
- }
- json.endArray();
-}
#define UPDATE_ERR_OFFSET 20
#define ERR_DOWNLOAD_HTTP -40
#define ERR_DOWNLOAD_BUFFER -41
@@ -261,14 +231,14 @@ void GitUpdater::loop() {
}
}
else if(this->status == GIT_AWAITING_UPDATE) {
- Serial.println("Starting update process.........");
+ ESP_LOGI(TAG, "Starting update process.........");
this->status = GIT_UPDATING;
this->beginUpdate(this->targetRelease);
this->status = GIT_STATUS_READY;
this->emitUpdateCheck();
}
else if(this->status == GIT_UPDATE_CANCELLING) {
- Serial.println("Cancelling update process..........");
+ ESP_LOGI(TAG, "Cancelling update process..........");
if(!this->lockFS) {
this->status = GIT_UPDATE_CANCELLED;
this->cancelled = true;
@@ -278,8 +248,8 @@ void GitUpdater::loop() {
}
void GitUpdater::checkForUpdate() {
if(this->status != 0) return; // If we are already checking.
- Serial.println("Check github for updates...");
-
+ ESP_LOGI(TAG, "Check github for updates...");
+
this->status = GIT_STATUS_CHECK;
settings.printAvailHeap();
this->lastCheck = millis();
@@ -310,23 +280,6 @@ void GitUpdater::setCurrentRelease(GitRepo &repo) {
}
this->emitUpdateCheck();
}
-void GitUpdater::toJSON(JsonResponse &json) {
- json.addElem("available", this->updateAvailable);
- json.addElem("status", this->status);
- json.addElem("error", (int32_t)this->error);
- json.addElem("cancelled", this->cancelled);
- json.addElem("checkForUpdate", settings.checkForUpdate);
- json.addElem("inetAvailable", this->inetAvailable);
- json.beginObject("fwVersion");
- settings.fwVersion.toJSON(json);
- json.endObject();
- json.beginObject("appVersion");
- settings.appVersion.toJSON(json);
- json.endObject();
- json.beginObject("latest");
- this->latest.toJSON(json);
- json.endObject();
-}
void GitUpdater::emitUpdateCheck(uint8_t num) {
JsonSockEvent *json = sockEmit.beginEmit("fwStatus");
json->beginObject();
@@ -365,12 +318,12 @@ int GitUpdater::checkInternet() {
esp_task_wdt_reset();
if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY || httpCode == HTTP_CODE_FOUND) {
err = 0;
- Serial.printf("Internet is Available: %ldms\n", millis() - t);
+ ESP_LOGI(TAG, "Internet is Available: %ldms", millis() - t);
this->inetAvailable = true;
}
else {
err = httpCode;
- Serial.printf("Internet is Unavailable: %d: %ldms\n", err, millis() - t);
+ ESP_LOGE(TAG, "Internet is Unavailable: %d: %ldms", err, millis() - t);
this->inetAvailable = false;
}
https.end();
@@ -420,7 +373,7 @@ void GitUpdater::setFirmwareFile() {
}
bool GitUpdater::beginUpdate(const char *version) {
- Serial.println("Begin update called...");
+ ESP_LOGI(TAG, "Begin update called...");
if(strcmp(version, "Main") == 0) strcpy(this->baseUrl, "https://raw.githubusercontent.com/rstrouse/ESPSomfy-RTS/master/");
else sprintf(this->baseUrl, "https://github.com/rstrouse/ESPSomfy-RTS/releases/download/%s/", version);
@@ -441,7 +394,7 @@ bool GitUpdater::beginUpdate(const char *version) {
if(this->error == 0) {
settings.fwVersion.parse(version);
delay(100);
- Serial.println("Committing Configuration...");
+ ESP_LOGI(TAG, "Committing Configuration...");
somfy.commit();
}
rebootDelay.reboot = true;
@@ -461,7 +414,7 @@ bool GitUpdater::recoverFilesystem() {
this->lockFS = false;
if(this->error == 0) {
delay(100);
- Serial.println("Committing Configuration...");
+ ESP_LOGI(TAG, "Committing Configuration...");
somfy.commit();
}
this->status = GIT_UPDATE_COMPLETE;
@@ -471,28 +424,27 @@ bool GitUpdater::recoverFilesystem() {
}
bool GitUpdater::endUpdate() { return true; }
int8_t GitUpdater::downloadFile() {
- Serial.printf("Begin update %s\n", this->currentFile);
+ ESP_LOGI(TAG, "Begin update %s", this->currentFile);
WiFiClientSecure sclient;
sclient.setInsecure();
HTTPClient https;
char url[196];
sprintf(url, "%s%s", this->baseUrl, this->currentFile);
- Serial.println(url);
+ ESP_LOGD(TAG, "%s", url);
esp_task_wdt_reset();
if(https.begin(sclient, url)) {
https.setFollowRedirects(HTTPC_FORCE_FOLLOW_REDIRECTS);
- Serial.print("[HTTPS] GET...\n");
+ ESP_LOGD(TAG, "[HTTPS] GET...");
int httpCode = https.GET();
if(httpCode > 0) {
size_t len = https.getSize();
size_t total = 0;
uint8_t pct = 0;
- Serial.printf("[HTTPS] GET... code: %d - %d\n", httpCode, len);
+ ESP_LOGD(TAG, "[HTTPS] GET... code: %d - %d", httpCode, len);
if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY || httpCode == HTTP_CODE_FOUND) {
WiFiClient *stream = https.getStreamPtr();
if(!Update.begin(len, this->partition)) {
- Serial.println("Update Error detected!!!!!");
- Update.printError(Serial);
+ ESP_LOGE(TAG, "Update Error detected!!!!!");
https.end();
return -(Update.getError() + UPDATE_ERR_OFFSET);
}
@@ -515,8 +467,7 @@ int8_t GitUpdater::downloadFile() {
total += c;
//Serial.println(total);
if (Update.write(buff, c) != c) {
- Update.printError(Serial);
- Serial.printf("Upload of %s aborted invalid size %d\n", url, c);
+ ESP_LOGE(TAG, "Upload of %s aborted invalid size %d", url, c);
free(buff);
https.end();
sclient.stop();
@@ -526,17 +477,16 @@ int8_t GitUpdater::downloadFile() {
uint8_t p = (uint8_t)floor(((float)total / (float)len) * 100.0f);
if(p != pct) {
pct = p;
- Serial.printf("LEN:%d TOTAL:%d %d%%\n", len, total, pct);
+ ESP_LOGD(TAG, "LEN:%d TOTAL:%d %d%%", len, total, pct);
this->emitDownloadProgress(len, total);
}
delay(1);
if(total >= len) {
if(!Update.end(true)) {
- Serial.println("Error downloading update...");
- Update.printError(Serial);
+ ESP_LOGE(TAG, "Error downloading update...");
}
else {
- Serial.println("Update.end Called...");
+ ESP_LOGI(TAG, "Update.end Called...");
}
https.end();
sclient.stop();
@@ -548,7 +498,7 @@ int8_t GitUpdater::downloadFile() {
Update.abort();
https.end();
free(buff);
- Serial.println("Stream timeout!!!");
+ ESP_LOGE(TAG, "Stream timeout!!!");
return -43;
}
sockEmit.loop();
@@ -560,28 +510,28 @@ int8_t GitUpdater::downloadFile() {
if(len > total) {
Update.abort();
somfy.commit();
- Serial.println("Error downloading file!!!");
+ ESP_LOGE(TAG, "Error downloading file!!!");
return -42;
}
else
- Serial.printf("Update %s complete\n", this->currentFile);
+ ESP_LOGI(TAG, "Update %s complete", this->currentFile);
}
else {
// TODO: memory allocation error.
- Serial.println("Unable to allocate memory for update!!!");
+ ESP_LOGE(TAG, "Unable to allocate memory for update!!!");
}
}
else {
- Serial.printf("Invalid HTTP Code... %d", httpCode);
+ ESP_LOGE(TAG, "Invalid HTTP Code... %d", httpCode);
return httpCode;
}
}
else {
- Serial.printf("Invalid HTTP Code: %d\n", httpCode);
+ ESP_LOGE(TAG, "Invalid HTTP Code: %d", httpCode);
}
https.end();
sclient.stop();
- Serial.printf("End update %s\n", this->currentFile);
+ ESP_LOGI(TAG, "End update %s", this->currentFile);
}
esp_task_wdt_reset();
return 0;
diff --git a/GitOTA.h b/src/GitOTA.h
similarity index 94%
rename from GitOTA.h
rename to src/GitOTA.h
index f79aaea..6996f83 100644
--- a/GitOTA.h
+++ b/src/GitOTA.h
@@ -28,13 +28,13 @@ class GitRelease {
appver_t version;
void setReleaseProperty(const char *key, const char *val);
void setAssetProperty(const char *key, const char *val);
- void toJSON(JsonResponse &json);
+
};
class GitRepo {
public:
int16_t getReleases(uint8_t num = GIT_MAX_RELEASES);
GitRelease releases[GIT_MAX_RELEASES + 1];
- void toJSON(JsonResponse &json);
+
};
class GitUpdater {
public:
@@ -58,7 +58,7 @@ class GitUpdater {
void setFirmwareFile();
void setCurrentRelease(GitRepo &repo);
void loop();
- void toJSON(JsonResponse &json);
+
bool recoverFilesystem();
int checkInternet();
void emitUpdateCheck(uint8_t num=255);
diff --git a/MQTT.cpp b/src/MQTT.cpp
similarity index 93%
rename from MQTT.cpp
rename to src/MQTT.cpp
index 55f693a..4332227 100644
--- a/MQTT.cpp
+++ b/src/MQTT.cpp
@@ -2,12 +2,15 @@
#include
#include
#include
+#include "esp_log.h"
#include "ConfigSettings.h"
#include "MQTT.h"
#include "Somfy.h"
-#include "Network.h"
+#include "ESPNetwork.h"
#include "Utils.h"
+static const char *TAG = "MQTT";
+
WiFiClient tcpClient;
PubSubClient mqttClient(tcpClient);
@@ -16,7 +19,7 @@ static char g_content[MQTT_MAX_RESPONSE];
extern ConfigSettings settings;
extern SomfyShadeController somfy;
-extern Network net;
+extern ESPNetwork net;
extern rebootDelay_t rebootDelay;
@@ -45,12 +48,7 @@ bool MQTTClass::loop() {
}
void MQTTClass::receive(const char *topic, byte*payload, uint32_t length) {
esp_task_wdt_reset(); // Make sure we do not reboot here.
- Serial.print("MQTT Topic:");
- Serial.print(topic);
- Serial.print(" payload:");
- for(uint32_t i=0; i= 0 && val <= 100)
+ if(val >= 0 && val <= 100) {
+ ESP_LOGI(TAG, "MQTT shade %s target=%d", entityId, val);
shade->moveToTarget(shade->transformPosition(atoi(value)));
+ }
}
if(strncmp(command, "tiltTarget", sizeof(command)) == 0) {
if(val >= 0 && val <= 100)
@@ -161,6 +154,7 @@ void MQTTClass::receive(const char *topic, byte*payload, uint32_t length) {
SomfyGroup* group = somfy.getGroupById(atoi(entityId));
if (group) {
int val = atoi(value);
+ ESP_LOGI(TAG, "MQTT group %s command=%s value=%d", entityId, command, val);
if(strncmp(command, "direction", sizeof(command)) == 0) {
if(val < 0)
group->sendCommand(somfy_commands::Up);
@@ -204,8 +198,7 @@ bool MQTTClass::connect() {
snprintf(lwtTopic, sizeof(lwtTopic), "%s/status", settings.MQTT.rootTopic);
esp_task_wdt_reset();
if(mqttClient.connect(this->clientId, settings.MQTT.username, settings.MQTT.password, lwtTopic, 0, true, "offline")) {
- Serial.print("Successfully connected MQTT client ");
- Serial.println(this->clientId);
+ ESP_LOGI(TAG, "Successfully connected MQTT client %s", this->clientId);
this->publish("status", "online", true);
this->publish("ipAddress", settings.IP.ip.toString().c_str(), true);
this->publish("host", settings.hostname, true);
@@ -228,14 +221,13 @@ bool MQTTClass::connect() {
this->subscribe("groups/+/sunny/set");
this->subscribe("groups/+/windy/set");
mqttClient.setCallback(MQTTClass::receive);
- Serial.println("MQTT Startup Completed");
+ ESP_LOGI(TAG, "MQTT Startup Completed");
esp_task_wdt_reset();
this->lastConnect = millis();
return true;
}
else {
- Serial.print("MQTT Connection failed for: ");
- Serial.println(mqttClient.state());
+ ESP_LOGE(TAG, "MQTT Connection failed for: %d", mqttClient.state());
this->lastConnect = millis();
return false;
}
@@ -273,8 +265,7 @@ bool MQTTClass::unsubscribe(const char *topic) {
snprintf(top, sizeof(top), "%s/%s", settings.MQTT.rootTopic, topic);
else
strlcpy(top, topic, sizeof(top));
- Serial.print("MQTT Unsubscribed from:");
- Serial.println(top);
+ ESP_LOGD(TAG, "MQTT Unsubscribed from:%s", top);
return mqttClient.unsubscribe(top);
}
return true;
@@ -287,8 +278,7 @@ bool MQTTClass::subscribe(const char *topic) {
snprintf(top, sizeof(top), "%s/%s", settings.MQTT.rootTopic, topic);
else
strlcpy(top, topic, sizeof(top));
- Serial.print("MQTT Subscribed to:");
- Serial.println(top);
+ ESP_LOGD(TAG, "MQTT Subscribed to:%s", top);
return mqttClient.subscribe(top);
}
return true;
diff --git a/MQTT.h b/src/MQTT.h
similarity index 100%
rename from MQTT.h
rename to src/MQTT.h
diff --git a/SSDP.cpp b/src/SSDP.cpp
similarity index 97%
rename from SSDP.cpp
rename to src/SSDP.cpp
index f866865..7dd4a62 100644
--- a/SSDP.cpp
+++ b/src/SSDP.cpp
@@ -1,5 +1,6 @@
#include
#include
+#include "esp_log.h"
#include "Utils.h"
#include "ConfigSettings.h"
#include "SSDP.h"
@@ -12,6 +13,7 @@
#define SSDP_MULTICAST_ADDR 239, 255, 255, 250
//#define DEBUG_SSDP Serial
//#define DEBUG_SSDP_PACKET Serial
+static const char *TAG = "SSDP";
extern ConfigSettings settings;
static const char _ssdp_uuid_template[] PROGMEM = "C2496952-5610-47E6-A968-2FC1%02X%02X%02X%02X";
@@ -193,12 +195,12 @@ bool SSDPClass::begin() {
return false;
}
for(uint8_t i = 0; i < this->m_cdeviceTypes; i++) {
- Serial.printf("SSDP: %s - %s\n", this->deviceTypes[i].deviceType, this->deviceTypes[i].isActive ? "true" : "false");
+ ESP_LOGI(TAG, "SSDP: %s - %s", this->deviceTypes[i].deviceType, this->deviceTypes[i].isActive ? "true" : "false");
}
this->isStarted = true;
this->_sendByeBye();
this->_sendNotify();
- Serial.println("Connected to SSDP...");
+ ESP_LOGI(TAG, "Connected to SSDP...");
return true;
}
void SSDPClass::end() {
@@ -209,7 +211,7 @@ void SSDPClass::end() {
if(this->_server.connected()) {
this->_sendByeBye();
this->_server.close();
- Serial.println("Disconnected from SSDP...");
+ ESP_LOGI(TAG, "Disconnected from SSDP...");
}
this->isStarted = false;
// Clear out the last notified so if the user starts us up again it will notify
@@ -381,6 +383,9 @@ void SSDPClass::_parsePacket(ssdp_packet_t *pkt, AsyncUDPPacket &p) {
IPAddress SSDPClass::localIP()
{
// Make sure we don't get a null IPAddress.
+#ifdef CONFIG_IDF_TARGET_ESP32C6
+ return WiFi.localIP();
+#else
tcpip_adapter_ip_info_t ip;
if (WiFi.getMode() == WIFI_STA) {
if (tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ip)) {
@@ -392,6 +397,7 @@ IPAddress SSDPClass::localIP()
}
}
return IPAddress(ip.ip.addr);
+#endif
}
void SSDPClass::_sendResponse(IPAddress addr, uint16_t port, UPNPDeviceType *d, const char *st, response_types_t responseType) {
char buffer[1460];
@@ -432,7 +438,7 @@ void SSDPClass::_sendResponse(IPAddress addr, uint16_t port, const char *buff) {
void SSDPClass::_sendNotify() {
for(uint8_t i = 0; i < this->m_cdeviceTypes; i++) {
UPNPDeviceType *dev = &this->deviceTypes[i];
- if(i == 0 && (strlen(dev->deviceType) == 0 || !dev->isActive)) Serial.printf("The device type is empty: %s\n", dev->isActive ? "true" : "false");
+ if(i == 0 && (strlen(dev->deviceType) == 0 || !dev->isActive)) ESP_LOGD(TAG, "The device type is empty: %s", dev->isActive ? "true" : "false");
if(strlen(dev->deviceType) > 0 && dev->isActive) {
unsigned long elapsed = (millis() - dev->lastNotified);
if(!dev->lastNotified || (elapsed * 5) > (this->_interval * 1000)) {
@@ -653,28 +659,23 @@ void SSDPClass::_sendQueuedResponses() {
}
}
void SSDPClass::_printPacket(ssdp_packet_t *pkt) {
- Serial.printf("Rec: %lu\n", pkt->recvd);
+ ESP_LOGD(TAG, "Rec: %lu", pkt->recvd);
switch(pkt->method) {
case NONE:
- Serial.println("Method: NONE");
+ ESP_LOGD(TAG, "Method: NONE");
break;
case SEARCH:
- Serial.println("Method: SEARCH");
+ ESP_LOGD(TAG, "Method: SEARCH");
break;
case NOTIFY:
- Serial.println("Method: NOTIFY");
+ ESP_LOGD(TAG, "Method: NOTIFY");
break;
default:
- Serial.println("Method: UNKOWN");
+ ESP_LOGD(TAG, "Method: UNKNOWN");
break;
}
- Serial.printf("ST: %s\n", pkt->st);
- Serial.printf("MAN: %s\n", pkt->man);
- Serial.printf("AGENT: %s\n", pkt->agent);
- Serial.printf("HOST: %s\n", pkt->host);
- Serial.printf("MX: %d\n", pkt->mx);
- Serial.printf("type: %d\n", pkt->type);
- Serial.printf("valid: %d\n", pkt->valid);
+ ESP_LOGD(TAG, "ST: %s MAN: %s AGENT: %s HOST: %s MX: %d type: %d valid: %d",
+ pkt->st, pkt->man, pkt->agent, pkt->host, pkt->mx, pkt->type, pkt->valid);
}
void SSDPClass::_processRequest(AsyncUDPPacket &p) {
// This pending BS should probably be for unicast request only but we will play along for now.
diff --git a/SSDP.h b/src/SSDP.h
similarity index 100%
rename from SSDP.h
rename to src/SSDP.h
diff --git a/src/Sockets.cpp b/src/Sockets.cpp
new file mode 100644
index 0000000..ba4babe
--- /dev/null
+++ b/src/Sockets.cpp
@@ -0,0 +1,224 @@
+#include
+#include
+#include
+#include "esp_log.h"
+#include "Sockets.h"
+#include "ConfigSettings.h"
+#include "Somfy.h"
+#include "ESPNetwork.h"
+#include "GitOTA.h"
+
+static const char *TAG = "Sockets";
+
+extern ConfigSettings settings;
+extern ESPNetwork net;
+extern SomfyShadeController somfy;
+extern SocketEmitter sockEmit;
+extern GitUpdater git;
+
+AsyncWebServer wsServer(8080);
+AsyncWebSocket ws("/");
+
+#define MAX_WS_CLIENTS 5
+static uint32_t clientMap[MAX_WS_CLIENTS] = {0,0,0,0,0};
+
+static uint8_t mapClientId(uint32_t asyncId) {
+ for(uint8_t i = 0; i < MAX_WS_CLIENTS; i++)
+ if(clientMap[i] == asyncId) return i;
+ return 255;
+}
+static uint32_t getAsyncId(uint8_t slot) {
+ if(slot < MAX_WS_CLIENTS) return clientMap[slot];
+ return 0;
+}
+static uint8_t addClient(uint32_t asyncId) {
+ for(uint8_t i = 0; i < MAX_WS_CLIENTS; i++) {
+ if(clientMap[i] == 0) { clientMap[i] = asyncId; return i; }
+ }
+ return 255;
+}
+static void removeClient(uint32_t asyncId) {
+ for(uint8_t i = 0; i < MAX_WS_CLIENTS; i++)
+ if(clientMap[i] == asyncId) clientMap[i] = 0;
+}
+
+#define MAX_SOCK_RESPONSE 2048
+static char g_response[MAX_SOCK_RESPONSE];
+
+bool room_t::isJoined(uint8_t num) {
+ for(uint8_t i = 0; i < sizeof(this->clients); i++) {
+ if(this->clients[i] == num) return true;
+ }
+ return false;
+}
+bool room_t::join(uint8_t num) {
+ if(this->isJoined(num)) return true;
+ for(uint8_t i = 0; i < sizeof(this->clients); i++) {
+ if(this->clients[i] == 255) {
+ this->clients[i] = num;
+ return true;
+ }
+ }
+ return false;
+}
+bool room_t::leave(uint8_t num) {
+ if(!this->isJoined(num)) return false;
+ for(uint8_t i = 0; i < sizeof(this->clients); i++) {
+ if(this->clients[i] == num) this->clients[i] = 255;
+ }
+ return true;
+}
+void room_t::clear() {
+ memset(this->clients, 255, sizeof(this->clients));
+}
+uint8_t room_t::activeClients() {
+ uint8_t n = 0;
+ for(uint8_t i = 0; i < sizeof(this->clients); i++) {
+ if(this->clients[i] != 255) n++;
+ }
+ return n;
+}
+
+/*********************************************************************
+ * SocketEmitter class members
+ ********************************************************************/
+void SocketEmitter::startup() {
+
+}
+void SocketEmitter::begin() {
+ ws.onEvent(SocketEmitter::wsEvent);
+ wsServer.addHandler(&ws);
+ wsServer.begin();
+ ESP_LOGI(TAG, "Socket Server Started...");
+}
+void SocketEmitter::loop() {
+ ws.cleanupClients();
+ this->initClients();
+}
+JsonSockEvent *SocketEmitter::beginEmit(const char *evt) {
+ this->json.beginEvent(&ws, evt, g_response, sizeof(g_response));
+ return &this->json;
+}
+void SocketEmitter::endEmit(uint8_t num) {
+ if(num == 255) {
+ this->json.endEvent(0);
+ } else {
+ uint32_t asyncId = getAsyncId(num);
+ this->json.endEvent(asyncId);
+ }
+ esp_task_wdt_reset();
+}
+void SocketEmitter::endEmitRoom(uint8_t room) {
+ if(room < SOCK_MAX_ROOMS) {
+ room_t *r = &this->rooms[room];
+ for(uint8_t i = 0; i < sizeof(r->clients); i++) {
+ if(r->clients[i] != 255) {
+ uint32_t asyncId = getAsyncId(r->clients[i]);
+ if(asyncId != 0) this->json.endEvent(asyncId);
+ }
+ }
+ }
+}
+uint8_t SocketEmitter::activeClients(uint8_t room) {
+ if(room < SOCK_MAX_ROOMS) return this->rooms[room].activeClients();
+ return 0;
+}
+void SocketEmitter::initClients() {
+ for(uint8_t i = 0; i < sizeof(this->newClients); i++) {
+ uint8_t slot = this->newClients[i];
+ if(slot != 255) {
+ uint32_t asyncId = getAsyncId(slot);
+ if(asyncId != 0 && ws.hasClient(asyncId)) {
+ ESP_LOGD(TAG, "Initializing Socket Client %u (asyncId=%lu)", slot, asyncId);
+ esp_task_wdt_reset();
+ settings.emitSockets(slot);
+ if(!ws.hasClient(asyncId)) { this->newClients[i] = 255; continue; }
+ somfy.emitState(slot);
+ if(!ws.hasClient(asyncId)) { this->newClients[i] = 255; continue; }
+ git.emitUpdateCheck(slot);
+ if(!ws.hasClient(asyncId)) { this->newClients[i] = 255; continue; }
+ net.emitSockets(slot);
+ esp_task_wdt_reset();
+ }
+ this->newClients[i] = 255;
+ }
+ }
+}
+void SocketEmitter::delayInit(uint8_t num) {
+ for(uint8_t i=0; i < sizeof(this->newClients); i++) {
+ if(this->newClients[i] == num) break;
+ else if(this->newClients[i] == 255) {
+ this->newClients[i] = num;
+ break;
+ }
+ }
+}
+void SocketEmitter::end() {
+ ws.closeAll();
+ wsServer.end();
+ for(uint8_t i = 0; i < SOCK_MAX_ROOMS; i++)
+ this->rooms[i].clear();
+ memset(clientMap, 0, sizeof(clientMap));
+}
+void SocketEmitter::disconnect() {
+ ws.closeAll();
+ memset(clientMap, 0, sizeof(clientMap));
+}
+void SocketEmitter::wsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) {
+ uint32_t asyncId = client->id();
+ switch(type) {
+ case WS_EVT_CONNECT:
+ {
+ uint8_t slot = addClient(asyncId);
+ if(slot == 255) {
+ ESP_LOGE(TAG, "Socket: No free client slots, closing %lu", asyncId);
+ client->close();
+ return;
+ }
+ IPAddress ip = client->remoteIP();
+ ESP_LOGD(TAG, "Socket [%lu] Connected from %d.%d.%d.%d (slot %u)", asyncId, ip[0], ip[1], ip[2], ip[3], slot);
+ client->text("Connected");
+ client->setCloseClientOnQueueFull(false);
+ sockEmit.delayInit(slot);
+ }
+ break;
+ case WS_EVT_DISCONNECT:
+ {
+ uint8_t slot = mapClientId(asyncId);
+ ESP_LOGD(TAG, "Socket [%lu] Disconnected (slot %u)", asyncId, slot);
+ if(slot != 255) {
+ for(uint8_t i = 0; i < SOCK_MAX_ROOMS; i++)
+ sockEmit.rooms[i].leave(slot);
+ }
+ removeClient(asyncId);
+ }
+ break;
+ case WS_EVT_DATA:
+ {
+ AwsFrameInfo *info = (AwsFrameInfo*)arg;
+ if(info->final && info->index == 0 && info->len == len && info->opcode == WS_TEXT) {
+ uint8_t slot = mapClientId(asyncId);
+ data[len] = 0;
+ if(strncmp((char*)data, "join:", 5) == 0) {
+ uint8_t roomNum = atoi((char*)&data[5]);
+ ESP_LOGD(TAG, "Client %u joining room %u", slot, roomNum);
+ if(roomNum < SOCK_MAX_ROOMS && slot != 255) sockEmit.rooms[roomNum].join(slot);
+ }
+ else if(strncmp((char*)data, "leave:", 6) == 0) {
+ uint8_t roomNum = atoi((char*)&data[6]);
+ ESP_LOGD(TAG, "Client %u leaving room %u", slot, roomNum);
+ if(roomNum < SOCK_MAX_ROOMS && slot != 255) sockEmit.rooms[roomNum].leave(slot);
+ }
+ else {
+ ESP_LOGD(TAG, "Socket [%lu] text: %s", asyncId, data);
+ }
+ }
+ }
+ break;
+ case WS_EVT_ERROR:
+ ESP_LOGE(TAG, "Socket [%lu] Error", asyncId);
+ break;
+ case WS_EVT_PONG:
+ break;
+ }
+}
diff --git a/Sockets.h b/src/Sockets.h
similarity index 82%
rename from Sockets.h
rename to src/Sockets.h
index d76ab93..948b730 100644
--- a/Sockets.h
+++ b/src/Sockets.h
@@ -1,4 +1,4 @@
-#include
+#include
#include "WResp.h"
#ifndef sockets_h
#define sockets_h
@@ -21,7 +21,6 @@ class SocketEmitter {
void delayInit(uint8_t num);
public:
JsonSockEvent json;
- //ClientSocketEvent evt;
room_t rooms[SOCK_MAX_ROOMS];
uint8_t activeClients(uint8_t room);
void initClients();
@@ -33,6 +32,6 @@ class SocketEmitter {
JsonSockEvent * beginEmit(const char *evt);
void endEmit(uint8_t num = 255);
void endEmitRoom(uint8_t num);
- static void wsEvent(uint8_t num, WStype_t type, uint8_t *payload, size_t length);
+ static void wsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len);
};
#endif
diff --git a/Somfy.cpp b/src/Somfy.cpp
similarity index 89%
rename from Somfy.cpp
rename to src/Somfy.cpp
index bffebd2..7a8ad51 100644
--- a/Somfy.cpp
+++ b/src/Somfy.cpp
@@ -1,8 +1,9 @@
#include
#include
#include
-#include
#include
+#include
+#include "esp_log.h"
#include "Utils.h"
#include "ConfigSettings.h"
#include "Somfy.h"
@@ -11,6 +12,8 @@
#include "ConfigFile.h"
#include "GitOTA.h"
+static const char *TAG = "Somfy";
+
extern Preferences pref;
extern SomfyShadeController somfy;
extern SocketEmitter sockEmit;
@@ -156,9 +159,9 @@ void somfy_frame_t::decodeFrame(byte* frame) {
this->proto = radio_proto::RTV;
this->cmd = (somfy_commands)(this->encKey - 148);
}
- else if(this->encKey > 133) {
+ else if(this->encKey >= 133) {
this->proto = radio_proto::RTW;
- this->cmd = (somfy_commands)(this->encKey - 133);
+ this->cmd = this->encKey == 133 ? somfy_commands::My : (somfy_commands)(this->encKey - 133);
}
}
else this->proto = radio_proto::RTS;
@@ -217,37 +220,21 @@ void somfy_frame_t::decodeFrame(byte* frame) {
}
if(this->valid && this->encKey == 0) this->valid = false;
if (!this->valid) {
- Serial.print("INVALID FRAME ");
- Serial.print("KEY:");
- Serial.print(this->encKey);
- Serial.print(" ADDR:");
- Serial.print(this->remoteAddress);
- Serial.print(" CMD:");
- Serial.print(translateSomfyCommand(this->cmd));
- Serial.print(" RCODE:");
- Serial.println(this->rollingCode);
- Serial.println(" KEY 1 2 3 4 5 6 ");
- Serial.println("--------------------------------");
- Serial.print("ENC ");
+ ESP_LOGW(TAG, "INVALID FRAME KEY:%d ADDR:%d CMD:%s RCODE:%d",
+ this->encKey, this->remoteAddress,
+ translateSomfyCommand(this->cmd).c_str(), this->rollingCode);
+ ESP_LOGD(TAG, " KEY 1 2 3 4 5 6 ");
+ ESP_LOGD(TAG, "--------------------------------");
+ char enc_buf[64], dec_buf[64];
+ int enc_pos = 0, dec_pos = 0;
+ enc_pos += snprintf(enc_buf + enc_pos, sizeof(enc_buf) - enc_pos, "ENC ");
+ dec_pos += snprintf(dec_buf + dec_pos, sizeof(dec_buf) - dec_pos, "DEC ");
for (byte i = 0; i < 10; i++) {
- if (frame[i] < 10)
- Serial.print(" ");
- else if (frame[i] < 100)
- Serial.print(" ");
- Serial.print(frame[i]);
- Serial.print(" ");
+ enc_pos += snprintf(enc_buf + enc_pos, sizeof(enc_buf) - enc_pos, "%3d ", frame[i]);
+ dec_pos += snprintf(dec_buf + dec_pos, sizeof(dec_buf) - dec_pos, "%3d ", decoded[i]);
}
- Serial.println();
- Serial.print("DEC ");
- for (byte i = 0; i < 10; i++) {
- if (decoded[i] < 10)
- Serial.print(" ");
- else if (decoded[i] < 100)
- Serial.print(" ");
- Serial.print(decoded[i]);
- Serial.print(" ");
- }
- Serial.println();
+ ESP_LOGD(TAG, "%s", enc_buf);
+ ESP_LOGD(TAG, "%s", dec_buf);
}
}
void somfy_frame_t::decodeFrame(somfy_rx_t *rx) {
@@ -436,21 +423,11 @@ void somfy_frame_t::encodeFrame(byte *frame) {
}
}
void somfy_frame_t::print() {
- Serial.println("----------- Receiving -------------");
- Serial.print("RSSI:");
- Serial.print(this->rssi);
- Serial.print(" LQI:");
- Serial.println(this->lqi);
- Serial.print("CMD:");
- Serial.print(translateSomfyCommand(this->cmd));
- Serial.print(" ADDR:");
- Serial.print(this->remoteAddress);
- Serial.print(" RCODE:");
- Serial.println(this->rollingCode);
- Serial.print("KEY:");
- Serial.print(this->encKey, HEX);
- Serial.print(" CS:");
- Serial.println(this->checksum);
+ ESP_LOGD(TAG, "----------- Receiving -------------");
+ ESP_LOGD(TAG, "RSSI:%d LQI:%d", this->rssi, this->lqi);
+ ESP_LOGD(TAG, "CMD:%s ADDR:%d RCODE:%d",
+ translateSomfyCommand(this->cmd).c_str(), this->remoteAddress, this->rollingCode);
+ ESP_LOGD(TAG, "KEY:%02X CS:%d", this->encKey, this->checksum);
}
bool somfy_frame_t::isSynonym(somfy_frame_t &frame) { return this->remoteAddress == frame.remoteAddress && this->cmd != frame.cmd && this->rollingCode == frame.rollingCode; }
bool somfy_frame_t::isRepeat(somfy_frame_t &frame) { return this->remoteAddress == frame.remoteAddress && this->cmd == frame.cmd && this->rollingCode == frame.rollingCode; }
@@ -514,7 +491,7 @@ void SomfyShadeController::updateGroupFlags() {
}
#ifdef USE_NVS
bool SomfyShadeController::loadLegacy() {
- Serial.println("Loading Legacy shades using NVS");
+ ESP_LOGI(TAG, "Loading Legacy shades using NVS");
pref.begin("Shades", true);
pref.getBytes("shadeIds", this->m_shadeIds, sizeof(this->m_shadeIds));
pref.end();
@@ -550,7 +527,6 @@ bool SomfyShadeController::loadLegacy() {
DEBUG_SOMFY.print(this->m_shadeIds[i]);
if(i < SOMFY_MAX_SHADES - 1) DEBUG_SOMFY.print(",");
}
- Serial.println();
#endif
#ifdef USE_NVS
if(!this->useNVS()) {
@@ -566,12 +542,12 @@ bool SomfyShadeController::loadLegacy() {
bool SomfyShadeController::begin() {
// Load up all the configuration data.
//ShadeConfigFile::getAppVersion(this->appVersion);
- Serial.printf("App Version:%u.%u.%u\n", settings.appVersion.major, settings.appVersion.minor, settings.appVersion.build);
+ ESP_LOGI(TAG, "App Version:%u.%u.%u", settings.appVersion.major, settings.appVersion.minor, settings.appVersion.build);
#ifdef USE_NVS
if(!this->useNVS()) { // At 1.4 we started using the configuration file. If the file doesn't exist then booh.
// We need to remove all the extraeneous data from NVS for the shades. From here on out we
// will rely on the shade configuration.
- Serial.println("No longer using NVS");
+ ESP_LOGI(TAG, "No longer using NVS");
if(ShadeConfigFile::exists()) {
ShadeConfigFile::load(this);
}
@@ -596,11 +572,11 @@ bool SomfyShadeController::begin() {
}
#endif
if(ShadeConfigFile::exists()) {
- Serial.println("shades.cfg exists so we are using that");
+ ESP_LOGI(TAG, "shades.cfg exists so we are using that");
ShadeConfigFile::load(this);
}
else {
- Serial.println("Starting clean");
+ ESP_LOGI(TAG, "Starting clean");
#ifdef USE_NVS
this->loadLegacy();
#endif
@@ -633,8 +609,10 @@ void SomfyShadeController::commit() {
void SomfyShadeController::writeBackup() {
if(git.lockFS) return;
esp_task_wdt_reset(); // Make sure we don't reset inadvertently.
+ this->backupData = "";
+ this->backupData.reserve(16384);
ShadeConfigFile file;
- file.begin("/controller.backup", false);
+ file.beginRAM(&this->backupData);
file.backup(this);
file.end();
}
@@ -773,8 +751,7 @@ void SomfyShade::commitShadePosition() {
char shadeKey[15];
if(somfy.useNVS()) {
snprintf(shadeKey, sizeof(shadeKey), "SomfyShade%u", this->shadeId);
- Serial.print("Writing current shade position: ");
- Serial.println(this->currentPos, 4);
+ ESP_LOGD(TAG, "Writing current shade position: %.4f", this->currentPos);
pref.begin(shadeKey);
pref.putFloat("currentPos", this->currentPos);
pref.end();
@@ -787,9 +764,7 @@ void SomfyShade::commitMyPosition() {
if(somfy.useNVS()) {
char shadeKey[15];
snprintf(shadeKey, sizeof(shadeKey), "SomfyShade%u", this->shadeId);
- Serial.print("Writing my shade position:");
- Serial.print(this->myPos);
- Serial.println("%");
+ ESP_LOGD(TAG, "Writing my shade position:%.2f%%", this->myPos);
pref.begin(shadeKey);
pref.putUShort("myPos", this->myPos);
pref.end();
@@ -802,8 +777,7 @@ void SomfyShade::commitTiltPosition() {
if(somfy.useNVS()) {
char shadeKey[15];
snprintf(shadeKey, sizeof(shadeKey), "SomfyShade%u", this->shadeId);
- Serial.print("Writing current shade tilt position: ");
- Serial.println(this->currentTiltPos, 4);
+ ESP_LOGD(TAG, "Writing current shade tilt position: %.4f", this->currentTiltPos);
pref.begin(shadeKey);
pref.putFloat("currentTiltPos", this->currentTiltPos);
pref.end();
@@ -955,19 +929,19 @@ void SomfyShade::setGPIOs() {
case -1:
digitalWrite(this->gpioDown, p_off);
digitalWrite(this->gpioUp, p_on);
- if(dir != this->gpioDir) Serial.printf("UP: true, DOWN: false\n");
+ if(dir != this->gpioDir) ESP_LOGD(TAG, "UP: true, DOWN: false");
this->gpioDir = dir;
break;
case 1:
digitalWrite(this->gpioUp, p_off);
digitalWrite(this->gpioDown, p_on);
- if(dir != this->gpioDir) Serial.printf("UP: false, DOWN: true\n");
+ if(dir != this->gpioDir) ESP_LOGD(TAG, "UP: false, DOWN: true");
this->gpioDir = dir;
break;
default:
digitalWrite(this->gpioUp, p_off);
digitalWrite(this->gpioDown, p_off);
- if(dir != this->gpioDir) Serial.printf("UP: false, DOWN: false\n");
+ if(dir != this->gpioDir) ESP_LOGD(TAG, "UP: false, DOWN: false");
this->gpioDir = dir;
break;
}
@@ -997,7 +971,7 @@ void SomfyShade::triggerGPIOs(somfy_frame_t &frame) {
digitalWrite(this->gpioDown, p_off);
digitalWrite(this->gpioMy, p_on);
dir = 0;
- if(dir != this->gpioDir) Serial.printf("UP: false, DOWN: false, MY: true\n");
+ if(dir != this->gpioDir) ESP_LOGD(TAG, "UP: false, DOWN: false, MY: true");
}
break;
case somfy_commands::Up:
@@ -1006,7 +980,7 @@ void SomfyShade::triggerGPIOs(somfy_frame_t &frame) {
digitalWrite(this->gpioDown, p_off);
digitalWrite(this->gpioUp, p_on);
dir = -1;
- Serial.printf("UP: true, DOWN: false, MY: false\n");
+ ESP_LOGD(TAG, "UP: true, DOWN: false, MY: false");
}
break;
case somfy_commands::Toggle:
@@ -1017,14 +991,14 @@ void SomfyShade::triggerGPIOs(somfy_frame_t &frame) {
}
digitalWrite(this->gpioDown, p_on);
dir = 1;
- Serial.printf("UP: false, DOWN: true, MY: false\n");
+ ESP_LOGD(TAG, "UP: false, DOWN: true, MY: false");
break;
case somfy_commands::MyUp:
if(this->shadeType != shade_types::drycontact && !this->isToggle() && this->shadeType != shade_types::drycontact2) {
digitalWrite(this->gpioDown, p_off);
digitalWrite(this->gpioMy, p_on);
digitalWrite(this->gpioUp, p_on);
- Serial.printf("UP: true, DOWN: false, MY: true\n");
+ ESP_LOGD(TAG, "UP: true, DOWN: false, MY: true");
}
break;
case somfy_commands::MyDown:
@@ -1032,7 +1006,7 @@ void SomfyShade::triggerGPIOs(somfy_frame_t &frame) {
digitalWrite(this->gpioUp, p_off);
digitalWrite(this->gpioMy, p_on);
digitalWrite(this->gpioDown, p_on);
- Serial.printf("UP: false, DOWN: true, MY: true\n");
+ ESP_LOGD(TAG, "UP: false, DOWN: true, MY: true");
}
break;
case somfy_commands::MyUpDown:
@@ -1040,7 +1014,7 @@ void SomfyShade::triggerGPIOs(somfy_frame_t &frame) {
digitalWrite(this->gpioUp, p_on);
digitalWrite(this->gpioMy, p_on);
digitalWrite(this->gpioDown, p_on);
- Serial.printf("UP: true, DOWN: true, MY: true\n");
+ ESP_LOGD(TAG, "UP: true, DOWN: true, MY: true");
}
break;
default:
@@ -1088,7 +1062,7 @@ void SomfyShade::checkMovement() {
this->p_target(this->myPos >= 0 ? this->myPos : 100.0f);
//this->target = this->myPos >= 0 ? this->myPos : 100.0f;
this->sunDone = true;
- Serial.printf("[%u] Sun -> done\r\n", this->shadeId);
+ ESP_LOGD(TAG, "[%u] Sun -> done", this->shadeId);
}
if (!this->noWindDone
&& this->noWindStart
@@ -1097,7 +1071,7 @@ void SomfyShade::checkMovement() {
this->p_target(this->myPos >= 0 ? this->myPos : 100.0f);
//this->target = this->myPos >= 0 ? this->myPos : 100.0f;
this->noWindDone = true;
- Serial.printf("[%u] No Wind -> done\r\n", this->shadeId);
+ ESP_LOGD(TAG, "[%u] No Wind -> done", this->shadeId);
}
}
if (!isSunny
@@ -1108,7 +1082,7 @@ void SomfyShade::checkMovement() {
if(this->tiltType == tilt_types::tiltonly) this->p_tiltTarget(0.0f);
this->p_target(0.0f);
this->noSunDone = true;
- Serial.printf("[%u] No Sun -> done\r\n", this->shadeId);
+ ESP_LOGD(TAG, "[%u] No Sun -> done", this->shadeId);
}
}
@@ -1120,7 +1094,7 @@ void SomfyShade::checkMovement() {
if(this->tiltType == tilt_types::tiltonly) this->p_tiltTarget(0.0f);
this->p_target(0.0f);
this->windDone = true;
- Serial.printf("[%u] Wind -> done\r\n", this->shadeId);
+ ESP_LOGD(TAG, "[%u] Wind -> done", this->shadeId);
}
if(!tilt_first && this->direction > 0) {
@@ -1166,7 +1140,7 @@ void SomfyShade::checkMovement() {
// not moving otherwise the my function will kick in.
if(this->settingPos) {
if(!isAtTarget()) {
- Serial.printf("We are not at our tilt target: %.2f\n", this->tiltTarget);
+ ESP_LOGD(TAG, "We are not at our tilt target: %.2f", this->tiltTarget);
if(this->target != 100.0) SomfyRemote::sendCommand(somfy_commands::My, this->repeats);
delay(100);
// We now need to move the tilt to the position we requested.
@@ -1218,7 +1192,7 @@ void SomfyShade::checkMovement() {
// not moving otherwise the my function will kick in.
if(this->settingPos) {
if(!isAtTarget()) {
- Serial.printf("We are not at our tilt target: %.2f\n", this->tiltTarget);
+ ESP_LOGD(TAG, "We are not at our tilt target: %.2f", this->tiltTarget);
if(this->target != 0.0) SomfyRemote::sendCommand(somfy_commands::My, this->repeats);
delay(100);
// We now need to move the tilt to the position we requested.
@@ -1329,7 +1303,7 @@ void SomfyShade::checkMovement() {
}
this->p_tiltDirection(0);
this->settingTiltPos = false;
- Serial.println("Stopping at tilt position");
+ ESP_LOGI(TAG, "Stopping at tilt position");
if(this->isAtTarget()) this->commitShadePosition();
}
}
@@ -1401,16 +1375,9 @@ void SomfyShade::load() {
this->tiltTarget = floor(this->currentTiltPos);
pref.getBytes("linkedAddr", linkedAddresses, sizeof(linkedAddresses));
pref.end();
- Serial.print("shadeId:");
- Serial.print(this->getShadeId());
- Serial.print(" name:");
- Serial.print(this->name);
- Serial.print(" address:");
- Serial.print(this->getRemoteAddress());
- Serial.print(" position:");
- Serial.print(this->currentPos);
- Serial.print(" myPos:");
- Serial.println(this->myPos);
+ ESP_LOGI(TAG, "shadeId:%d name:%s address:%d position:%.2f myPos:%.2f",
+ this->getShadeId(), this->name, this->getRemoteAddress(),
+ this->currentPos, this->myPos);
pref.begin("ShadeCodes");
this->lastRollingCode = pref.getUShort(this->m_remotePrefId, 0);
for(uint8_t j = 0; j < SOMFY_MAX_LINKED_REMOTES; j++) {
@@ -1445,9 +1412,9 @@ void SomfyRoom::unpublish() {
}
void SomfyShade::publishState() {
if(mqtt.connected()) {
- this->publish("position", this->transformPosition(this->currentPos), true);
- this->publish("direction", this->direction, true);
- this->publish("target", this->transformPosition(this->target), true);
+ this->publish("position", (uint8_t)50, true);
+ this->publish("direction", (int8_t)0, true);
+ this->publish("target", (uint8_t)50, true);
this->publish("lastRollingCode", this->lastRollingCode);
this->publish("mypos", this->transformPosition(this->myPos), true);
this->publish("myTiltPos", this->transformPosition(this->myTiltPos), true);
@@ -1469,7 +1436,7 @@ void SomfyShade::publishState() {
void SomfyShade::publishDisco() {
if(!mqtt.connected() || !settings.MQTT.pubDisco) return;
char topic[128] = "";
- DynamicJsonDocument doc(2048);
+ JsonDocument doc;
JsonObject obj = doc.to();
snprintf(topic, sizeof(topic), "%s/shades/%d", settings.MQTT.rootTopic, this->shadeId);
obj["~"] = topic;
@@ -1794,7 +1761,7 @@ bool SomfyGroup::publish(const char *topic, bool val, bool retain) {
float SomfyShade::p_currentPos(float pos) {
float old = this->currentPos;
this->currentPos = pos;
- if(floor(old) != floor(pos)) this->publish("position", this->transformPosition(static_cast(floor(this->currentPos))));
+ if(floor(old) != floor(pos)) this->publish("position", (uint8_t)50);
return old;
}
float SomfyShade::p_currentTiltPos(float pos) {
@@ -2094,12 +2061,8 @@ void SomfyShade::processWaitingFrame() {
this->p_tiltTarget(dir > 0 ? 100.0f : 0.0f);
this->setTiltMovement(dir);
this->lastFrame.processed = true;
- Serial.print(this->name);
- Serial.print(" Processing tilt ");
- Serial.print(translateSomfyCommand(this->lastFrame.cmd));
- Serial.print(" after ");
- Serial.print(this->lastFrame.repeats);
- Serial.println(" repeats");
+ ESP_LOGD(TAG, "%s Processing tilt %s after %d repeats",
+ this->name, translateSomfyCommand(this->lastFrame.cmd).c_str(), this->lastFrame.repeats);
this->emitCommand(cmd, "remote", this->lastFrame.remoteAddress);
}
else {
@@ -2120,12 +2083,8 @@ void SomfyShade::processWaitingFrame() {
this->p_target(dir > 0 ? 100.0f : 0.0f);
this->setMovement(dir);
this->lastFrame.processed = true;
- Serial.print(this->name);
- Serial.print(" Processing ");
- Serial.print(translateSomfyCommand(this->lastFrame.cmd));
- Serial.print(" after ");
- Serial.print(this->lastFrame.repeats);
- Serial.println(" repeats");
+ ESP_LOGD(TAG, "%s Processing %s after %d repeats",
+ this->name, translateSomfyCommand(this->lastFrame.cmd).c_str(), this->lastFrame.repeats);
this->emitCommand(cmd, "remote", this->lastFrame.remoteAddress);
}
else {
@@ -2173,10 +2132,7 @@ void SomfyShade::processWaitingFrame() {
}
if(this->lastFrame.repeats > SETMY_REPEATS + 2) this->lastFrame.processed = true;
if(this->lastFrame.processed) {
- Serial.print(this->name);
- Serial.print(" Processing MY after ");
- Serial.print(this->lastFrame.repeats);
- Serial.println(" repeats");
+ ESP_LOGD(TAG, "%s Processing MY after %d repeats", this->name, this->lastFrame.repeats);
}
break;
default:
@@ -2263,25 +2219,25 @@ void SomfyShade::processFrame(somfy_frame_t &frame, bool internal) {
{
this->sunStart = curTime;
this->sunDone = false;
- Serial.printf("[%u] Sun -> start\r\n", this->shadeId);
+ ESP_LOGD(TAG, "[%u] Sun -> start", this->shadeId);
}
else if (!isSunny && wasSunny)
{
this->noSunStart = curTime;
this->noSunDone = false;
- Serial.printf("[%u] No Sun -> start\r\n", this->shadeId);
+ ESP_LOGD(TAG, "[%u] No Sun -> start", this->shadeId);
}
if (isWindy && !wasWindy)
{
this->windStart = curTime;
this->windDone = false;
- Serial.printf("[%u] Wind -> start\r\n", this->shadeId);
+ ESP_LOGD(TAG, "[%u] Wind -> start", this->shadeId);
}
else if (!isWindy && wasWindy)
{
this->noWindStart = curTime;
this->noWindDone = false;
- Serial.printf("[%u] No Wind -> start\r\n", this->shadeId);
+ ESP_LOGD(TAG, "[%u] No Wind -> start", this->shadeId);
}
this->emitState();
somfy.updateGroupFlags();
@@ -2424,7 +2380,7 @@ void SomfyShade::processFrame(somfy_frame_t &frame, bool internal) {
}
else {
if(this->lastFrame.processed) return;
- Serial.println("Moving to My target");
+ ESP_LOGI(TAG, "Moving to My target");
this->lastFrame.processed = true;
if(this->myTiltPos >= 0.0f && this->myTiltPos <= 100.0f) this->p_tiltTarget(this->myTiltPos);
if(this->myPos >= 0.0f && this->myPos <= 100.0f && this->tiltType != tilt_types::tiltonly) this->p_target(this->myPos);
@@ -2609,7 +2565,7 @@ void SomfyShade::processInternalCommand(somfy_commands cmd, uint8_t repeat) {
break;
case somfy_commands::My:
if(this->isIdle()) {
- Serial.printf("Shade #%d is idle\n", this->getShadeId());
+ ESP_LOGD(TAG, "Shade #%d is idle", this->getShadeId());
if(this->simMy()) {
this->moveToMyPosition();
}
@@ -2704,7 +2660,7 @@ void SomfyShade::processInternalCommand(somfy_commands cmd, uint8_t repeat) {
this->emitState();
}
else {
- Serial.printf("Shade does not have sensor %d\n", this->flags);
+ ESP_LOGW(TAG, "Shade does not have sensor %d", this->flags);
}
break;
case somfy_commands::SunFlag:
@@ -2724,7 +2680,7 @@ void SomfyShade::processInternalCommand(somfy_commands cmd, uint8_t repeat) {
this->emitState();
}
else
- Serial.printf("Shade does not have sensor %d\n", this->flags);
+ ESP_LOGW(TAG, "Shade does not have sensor %d", this->flags);
break;
default:
dir = 0;
@@ -2863,7 +2819,7 @@ void SomfyShade::setMyPosition(int8_t pos, int8_t tilt) {
}
void SomfyShade::moveToMyPosition() {
if(!this->isIdle()) return;
- Serial.println("Moving to My Position");
+ ESP_LOGI(TAG, "Moving to My Position");
if(this->tiltType == tilt_types::tiltonly) {
this->p_currentPos(100.0f);
this->p_myPos(-1.0f);
@@ -2880,7 +2836,7 @@ void SomfyShade::moveToMyPosition() {
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");
+ ESP_LOGI(TAG, "Moving to simulated favorite");
this->moveToTarget(this->myPos, this->myTiltPos);
}
else
@@ -2888,9 +2844,21 @@ void SomfyShade::moveToMyPosition() {
}
void SomfyShade::sendCommand(somfy_commands cmd) { this->sendCommand(cmd, this->repeats); }
void SomfyShade::sendCommand(somfy_commands cmd, uint8_t repeat, uint8_t stepSize) {
+ ESP_LOGD(TAG, "Send command start");
// This sendCommand function will always be called externally. sendCommand at the remote level
// is expected to be called internally when the motor needs commanded.
if(this->bitLength == 0) this->bitLength = somfy.transceiver.config.type;
+ // If same direction command sent while already moving, stop the state tracking.
+ // The real motor stops on its own when it receives the same direction again.
+ if((cmd == somfy_commands::Up && this->direction == -1) ||
+ (cmd == somfy_commands::Down && this->direction == 1)) {
+ ESP_LOGD(TAG, "Same command as dir");
+ SomfyRemote::sendCommand(cmd, repeat);
+ this->p_target(this->currentPos);
+ this->p_tiltTarget(this->currentTiltPos);
+ this->setMovement(0);
+ return;
+ }
if(cmd == somfy_commands::Up) {
if(this->tiltType == tilt_types::euromode) {
// In euromode we need to long press for 2 seconds on the
@@ -2930,9 +2898,13 @@ void SomfyShade::sendCommand(somfy_commands cmd, uint8_t repeat, uint8_t stepSiz
else if(cmd == somfy_commands::My) {
if(this->isToggle() || this->shadeType == shade_types::drycontact)
SomfyRemote::sendCommand(cmd, repeat);
- else if(this->shadeType == shade_types::drycontact2) return;
+ else if(this->shadeType == shade_types::drycontact2){
+ ESP_LOGD(TAG, "Send command start 1");
+ return;
+ }
else if(this->isIdle()) {
- this->moveToMyPosition();
+ this->moveToMyPosition();
+ ESP_LOGD(TAG, "Send command end 2");
return;
}
else {
@@ -2951,14 +2923,16 @@ void SomfyShade::sendCommand(somfy_commands cmd, uint8_t repeat, uint8_t stepSiz
else {
SomfyRemote::sendCommand(cmd, repeat, stepSize);
}
+ ESP_LOGD(TAG, "Send command end");
}
void SomfyGroup::sendCommand(somfy_commands cmd) { this->sendCommand(cmd, this->repeats); }
void SomfyGroup::sendCommand(somfy_commands cmd, uint8_t repeat, uint8_t stepSize) {
+ ESP_LOGI(TAG, "[Group %u] sendCommand cmd=%s repeat=%u", this->getGroupId(), translateSomfyCommand(cmd).c_str(), repeat);
// This sendCommand function will always be called externally. sendCommand at the remote level
// is expected to be called internally when the motor needs commanded.
if(this->bitLength == 0) this->bitLength = somfy.transceiver.config.type;
SomfyRemote::sendCommand(cmd, repeat, stepSize);
-
+
switch(cmd) {
case somfy_commands::My:
this->p_direction(0);
@@ -2977,6 +2951,7 @@ void SomfyGroup::sendCommand(somfy_commands cmd, uint8_t repeat, uint8_t stepSiz
if(this->linkedShades[i] != 0) {
SomfyShade *shade = somfy.getShadeById(this->linkedShades[i]);
if(shade) {
+ ESP_LOGI(TAG, "[Group %u] processInternalCommand on shade %u cmd=%s", this->getGroupId(), shade->getShadeId(), translateSomfyCommand(cmd).c_str());
shade->processInternalCommand(cmd, repeat);
shade->emitCommand(cmd, "group", this->getRemoteAddress());
}
@@ -3010,12 +2985,8 @@ void SomfyShade::moveToTiltTarget(float target) {
// Only send a command if the lift is not moving.
if(this->currentPos == this->target || this->tiltType == tilt_types::tiltmotor) {
if(cmd != somfy_commands::My) {
- Serial.print("Moving Tilt to ");
- Serial.print(target);
- Serial.print("% from ");
- Serial.print(this->currentTiltPos);
- Serial.print("% using ");
- Serial.println(translateSomfyCommand(cmd));
+ ESP_LOGI(TAG, "Moving Tilt to %.2f%% from %.2f%% using %s",
+ target, this->currentTiltPos, translateSomfyCommand(cmd).c_str());
SomfyRemote::sendCommand(cmd, this->tiltType == tilt_types::tiltmotor ? TILT_REPEATS : this->repeats);
}
// If the blind is currently moving then the command to stop it
@@ -3026,6 +2997,8 @@ void SomfyShade::moveToTiltTarget(float target) {
if(cmd != somfy_commands::My) this->settingTiltPos = true;
}
void SomfyShade::moveToTarget(float pos, float tilt) {
+ ESP_LOGI(TAG, "[Shade %u] moveToTarget(pos=%.2f, tilt=%.2f) settingPos=%d direction=%d currentTarget=%.2f currentPos=%.2f",
+ this->getShadeId(), pos, tilt, this->settingPos, this->direction, this->target, this->currentPos);
somfy_commands cmd = somfy_commands::My;
if(this->isToggle()) {
// Overload this as we cannot seek a position on a garage door or single button device.
@@ -3053,18 +3026,13 @@ void SomfyShade::moveToTarget(float pos, float tilt) {
cmd = somfy_commands::Down;
}
if(cmd != somfy_commands::My) {
- Serial.print("Moving to ");
- Serial.print(pos);
- Serial.print("% from ");
- Serial.print(this->currentPos);
if(tilt >= 0) {
- Serial.print(" tilt ");
- Serial.print(tilt);
- Serial.print("% from ");
- Serial.print(this->currentTiltPos);
+ ESP_LOGI(TAG, "Moving to %.2f%% from %.2f%% tilt %.2f%% from %.2f%% using %s",
+ pos, this->currentPos, tilt, this->currentTiltPos, translateSomfyCommand(cmd).c_str());
+ } else {
+ ESP_LOGI(TAG, "Moving to %.2f%% from %.2f%% using %s",
+ pos, this->currentPos, translateSomfyCommand(cmd).c_str());
}
- Serial.print("% using ");
- Serial.println(translateSomfyCommand(cmd));
SomfyRemote::sendCommand(cmd, this->tiltType == tilt_types::euromode ? TILT_REPEATS : this->repeats);
this->settingPos = true;
this->p_target(pos);
@@ -3079,7 +3047,9 @@ bool SomfyShade::save() {
if(somfy.useNVS()) {
char shadeKey[15];
snprintf(shadeKey, sizeof(shadeKey), "SomfyShade%u", this->getShadeId());
- pref.begin(shadeKey);
+ if(!pref.begin(shadeKey)) {
+ ESP_LOGE(TAG, "[Shade %u] save() pref.begin(%s) FAILED", this->getShadeId(), shadeKey);
+ }
pref.clear();
pref.putChar("shadeType", static_cast(this->shadeType));
pref.putUInt("remoteAddress", this->getRemoteAddress());
@@ -3142,7 +3112,7 @@ bool SomfyShade::usesPin(uint8_t pin) {
int8_t SomfyShade::validateJSON(JsonObject &obj) {
int8_t ret = 0;
shade_types type = this->shadeType;
- if(obj.containsKey("shadeType")) {
+ if(!obj["shadeType"].isNull()) {
if(obj["shadeType"].is()) {
if(strncmp(obj["shadeType"].as(), "roller", 7) == 0)
type = shade_types::roller;
@@ -3171,14 +3141,14 @@ int8_t SomfyShade::validateJSON(JsonObject &obj) {
this->shadeType = static_cast(obj["shadeType"].as());
}
}
- if(obj.containsKey("proto")) {
+ if(!obj["proto"].isNull()) {
radio_proto proto = this->proto;
if(proto == radio_proto::GP_Relay || proto == radio_proto::GP_Remote) {
// Check to see if we are using the up and or down
// GPIOs anywhere else.
- uint8_t upPin = obj.containsKey("gpioUp") ? obj["gpioUp"].as() : this->gpioUp;
- uint8_t downPin = obj.containsKey("gpioDown") ? obj["gpioDown"].as() : this->gpioDown;
- uint8_t myPin = obj.containsKey("gpioMy") ? obj["gpioMy"].as() : this->gpioMy;
+ uint8_t upPin = !obj["gpioUp"].isNull() ? obj["gpioUp"].as() : this->gpioUp;
+ uint8_t downPin = !obj["gpioDown"].isNull() ? obj["gpioDown"].as() : this->gpioDown;
+ uint8_t myPin = !obj["gpioMy"].isNull() ? obj["gpioMy"].as() : this->gpioMy;
if(type == shade_types::drycontact ||
((type == shade_types::garage1 || type == shade_types::lgate1 || type == shade_types::cgate1 || type == shade_types::rgate1)
&& proto == radio_proto::GP_Remote)) upPin = myPin = 255;
@@ -3190,12 +3160,14 @@ int8_t SomfyShade::validateJSON(JsonObject &obj) {
(myPin != 255 && somfy.transceiver.usesPin(myPin)))
ret = -10;
}
+#ifndef CONFIG_IDF_TARGET_ESP32C6
if(settings.connType == conn_types_t::ethernet || settings.connType == conn_types_t::ethernetpref) {
if((upPin != 255 && settings.Ethernet.usesPin(upPin)) ||
(downPin != 255 && somfy.transceiver.usesPin(downPin)) ||
(myPin != 255 && somfy.transceiver.usesPin(myPin)))
ret = -11;
}
+#endif
if(ret == 0) {
for(uint8_t i = 0; i < SOMFY_MAX_SHADES; i++) {
SomfyShade *shade = &somfy.shades[i];
@@ -3215,28 +3187,28 @@ int8_t SomfyShade::validateJSON(JsonObject &obj) {
int8_t SomfyShade::fromJSON(JsonObject &obj) {
int8_t err = this->validateJSON(obj);
if(err == 0) {
- if(obj.containsKey("name")) strlcpy(this->name, obj["name"], sizeof(this->name));
- if(obj.containsKey("roomId")) this->roomId = obj["roomId"];
- if(obj.containsKey("upTime")) this->upTime = obj["upTime"];
- if(obj.containsKey("downTime")) this->downTime = obj["downTime"];
- if(obj.containsKey("remoteAddress")) this->setRemoteAddress(obj["remoteAddress"]);
- if(obj.containsKey("tiltTime")) this->tiltTime = obj["tiltTime"];
- if(obj.containsKey("stepSize")) this->stepSize = obj["stepSize"];
- if(obj.containsKey("hasTilt")) this->tiltType = static_cast(obj["hasTilt"]) ? tilt_types::none : tilt_types::tiltmotor;
- if(obj.containsKey("bitLength")) this->bitLength = obj["bitLength"];
- if(obj.containsKey("proto")) this->proto = static_cast(obj["proto"].as());
- if(obj.containsKey("sunSensor")) this->setSunSensor(obj["sunSensor"]);
- if(obj.containsKey("simMy")) this->setSimMy(obj["simMy"]);
- if(obj.containsKey("light")) this->setLight(obj["light"]);
- if(obj.containsKey("gpioFlags")) this->gpioFlags = obj["gpioFlags"];
- if(obj.containsKey("gpioLLTrigger")) {
+ if(!obj["name"].isNull()) strlcpy(this->name, obj["name"], sizeof(this->name));
+ if(!obj["roomId"].isNull()) this->roomId = obj["roomId"];
+ if(!obj["upTime"].isNull()) this->upTime = obj["upTime"];
+ if(!obj["downTime"].isNull()) this->downTime = obj["downTime"];
+ if(!obj["remoteAddress"].isNull()) this->setRemoteAddress(obj["remoteAddress"]);
+ if(!obj["tiltTime"].isNull()) this->tiltTime = obj["tiltTime"];
+ if(!obj["stepSize"].isNull()) this->stepSize = obj["stepSize"];
+ if(!obj["hasTilt"].isNull()) this->tiltType = static_cast(obj["hasTilt"]) ? tilt_types::none : tilt_types::tiltmotor;
+ if(!obj["bitLength"].isNull()) this->bitLength = obj["bitLength"];
+ if(!obj["proto"].isNull()) this->proto = static_cast(obj["proto"].as());
+ if(!obj["sunSensor"].isNull()) this->setSunSensor(obj["sunSensor"]);
+ if(!obj["simMy"].isNull()) this->setSimMy(obj["simMy"]);
+ if(!obj["light"].isNull()) this->setLight(obj["light"]);
+ if(!obj["gpioFlags"].isNull()) this->gpioFlags = obj["gpioFlags"];
+ if(!obj["gpioLLTrigger"].isNull()) {
if(obj["gpioLLTrigger"].as())
this->gpioFlags |= (uint8_t)gpio_flags_t::LowLevelTrigger;
else
this->gpioFlags &= ~(uint8_t)gpio_flags_t::LowLevelTrigger;
}
- if(obj.containsKey("shadeType")) {
+ if(!obj["shadeType"].isNull()) {
if(obj["shadeType"].is()) {
if(strncmp(obj["shadeType"].as(), "roller", 7) == 0)
this->shadeType = shade_types::roller;
@@ -3265,10 +3237,10 @@ int8_t SomfyShade::fromJSON(JsonObject &obj) {
this->shadeType = static_cast(obj["shadeType"].as());
}
}
- if(obj.containsKey("flipCommands")) this->flipCommands = obj["flipCommands"].as();
- if(obj.containsKey("flipPosition")) this->flipPosition = obj["flipPosition"].as();
- if(obj.containsKey("repeats")) this->repeats = obj["repeats"];
- if(obj.containsKey("tiltType")) {
+ if(!obj["flipCommands"].isNull()) this->flipCommands = obj["flipCommands"].as();
+ if(!obj["flipPosition"].isNull()) this->flipPosition = obj["flipPosition"].as();
+ if(!obj["repeats"].isNull()) this->repeats = obj["repeats"];
+ if(!obj["tiltType"].isNull()) {
if(obj["tiltType"].is()) {
if(strncmp(obj["tiltType"].as(), "none", 4) == 0)
this->tiltType = tilt_types::none;
@@ -3283,7 +3255,7 @@ int8_t SomfyShade::fromJSON(JsonObject &obj) {
this->tiltType = static_cast(obj["tiltType"].as());
}
}
- if(obj.containsKey("linkedAddresses")) {
+ if(!obj["linkedAddresses"].isNull()) {
uint32_t linkedAddresses[SOMFY_MAX_LINKED_REMOTES];
memset(linkedAddresses, 0x00, sizeof(linkedAddresses));
JsonArray arr = obj["linkedAddresses"];
@@ -3295,86 +3267,20 @@ int8_t SomfyShade::fromJSON(JsonObject &obj) {
this->linkedRemotes[j].setRemoteAddress(linkedAddresses[j]);
}
}
- if(obj.containsKey("flags")) this->flags = obj["flags"];
+ if(!obj["flags"].isNull()) this->flags = obj["flags"];
if(this->proto == radio_proto::GP_Remote || this->proto == radio_proto::GP_Relay) {
- if(obj.containsKey("gpioUp")) this->gpioUp = obj["gpioUp"];
- if(obj.containsKey("gpioDown")) this->gpioDown = obj["gpioDown"];
+ if(!obj["gpioUp"].isNull()) this->gpioUp = obj["gpioUp"];
+ if(!obj["gpioDown"].isNull()) this->gpioDown = obj["gpioDown"];
pinMode(this->gpioUp, OUTPUT);
pinMode(this->gpioDown, OUTPUT);
}
if(this->proto == radio_proto::GP_Remote) {
- if(obj.containsKey("gpioMy")) this->gpioMy = obj["gpioMy"];
+ if(!obj["gpioMy"].isNull()) this->gpioMy = obj["gpioMy"];
pinMode(this->gpioMy, OUTPUT);
}
}
return err;
}
-void SomfyShade::toJSONRef(JsonResponse &json) {
- json.addElem("shadeId", this->getShadeId());
- json.addElem("roomId", this->roomId);
- json.addElem("name", this->name);
- json.addElem("remoteAddress", (uint32_t)this->m_remoteAddress);
- json.addElem("paired", this->paired);
- json.addElem("shadeType", static_cast(this->shadeType));
- json.addElem("flipCommands", this->flipCommands);
- json.addElem("flipPosition", this->flipCommands);
- json.addElem("bitLength", this->bitLength);
- json.addElem("proto", static_cast(this->proto));
- json.addElem("flags", this->flags);
- json.addElem("sunSensor", this->hasSunSensor());
- json.addElem("hasLight", this->hasLight());
- json.addElem("repeats", this->repeats);
- //SomfyRemote::toJSON(json);
-}
-
-void SomfyShade::toJSON(JsonResponse &json) {
- json.addElem("shadeId", this->getShadeId());
- json.addElem("roomId", this->roomId);
- json.addElem("name", this->name);
- json.addElem("remoteAddress", (uint32_t)this->m_remoteAddress);
- json.addElem("upTime", (uint32_t)this->upTime);
- json.addElem("downTime", (uint32_t)this->downTime);
- json.addElem("paired", this->paired);
- json.addElem("lastRollingCode", (uint32_t)this->lastRollingCode);
- json.addElem("position", this->transformPosition(this->currentPos));
- json.addElem("tiltType", static_cast(this->tiltType));
- json.addElem("tiltPosition", this->transformPosition(this->currentTiltPos));
- json.addElem("tiltDirection", this->tiltDirection);
- json.addElem("tiltTime", (uint32_t)this->tiltTime);
- json.addElem("stepSize", (uint32_t)this->stepSize);
- json.addElem("tiltTarget", this->transformPosition(this->tiltTarget));
- json.addElem("target", this->transformPosition(this->target));
- json.addElem("myPos", this->transformPosition(this->myPos));
- json.addElem("myTiltPos", this->transformPosition(this->myTiltPos));
- json.addElem("direction", this->direction);
- json.addElem("shadeType", static_cast(this->shadeType));
- json.addElem("bitLength", this->bitLength);
- json.addElem("proto", static_cast(this->proto));
- json.addElem("flags", this->flags);
- json.addElem("flipCommands", this->flipCommands);
- json.addElem("flipPosition", this->flipPosition);
- json.addElem("inGroup", this->isInGroup());
- json.addElem("sunSensor", this->hasSunSensor());
- json.addElem("light", this->hasLight());
- json.addElem("repeats", this->repeats);
- json.addElem("sortOrder", this->sortOrder);
- json.addElem("gpioUp", this->gpioUp);
- json.addElem("gpioDown", this->gpioDown);
- json.addElem("gpioMy", this->gpioMy);
- json.addElem("gpioLLTrigger", ((this->gpioFlags & (uint8_t)gpio_flags_t::LowLevelTrigger) == 0) ? false : true);
- json.addElem("simMy", this->simMy());
- json.beginArray("linkedRemotes");
- for(uint8_t i = 0; i < SOMFY_MAX_LINKED_REMOTES; i++) {
- SomfyLinkedRemote &lremote = this->linkedRemotes[i];
- if(lremote.getRemoteAddress() != 0) {
- json.beginObject();
- lremote.toJSON(json);
- json.endObject();
- }
- }
- json.endArray();
-}
-
/*
bool SomfyShade::toJSON(JsonObject &obj) {
//Serial.print("Serializing Shade:");
@@ -3430,8 +3336,8 @@ bool SomfyShade::toJSON(JsonObject &obj) {
}
*/
bool SomfyRoom::fromJSON(JsonObject &obj) {
- if(obj.containsKey("name")) strlcpy(this->name, obj["name"], sizeof(this->name));
- if(obj.containsKey("sortOrder")) this->sortOrder = obj["sortOrder"];
+ if(!obj["name"].isNull()) strlcpy(this->name, obj["name"], sizeof(this->name));
+ if(!obj["sortOrder"].isNull()) this->sortOrder = obj["sortOrder"];
return true;
}
/*
@@ -3442,23 +3348,17 @@ bool SomfyRoom::toJSON(JsonObject &obj) {
return true;
}
*/
-void SomfyRoom::toJSON(JsonResponse &json) {
- json.addElem("roomId", this->roomId);
- json.addElem("name", this->name);
- json.addElem("sortOrder", this->sortOrder);
-}
-
bool SomfyGroup::fromJSON(JsonObject &obj) {
- if(obj.containsKey("name")) strlcpy(this->name, obj["name"], sizeof(this->name));
- if(obj.containsKey("roomId")) this->roomId = obj["roomId"];
- if(obj.containsKey("remoteAddress")) this->setRemoteAddress(obj["remoteAddress"]);
- if(obj.containsKey("bitLength")) this->bitLength = obj["bitLength"];
- if(obj.containsKey("proto")) this->proto = static_cast(obj["proto"].as());
- if(obj.containsKey("flipCommands")) this->flipCommands = obj["flipCommands"].as();
+ if(!obj["name"].isNull()) strlcpy(this->name, obj["name"], sizeof(this->name));
+ if(!obj["roomId"].isNull()) this->roomId = obj["roomId"];
+ if(!obj["remoteAddress"].isNull()) this->setRemoteAddress(obj["remoteAddress"]);
+ if(!obj["bitLength"].isNull()) this->bitLength = obj["bitLength"];
+ if(!obj["proto"].isNull()) this->proto = static_cast(obj["proto"].as());
+ if(!obj["flipCommands"].isNull()) this->flipCommands = obj["flipCommands"].as();
- //if(obj.containsKey("sunSensor")) this->hasSunSensor() = obj["sunSensor"]; This is calculated
- if(obj.containsKey("repeats")) this->repeats = obj["repeats"];
- if(obj.containsKey("linkedShades")) {
+ //if(!obj["sunSensor"].isNull()) this->hasSunSensor() = obj["sunSensor"]; This is calculated
+ if(!obj["repeats"].isNull()) this->repeats = obj["repeats"];
+ if(!obj["linkedShades"].isNull()) {
uint8_t linkedShades[SOMFY_MAX_GROUPED_SHADES];
memset(linkedShades, 0x00, sizeof(linkedShades));
JsonArray arr = obj["linkedShades"];
@@ -3469,50 +3369,6 @@ bool SomfyGroup::fromJSON(JsonObject &obj) {
}
return true;
}
-void SomfyGroup::toJSON(JsonResponse &json) {
- this->updateFlags();
- json.addElem("groupId", this->getGroupId());
- json.addElem("roomId", this->roomId);
- json.addElem("name", this->name);
- json.addElem("remoteAddress", (uint32_t)this->m_remoteAddress);
- json.addElem("lastRollingCode", (uint32_t)this->lastRollingCode);
- json.addElem("bitLength", this->bitLength);
- json.addElem("proto", static_cast(this->proto));
- json.addElem("sunSensor", this->hasSunSensor());
- json.addElem("flipCommands", this->flipCommands);
- json.addElem("flags", this->flags);
- json.addElem("repeats", this->repeats);
- json.addElem("sortOrder", this->sortOrder);
- json.beginArray("linkedShades");
- for(uint8_t i = 0; i < SOMFY_MAX_GROUPED_SHADES; i++) {
- uint8_t shadeId = this->linkedShades[i];
- if(shadeId > 0 && shadeId < 255) {
- SomfyShade *shade = somfy.getShadeById(shadeId);
- if(shade) {
- json.beginObject();
- shade->toJSONRef(json);
- json.endObject();
- }
- }
- }
- json.endArray();
-}
-void SomfyGroup::toJSONRef(JsonResponse &json) {
- this->updateFlags();
- json.addElem("groupId", this->getGroupId());
- json.addElem("roomId", this->roomId);
- json.addElem("name", this->name);
- json.addElem("remoteAddress", (uint32_t)this->m_remoteAddress);
- json.addElem("lastRollingCode", (uint32_t)this->lastRollingCode);
- json.addElem("bitLength", this->bitLength);
- json.addElem("proto", static_cast(this->proto));
- json.addElem("sunSensor", this->hasSunSensor());
- json.addElem("flipCommands", this->flipCommands);
- json.addElem("flags", this->flags);
- json.addElem("repeats", this->repeats);
- json.addElem("sortOrder", this->sortOrder);
-}
-
/*
bool SomfyGroup::toJSON(JsonObject &obj) {
this->updateFlags();
@@ -3544,10 +3400,6 @@ bool SomfyGroup::toJSON(JsonObject &obj) {
}
*/
-void SomfyRemote::toJSON(JsonResponse &json) {
- json.addElem("remoteAddress", (uint32_t)this->getRemoteAddress());
- json.addElem("lastRollingCode", (uint32_t)this->lastRollingCode);
-}
/*
bool SomfyRemote::toJSON(JsonObject &obj) {
//obj["remotePrefId"] = this->getRemotePrefId();
@@ -3571,6 +3423,7 @@ void SomfyShadeController::emitState(uint8_t num) {
for(uint8_t i = 0; i < SOMFY_MAX_SHADES; i++) {
SomfyShade *shade = &this->shades[i];
if(shade->getShadeId() == 255) continue;
+ esp_task_wdt_reset();
shade->emitState(num);
}
}
@@ -3622,8 +3475,7 @@ uint8_t SomfyShadeController::getNextShadeId() {
}
}
if(!id_exists) {
- Serial.print("Got next Shade Id:");
- Serial.print(i);
+ ESP_LOGD(TAG, "Got next Shade Id:%d", i);
return i;
}
}
@@ -3660,8 +3512,7 @@ uint8_t SomfyShadeController::getNextGroupId() {
}
}
if(!id_exists) {
- Serial.print("Got next Group Id:");
- Serial.print(i);
+ ESP_LOGD(TAG, "Got next Group Id:%d", i);
return i;
}
}
@@ -3680,8 +3531,7 @@ uint8_t SomfyShadeController::getNextRoomId() {
}
}
if(!id_exists) {
- Serial.print("Got next room Id:");
- Serial.print(i);
+ ESP_LOGD(TAG, "Got next room Id:%d", i);
return i;
}
}
@@ -3760,7 +3610,7 @@ SomfyShade *SomfyShadeController::addShade() {
if(shade) {
shade->setShadeId(shadeId);
shade->sortOrder = this->getMaxShadeOrder() + 1;
- Serial.printf("Sort order set to %d\n", shade->sortOrder);
+ ESP_LOGD(TAG, "Sort order set to %d", shade->sortOrder);
this->isDirty = true;
#ifdef USE_NVS
if(this->useNVS()) {
@@ -3789,25 +3639,30 @@ SomfyShade *SomfyShadeController::addShade() {
pref.begin("Shades");
pref.remove("shadeIds");
int x = pref.putBytes("shadeIds", this->m_shadeIds, sizeof(this->m_shadeIds));
- Serial.printf("WROTE %d bytes to shadeIds\n", x);
+ ESP_LOGD(TAG, "WROTE %d bytes to shadeIds", x);
pref.end();
- for(uint8_t i = 0; i < sizeof(this->m_shadeIds); i++) {
- if(i != 0) Serial.print(",");
- else Serial.print("Shade Ids: ");
- Serial.print(this->m_shadeIds[i]);
+ {
+ char shade_ids_buf[256];
+ int spos = snprintf(shade_ids_buf, sizeof(shade_ids_buf), "Shade Ids: ");
+ for(uint8_t i = 0; i < sizeof(this->m_shadeIds); i++) {
+ if(i != 0) spos += snprintf(shade_ids_buf + spos, sizeof(shade_ids_buf) - spos, ",");
+ spos += snprintf(shade_ids_buf + spos, sizeof(shade_ids_buf) - spos, "%d", this->m_shadeIds[i]);
+ }
+ ESP_LOGD(TAG, "%s", shade_ids_buf);
}
- Serial.println();
pref.begin("Shades");
pref.getBytes("shadeIds", this->m_shadeIds, sizeof(this->m_shadeIds));
- Serial.print("LENGTH:");
- Serial.println(pref.getBytesLength("shadeIds"));
+ ESP_LOGD(TAG, "LENGTH:%d", pref.getBytesLength("shadeIds"));
pref.end();
- for(uint8_t i = 0; i < sizeof(this->m_shadeIds); i++) {
- if(i != 0) Serial.print(",");
- else Serial.print("Shade Ids: ");
- Serial.print(this->m_shadeIds[i]);
+ {
+ char shade_ids_buf[256];
+ int spos = snprintf(shade_ids_buf, sizeof(shade_ids_buf), "Shade Ids: ");
+ for(uint8_t i = 0; i < sizeof(this->m_shadeIds); i++) {
+ if(i != 0) spos += snprintf(shade_ids_buf + spos, sizeof(shade_ids_buf) - spos, ",");
+ spos += snprintf(shade_ids_buf + spos, sizeof(shade_ids_buf) - spos, "%d", this->m_shadeIds[i]);
+ }
+ ESP_LOGD(TAG, "%s", shade_ids_buf);
}
- Serial.println();
}
#endif
}
@@ -3919,14 +3774,9 @@ void SomfyRemote::sendSensorCommand(int8_t isWindy, int8_t isSunny, uint8_t repe
this->lastFrame.encKey = 160; // Sensor commands are always encryption code 160.
this->lastFrame.cmd = somfy_commands::Sensor;
this->lastFrame.processed = false;
- Serial.print("CMD:");
- Serial.print(translateSomfyCommand(this->lastFrame.cmd));
- Serial.print(" ADDR:");
- Serial.print(this->lastFrame.remoteAddress);
- Serial.print(" RCODE:");
- Serial.print(this->lastFrame.rollingCode);
- Serial.print(" REPEAT:");
- Serial.println(repeat);
+ ESP_LOGD(TAG, "CMD:%s ADDR:%d RCODE:%d REPEAT:%d",
+ translateSomfyCommand(this->lastFrame.cmd).c_str(),
+ this->lastFrame.remoteAddress, this->lastFrame.rollingCode, repeat);
somfy.sendFrame(this->lastFrame, repeat);
somfy.processFrame(this->lastFrame, true);
}
@@ -3943,46 +3793,33 @@ void SomfyRemote::sendCommand(somfy_commands cmd, uint8_t repeat, uint8_t stepSi
this->lastFrame.encKey = 0xA0 | static_cast(this->lastFrame.rollingCode & 0x000F);
this->lastFrame.proto = this->proto;
if(this->lastFrame.bitLength == 0) this->lastFrame.bitLength = bit_length;
- if(this->lastFrame.rollingCode == 0) Serial.println("ERROR: Setting rcode to 0");
+ if(this->lastFrame.rollingCode == 0) ESP_LOGE(TAG, "Setting rcode to 0");
this->p_lastRollingCode(this->lastFrame.rollingCode);
// We have to set the processed to clear this if we are sending
// another command.
this->lastFrame.processed = false;
if(this->proto == radio_proto::GP_Relay) {
- Serial.print("CMD:");
- Serial.print(translateSomfyCommand(this->lastFrame.cmd));
- Serial.print(" ADDR:");
- Serial.print(this->lastFrame.remoteAddress);
- Serial.print(" RCODE:");
- Serial.print(this->lastFrame.rollingCode);
- Serial.println(" SETTING GPIO");
+ ESP_LOGD(TAG, "CMD:%s ADDR:%d RCODE:%d SETTING GPIO",
+ translateSomfyCommand(this->lastFrame.cmd).c_str(),
+ this->lastFrame.remoteAddress, this->lastFrame.rollingCode);
}
else if(this->proto == radio_proto::GP_Remote) {
- Serial.print("CMD:");
- Serial.print(translateSomfyCommand(this->lastFrame.cmd));
- Serial.print(" ADDR:");
- Serial.print(this->lastFrame.remoteAddress);
- Serial.print(" RCODE:");
- Serial.print(this->lastFrame.rollingCode);
- Serial.println(" TRIGGER GPIO");
+ ESP_LOGD(TAG, "CMD:%s ADDR:%d RCODE:%d TRIGGER GPIO",
+ translateSomfyCommand(this->lastFrame.cmd).c_str(),
+ this->lastFrame.remoteAddress, this->lastFrame.rollingCode);
this->triggerGPIOs(this->lastFrame);
}
else {
- Serial.print("CMD:");
- Serial.print(translateSomfyCommand(this->lastFrame.cmd));
- Serial.print(" ADDR:");
- Serial.print(this->lastFrame.remoteAddress);
- Serial.print(" RCODE:");
- Serial.print(this->lastFrame.rollingCode);
- Serial.print(" REPEAT:");
- Serial.println(repeat);
+ ESP_LOGD(TAG, "CMD:%s ADDR:%d RCODE:%d REPEAT:%d",
+ translateSomfyCommand(this->lastFrame.cmd).c_str(),
+ this->lastFrame.remoteAddress, this->lastFrame.rollingCode, repeat);
somfy.sendFrame(this->lastFrame, repeat);
}
somfy.processFrame(this->lastFrame, true);
}
bool SomfyRemote::isLastCommand(somfy_commands cmd) {
if(this->lastFrame.cmd != cmd || this->lastFrame.rollingCode != this->lastRollingCode) {
- Serial.printf("Not the last command %d: %d - %d\n", static_cast(this->lastFrame.cmd), this->lastFrame.rollingCode, this->lastRollingCode);
+ ESP_LOGD(TAG, "Not the last command %d: %d - %d", static_cast(this->lastFrame.cmd), this->lastFrame.rollingCode, this->lastRollingCode);
return false;
}
return true;
@@ -4087,7 +3924,9 @@ bool SomfyShadeController::deleteGroup(uint8_t groupId) {
bool SomfyShadeController::loadShadesFile(const char *filename) { return ShadeConfigFile::load(this, filename); }
uint16_t SomfyRemote::getNextRollingCode() {
- pref.begin("ShadeCodes");
+ if(!pref.begin("ShadeCodes")) {
+ ESP_LOGE(TAG, "getNextRollingCode() pref.begin(ShadeCodes) FAILED");
+ }
uint16_t code = pref.getUShort(this->m_remotePrefId, 0);
code++;
pref.putUShort(this->m_remotePrefId, code);
@@ -4107,30 +3946,10 @@ uint16_t SomfyRemote::setRollingCode(uint16_t code) {
pref.putUShort(this->m_remotePrefId, code);
pref.end();
this->lastRollingCode = code;
- Serial.printf("Setting Last Rolling code %d\n", this->lastRollingCode);
+ ESP_LOGD(TAG, "Setting Last Rolling code %d", this->lastRollingCode);
}
return code;
}
-void SomfyShadeController::toJSONRooms(JsonResponse &json) {
- for(uint8_t i = 0; i < SOMFY_MAX_ROOMS; i++) {
- SomfyRoom *room = &this->rooms[i];
- if(room->roomId != 0) {
- json.beginObject();
- room->toJSON(json);
- json.endObject();
- }
- }
-}
-void SomfyShadeController::toJSONShades(JsonResponse &json) {
- for(uint8_t i = 0; i < SOMFY_MAX_SHADES; i++) {
- SomfyShade &shade = this->shades[i];
- if(shade.getShadeId() != 255) {
- json.beginObject();
- shade.toJSON(json);
- json.endObject();
- }
- }
-}
/*
bool SomfyShadeController::toJSON(DynamicJsonDocument &doc) {
@@ -4185,21 +4004,6 @@ bool SomfyShadeController::toJSONGroups(JsonArray &arr) {
return true;
}
*/
-void SomfyShadeController::toJSONGroups(JsonResponse &json) {
- for(uint8_t i = 0; i < SOMFY_MAX_GROUPS; i++) {
- SomfyGroup &group = this->groups[i];
- if(group.getGroupId() != 255) {
- json.beginObject();
- group.toJSON(json);
- json.endObject();
- }
- }
-}
-void SomfyShadeController::toJSONRepeaters(JsonResponse &json) {
- for(uint8_t i = 0; i < SOMFY_MAX_REPEATERS; i++) {
- if(somfy.repeaters[i] != 0) json.addElem((uint32_t)somfy.repeaters[i]);
- }
-}
void SomfyShadeController::loop() {
this->transceiver.loop();
for(uint8_t i = 0; i < SOMFY_MAX_SHADES; i++) {
@@ -4287,7 +4091,7 @@ void somfy_tx_queue_t::push(uint8_t hwsync, uint8_t *payload, uint8_t bit_length
this->delay_time = millis() + TX_QUEUE_DELAY; // We do not want to process this frame until a full frame beat has passed.
}
void somfy_rx_queue_t::init() {
- Serial.println("Initializing RX Queue");
+ ESP_LOGD(TAG, "Initializing RX Queue");
for (uint8_t i = 0; i < MAX_RX_BUFFER; i++)
this->items[i].clear();
memset(&this->index[0], 0xFF, MAX_RX_BUFFER);
@@ -4531,7 +4335,7 @@ void Transceiver::beginFrequencyScan() {
markFreq = currFreq = 433.0f;
markRSSI = -100;
ELECHOUSE_cc1101.setMHZ(currFreq);
- Serial.printf("Begin frequency scan on Pin #%d\n", this->config.RXPin);
+ ESP_LOGD(TAG, "Begin frequency scan on Pin #%d", this->config.RXPin);
attachInterrupt(interruptPin, handleReceive, CHANGE);
this->emitFrequencyScan();
}
@@ -4674,7 +4478,7 @@ void Transceiver::enableReceive(void) {
ELECHOUSE_cc1101.SetRx();
//attachInterrupt(interruptPin, handleReceive, FALLING);
attachInterrupt(interruptPin, handleReceive, CHANGE);
- Serial.printf("Enabled receive on Pin #%d Timing: %ld\n", this->config.RXPin, millis() - timing);
+ ESP_LOGD(TAG, "Enabled receive on Pin #%d Timing: %ld", this->config.RXPin, millis() - timing);
}
}
void Transceiver::disableReceive(void) {
@@ -4683,11 +4487,6 @@ void Transceiver::disableReceive(void) {
interruptPin = 0;
}
-void Transceiver::toJSON(JsonResponse& json) {
- json.beginObject("config");
- this->config.toJSON(json);
- json.endObject();
-}
/*
bool Transceiver::toJSON(JsonObject& obj) {
//Serial.println("Setting Transceiver Json");
@@ -4697,7 +4496,7 @@ bool Transceiver::toJSON(JsonObject& obj) {
}
*/
bool Transceiver::fromJSON(JsonObject& obj) {
- if (obj.containsKey("config")) {
+ if (!obj["config"].isNull()) {
JsonObject objConfig = obj["config"];
this->config.fromJSON(objConfig);
}
@@ -4726,61 +4525,45 @@ bool Transceiver::end() {
}
void transceiver_config_t::fromJSON(JsonObject& obj) {
//Serial.print("Deserialize Radio JSON ");
- if(obj.containsKey("type")) this->type = obj["type"];
- if(obj.containsKey("CSNPin")) this->CSNPin = obj["CSNPin"];
- if(obj.containsKey("MISOPin")) this->MISOPin = obj["MISOPin"];
- if(obj.containsKey("MOSIPin")) this->MOSIPin = obj["MOSIPin"];
- if(obj.containsKey("RXPin")) this->RXPin = obj["RXPin"];
- if(obj.containsKey("SCKPin")) this->SCKPin = obj["SCKPin"];
- if(obj.containsKey("TXPin")) this->TXPin = obj["TXPin"];
- if(obj.containsKey("rxBandwidth")) this->rxBandwidth = obj["rxBandwidth"]; // float
- if(obj.containsKey("frequency")) this->frequency = obj["frequency"]; // float
- if(obj.containsKey("deviation")) this->deviation = obj["deviation"]; // float
- if(obj.containsKey("enabled")) this->enabled = obj["enabled"];
- if(obj.containsKey("txPower")) this->txPower = obj["txPower"];
- if(obj.containsKey("proto")) this->proto = static_cast(obj["proto"].as());
+ if(!obj["type"].isNull()) this->type = obj["type"];
+ if(!obj["CSNPin"].isNull()) this->CSNPin = obj["CSNPin"];
+ if(!obj["MISOPin"].isNull()) this->MISOPin = obj["MISOPin"];
+ if(!obj["MOSIPin"].isNull()) this->MOSIPin = obj["MOSIPin"];
+ if(!obj["RXPin"].isNull()) this->RXPin = obj["RXPin"];
+ if(!obj["SCKPin"].isNull()) this->SCKPin = obj["SCKPin"];
+ if(!obj["TXPin"].isNull()) this->TXPin = obj["TXPin"];
+ if(!obj["rxBandwidth"].isNull()) this->rxBandwidth = obj["rxBandwidth"]; // float
+ if(!obj["frequency"].isNull()) this->frequency = obj["frequency"]; // float
+ if(!obj["deviation"].isNull()) this->deviation = obj["deviation"]; // float
+ if(!obj["enabled"].isNull()) this->enabled = obj["enabled"];
+ if(!obj["txPower"].isNull()) this->txPower = obj["txPower"];
+ if(!obj["proto"].isNull()) this->proto = static_cast(obj["proto"].as());
/*
- if (obj.containsKey("internalCCMode")) this->internalCCMode = obj["internalCCMode"];
- if (obj.containsKey("modulationMode")) this->modulationMode = obj["modulationMode"];
- if (obj.containsKey("channel")) this->channel = obj["channel"];
- if (obj.containsKey("channelSpacing")) this->channelSpacing = obj["channelSpacing"]; // float
- if (obj.containsKey("dataRate")) this->dataRate = obj["dataRate"]; // float
- if (obj.containsKey("syncMode")) this->syncMode = obj["syncMode"];
- if (obj.containsKey("syncWordHigh")) this->syncWordHigh = obj["syncWordHigh"];
- if (obj.containsKey("syncWordLow")) this->syncWordLow = obj["syncWordLow"];
- if (obj.containsKey("addrCheckMode")) this->addrCheckMode = obj["addrCheckMode"];
- if (obj.containsKey("checkAddr")) this->checkAddr = obj["checkAddr"];
- if (obj.containsKey("dataWhitening")) this->dataWhitening = obj["dataWhitening"];
- if (obj.containsKey("pktFormat")) this->pktFormat = obj["pktFormat"];
- if (obj.containsKey("pktLengthMode")) this->pktLengthMode = obj["pktLengthMode"];
- if (obj.containsKey("pktLength")) this->pktLength = obj["pktLength"];
- if (obj.containsKey("useCRC")) this->useCRC = obj["useCRC"];
- if (obj.containsKey("autoFlushCRC")) this->autoFlushCRC = obj["autoFlushCRC"];
- if (obj.containsKey("disableDCFilter")) this->disableDCFilter = obj["disableCRCFilter"];
- if (obj.containsKey("enableManchester")) this->enableManchester = obj["enableManchester"];
- if (obj.containsKey("enableFEC")) this->enableFEC = obj["enableFEC"];
- if (obj.containsKey("minPreambleBytes")) this->minPreambleBytes = obj["minPreambleBytes"];
- if (obj.containsKey("pqtThreshold")) this->pqtThreshold = obj["pqtThreshold"];
- if (obj.containsKey("appendStatus")) this->appendStatus = obj["appendStatus"];
- if (obj.containsKey("printBuffer")) this->printBuffer = obj["printBuffer"];
+ if (!obj["internalCCMode"].isNull()) this->internalCCMode = obj["internalCCMode"];
+ if (!obj["modulationMode"].isNull()) this->modulationMode = obj["modulationMode"];
+ if (!obj["channel"].isNull()) this->channel = obj["channel"];
+ if (!obj["channelSpacing"].isNull()) this->channelSpacing = obj["channelSpacing"]; // float
+ if (!obj["dataRate"].isNull()) this->dataRate = obj["dataRate"]; // float
+ if (!obj["syncMode"].isNull()) this->syncMode = obj["syncMode"];
+ if (!obj["syncWordHigh"].isNull()) this->syncWordHigh = obj["syncWordHigh"];
+ if (!obj["syncWordLow"].isNull()) this->syncWordLow = obj["syncWordLow"];
+ if (!obj["addrCheckMode"].isNull()) this->addrCheckMode = obj["addrCheckMode"];
+ if (!obj["checkAddr"].isNull()) this->checkAddr = obj["checkAddr"];
+ if (!obj["dataWhitening"].isNull()) this->dataWhitening = obj["dataWhitening"];
+ if (!obj["pktFormat"].isNull()) this->pktFormat = obj["pktFormat"];
+ if (!obj["pktLengthMode"].isNull()) this->pktLengthMode = obj["pktLengthMode"];
+ if (!obj["pktLength"].isNull()) this->pktLength = obj["pktLength"];
+ if (!obj["useCRC"].isNull()) this->useCRC = obj["useCRC"];
+ if (!obj["autoFlushCRC"].isNull()) this->autoFlushCRC = obj["autoFlushCRC"];
+ if (!obj["disableDCFilter"].isNull()) this->disableDCFilter = obj["disableCRCFilter"];
+ if (!obj["enableManchester"].isNull()) this->enableManchester = obj["enableManchester"];
+ if (!obj["enableFEC"].isNull()) this->enableFEC = obj["enableFEC"];
+ if (!obj["minPreambleBytes"].isNull()) this->minPreambleBytes = obj["minPreambleBytes"];
+ if (!obj["pqtThreshold"].isNull()) this->pqtThreshold = obj["pqtThreshold"];
+ if (!obj["appendStatus"].isNull()) this->appendStatus = obj["appendStatus"];
+ if (!obj["printBuffer"].isNull()) this->printBuffer = obj["printBuffer"];
*/
- Serial.printf("SCK:%u MISO:%u MOSI:%u CSN:%u RX:%u TX:%u\n", this->SCKPin, this->MISOPin, this->MOSIPin, this->CSNPin, this->RXPin, this->TXPin);
-}
-void transceiver_config_t::toJSON(JsonResponse &json) {
- json.addElem("type", this->type);
- json.addElem("TXPin", this->TXPin);
- json.addElem("RXPin", this->RXPin);
- json.addElem("SCKPin", this->SCKPin);
- json.addElem("MOSIPin", this->MOSIPin);
- json.addElem("MISOPin", this->MISOPin);
- json.addElem("CSNPin", this->CSNPin);
- json.addElem("rxBandwidth", this->rxBandwidth); // float
- json.addElem("frequency", this->frequency); // float
- json.addElem("deviation", this->deviation); // float
- json.addElem("txPower", this->txPower);
- json.addElem("proto", static_cast(this->proto));
- json.addElem("enabled", this->enabled);
- json.addElem("radioInit", this->radioInit);
+ ESP_LOGD(TAG, "SCK:%u MISO:%u MOSI:%u CSN:%u RX:%u TX:%u", this->SCKPin, this->MISOPin, this->MOSIPin, this->CSNPin, this->RXPin, this->TXPin);
}
/*
void transceiver_config_t::toJSON(JsonObject& obj) {
@@ -4871,12 +4654,11 @@ void transceiver_config_t::save() {
*/
pref.end();
- Serial.print("Save Radio Settings ");
- Serial.printf("SCK:%u MISO:%u MOSI:%u CSN:%u RX:%u TX:%u\n", this->SCKPin, this->MISOPin, this->MOSIPin, this->CSNPin, this->RXPin, this->TXPin);
+ ESP_LOGI(TAG, "Save Radio Settings SCK:%u MISO:%u MOSI:%u CSN:%u RX:%u TX:%u", this->SCKPin, this->MISOPin, this->MOSIPin, this->CSNPin, this->RXPin, this->TXPin);
}
void transceiver_config_t::removeNVSKey(const char *key) {
if(pref.isKey(key)) {
- Serial.printf("Removing NVS Key: CC1101.%s\n", key);
+ ESP_LOGD(TAG, "Removing NVS Key: CC1101.%s", key);
pref.remove(key);
}
}
@@ -4885,7 +4667,7 @@ void transceiver_config_t::load() {
esp_chip_info(&ci);
switch(ci.model) {
case esp_chip_model_t::CHIP_ESP32S3:
- Serial.println("Setting S3 Transceiver Defaults...");
+ ESP_LOGD(TAG, "Setting S3 Transceiver Defaults...");
this->TXPin = 15;
this->RXPin = 14;
this->MOSIPin = 11;
@@ -4909,6 +4691,16 @@ void transceiver_config_t::load() {
this->SCKPin = 15;
this->CSNPin = 14;
break;
+#ifdef CHIP_ESP32C6
+ case esp_chip_model_t::CHIP_ESP32C6:
+ this->TXPin = 13;
+ this->RXPin = 12;
+ this->MOSIPin = 16;
+ this->MISOPin = 17;
+ this->SCKPin = 15;
+ this->CSNPin = 14;
+ break;
+#endif
default:
this->TXPin = 13;
this->RXPin = 12;
@@ -4969,8 +4761,7 @@ void transceiver_config_t::apply() {
this->radioInit = false;
pref.end();
if(!radioInit) return;
- Serial.print("Applying radio settings ");
- Serial.printf("Setting Data Pins RX:%u TX:%u\n", this->RXPin, this->TXPin);
+ ESP_LOGD(TAG, "Applying radio settings - Setting Data Pins RX:%u TX:%u", this->RXPin, this->TXPin);
//if(this->TXPin != this->RXPin)
// pinMode(this->TXPin, OUTPUT);
//pinMode(this->RXPin, INPUT);
@@ -4979,9 +4770,9 @@ void transceiver_config_t::apply() {
ELECHOUSE_cc1101.setGDO0(this->TXPin); // This pin may be shared.
else
ELECHOUSE_cc1101.setGDO(this->TXPin, this->RXPin); // GDO0, GDO2
- Serial.printf("Setting SPI Pins SCK:%u MISO:%u MOSI:%u CSN:%u\n", this->SCKPin, this->MISOPin, this->MOSIPin, this->CSNPin);
+ ESP_LOGD(TAG, "Setting SPI Pins SCK:%u MISO:%u MOSI:%u CSN:%u", this->SCKPin, this->MISOPin, this->MOSIPin, this->CSNPin);
ELECHOUSE_cc1101.setSpiPin(this->SCKPin, this->MISOPin, this->MOSIPin, this->CSNPin);
- Serial.println("Radio Pins Configured!");
+ ESP_LOGD(TAG, "Radio Pins Configured!");
ELECHOUSE_cc1101.Init();
ELECHOUSE_cc1101.setCCMode(0); // set config for internal transmission mode.
ELECHOUSE_cc1101.setMHZ(this->frequency); // Here you can set your basic frequency. The lib calculates the frequency automatically (default = 433.92).The cc1101 can: 300-348 MHZ, 387-464MHZ and 779-928MHZ. Read More info from datasheet.
@@ -5017,11 +4808,11 @@ void transceiver_config_t::apply() {
if (!ELECHOUSE_cc1101.getCC1101()) {
- Serial.println("Error setting up the radio");
+ ESP_LOGE(TAG, "Error setting up the radio");
this->radioInit = false;
}
else {
- Serial.println("Successfully set up the radio");
+ ESP_LOGI(TAG, "Successfully set up the radio");
somfy.transceiver.enableReceive();
this->radioInit = true;
}
@@ -5076,7 +4867,7 @@ void Transceiver::loop() {
for(uint8_t i = 0; i < SOMFY_MAX_REPEATERS; i++) {
if(somfy.repeaters[i] == frame.remoteAddress) {
tx_queue.push(&rx);
- Serial.println("Queued repeater frame...");
+ ESP_LOGD(TAG, "Queued repeater frame...");
break;
}
}
@@ -5090,12 +4881,15 @@ void Transceiver::loop() {
somfy_tx_t tx;
tx_queue.pop(&tx);
- Serial.printf("Sending frame %d - %d-BIT [", tx.hwsync, tx.bit_length);
- for(uint8_t j = 0; j < 10; j++) {
- Serial.print(tx.payload[j]);
- if(j < 9) Serial.print(", ");
+ {
+ char frame_buf[128];
+ int fpos = snprintf(frame_buf, sizeof(frame_buf), "Sending frame %d - %d-BIT [", tx.hwsync, tx.bit_length);
+ for(uint8_t j = 0; j < 10; j++) {
+ fpos += snprintf(frame_buf + fpos, sizeof(frame_buf) - fpos, "%d%s", tx.payload[j], j < 9 ? ", " : "");
+ }
+ snprintf(frame_buf + fpos, sizeof(frame_buf) - fpos, "]");
+ ESP_LOGD(TAG, "%s", frame_buf);
}
- Serial.println("]");
this->sendFrame(tx.payload, tx.hwsync, tx.bit_length);
tx_queue.delay_time = millis() + TX_QUEUE_DELAY;
diff --git a/Somfy.h b/src/Somfy.h
similarity index 97%
rename from Somfy.h
rename to src/Somfy.h
index bc5c99b..1ef4255 100644
--- a/Somfy.h
+++ b/src/Somfy.h
@@ -209,7 +209,6 @@ class SomfyRoom {
void clear();
bool save();
bool fromJSON(JsonObject &obj);
- void toJSON(JsonResponse &json);
void emitState(const char *evt = "roomState");
void emitState(uint8_t num, const char *evt = "roomState");
void publish();
@@ -239,7 +238,6 @@ class SomfyRemote {
uint8_t repeats = 1;
virtual bool isLastCommand(somfy_commands cmd);
char *getRemotePrefId() {return m_remotePrefId;}
- virtual void toJSON(JsonResponse &json);
virtual void setRemoteAddress(uint32_t address);
virtual uint32_t getRemoteAddress();
virtual uint16_t getNextRollingCode();
@@ -304,9 +302,7 @@ class SomfyShade : public SomfyRemote {
SomfyLinkedRemote linkedRemotes[SOMFY_MAX_LINKED_REMOTES];
bool paired = false;
int8_t validateJSON(JsonObject &obj);
- void toJSONRef(JsonResponse &json);
int8_t fromJSON(JsonObject &obj);
- void toJSON(JsonResponse &json) override;
char name[21] = "";
void setShadeId(uint8_t id) { shadeId = id; }
@@ -393,9 +389,6 @@ class SomfyGroup : public SomfyRemote {
void clear();
bool fromJSON(JsonObject &obj);
//bool toJSON(JsonObject &obj);
- void toJSON(JsonResponse &json);
- void toJSONRef(JsonResponse &json);
-
bool linkShade(uint8_t shadeId);
bool unlinkShade(uint8_t shadeId);
bool hasShadeId(uint8_t shadeId);
@@ -485,7 +478,6 @@ struct transceiver_config_t {
*/
void fromJSON(JsonObject& obj);
//void toJSON(JsonObject& obj);
- void toJSON(JsonResponse& json);
void save();
void load();
void apply();
@@ -500,7 +492,6 @@ class Transceiver {
transceiver_config_t config;
bool printBuffer = false;
//bool toJSON(JsonObject& obj);
- void toJSON(JsonResponse& json);
bool fromJSON(JsonObject& obj);
bool save();
bool begin();
@@ -557,10 +548,6 @@ class SomfyShadeController {
SomfyGroup groups[SOMFY_MAX_GROUPS];
bool linkRepeater(uint32_t address);
bool unlinkRepeater(uint32_t address);
- void toJSONShades(JsonResponse &json);
- void toJSONRooms(JsonResponse &json);
- void toJSONGroups(JsonResponse &json);
- void toJSONRepeaters(JsonResponse &json);
uint8_t repeaterCount();
uint8_t roomCount();
uint8_t shadeCount();
@@ -578,6 +565,7 @@ class SomfyShadeController {
void processWaitingFrame();
void commit();
void writeBackup();
+ String backupData;
bool loadShadesFile(const char *filename);
#ifdef USE_NVS
bool loadLegacy();
diff --git a/src/SomfyController.ino b/src/SomfyController.ino
new file mode 100644
index 0000000..976bb44
--- /dev/null
+++ b/src/SomfyController.ino
@@ -0,0 +1,141 @@
+#include "esp_log.h"
+#include
+#include
+#include
+#include "ConfigSettings.h"
+#include "ESPNetwork.h"
+#include "Web.h"
+#include "Sockets.h"
+#include "Utils.h"
+#include "Somfy.h"
+#include "MQTT.h"
+#include "GitOTA.h"
+#include "esp_core_dump.h"
+
+static const char *TAG = "Main";
+
+ConfigSettings settings;
+Web webServer;
+SocketEmitter sockEmit;
+ESPNetwork net;
+rebootDelay_t rebootDelay;
+SomfyShadeController somfy;
+MQTTClass mqtt;
+GitUpdater git;
+
+uint32_t oldheap = 0;
+
+void listDir(const char *dirname, uint8_t levels) {
+ ESP_LOGI(TAG, "Listing: %s", dirname);
+ File root = LittleFS.open(dirname);
+ if (!root || !root.isDirectory()) {
+ ESP_LOGE(TAG, "Failed to open directory");
+ return;
+ }
+ File file = root.openNextFile();
+ while (file) {
+ if (file.isDirectory()) {
+ ESP_LOGI(TAG, " DIR : %s", file.name());
+ if (levels) listDir(file.path(), levels - 1);
+ } else {
+ ESP_LOGI(TAG, " FILE: %-30s %d bytes", file.name(), file.size());
+ }
+ file = root.openNextFile();
+ }
+}
+
+void setup() {
+ Serial.begin(115200);
+ ESP_LOGI(TAG, "Startup/Boot....");
+ esp_core_dump_summary_t summary;
+ if (esp_core_dump_get_summary(&summary) == ESP_OK) {
+ ESP_LOGW(TAG, "*** Previous crash coredump found ***");
+ ESP_LOGW(TAG, " Task: %s", summary.exc_task);
+ ESP_LOGW(TAG, " PC: 0x%08x", summary.exc_pc);
+#ifdef CONFIG_IDF_TARGET_ARCH_XTENSA
+ ESP_LOGW(TAG, " Cause: %d", summary.ex_info.exc_cause);
+ char bt_buf[256] = {0};
+ int pos = 0;
+ for (int i = 0; i < summary.exc_bt_info.depth; i++) {
+ pos += snprintf(bt_buf + pos, sizeof(bt_buf) - pos, " 0x%08x", summary.exc_bt_info.bt[i]);
+ }
+ ESP_LOGW(TAG, " Backtrace:%s", bt_buf);
+#elif CONFIG_IDF_TARGET_ARCH_RISCV
+ ESP_LOGW(TAG, " Cause: %d", summary.ex_info.mcause);
+ ESP_LOGW(TAG, " MTVAL: 0x%08x RA: 0x%08x SP: 0x%08x",
+ summary.ex_info.mtval, summary.ex_info.ra, summary.ex_info.sp);
+#endif
+ }
+ ESP_LOGI(TAG, "Mounting File System...");
+
+
+ if (LittleFS.begin()) {
+ ESP_LOGI(TAG, "Total: %d bytes", LittleFS.totalBytes());
+ ESP_LOGI(TAG, "Used: %d bytes", LittleFS.usedBytes());
+ ESP_LOGI(TAG, "Free: %d bytes", LittleFS.totalBytes() - LittleFS.usedBytes());
+ listDir("/", 3);
+ } else {
+ ESP_LOGE(TAG, "LittleFS mount failed!");
+ }
+
+ if(LittleFS.begin()) ESP_LOGI(TAG, "File system mounted successfully");
+ else ESP_LOGE(TAG, "Error mounting file system");
+ settings.begin();
+ if(WiFi.status() == WL_CONNECTED) WiFi.disconnect(true);
+ delay(10);
+ webServer.startup();
+ webServer.begin();
+ delay(1000);
+ net.setup();
+ somfy.begin();
+ //git.checkForUpdate();
+#if ESP_ARDUINO_VERSION_MAJOR >= 3
+ const esp_task_wdt_config_t wdt_config = { .timeout_ms = 15000, .idle_core_mask = 1, .trigger_panic = true };
+ esp_task_wdt_init(&wdt_config);
+#else
+ esp_task_wdt_init(15, true); //enable panic so ESP32 restarts
+#endif
+ esp_task_wdt_add(NULL); //add current thread to WDT watch
+
+}
+
+void loop() {
+ // put your main code here, to run repeatedly:
+ //uint32_t heap = ESP.getFreeHeap();
+ if(rebootDelay.reboot && millis() > rebootDelay.rebootTime) {
+ ESP_LOGI(TAG, "Rebooting after %lums", rebootDelay.rebootTime);
+ net.end();
+ ESP.restart();
+ return;
+ }
+ uint32_t timing = millis();
+
+ net.loop();
+ if(millis() - timing > 100) ESP_LOGD(TAG, "Timing Net: %ldms", millis() - timing);
+ timing = millis();
+ esp_task_wdt_reset();
+ somfy.loop();
+ if(millis() - timing > 100) ESP_LOGD(TAG, "Timing Somfy: %ldms", millis() - timing);
+ timing = millis();
+ esp_task_wdt_reset();
+ if(net.connected() || net.softAPOpened) {
+ if(!rebootDelay.reboot && net.connected() && !net.softAPOpened) {
+ git.loop();
+ esp_task_wdt_reset();
+ }
+ webServer.loop();
+ esp_task_wdt_reset();
+ if(millis() - timing > 100) ESP_LOGD(TAG, "Timing WebServer: %ldms", millis() - timing);
+ esp_task_wdt_reset();
+ timing = millis();
+ sockEmit.loop();
+ if(millis() - timing > 100) ESP_LOGD(TAG, "Timing Socket: %ldms", millis() - timing);
+ esp_task_wdt_reset();
+ timing = millis();
+ }
+ if(rebootDelay.reboot && millis() > rebootDelay.rebootTime) {
+ net.end();
+ ESP.restart();
+ }
+ esp_task_wdt_reset();
+}
diff --git a/Utils.cpp b/src/Utils.cpp
similarity index 100%
rename from Utils.cpp
rename to src/Utils.cpp
diff --git a/Utils.h b/src/Utils.h
similarity index 100%
rename from Utils.h
rename to src/Utils.h
diff --git a/WResp.cpp b/src/WResp.cpp
similarity index 87%
rename from WResp.cpp
rename to src/WResp.cpp
index e44e3ba..c00c1c5 100644
--- a/WResp.cpp
+++ b/src/WResp.cpp
@@ -1,5 +1,8 @@
#include "WResp.h"
-void JsonSockEvent::beginEvent(WebSocketsServer *server, const char *evt, char *buff, size_t buffSize) {
+#include "esp_log.h"
+
+static const char *TAG = "WResp";
+void JsonSockEvent::beginEvent(AsyncWebSocket *server, const char *evt, char *buff, size_t buffSize) {
this->server = server;
this->buff = buff;
this->buffSize = buffSize;
@@ -15,17 +18,16 @@ void JsonSockEvent::closeEvent() {
this->_nocomma = true;
this->_closed = true;
}
-void JsonSockEvent::endEvent(uint8_t num) {
+void JsonSockEvent::endEvent(uint32_t clientId) {
this->closeEvent();
- if(num == 255) this->server->broadcastTXT(this->buff);
- else this->server->sendTXT(num, this->buff);
+ if(clientId == 0) this->server->textAll(this->buff);
+ else this->server->text(clientId, this->buff);
}
void JsonSockEvent::_safecat(const char *val, bool escape) {
size_t len = (escape ? this->calcEscapedLength(val) : strlen(val)) + strlen(this->buff);
if(escape) len += 2;
if(len >= this->buffSize) {
- Serial.printf("Socket exceeded buffer size %d - %d\n", this->buffSize, len);
- Serial.println(this->buff);
+ ESP_LOGE(TAG, "Socket exceeded buffer size %d - %d: %s", this->buffSize, len, this->buff);
return;
}
if(escape) strcat(this->buff, "\"");
@@ -33,30 +35,32 @@ void JsonSockEvent::_safecat(const char *val, bool escape) {
else strcat(this->buff, val);
if(escape) strcat(this->buff, "\"");
}
-void JsonResponse::beginResponse(WebServer *server, char *buff, size_t buffSize) {
- this->server = server;
+void AsyncJsonResp::beginResponse(AsyncWebServerRequest *request, char *buff, size_t buffSize) {
+ this->_request = request;
this->buff = buff;
this->buffSize = buffSize;
this->buff[0] = 0x00;
this->_nocomma = true;
- server->setContentLength(CONTENT_LENGTH_UNKNOWN);
+ this->_headersSent = false;
+ this->_stream = request->beginResponseStream("application/json");
}
-void JsonResponse::endResponse() {
- if(strlen(buff)) this->send();
- server->sendContent("", 0);
+void AsyncJsonResp::endResponse() {
+ if(strlen(this->buff)) this->flush();
+ if(this->_request && this->_stream) {
+ this->_request->send(this->_stream);
+ }
}
-void JsonResponse::send() {
- if(!this->_headersSent) server->send_P(200, "application/json", this->buff);
- else server->sendContent(this->buff);
- //Serial.printf("Sent %d bytes %d\n", strlen(this->buff), this->buffSize);
+void AsyncJsonResp::flush() {
+ if(this->_stream && strlen(this->buff) > 0) {
+ this->_stream->print(this->buff);
this->buff[0] = 0x00;
- this->_headersSent = true;
+ }
}
-void JsonResponse::_safecat(const char *val, bool escape) {
+void AsyncJsonResp::_safecat(const char *val, bool escape) {
size_t len = (escape ? this->calcEscapedLength(val) : strlen(val)) + strlen(this->buff);
if(escape) len += 2;
if(len >= this->buffSize) {
- this->send();
+ this->flush();
}
if(escape) strcat(this->buff, "\"");
if(escape) this->escapeString(val, &this->buff[strlen(this->buff)]);
@@ -130,8 +134,9 @@ void JsonFormatter::addElem(const char *name, uint32_t nval) { sprintf(this->_nu
void JsonFormatter::addElem(const char *name, int16_t nval) { sprintf(this->_numbuff, "%d", nval); this->_appendNumber(name); }
void JsonFormatter::addElem(const char *name, uint16_t nval) { sprintf(this->_numbuff, "%u", nval); this->_appendNumber(name); }
void JsonFormatter::addElem(const char *name, int64_t lval) { sprintf(this->_numbuff, "%lld", (long long)lval); this->_appendNumber(name); }
-void JsonFormatter::addElem(const char *name, uint64_t lval) { sprintf(this->_numbuff, "%llu", (unsigned long long)lval); this->_appendNumber(name); }
*/
+void JsonFormatter::addElem(const char *name, uint64_t lval) { sprintf(this->_numbuff, "%llu", (unsigned long long)lval); this->_appendNumber(name); }
+
void JsonFormatter::addElem(const char *name, bool bval) { strcpy(this->_numbuff, bval ? "true" : "false"); this->_appendNumber(name); }
void JsonFormatter::_safecat(const char *val, bool escape) {
diff --git a/WResp.h b/src/WResp.h
similarity index 79%
rename from WResp.h
rename to src/WResp.h
index 4bda5d5..7356189 100644
--- a/WResp.h
+++ b/src/WResp.h
@@ -1,5 +1,4 @@
-#include
-#include
+#include
#include "Somfy.h"
#ifndef wresp_h
#define wresp_h
@@ -51,24 +50,26 @@ class JsonFormatter {
void addElem(const char* name, uint32_t lval);
void addElem(const char* name, bool bval);
void addElem(const char *name, const char *val);
+ void addElem(const char* name, uint64_t lval);
};
-class JsonResponse : public JsonFormatter {
+class AsyncJsonResp : public JsonFormatter {
protected:
void _safecat(const char *val, bool escape = false) override;
+ AsyncWebServerRequest *_request = nullptr;
+ AsyncResponseStream *_stream = nullptr;
public:
- WebServer *server;
- void beginResponse(WebServer *server, char *buff, size_t buffSize);
+ void beginResponse(AsyncWebServerRequest *request, char *buff, size_t buffSize);
void endResponse();
- void send();
+ void flush();
};
class JsonSockEvent : public JsonFormatter {
protected:
bool _closed = false;
void _safecat(const char *val, bool escape = false) override;
public:
- WebSocketsServer *server = nullptr;
- void beginEvent(WebSocketsServer *server, const char *evt, char *buff, size_t buffSize);
- void endEvent(uint8_t clientNum = 255);
+ AsyncWebSocket *server = nullptr;
+ void beginEvent(AsyncWebSocket *server, const char *evt, char *buff, size_t buffSize);
+ void endEvent(uint32_t clientId = 0);
void closeEvent();
};
#endif
diff --git a/src/Web.cpp b/src/Web.cpp
new file mode 100644
index 0000000..907f40f
--- /dev/null
+++ b/src/Web.cpp
@@ -0,0 +1,2445 @@
+#include
+#include
+#include
+#include
+#include "esp_log.h"
+#include "mbedtls/md.h"
+#include "ConfigSettings.h"
+#include "ConfigFile.h"
+#include "Utils.h"
+#include "SSDP.h"
+#include "Somfy.h"
+#include "WResp.h"
+#include "Web.h"
+#include "MQTT.h"
+#include "GitOTA.h"
+#include "ESPNetwork.h"
+#include
+#include
+#include
+
+extern ConfigSettings settings;
+extern SSDPClass SSDP;
+extern rebootDelay_t rebootDelay;
+extern SomfyShadeController somfy;
+extern Web webServer;
+extern MQTTClass mqtt;
+extern GitUpdater git;
+extern ESPNetwork net;
+
+//#define WEB_MAX_RESPONSE 34768
+#define WEB_MAX_RESPONSE 4096
+static char g_async_content[WEB_MAX_RESPONSE];
+
+
+// General responses
+static const char _response_404[] = "404: Service Not Found";
+
+
+// Encodings
+static const char _encoding_text[] = "text/plain";
+static const char _encoding_html[] = "text/html";
+static const char _encoding_json[] = "application/json";
+
+static const char *TAG = "Web";
+
+static QueueHandle_t webCmdQueue = nullptr;
+static SemaphoreHandle_t webCmdDone = nullptr;
+
+AsyncWebServer asyncServer(80);
+AsyncWebServer asyncApiServer(8081);
+void Web::startup() {
+ ESP_LOGI(TAG, "Launching web server...");
+ if(!webCmdQueue) webCmdQueue = xQueueCreate(WEB_CMD_QUEUE_SIZE, sizeof(web_command_t));
+ if(!webCmdDone) webCmdDone = xSemaphoreCreateBinary();
+
+ asyncServer.on("/loginContext", HTTP_GET, [](AsyncWebServerRequest *request) {
+ AsyncJsonResponse *response = new AsyncJsonResponse();
+ JsonObject root = response->getRoot().to();
+ root["type"] = static_cast(settings.Security.type);
+ root["permissions"] = settings.Security.permissions;
+ root["serverId"] = settings.serverId;
+ root["version"] = settings.fwVersion.name;
+ root["model"] = "ESPSomfyRTS";
+ root["hostname"] = settings.hostname;
+ response->setLength();
+ request->send(response);
+ });
+ asyncApiServer.begin();
+ ESP_LOGI(TAG, "Async API server started on port 8081");
+}
+void Web::loop() {
+ this->processQueue();
+ delay(1);
+}
+bool Web::queueCommand(const web_command_t &cmd) {
+ if(!webCmdQueue || !webCmdDone) return false;
+ // Clear any stale signal
+ xSemaphoreTake(webCmdDone, 0);
+ if(xQueueSend(webCmdQueue, &cmd, pdMS_TO_TICKS(100)) != pdTRUE) {
+ ESP_LOGE(TAG, "Command queue full, dropping command");
+ return false;
+ }
+ // Wait for main loop to process it
+ if(xSemaphoreTake(webCmdDone, pdMS_TO_TICKS(WEB_CMD_TIMEOUT_MS)) != pdTRUE) {
+ ESP_LOGW(TAG, "Command queue timeout waiting for processing");
+ return false;
+ }
+ return true;
+}
+void Web::processQueue() {
+ if(!webCmdQueue || !webCmdDone) return;
+ web_command_t cmd;
+ while(xQueueReceive(webCmdQueue, &cmd, 0) == pdTRUE) {
+ switch(cmd.type) {
+ case web_cmd_t::shade_command: {
+ SomfyShade *shade = somfy.getShadeById(cmd.shadeId);
+ if(shade) {
+ if(cmd.target <= 100) shade->moveToTarget(shade->transformPosition(cmd.target));
+ else shade->sendCommand(cmd.command, cmd.repeat > 0 ? cmd.repeat : shade->repeats, cmd.stepSize);
+ }
+ break;
+ }
+ case web_cmd_t::group_command: {
+ SomfyGroup *group = somfy.getGroupById(cmd.groupId);
+ if(group) group->sendCommand(cmd.command, cmd.repeat >= 0 ? cmd.repeat : group->repeats, cmd.stepSize);
+ break;
+ }
+ case web_cmd_t::tilt_command: {
+ SomfyShade *shade = somfy.getShadeById(cmd.shadeId);
+ if(shade) {
+ if(cmd.target <= 100) shade->moveToTiltTarget(shade->transformPosition(cmd.target));
+ else shade->sendTiltCommand(cmd.command);
+ }
+ break;
+ }
+ case web_cmd_t::shade_repeat: {
+ SomfyShade *shade = somfy.getShadeById(cmd.shadeId);
+ if(shade) {
+ if(shade->shadeType == shade_types::garage1 && cmd.command == somfy_commands::Prog) cmd.command = somfy_commands::Toggle;
+ if(!shade->isLastCommand(cmd.command)) shade->sendCommand(cmd.command, cmd.repeat >= 0 ? cmd.repeat : shade->repeats, cmd.stepSize);
+ else shade->repeatFrame(cmd.repeat >= 0 ? cmd.repeat : shade->repeats);
+ }
+ break;
+ }
+ case web_cmd_t::group_repeat: {
+ SomfyGroup *group = somfy.getGroupById(cmd.groupId);
+ if(group) {
+ if(!group->isLastCommand(cmd.command)) group->sendCommand(cmd.command, cmd.repeat >= 0 ? cmd.repeat : group->repeats, cmd.stepSize);
+ else group->repeatFrame(cmd.repeat >= 0 ? cmd.repeat : group->repeats);
+ }
+ break;
+ }
+ case web_cmd_t::set_positions: {
+ SomfyShade *shade = somfy.getShadeById(cmd.shadeId);
+ if(shade) {
+ if(cmd.position >= 0) shade->target = shade->currentPos = cmd.position;
+ if(cmd.tiltPosition >= 0 && shade->tiltType != tilt_types::none) shade->tiltTarget = shade->currentTiltPos = cmd.tiltPosition;
+ shade->emitState();
+ }
+ break;
+ }
+ case web_cmd_t::shade_sensor: {
+ SomfyShade *shade = somfy.getShadeById(cmd.shadeId);
+ if(shade) {
+ shade->sendSensorCommand(cmd.windy, cmd.sunny, cmd.repeat >= 0 ? (uint8_t)cmd.repeat : shade->repeats);
+ shade->emitState();
+ }
+ break;
+ }
+ case web_cmd_t::group_sensor: {
+ SomfyGroup *group = somfy.getGroupById(cmd.groupId);
+ if(group) {
+ group->sendSensorCommand(cmd.windy, cmd.sunny, cmd.repeat >= 0 ? (uint8_t)cmd.repeat : group->repeats);
+ group->emitState();
+ }
+ break;
+ }
+ }
+ xSemaphoreGive(webCmdDone);
+ }
+}
+bool Web::isAuthenticated(AsyncWebServerRequest *request, bool cfg) {
+ ESP_LOGD(TAG, "Checking async authentication");
+ if(settings.Security.type == security_types::None) return true;
+ else if(!cfg && (settings.Security.permissions & static_cast(security_permissions::ConfigOnly)) == 0x01) return true;
+ else if(request->hasHeader("apikey")) {
+ ESP_LOGD(TAG, "Checking API Key...");
+ char token[65];
+ memset(token, 0x00, sizeof(token));
+ this->createAPIToken(request->client()->remoteIP(), token);
+ if(String(token) != request->getHeader("apikey")->value()) {
+ request->send(401, _encoding_text, "Unauthorized API Key");
+ return false;
+ }
+ // Key is valid
+ }
+ else {
+ ESP_LOGE(TAG, "Not authenticated...");
+ request->send(401, _encoding_text, "Unauthorized API Key");
+ return false;
+ }
+ return true;
+}
+bool Web::createAPIPinToken(const IPAddress ipAddress, const char *pin, char *token) {
+ return this->createAPIToken((String(pin) + ":" + ipAddress.toString()).c_str(), token);
+}
+bool Web::createAPIPasswordToken(const IPAddress ipAddress, const char *username, const char *password, char *token) {
+ return this->createAPIToken((String(username) + ":" + String(password) + ":" + ipAddress.toString()).c_str(), token);
+}
+bool Web::createAPIToken(const char *payload, char *token) {
+ byte hmacResult[32];
+ mbedtls_md_context_t ctx;
+ mbedtls_md_type_t md_type = MBEDTLS_MD_SHA256;
+ mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 1);
+ mbedtls_md_hmac_starts(&ctx, (const unsigned char *)settings.serverId, strlen(settings.serverId));
+ mbedtls_md_hmac_update(&ctx, (const unsigned char *)payload, strlen(payload));
+ mbedtls_md_hmac_finish(&ctx, hmacResult);
+ token[0] = '\0';
+ for(int i = 0; i < sizeof(hmacResult); i++){
+ char str[3];
+ sprintf(str, "%02x", (int)hmacResult[i]);
+ strcat(token, str);
+ }
+ ESP_LOGD(TAG, "Hash: %s", token);
+ return true;
+}
+bool Web::createAPIToken(const IPAddress ipAddress, char *token) {
+ String payload;
+ if(settings.Security.type == security_types::Password) createAPIPasswordToken(ipAddress, settings.Security.username, settings.Security.password, token);
+ else if(settings.Security.type == security_types::PinEntry) createAPIPinToken(ipAddress, settings.Security.pin, token);
+ else createAPIToken(ipAddress.toString().c_str(), token);
+ return true;
+}
+// =====================================================
+// Async API Handlers
+// =====================================================
+// Helper: get a query param as String, or empty if missing
+static String asyncParam(AsyncWebServerRequest *request, const char *name) {
+ if(request->hasParam(name)) return request->getParam(name)->value();
+ return String();
+}
+static bool asyncHasParam(AsyncWebServerRequest *request, const char *name) {
+ return request->hasParam(name);
+}
+
+// -- Serialization helpers (accept JsonFormatter& so both sync and async can use them) --
+static void serializeRoom(SomfyRoom *room, JsonFormatter &json) {
+ json.addElem("roomId", room->roomId);
+ json.addElem("name", room->name);
+ json.addElem("sortOrder", room->sortOrder);
+}
+static void serializeShadeRef(SomfyShade *shade, JsonFormatter &json) {
+ json.addElem("shadeId", shade->getShadeId());
+ json.addElem("roomId", shade->roomId);
+ json.addElem("name", shade->name);
+ json.addElem("remoteAddress", (uint32_t)shade->getRemoteAddress());
+ json.addElem("paired", shade->paired);
+ json.addElem("shadeType", static_cast(shade->shadeType));
+ json.addElem("flipCommands", shade->flipCommands);
+ json.addElem("flipPosition", shade->flipCommands);
+ json.addElem("bitLength", shade->bitLength);
+ json.addElem("proto", static_cast(shade->proto));
+ json.addElem("flags", shade->flags);
+ json.addElem("sunSensor", shade->hasSunSensor());
+ json.addElem("hasLight", shade->hasLight());
+ json.addElem("repeats", shade->repeats);
+}
+static void serializeShade(SomfyShade *shade, JsonFormatter &json) {
+ json.addElem("shadeId", shade->getShadeId());
+ json.addElem("roomId", shade->roomId);
+ json.addElem("name", shade->name);
+ json.addElem("remoteAddress", (uint32_t)shade->getRemoteAddress());
+ json.addElem("upTime", (uint32_t)shade->upTime);
+ json.addElem("downTime", (uint32_t)shade->downTime);
+ json.addElem("paired", shade->paired);
+ json.addElem("lastRollingCode", (uint32_t)shade->lastRollingCode);
+ json.addElem("position", shade->transformPosition(shade->currentPos));
+ json.addElem("tiltType", static_cast(shade->tiltType));
+ json.addElem("tiltPosition", shade->transformPosition(shade->currentTiltPos));
+ json.addElem("tiltDirection", shade->tiltDirection);
+ json.addElem("tiltTime", (uint32_t)shade->tiltTime);
+ json.addElem("stepSize", (uint32_t)shade->stepSize);
+ json.addElem("tiltTarget", shade->transformPosition(shade->tiltTarget));
+ json.addElem("target", shade->transformPosition(shade->target));
+ json.addElem("myPos", shade->transformPosition(shade->myPos));
+ json.addElem("myTiltPos", shade->transformPosition(shade->myTiltPos));
+ json.addElem("direction", shade->direction);
+ json.addElem("shadeType", static_cast(shade->shadeType));
+ json.addElem("bitLength", shade->bitLength);
+ json.addElem("proto", static_cast(shade->proto));
+ json.addElem("flags", shade->flags);
+ json.addElem("flipCommands", shade->flipCommands);
+ json.addElem("flipPosition", shade->flipPosition);
+ json.addElem("inGroup", shade->isInGroup());
+ json.addElem("sunSensor", shade->hasSunSensor());
+ json.addElem("light", shade->hasLight());
+ json.addElem("repeats", shade->repeats);
+ json.addElem("sortOrder", shade->sortOrder);
+ json.addElem("gpioUp", shade->gpioUp);
+ json.addElem("gpioDown", shade->gpioDown);
+ json.addElem("gpioMy", shade->gpioMy);
+ json.addElem("gpioLLTrigger", ((shade->gpioFlags & (uint8_t)gpio_flags_t::LowLevelTrigger) == 0) ? false : true);
+ json.addElem("simMy", shade->simMy());
+ json.beginArray("linkedRemotes");
+ for(uint8_t i = 0; i < SOMFY_MAX_LINKED_REMOTES; i++) {
+ SomfyLinkedRemote &lremote = shade->linkedRemotes[i];
+ if(lremote.getRemoteAddress() != 0) {
+ json.beginObject();
+ json.addElem("remoteAddress", (uint32_t)lremote.getRemoteAddress());
+ json.addElem("lastRollingCode", (uint32_t)lremote.lastRollingCode);
+ json.endObject();
+ }
+ }
+ json.endArray();
+}
+static void serializeGroupRef(SomfyGroup *group, JsonFormatter &json) {
+ group->updateFlags();
+ json.addElem("groupId", group->getGroupId());
+ json.addElem("roomId", group->roomId);
+ json.addElem("name", group->name);
+ json.addElem("remoteAddress", (uint32_t)group->getRemoteAddress());
+ json.addElem("lastRollingCode", (uint32_t)group->lastRollingCode);
+ json.addElem("bitLength", group->bitLength);
+ json.addElem("proto", static_cast(group->proto));
+ json.addElem("sunSensor", group->hasSunSensor());
+ json.addElem("flipCommands", group->flipCommands);
+ json.addElem("flags", group->flags);
+ json.addElem("repeats", group->repeats);
+ json.addElem("sortOrder", group->sortOrder);
+}
+static void serializeGroup(SomfyGroup *group, JsonFormatter &json) {
+ serializeGroupRef(group, json);
+ json.beginArray("linkedShades");
+ for(uint8_t i = 0; i < SOMFY_MAX_GROUPED_SHADES; i++) {
+ uint8_t shadeId = group->linkedShades[i];
+ if(shadeId > 0 && shadeId < 255) {
+ SomfyShade *shade = somfy.getShadeById(shadeId);
+ if(shade) {
+ json.beginObject();
+ serializeShadeRef(shade, json);
+ json.endObject();
+ }
+ }
+ }
+ json.endArray();
+}
+static void serializeRooms(JsonFormatter &json) {
+ for(uint8_t i = 0; i < SOMFY_MAX_ROOMS; i++) {
+ SomfyRoom *room = &somfy.rooms[i];
+ if(room->roomId != 0) {
+ json.beginObject();
+ serializeRoom(room, json);
+ json.endObject();
+ }
+ }
+}
+static void serializeShades(JsonFormatter &json) {
+ for(uint8_t i = 0; i < SOMFY_MAX_SHADES; i++) {
+ SomfyShade &shade = somfy.shades[i];
+ if(shade.getShadeId() != 255) {
+ json.beginObject();
+ serializeShade(&shade, json);
+ json.endObject();
+ }
+ }
+}
+static void serializeGroups(JsonFormatter &json) {
+ for(uint8_t i = 0; i < SOMFY_MAX_GROUPS; i++) {
+ SomfyGroup &group = somfy.groups[i];
+ if(group.getGroupId() != 255) {
+ json.beginObject();
+ serializeGroup(&group, json);
+ json.endObject();
+ }
+ }
+}
+static void serializeRepeaters(JsonFormatter &json) {
+ for(uint8_t i = 0; i < SOMFY_MAX_REPEATERS; i++) {
+ if(somfy.repeaters[i] != 0) json.addElem((uint32_t)somfy.repeaters[i]);
+ }
+}
+static void serializeTransceiverConfig(JsonFormatter &json) {
+ auto &cfg = somfy.transceiver.config;
+ json.addElem("type", cfg.type);
+ json.addElem("TXPin", cfg.TXPin);
+ json.addElem("RXPin", cfg.RXPin);
+ json.addElem("SCKPin", cfg.SCKPin);
+ json.addElem("MOSIPin", cfg.MOSIPin);
+ json.addElem("MISOPin", cfg.MISOPin);
+ json.addElem("CSNPin", cfg.CSNPin);
+ json.addElem("rxBandwidth", cfg.rxBandwidth);
+ json.addElem("frequency", cfg.frequency);
+ json.addElem("deviation", cfg.deviation);
+ json.addElem("txPower", cfg.txPower);
+ json.addElem("proto", static_cast(cfg.proto));
+ json.addElem("enabled", cfg.enabled);
+ json.addElem("radioInit", cfg.radioInit);
+}
+static void serializeAppVersion(JsonFormatter &json, appver_t &ver) {
+ json.addElem("name", ver.name);
+ json.addElem("major", ver.major);
+ json.addElem("minor", ver.minor);
+ json.addElem("build", ver.build);
+ json.addElem("suffix", ver.suffix);
+}
+static void serializeGitVersion(JsonFormatter &json) {
+ json.addElem("available", git.updateAvailable);
+ json.addElem("status", git.status);
+ json.addElem("error", (int32_t)git.error);
+ json.addElem("cancelled", git.cancelled);
+ json.addElem("checkForUpdate", settings.checkForUpdate);
+ json.addElem("inetAvailable", git.inetAvailable);
+ json.beginObject("fwVersion");
+ serializeAppVersion(json, settings.fwVersion);
+ json.endObject();
+ json.beginObject("appVersion");
+ serializeAppVersion(json, settings.appVersion);
+ json.endObject();
+ json.beginObject("latest");
+ serializeAppVersion(json, git.latest);
+ json.endObject();
+}
+static void serializeGitRelease(GitRelease *rel, JsonFormatter &json) {
+ Timestamp ts;
+ char buff[20];
+ sprintf(buff, "%llu", rel->id);
+ json.addElem("id", buff);
+ json.addElem("name", rel->name);
+ json.addElem("date", ts.getISOTime(rel->releaseDate));
+ json.addElem("draft", rel->draft);
+ json.addElem("preRelease", rel->preRelease);
+ json.addElem("main", rel->main);
+ json.addElem("hasFS", rel->hasFS);
+ json.addElem("hwVersions", rel->hwVersions);
+ json.beginObject("version");
+ serializeAppVersion(json, rel->version);
+ json.endObject();
+}
+
+// -- Async handler implementations --
+void Web::handleDiscovery(AsyncWebServerRequest *request) {
+ if(request->method() == HTTP_POST || request->method() == HTTP_GET) {
+ ESP_LOGD(TAG, "Async Discovery Requested");
+ char connType[10] = "Unknown";
+ if(net.connType == conn_types_t::ethernet) strcpy(connType, "Ethernet");
+ else if(net.connType == conn_types_t::wifi) strcpy(connType, "Wifi");
+ AsyncJsonResp resp;
+ resp.beginResponse(request, g_async_content, sizeof(g_async_content));
+ resp.beginObject();
+ resp.addElem("serverId", settings.serverId);
+ resp.addElem("version", settings.fwVersion.name);
+ resp.addElem("latest", git.latest.name);
+ resp.addElem("model", "ESPSomfyRTS");
+ resp.addElem("hostname", settings.hostname);
+ resp.addElem("authType", static_cast(settings.Security.type));
+ resp.addElem("permissions", settings.Security.permissions);
+ resp.addElem("chipModel", settings.chipModel);
+ resp.addElem("connType", connType);
+ resp.addElem("checkForUpdate", settings.checkForUpdate);
+ resp.beginObject("memory");
+ resp.addElem("max", ESP.getMaxAllocHeap());
+ resp.addElem("free", ESP.getFreeHeap());
+ resp.addElem("min", ESP.getMinFreeHeap());
+ resp.addElem("total", ESP.getHeapSize());
+ resp.addElem("uptime", (uint64_t)millis());
+ resp.endObject();
+ resp.beginArray("rooms");
+ serializeRooms(resp);
+ resp.endArray();
+ resp.beginArray("shades");
+ serializeShades(resp);
+ resp.endArray();
+ resp.beginArray("groups");
+ serializeGroups(resp);
+ resp.endArray();
+ resp.endObject();
+ resp.endResponse();
+ net.needsBroadcast = true;
+ }
+ else
+ request->send(500, _encoding_text, "Invalid http method");
+}
+void Web::handleGetRooms(AsyncWebServerRequest *request) {
+ if(!this->isAuthenticated(request)) return;
+ if(request->method() == HTTP_POST || request->method() == HTTP_GET) {
+ AsyncJsonResp resp;
+ resp.beginResponse(request, g_async_content, sizeof(g_async_content));
+ resp.beginArray();
+ serializeRooms(resp);
+ resp.endArray();
+ resp.endResponse();
+ }
+ else request->send(404, _encoding_text, _response_404);
+}
+void Web::handleGetShades(AsyncWebServerRequest *request) {
+ if(!this->isAuthenticated(request)) return;
+ if(request->method() == HTTP_POST || request->method() == HTTP_GET) {
+ AsyncJsonResp resp;
+ resp.beginResponse(request, g_async_content, sizeof(g_async_content));
+ resp.beginArray();
+ serializeShades(resp);
+ resp.endArray();
+ resp.endResponse();
+ }
+ else request->send(404, _encoding_text, _response_404);
+}
+void Web::handleGetGroups(AsyncWebServerRequest *request) {
+ if(!this->isAuthenticated(request)) return;
+ if(request->method() == HTTP_POST || request->method() == HTTP_GET) {
+ AsyncJsonResp resp;
+ resp.beginResponse(request, g_async_content, sizeof(g_async_content));
+ resp.beginArray();
+ serializeGroups(resp);
+ resp.endArray();
+ resp.endResponse();
+ }
+ else request->send(404, _encoding_text, _response_404);
+}
+void Web::handleController(AsyncWebServerRequest *request) {
+ if(!this->isAuthenticated(request)) return;
+ if(request->method() == HTTP_POST || request->method() == HTTP_GET) {
+ settings.printAvailHeap();
+ AsyncJsonResp resp;
+ resp.beginResponse(request, g_async_content, sizeof(g_async_content));
+ resp.beginObject();
+ resp.addElem("maxRooms", (uint8_t)SOMFY_MAX_ROOMS);
+ resp.addElem("maxShades", (uint8_t)SOMFY_MAX_SHADES);
+ resp.addElem("maxGroups", (uint8_t)SOMFY_MAX_GROUPS);
+ resp.addElem("maxGroupedShades", (uint8_t)SOMFY_MAX_GROUPED_SHADES);
+ resp.addElem("maxLinkedRemotes", (uint8_t)SOMFY_MAX_LINKED_REMOTES);
+ resp.addElem("startingAddress", (uint32_t)somfy.startingAddress);
+ resp.beginObject("transceiver");
+ resp.beginObject("config");
+ serializeTransceiverConfig(resp);
+ resp.endObject();
+ resp.endObject();
+ resp.beginObject("version");
+ serializeGitVersion(resp);
+ resp.endObject();
+ resp.beginArray("rooms");
+ serializeRooms(resp);
+ resp.endArray();
+ resp.beginArray("shades");
+ serializeShades(resp);
+ resp.endArray();
+ resp.beginArray("groups");
+ serializeGroups(resp);
+ resp.endArray();
+ resp.beginArray("repeaters");
+ serializeRepeaters(resp);
+ resp.endArray();
+ resp.endObject();
+ resp.endResponse();
+ }
+ else request->send(404, _encoding_text, _response_404);
+}
+void Web::handleLogin(AsyncWebServerRequest *request, JsonVariant &json) {
+ if(request->method() == HTTP_OPTIONS) { request->send(200); return; }
+ char token[65];
+ memset(&token, 0x00, sizeof(token));
+ this->createAPIToken(request->client()->remoteIP(), token);
+ if(settings.Security.type == security_types::None) {
+ snprintf(g_async_content, sizeof(g_async_content),
+ "{\"type\":%u,\"apiKey\":\"%s\",\"msg\":\"Success\",\"success\":true}",
+ static_cast(settings.Security.type), token);
+ request->send(200, _encoding_json, g_async_content);
+ return;
+ }
+ char username[33] = "";
+ char password[33] = "";
+ char pin[5] = "";
+ // Try query params first
+ if(asyncHasParam(request, "username")) strlcpy(username, asyncParam(request, "username").c_str(), sizeof(username));
+ if(asyncHasParam(request, "password")) strlcpy(password, asyncParam(request, "password").c_str(), sizeof(password));
+ if(asyncHasParam(request, "pin")) strlcpy(pin, asyncParam(request, "pin").c_str(), sizeof(pin));
+ // Override from JSON body if present
+ if(!json.isNull()) {
+ JsonObject obj = json.as();
+ if(!obj["username"].isNull()) strlcpy(username, obj["username"], sizeof(username));
+ if(!obj["password"].isNull()) strlcpy(password, obj["password"], sizeof(password));
+ if(!obj["pin"].isNull()) strlcpy(pin, obj["pin"], sizeof(pin));
+ }
+ bool success = false;
+ if(settings.Security.type == security_types::PinEntry) {
+ char ptok[65];
+ memset(ptok, 0x00, sizeof(ptok));
+ this->createAPIPinToken(request->client()->remoteIP(), pin, ptok);
+ if(String(ptok) == String(token)) success = true;
+ }
+ else if(settings.Security.type == security_types::Password) {
+ char ptok[65];
+ memset(ptok, 0x00, sizeof(ptok));
+ this->createAPIPasswordToken(request->client()->remoteIP(), username, password, ptok);
+ if(String(ptok) == String(token)) success = true;
+ }
+ if(success) {
+ snprintf(g_async_content, sizeof(g_async_content),
+ "{\"type\":%u,\"apiKey\":\"%s\",\"msg\":\"Success\",\"success\":true}",
+ static_cast(settings.Security.type), token);
+ request->send(200, _encoding_json, g_async_content);
+ }
+ else {
+ snprintf(g_async_content, sizeof(g_async_content),
+ "{\"type\":%u,\"msg\":\"Invalid credentials\",\"success\":false}",
+ static_cast(settings.Security.type));
+ request->send(401, _encoding_json, g_async_content);
+ }
+}
+void Web::handleShadeCommand(AsyncWebServerRequest *request, JsonVariant &json) {
+ if(request->method() == HTTP_OPTIONS) { request->send(200); return; }
+ if(!this->isAuthenticated(request)) return;
+ uint8_t shadeId = 255;
+ uint8_t target = 255;
+ uint8_t stepSize = 0;
+ int8_t repeat = -1;
+ somfy_commands command = somfy_commands::My;
+ // Try query params
+ if(asyncHasParam(request, "shadeId")) {
+ shadeId = asyncParam(request, "shadeId").toInt();
+ if(asyncHasParam(request, "command")) command = translateSomfyCommand(asyncParam(request, "command"));
+ else if(asyncHasParam(request, "target")) target = asyncParam(request, "target").toInt();
+ if(asyncHasParam(request, "repeat")) repeat = asyncParam(request, "repeat").toInt();
+ if(asyncHasParam(request, "stepSize")) stepSize = asyncParam(request, "stepSize").toInt();
+ }
+ else if(!json.isNull()) {
+ JsonObject obj = json.as();
+ if(!obj["shadeId"].isNull()) shadeId = obj["shadeId"];
+ else { request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No shade id was supplied.\"}")); return; }
+ if(!obj["command"].isNull()) { String scmd = obj["command"]; command = translateSomfyCommand(scmd); }
+ else if(!obj["target"].isNull()) target = obj["target"].as();
+ if(!obj["repeat"].isNull()) repeat = obj["repeat"].as();
+ if(!obj["stepSize"].isNull()) stepSize = obj["stepSize"].as();
+ }
+ else { request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No shade object supplied.\"}")); return; }
+ SomfyShade *shade = somfy.getShadeById(shadeId);
+ if(shade) {
+ ESP_LOGI(TAG, "handleShadeCommand shade=%u target=%u command=%s", shadeId, target, translateSomfyCommand(command).c_str());
+ web_command_t cmd = {};
+ cmd.type = web_cmd_t::shade_command;
+ cmd.shadeId = shadeId;
+ cmd.target = target;
+ cmd.command = command;
+ cmd.repeat = repeat;
+ cmd.stepSize = stepSize;
+ this->queueCommand(cmd);
+ AsyncJsonResp resp;
+ resp.beginResponse(request, g_async_content, sizeof(g_async_content));
+ resp.beginObject();
+ serializeShadeRef(shade, resp);
+ resp.endObject();
+ resp.endResponse();
+ }
+ else request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade with the specified id not found.\"}"));
+}
+void Web::handleGroupCommand(AsyncWebServerRequest *request, JsonVariant &json) {
+ if(request->method() == HTTP_OPTIONS) { request->send(200); return; }
+ if(!this->isAuthenticated(request)) return;
+ uint8_t groupId = 255;
+ uint8_t stepSize = 0;
+ int8_t repeat = -1;
+ somfy_commands command = somfy_commands::My;
+ if(asyncHasParam(request, "groupId")) {
+ groupId = asyncParam(request, "groupId").toInt();
+ if(asyncHasParam(request, "command")) command = translateSomfyCommand(asyncParam(request, "command"));
+ if(asyncHasParam(request, "repeat")) repeat = asyncParam(request, "repeat").toInt();
+ if(asyncHasParam(request, "stepSize")) stepSize = asyncParam(request, "stepSize").toInt();
+ }
+ else if(!json.isNull()) {
+ JsonObject obj = json.as();
+ if(!obj["groupId"].isNull()) groupId = obj["groupId"];
+ else { request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No group id was supplied.\"}")); return; }
+ if(!obj["command"].isNull()) { String scmd = obj["command"]; command = translateSomfyCommand(scmd); }
+ if(!obj["repeat"].isNull()) repeat = obj["repeat"].as();
+ if(!obj["stepSize"].isNull()) stepSize = obj["stepSize"].as();
+ }
+ else { request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No group object supplied.\"}")); return; }
+ SomfyGroup *group = somfy.getGroupById(groupId);
+ if(group) {
+ ESP_LOGI(TAG, "handleGroupCommand group=%u command=%s", groupId, translateSomfyCommand(command).c_str());
+ web_command_t cmd = {};
+ cmd.type = web_cmd_t::group_command;
+ cmd.groupId = groupId;
+ cmd.command = command;
+ cmd.repeat = repeat;
+ cmd.stepSize = stepSize;
+ this->queueCommand(cmd);
+ AsyncJsonResp resp;
+ resp.beginResponse(request, g_async_content, sizeof(g_async_content));
+ resp.beginObject();
+ serializeGroupRef(group, resp);
+ resp.endObject();
+ resp.endResponse();
+ }
+ else request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Group with the specified id not found.\"}"));
+}
+void Web::handleTiltCommand(AsyncWebServerRequest *request, JsonVariant &json) {
+ if(request->method() == HTTP_OPTIONS) { request->send(200); return; }
+ if(!this->isAuthenticated(request)) return;
+ uint8_t shadeId = 255;
+ uint8_t target = 255;
+ somfy_commands command = somfy_commands::My;
+ if(asyncHasParam(request, "shadeId")) {
+ shadeId = asyncParam(request, "shadeId").toInt();
+ if(asyncHasParam(request, "command")) command = translateSomfyCommand(asyncParam(request, "command"));
+ else if(asyncHasParam(request, "target")) target = asyncParam(request, "target").toInt();
+ }
+ else if(!json.isNull()) {
+ JsonObject obj = json.as();
+ if(!obj["shadeId"].isNull()) shadeId = obj["shadeId"];
+ else { request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No shade id was supplied.\"}")); return; }
+ if(!obj["command"].isNull()) { String scmd = obj["command"]; command = translateSomfyCommand(scmd); }
+ else if(!obj["target"].isNull()) target = obj["target"].as();
+ }
+ else { request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No shade object supplied.\"}")); return; }
+ SomfyShade *shade = somfy.getShadeById(shadeId);
+ if(shade) {
+ ESP_LOGI(TAG, "handleTiltCommand shade=%u target=%u command=%s", shadeId, target, translateSomfyCommand(command).c_str());
+ web_command_t cmd = {};
+ cmd.type = web_cmd_t::tilt_command;
+ cmd.shadeId = shadeId;
+ cmd.target = target;
+ cmd.command = command;
+ this->queueCommand(cmd);
+ AsyncJsonResp resp;
+ resp.beginResponse(request, g_async_content, sizeof(g_async_content));
+ resp.beginObject();
+ serializeShadeRef(shade, resp);
+ resp.endObject();
+ resp.endResponse();
+ }
+ else request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade with the specified id not found.\"}"));
+}
+void Web::handleRepeatCommand(AsyncWebServerRequest *request, JsonVariant &json) {
+ if(request->method() == HTTP_OPTIONS) { request->send(200); return; }
+ if(!this->isAuthenticated(request)) return;
+ uint8_t shadeId = 255;
+ uint8_t groupId = 255;
+ uint8_t stepSize = 0;
+ int8_t repeat = -1;
+ somfy_commands command = somfy_commands::My;
+ if(asyncHasParam(request, "shadeId")) shadeId = asyncParam(request, "shadeId").toInt();
+ else if(asyncHasParam(request, "groupId")) groupId = asyncParam(request, "groupId").toInt();
+ if(asyncHasParam(request, "command")) command = translateSomfyCommand(asyncParam(request, "command"));
+ if(asyncHasParam(request, "repeat")) repeat = asyncParam(request, "repeat").toInt();
+ if(asyncHasParam(request, "stepSize")) stepSize = asyncParam(request, "stepSize").toInt();
+ if(shadeId == 255 && groupId == 255 && !json.isNull()) {
+ JsonObject obj = json.as