From 37a94f8e0f8c577f5f19bec384cf6ff5c6316318 Mon Sep 17 00:00:00 2001 From: proddy Date: Sun, 10 May 2026 13:21:28 +0200 Subject: [PATCH 1/4] auto-formatting --- src/devices/connect.cpp | 8 ++++---- src/uart/emsuart_esp32.cpp | 2 +- src/web/WebSchedulerService.cpp | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/devices/connect.cpp b/src/devices/connect.cpp index 068a91342..1b87a695e 100644 --- a/src/devices/connect.cpp +++ b/src/devices/connect.cpp @@ -39,11 +39,11 @@ Connect::Connect(uint8_t device_type, uint8_t device_id, uint8_t product_id, con DeviceValueUOM::DEGREES); // Roomthermostats for (uint8_t i = 0; i < 16; i++) { - register_telegram_type(0x0BDD + i, "Room", false, MAKE_PF_CB(process_roomThermostat)); // broadcasted - register_telegram_type(0x0B3D + i, "Roomname", false, MAKE_PF_CB(process_roomThermostatName), 100); // fetch for active circuits + register_telegram_type(0x0BDD + i, "Room", false, MAKE_PF_CB(process_roomThermostat)); // broadcasted + register_telegram_type(0x0B3D + i, "Roomname", false, MAKE_PF_CB(process_roomThermostatName), 100); // fetch for active circuits register_telegram_type(0x0BB5 + i, "Roomsettings", false, MAKE_PF_CB(process_roomThermostatSettings), 8); // fetch for active circuits - register_telegram_type(0x1230 + i, "Roomparams", false, MAKE_PF_CB(process_roomThermostatParam)); // fetch for active circuits - register_telegram_type(0x1244 + i, "Roomdata", false, MAKE_PF_CB(process_roomThermostatData)); // broadcasted + register_telegram_type(0x1230 + i, "Roomparams", false, MAKE_PF_CB(process_roomThermostatParam)); // fetch for active circuits + register_telegram_type(0x1244 + i, "Roomdata", false, MAKE_PF_CB(process_roomThermostatData)); // broadcasted } register_telegram_type(0xDB65, "Roomschedule", true, MAKE_PF_CB(process_roomSchedule)); // 0x2040, broadcast 36 bytes: diff --git a/src/uart/emsuart_esp32.cpp b/src/uart/emsuart_esp32.cpp index 73da13640..8c22f8919 100644 --- a/src/uart/emsuart_esp32.cpp +++ b/src/uart/emsuart_esp32.cpp @@ -149,7 +149,7 @@ uint8_t EMSuart::transmit(const uint8_t * buf, const uint8_t len) { return EMS_TX_STATUS_OK; } - auto tx_mode = tx_mode_ != EMS_TXMODE_AUTO ? tx_mode_ : EMSbus::is_ht3() ? EMS_TXMODE_HT3 : EMSbus::is_ems2() ? EMS_TXMODE_EMSPLUS : EMS_TXMODE_EMS; + auto tx_mode = tx_mode_ != EMS_TXMODE_AUTO ? tx_mode_ : EMSbus::is_ht3() ? EMS_TXMODE_HT3 : EMSbus::is_ems2() ? EMS_TXMODE_EMSPLUS : EMS_TXMODE_EMS; // TXMODE is EMS+ with long delay if (tx_mode == EMS_TXMODE_EMSPLUS) { diff --git a/src/web/WebSchedulerService.cpp b/src/web/WebSchedulerService.cpp index e9a8f5351..3ab7900ee 100644 --- a/src/web/WebSchedulerService.cpp +++ b/src/web/WebSchedulerService.cpp @@ -352,8 +352,8 @@ bool WebSchedulerService::command(const char * name, const std::string & command // parse json JsonDocument doc; if (deserializeJson(doc, cmd) == DeserializationError::Ok) { - HTTPClient * http = new HTTPClient; - std::string url = doc["url"] | ""; + HTTPClient * http = new HTTPClient; + std::string url = doc["url"] | ""; // for a GET with parameters replace commands with values // don't search the complete url, it may contain a devicename in path auto q = url.find_first_of('?'); From b988c67c8ee1f71cfb0af81bfd28b3055292ebfa Mon Sep 17 00:00:00 2001 From: proddy Date: Sun, 10 May 2026 13:21:37 +0200 Subject: [PATCH 2/4] update example --- data/pre_load.json | 47 ++++++++++++++-------------------------------- 1 file changed, 14 insertions(+), 33 deletions(-) diff --git a/data/pre_load.json b/data/pre_load.json index cd5fdd2c7..45e8742d3 100644 --- a/data/pre_load.json +++ b/data/pre_load.json @@ -1,7 +1,6 @@ { "type": "systembackup", "version": "3.8.2", - "date": "2026-03-29T13:28:15", "systembackup": [ { "type": "settings", @@ -19,7 +18,7 @@ "tx_power": 0 }, "AP": { - "provision_mode": 2, + "provision_mode": 1, "ssid": "ems-esp", "password": "ems-esp-neo", "channel": 1, @@ -62,7 +61,7 @@ "send_response": false }, "NTP": { - "enabled": true, + "enabled": false, "server": "time.google.com", "tz_label": "Europe/Amsterdam", "tz_format": "CET-1CEST,M3.5.0,M10.5.0/3" @@ -83,12 +82,12 @@ ] }, "Settings": { - "version": "3.8.2", + "version": "3.8.2-dev.22", "board_profile": "E32V2_2", "platform": "ESP32", "locale": "en", - "tx_mode": 1, - "ems_bus_id": 11, + "tx_mode": 5, + "ems_bus_id": 73, "syslog_enabled": false, "syslog_level": 3, "trace_raw": false, @@ -132,17 +131,7 @@ "modbus_port": 502, "modbus_max_clients": 10, "modbus_timeout": 300, - "developer_mode": true, - "email_enabled": false, - "email_ssl": false, - "email_starttls": true, - "email_server": "smtp.example.net", - "email_port": 587, - "email_login": "", - "email_pass": "", - "email_sender": "ems-esp@example.net", - "email_recp": "", - "email_subject": "ems-esp notification" + "developer_mode": false } }, { @@ -207,22 +196,14 @@ } }, { - "type": "customSupport", - "Support": { - "html": [ - "This product is installed and managed by:", - "", - "Bosch Installer Example", - "", - "Nefit Road 12", - "1234 AB Amsterdam", - "Phone: +31 123 456 789", - "email: support@boschinstaller.nl", - "", - "For help and questions please contact your installer." - ], - "img_url": "https://emsesp.org/media/images/designer.png" - } + "type": "nvs", + "nvs": [ + { + "type": 1, + "key": "fresh_firmware", + "value": 0 + } + ] } ] } \ No newline at end of file From d1d046f3fd8ddb6bc1c849074052169abff9fd6b Mon Sep 17 00:00:00 2001 From: proddy Date: Sun, 10 May 2026 13:21:47 +0200 Subject: [PATCH 3/4] package update --- interface/package.json | 6 +-- interface/pnpm-lock.yaml | 108 +++++++++++++++++++-------------------- mock-api/package.json | 2 +- mock-api/pnpm-lock.yaml | 14 ++--- 4 files changed, 65 insertions(+), 65 deletions(-) diff --git a/interface/package.json b/interface/package.json index 10917ffaf..ae3955baa 100644 --- a/interface/package.json +++ b/interface/package.json @@ -52,7 +52,7 @@ "@preact/compat": "^18.3.2", "@preact/preset-vite": "^2.10.5", "@trivago/prettier-plugin-sort-imports": "^6.0.2", - "@types/node": "^25.6.0", + "@types/node": "^25.6.2", "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", "axe-core": "^4.11.4", @@ -61,11 +61,11 @@ "eslint-config-prettier": "^10.1.8", "prettier": "^3.8.3", "rollup-plugin-visualizer": "^7.0.1", - "terser": "^5.47.0", + "terser": "^5.47.1", "typescript-eslint": "^8.59.2", "vite": "^8.0.11", "vite-plugin-imagemin": "^0.6.1", "vite-tsconfig-paths": "^6.1.1" }, - "packageManager": "pnpm@10.33.4" + "packageManager": "pnpm@10.33.4+sha512.1c67b3b359b2d408119ba1ed289f34b8fc3c6873412bec6fd264fbdc82489e510fcbecb9ce9d22dae7f3b76269d8441046014bdca53b9979cd7a561ad631b800" } diff --git a/interface/pnpm-lock.yaml b/interface/pnpm-lock.yaml index 261281351..bf0a2bce5 100644 --- a/interface/pnpm-lock.yaml +++ b/interface/pnpm-lock.yaml @@ -83,13 +83,13 @@ importers: version: 10.0.1(eslint@10.3.0) '@preact/preset-vite': specifier: ^2.10.5 - version: 2.10.5(@babel/core@7.29.0)(preact@10.29.1)(rollup@4.59.0)(vite@8.0.11(@types/node@25.6.0)(esbuild@0.27.4)(terser@5.47.0)) + version: 2.10.5(@babel/core@7.29.0)(preact@10.29.1)(rollup@4.59.0)(vite@8.0.11(@types/node@25.6.2)(esbuild@0.27.4)(terser@5.47.1)) '@trivago/prettier-plugin-sort-imports': specifier: ^6.0.2 version: 6.0.2(prettier@3.8.3) '@types/node': - specifier: ^25.6.0 - version: 25.6.0 + specifier: ^25.6.2 + version: 25.6.2 '@types/react': specifier: ^19.2.14 version: 19.2.14 @@ -115,20 +115,20 @@ importers: specifier: ^7.0.1 version: 7.0.1(rolldown@1.0.0-rc.18)(rollup@4.59.0) terser: - specifier: ^5.47.0 - version: 5.47.0 + specifier: ^5.47.1 + version: 5.47.1 typescript-eslint: specifier: ^8.59.2 version: 8.59.2(eslint@10.3.0)(typescript@6.0.3) vite: specifier: ^8.0.11 - version: 8.0.11(@types/node@25.6.0)(esbuild@0.27.4)(terser@5.47.0) + version: 8.0.11(@types/node@25.6.2)(esbuild@0.27.4)(terser@5.47.1) vite-plugin-imagemin: specifier: ^0.6.1 - version: 0.6.1(vite@8.0.11(@types/node@25.6.0)(esbuild@0.27.4)(terser@5.47.0)) + version: 0.6.1(vite@8.0.11(@types/node@25.6.2)(esbuild@0.27.4)(terser@5.47.1)) vite-tsconfig-paths: specifier: ^6.1.1 - version: 6.1.1(typescript@6.0.3)(vite@8.0.11(@types/node@25.6.0)(esbuild@0.27.4)(terser@5.47.0)) + version: 6.1.1(typescript@6.0.3)(vite@8.0.11(@types/node@25.6.2)(esbuild@0.27.4)(terser@5.47.1)) packages: @@ -1015,8 +1015,8 @@ packages: resolution: {integrity: sha512-zmPitbQ8+6zNutpwgcQuLcsEpn/Cj54Kbn7L5pX0Os5kdWplB7xPgEh/g+SWOB/qmows2gpuCaPyduq8ZZRnxA==} deprecated: This is a stub types definition. minimatch provides its own type definitions, so you do not need this installed. - '@types/node@25.6.0': - resolution: {integrity: sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==} + '@types/node@25.6.2': + resolution: {integrity: sha512-sokuT28dxf9JT5Kady1fsXOvI4HVpjZa95NKT5y9PNTIrs2AsobR4GFAA90ZG8M+nxVRLysCXsVj6eGC7Vbrlw==} '@types/parse-json@4.0.2': resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} @@ -1191,8 +1191,8 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - baseline-browser-mapping@2.10.27: - resolution: {integrity: sha512-zEs/ufmZoUd7WftKpKyXaT6RFxpQ5Qm9xytKRHvJfxFV9DFJkZph9RvJ1LcOUi0Z1ZVijMte65JbILeV+8QQEA==} + baseline-browser-mapping@2.10.29: + resolution: {integrity: sha512-Asa2krT+XTPZINCS+2QcyS8WTkObE77RwkydwF7h6DmnKqbvlalz93m/dnphUyCa6SWSP51VgtEUf2FN+gelFQ==} engines: {node: '>=6.0.0'} hasBin: true @@ -1228,8 +1228,8 @@ packages: brace-expansion@2.1.0: resolution: {integrity: sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==} - brace-expansion@5.0.5: - resolution: {integrity: sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==} + brace-expansion@5.0.6: + resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==} engines: {node: 18 || 20 || >=22} braces@3.0.3: @@ -1526,8 +1526,8 @@ packages: duplexer3@0.1.5: resolution: {integrity: sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==} - electron-to-chromium@1.5.352: - resolution: {integrity: sha512-9wHk8x6dyuimoe18EdiDPWKExNdxYqo4fn4FwOVVper6RxT3cmpBwBkWWfSOCYJjQdIco/nPhJhNLmn4Ufg1Yg==} + electron-to-chromium@1.5.353: + resolution: {integrity: sha512-kOrWphBi8TOZyiJZqsgqIle0lw+tzmnQK83pV9dZUd01Nm2POECSyFQMAuarzZdYqQW7FH9RaYOuaRo3h+bQ3w==} emoji-regex@10.6.0: resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} @@ -1928,8 +1928,8 @@ packages: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} - get-east-asian-width@1.5.0: - resolution: {integrity: sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==} + get-east-asian-width@1.6.0: + resolution: {integrity: sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==} engines: {node: '>=18'} get-intrinsic@1.3.0: @@ -2950,8 +2950,8 @@ packages: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true - semver@7.7.4: - resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} + semver@7.8.0: + resolution: {integrity: sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==} engines: {node: '>=10'} hasBin: true @@ -3137,8 +3137,8 @@ packages: resolution: {integrity: sha512-ZOn6nJUgvgC09+doCEF3oB+r3ag7kUvlsXEGX069QRD60p+P3uP7XG9N2/at+EyIRGSN//ZY3LyEotA1YpmjuA==} engines: {node: '>=4'} - terser@5.47.0: - resolution: {integrity: sha512-TV+JFkQFtljk12ffyYAA4+zVF4Hs+qaROsT+Qo9o2/z39x+IUn+pvsmomiCPlp5YigfR1OdbGHOvc0L+Ca1X7g==} + terser@5.47.1: + resolution: {integrity: sha512-tPbLXTI6ohPASb/1YViL428oEHu6/qv1OxqYnfaonVCFHqx4+wCd95pHrQWsL5X4pl90CTyW9piSAsS2L0VoMw==} engines: {node: '>=10'} hasBin: true @@ -3915,19 +3915,19 @@ snapshots: dependencies: preact: 10.29.1 - '@preact/preset-vite@2.10.5(@babel/core@7.29.0)(preact@10.29.1)(rollup@4.59.0)(vite@8.0.11(@types/node@25.6.0)(esbuild@0.27.4)(terser@5.47.0))': + '@preact/preset-vite@2.10.5(@babel/core@7.29.0)(preact@10.29.1)(rollup@4.59.0)(vite@8.0.11(@types/node@25.6.2)(esbuild@0.27.4)(terser@5.47.1))': dependencies: '@babel/core': 7.29.0 '@babel/plugin-transform-react-jsx': 7.28.6(@babel/core@7.29.0) '@babel/plugin-transform-react-jsx-development': 7.27.1(@babel/core@7.29.0) - '@prefresh/vite': 2.4.12(preact@10.29.1)(vite@8.0.11(@types/node@25.6.0)(esbuild@0.27.4)(terser@5.47.0)) + '@prefresh/vite': 2.4.12(preact@10.29.1)(vite@8.0.11(@types/node@25.6.2)(esbuild@0.27.4)(terser@5.47.1)) '@rollup/pluginutils': 5.3.0(rollup@4.59.0) babel-plugin-transform-hook-names: 1.0.2(@babel/core@7.29.0) debug: 4.4.3 magic-string: 0.30.21 picocolors: 1.1.1 - vite: 8.0.11(@types/node@25.6.0)(esbuild@0.27.4)(terser@5.47.0) - vite-prerender-plugin: 0.5.13(vite@8.0.11(@types/node@25.6.0)(esbuild@0.27.4)(terser@5.47.0)) + vite: 8.0.11(@types/node@25.6.2)(esbuild@0.27.4)(terser@5.47.1) + vite-prerender-plugin: 0.5.13(vite@8.0.11(@types/node@25.6.2)(esbuild@0.27.4)(terser@5.47.1)) zimmerframe: 1.1.4 transitivePeerDependencies: - preact @@ -3942,7 +3942,7 @@ snapshots: '@prefresh/utils@1.2.1': {} - '@prefresh/vite@2.4.12(preact@10.29.1)(vite@8.0.11(@types/node@25.6.0)(esbuild@0.27.4)(terser@5.47.0))': + '@prefresh/vite@2.4.12(preact@10.29.1)(vite@8.0.11(@types/node@25.6.2)(esbuild@0.27.4)(terser@5.47.1))': dependencies: '@babel/core': 7.29.0 '@prefresh/babel-plugin': 0.5.3 @@ -3950,7 +3950,7 @@ snapshots: '@prefresh/utils': 1.2.1 '@rollup/pluginutils': 4.2.1 preact: 10.29.1 - vite: 8.0.11(@types/node@25.6.0)(esbuild@0.27.4)(terser@5.47.0) + vite: 8.0.11(@types/node@25.6.2)(esbuild@0.27.4)(terser@5.47.1) transitivePeerDependencies: - supports-color @@ -4133,7 +4133,7 @@ snapshots: '@types/glob@7.2.0': dependencies: '@types/minimatch': 6.0.0 - '@types/node': 25.6.0 + '@types/node': 25.6.2 '@types/imagemin-gifsicle@7.0.4': dependencies: @@ -4162,19 +4162,19 @@ snapshots: '@types/imagemin@7.0.1': dependencies: - '@types/node': 25.6.0 + '@types/node': 25.6.2 '@types/json-schema@7.0.15': {} '@types/keyv@3.1.4': dependencies: - '@types/node': 25.6.0 + '@types/node': 25.6.2 '@types/minimatch@6.0.0': dependencies: minimatch: 10.2.5 - '@types/node@25.6.0': + '@types/node@25.6.2': dependencies: undici-types: 7.19.2 @@ -4196,11 +4196,11 @@ snapshots: '@types/responselike@1.0.3': dependencies: - '@types/node': 25.6.0 + '@types/node': 25.6.2 '@types/svgo@2.6.4': dependencies: - '@types/node': 25.6.0 + '@types/node': 25.6.2 '@typescript-eslint/eslint-plugin@8.59.2(@typescript-eslint/parser@8.59.2(eslint@10.3.0)(typescript@6.0.3))(eslint@10.3.0)(typescript@6.0.3)': dependencies: @@ -4270,7 +4270,7 @@ snapshots: '@typescript-eslint/visitor-keys': 8.59.2 debug: 4.4.3 minimatch: 10.2.5 - semver: 7.7.4 + semver: 7.8.0 tinyglobby: 0.2.16 ts-api-utils: 2.5.0(typescript@6.0.3) typescript: 6.0.3 @@ -4361,7 +4361,7 @@ snapshots: base64-js@1.5.1: {} - baseline-browser-mapping@2.10.27: {} + baseline-browser-mapping@2.10.29: {} bin-build@3.0.0: dependencies: @@ -4412,7 +4412,7 @@ snapshots: dependencies: balanced-match: 1.0.2 - brace-expansion@5.0.5: + brace-expansion@5.0.6: dependencies: balanced-match: 4.0.4 @@ -4422,9 +4422,9 @@ snapshots: browserslist@4.28.2: dependencies: - baseline-browser-mapping: 2.10.27 + baseline-browser-mapping: 2.10.29 caniuse-lite: 1.0.30001792 - electron-to-chromium: 1.5.352 + electron-to-chromium: 1.5.353 node-releases: 2.0.38 update-browserslist-db: 1.2.3(browserslist@4.28.2) @@ -4788,7 +4788,7 @@ snapshots: duplexer3@0.1.5: {} - electron-to-chromium@1.5.352: {} + electron-to-chromium@1.5.353: {} emoji-regex@10.6.0: {} @@ -5193,7 +5193,7 @@ snapshots: get-caller-file@2.0.5: {} - get-east-asian-width@1.5.0: {} + get-east-asian-width@1.6.0: {} get-intrinsic@1.3.0: dependencies: @@ -5730,7 +5730,7 @@ snapshots: minimatch@10.2.5: dependencies: - brace-expansion: 5.0.5 + brace-expansion: 5.0.6 minimatch@3.1.5: dependencies: @@ -6191,7 +6191,7 @@ snapshots: semver@6.3.1: {} - semver@7.7.4: {} + semver@7.8.0: {} set-cookie-parser@2.7.2: {} @@ -6286,7 +6286,7 @@ snapshots: string-width@7.2.0: dependencies: emoji-regex: 10.6.0 - get-east-asian-width: 1.5.0 + get-east-asian-width: 1.6.0 strip-ansi: 7.2.0 string_decoder@1.1.1: @@ -6368,7 +6368,7 @@ snapshots: temp-dir: 1.0.0 uuid: 3.4.0 - terser@5.47.0: + terser@5.47.1: dependencies: '@jridgewell/source-map': 0.3.11 acorn: 8.16.0 @@ -6483,7 +6483,7 @@ snapshots: spdx-correct: 3.2.0 spdx-expression-parse: 3.0.1 - vite-plugin-imagemin@0.6.1(vite@8.0.11(@types/node@25.6.0)(esbuild@0.27.4)(terser@5.47.0)): + vite-plugin-imagemin@0.6.1(vite@8.0.11(@types/node@25.6.2)(esbuild@0.27.4)(terser@5.47.1)): dependencies: '@types/imagemin': 7.0.1 '@types/imagemin-gifsicle': 7.0.4 @@ -6508,11 +6508,11 @@ snapshots: imagemin-webp: 6.1.0 jpegtran-bin: 6.0.1 pathe: 0.2.0 - vite: 8.0.11(@types/node@25.6.0)(esbuild@0.27.4)(terser@5.47.0) + vite: 8.0.11(@types/node@25.6.2)(esbuild@0.27.4)(terser@5.47.1) transitivePeerDependencies: - supports-color - vite-prerender-plugin@0.5.13(vite@8.0.11(@types/node@25.6.0)(esbuild@0.27.4)(terser@5.47.0)): + vite-prerender-plugin@0.5.13(vite@8.0.11(@types/node@25.6.2)(esbuild@0.27.4)(terser@5.47.1)): dependencies: kolorist: 1.8.0 magic-string: 0.30.21 @@ -6520,19 +6520,19 @@ snapshots: simple-code-frame: 1.3.0 source-map: 0.7.6 stack-trace: 1.0.0 - vite: 8.0.11(@types/node@25.6.0)(esbuild@0.27.4)(terser@5.47.0) + vite: 8.0.11(@types/node@25.6.2)(esbuild@0.27.4)(terser@5.47.1) - vite-tsconfig-paths@6.1.1(typescript@6.0.3)(vite@8.0.11(@types/node@25.6.0)(esbuild@0.27.4)(terser@5.47.0)): + vite-tsconfig-paths@6.1.1(typescript@6.0.3)(vite@8.0.11(@types/node@25.6.2)(esbuild@0.27.4)(terser@5.47.1)): dependencies: debug: 4.4.3 globrex: 0.1.2 tsconfck: 3.1.6(typescript@6.0.3) - vite: 8.0.11(@types/node@25.6.0)(esbuild@0.27.4)(terser@5.47.0) + vite: 8.0.11(@types/node@25.6.2)(esbuild@0.27.4)(terser@5.47.1) transitivePeerDependencies: - supports-color - typescript - vite@8.0.11(@types/node@25.6.0)(esbuild@0.27.4)(terser@5.47.0): + vite@8.0.11(@types/node@25.6.2)(esbuild@0.27.4)(terser@5.47.1): dependencies: lightningcss: 1.32.0 picomatch: 4.0.4 @@ -6540,10 +6540,10 @@ snapshots: rolldown: 1.0.0-rc.18 tinyglobby: 0.2.16 optionalDependencies: - '@types/node': 25.6.0 + '@types/node': 25.6.2 esbuild: 0.27.4 fsevents: 2.3.3 - terser: 5.47.0 + terser: 5.47.1 which-typed-array@1.1.20: dependencies: diff --git a/mock-api/package.json b/mock-api/package.json index dea8d1846..2c65bf4e7 100644 --- a/mock-api/package.json +++ b/mock-api/package.json @@ -15,5 +15,5 @@ "itty-router": "^5.0.23", "prettier": "^3.8.3" }, - "packageManager": "pnpm@10.33.1+sha512.05ba3c1d5d1c18f68df06470d74055e62d41fc110a0c660db1b2dfb2785327f04cf0f68345d4609bc52089e7fa0343c31593b2f9594e2c5d5da426230acc9820" + "packageManager": "pnpm@10.33.4+sha512.1c67b3b359b2d408119ba1ed289f34b8fc3c6873412bec6fd264fbdc82489e510fcbecb9ce9d22dae7f3b76269d8441046014bdca53b9979cd7a561ad631b800" } diff --git a/mock-api/pnpm-lock.yaml b/mock-api/pnpm-lock.yaml index d1f155ffa..1ddedb1b1 100644 --- a/mock-api/pnpm-lock.yaml +++ b/mock-api/pnpm-lock.yaml @@ -46,8 +46,8 @@ packages: resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} engines: {node: '>=6.9.0'} - '@babel/parser@7.29.2': - resolution: {integrity: sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==} + '@babel/parser@7.29.3': + resolution: {integrity: sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==} engines: {node: '>=6.0.0'} hasBin: true @@ -185,7 +185,7 @@ snapshots: '@babel/generator@7.29.1': dependencies: - '@babel/parser': 7.29.2 + '@babel/parser': 7.29.3 '@babel/types': 7.29.0 '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 @@ -197,14 +197,14 @@ snapshots: '@babel/helper-validator-identifier@7.28.5': {} - '@babel/parser@7.29.2': + '@babel/parser@7.29.3': dependencies: '@babel/types': 7.29.0 '@babel/template@7.28.6': dependencies: '@babel/code-frame': 7.29.0 - '@babel/parser': 7.29.2 + '@babel/parser': 7.29.3 '@babel/types': 7.29.0 '@babel/traverse@7.29.0': @@ -212,7 +212,7 @@ snapshots: '@babel/code-frame': 7.29.0 '@babel/generator': 7.29.1 '@babel/helper-globals': 7.28.0 - '@babel/parser': 7.29.2 + '@babel/parser': 7.29.3 '@babel/template': 7.28.6 '@babel/types': 7.29.0 debug: 4.4.3 @@ -249,7 +249,7 @@ snapshots: '@trivago/prettier-plugin-sort-imports@6.0.2(prettier@3.8.3)': dependencies: '@babel/generator': 7.29.1 - '@babel/parser': 7.29.2 + '@babel/parser': 7.29.3 '@babel/traverse': 7.29.0 '@babel/types': 7.29.0 javascript-natural-sort: 0.7.1 From 7854349dbe539361bacd9ab7fe58f36abfd9483f Mon Sep 17 00:00:00 2001 From: proddy Date: Sun, 10 May 2026 13:22:00 +0200 Subject: [PATCH 4/4] support filesystem ota --- src/ESP32React/UploadFileService.cpp | 91 ++++++++++++++++++++-------- src/ESP32React/UploadFileService.h | 3 +- src/core/emsesp.cpp | 10 +++ src/core/system.cpp | 39 +++++++++++- src/core/system.h | 2 + src/core/telegram.h | 3 +- src/test/test.cpp | 41 +------------ src/test/test.h | 1 - 8 files changed, 120 insertions(+), 70 deletions(-) diff --git a/src/ESP32React/UploadFileService.cpp b/src/ESP32React/UploadFileService.cpp index 3d4e99152..4d0209d1f 100644 --- a/src/ESP32React/UploadFileService.cpp +++ b/src/ESP32React/UploadFileService.cpp @@ -4,6 +4,7 @@ #include #include +// #include static String getFilenameExtension(const String & filename) { const auto pos = filename.lastIndexOf('.'); @@ -16,8 +17,8 @@ static String getFilenameExtension(const String & filename) { UploadFileService::UploadFileService(AsyncWebServer * server, SecurityManager * securityManager) : _securityManager(securityManager) , _is_firmware(false) + , _is_filesystem(false) , _md5() { - // upload a file via a form server->on( UPLOAD_FILE_PATH, HTTP_POST, @@ -41,8 +42,14 @@ void UploadFileService::handleUpload(AsyncWebServerRequest * request, const Stri const String extension = getFilenameExtension(filename); const std::size_t filesize = request->contentLength(); - _is_firmware = false; - if ((extension == "bin") && (filesize > 1000000)) { + _is_firmware = false; + _is_filesystem = false; + + if (extension == "bin" && filename.endsWith("littlefs.bin")) { + // LittleFS filesystem image + _is_filesystem = true; + _md5[0] = '\0'; // clear any stale md5 so Update.end() doesn't compare against it + } else if ((extension == "bin") && (filesize > 2000000)) { _is_firmware = true; } else if (extension == "json") { _md5[0] = '\0'; // clear md5 @@ -88,6 +95,7 @@ void UploadFileService::handleUpload(AsyncWebServerRequest * request, const Stri #endif // it's firmware - initialize the ArduinoOTA updater emsesp::EMSESP::logger().info("Uploading firmware file %s (size: %d KB). Please wait...", filename.c_str(), filesize / 1024); + // turn off UART to prevent interference with the upload emsesp::EMSuart::stop(); @@ -96,28 +104,55 @@ void UploadFileService::handleUpload(AsyncWebServerRequest * request, const Stri Update.setMD5(_md5.data()); _md5.front() = '\0'; } - request->onDisconnect([this] { handleEarlyDisconnect(); }); // success, let's make sure we end the update if the client hangs up + request->onDisconnect([this] { handleDisconnect(); }); // success, let's make sure we end the update if the client hangs up } else { handleError(request, 507); // failed to begin, send an error response Insufficient Storage return; } + } else if (_is_filesystem) { + // LittleFS filesystem image - flash directly to the spiffs/littlefs partition + emsesp::EMSESP::logger().info("Uploading filesystem image %s (size: %u KB). Please wait...", filename.c_str(), static_cast(filesize / 1024)); + emsesp::EMSuart::stop(); + LittleFS.end(); // unmount LittleFS before we overwrite the partition under it + + // request->contentLength() is the multipart HTTP body size, not the file size, + // so it can exceed the partition by a few hundred bytes. Use UPDATE_SIZE_UNKNOWN + // and let the Update library size against the whole partition. + if (Update.begin(UPDATE_SIZE_UNKNOWN, U_SPIFFS)) { + // emsesp::EMSESP::logger().info("Update.begin(U_SPIFFS) ok, partition size %u bytes", static_cast(Update.size())); + request->onDisconnect([this] { handleDisconnect(); }); + } else { + emsesp::EMSESP::logger().err("Update.begin(U_SPIFFS) failed: %s", Update.errorString()); + handleError(request, 507); + return; + } } else { // its a normal file, open a new temp file to write the contents too request->_tempFile = LittleFS.open(TEMP_FILENAME_PATH, "w"); } } - if (!_is_firmware) { - if (len && len != request->_tempFile.write(data, len)) { // stream the incoming chunk to the opened file - handleError(request, 507); // 507-Insufficient Storage - } - } else if (!request->_tempObject) { // if we haven't delt with an error, continue with the firmware update - if (Update.write(data, len) != len) { - handleError(request, 500); // internal error, failed - return; - } - if (final && !Update.end(true)) { - handleError(request, 500); // internal error, failed + if (_is_firmware || _is_filesystem) { + if (!request->_tempObject) { // if we haven't delt with an error, continue with the OTA update + if (Update.write(data, len) != len) { + emsesp::EMSESP::logger().err("Update.write failed at offset %u (chunk %u): %s", + static_cast(Update.progress()), + static_cast(len), + Update.errorString()); + handleError(request, 500); // internal error, failed + return; + } + if (final) { + if (!Update.end(true)) { + emsesp::EMSESP::logger().err("Update.end failed: %s", Update.errorString()); + handleError(request, 500); + return; + } + } + } else { + if (len && len != request->_tempFile.write(data, len)) { // stream the incoming chunk to the opened file + handleError(request, 507); // 507-Insufficient Storage + } } } } @@ -135,11 +170,13 @@ void UploadFileService::uploadComplete(AsyncWebServerRequest * request) { return; } - // check if it was a firmware upgrade - // if no error, send the success response as a JSON - if (_is_firmware && !request->_tempObject) { - // set NVS to tell EMS-ESP this is a new fresh firmware on next restart - emsesp::EMSESP::nvs_.putBool(emsesp::EMSESP_NVS_BOOT_NEW_FIRMWARE, true); + // check if it was a firmware or filesystem image upgrade + // if no error, send the success response and request a restart + if ((_is_firmware || _is_filesystem) && !request->_tempObject) { + if (_is_firmware) { + // set NVS to tell EMS-ESP this is a new fresh firmware on next restart + emsesp::EMSESP::nvs_.putBool(emsesp::EMSESP_NVS_BOOT_NEW_FIRMWARE, true); + } AsyncWebServerResponse * response = request->beginResponse(200); request->send(response); @@ -178,15 +215,21 @@ void UploadFileService::handleError(AsyncWebServerRequest * request, int code) { // that is caught by the web code. Unfortunately the http error code is not sent to the client on fast network connections if (code == 406) { request->client()->close(); - _is_firmware = false; + _is_firmware = false; + _is_filesystem = false; Update.abort(); } + + // if we aborted a filesystem upload, remount LittleFS so the device keeps working + if (_is_filesystem) { + LittleFS.begin(); + } } -void UploadFileService::handleEarlyDisconnect() { +void UploadFileService::handleDisconnect() { emsesp::EMSESP::logger().info("Upload finished"); emsesp::EMSESP::system_.uart_init(); // re-enable UART - _is_firmware = false; - Update.abort(); + _is_firmware = false; + _is_filesystem = false; } diff --git a/src/ESP32React/UploadFileService.h b/src/ESP32React/UploadFileService.h index 352342148..4c8a20dbb 100644 --- a/src/ESP32React/UploadFileService.h +++ b/src/ESP32React/UploadFileService.h @@ -22,13 +22,14 @@ class UploadFileService { private: SecurityManager * _securityManager; bool _is_firmware; + bool _is_filesystem; std::array _md5; void handleUpload(AsyncWebServerRequest * request, const String & filename, size_t index, uint8_t * data, size_t len, bool final); void uploadComplete(AsyncWebServerRequest * request); void handleError(AsyncWebServerRequest * request, int code); - void handleEarlyDisconnect(); + void handleDisconnect(); }; #endif \ No newline at end of file diff --git a/src/core/emsesp.cpp b/src/core/emsesp.cpp index 9d583e427..dbbc79b2f 100644 --- a/src/core/emsesp.cpp +++ b/src/core/emsesp.cpp @@ -1710,6 +1710,11 @@ void EMSESP::start() { bool factory_settings = false; #endif +#if defined(EMSESP_DEBUG) + // LOG_DEBUG("Listing root directory before:"); + // system_.listDir("/", 3); // show the contents of the root directory +#endif + // start NVS storage if (!nvs_.begin("ems-esp", false, "nvs1")) { // try bigger nvs partition on 16M flash first nvs_.begin("ems-esp", false, "nvs"); // fallback to small nvs @@ -1724,6 +1729,11 @@ void EMSESP::start() { // loads core system services settings (network, mqtt, ap, ntp etc) esp32React.begin(); +#if defined(EMSESP_DEBUG) + // LOG_DEBUG("Listing root directory before:"); + // system_.listDir("/", 3); // show the contents of the root directory +#endif + #ifndef EMSESP_STANDALONE if (factory_settings) { LOG_WARNING("No settings found on filesystem. Using factory settings."); diff --git a/src/core/system.cpp b/src/core/system.cpp index 3a04d1028..f27ba427a 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -642,11 +642,9 @@ void System::start() { void System::button_OnClick(PButton & b) { LOG_NOTICE("Button pressed - single click"); -#if defined(EMSESP_TEST) #ifndef EMSESP_STANDALONE // show filesystem - Test::listDir(LittleFS, "/", 3); -#endif + listDir("/", 3); #endif } @@ -3435,4 +3433,39 @@ void System::restore_snapshot_gpios(std::vector & u_gpios, std::vector