diff --git a/CHANGELOG_LATEST.md b/CHANGELOG_LATEST.md
index e58094e3d..3b228373f 100644
--- a/CHANGELOG_LATEST.md
+++ b/CHANGELOG_LATEST.md
@@ -49,6 +49,7 @@ For more details go to [docs.emsesp.org](https://docs.emsesp.org/).
- ventilation bypass state from telegram 0x55C [#1197](https://github.com/emsesp/EMS-ESP32/issues/1197)
- set selflowtemp for ems+ boilers [#2641](https://github.com/emsesp/EMS-ESP32/discussions/2641)
- syslog timestamp [#2704](https://github.com/emsesp/EMS-ESP32/issues/2704)
+- fixed FS format command [#2720](https://github.com/emsesp/EMS-ESP32/discussions/2720)
## Changed
@@ -57,4 +58,7 @@ For more details go to [docs.emsesp.org](https://docs.emsesp.org/).
- updated core libraries like AsyncTCP, AsyncWebServer and Modbus
- remove command `scan deep`
- ignore repeated `forceheatingoff` commands [#2641](https://github.com/emsesp/EMS-ESP32/discussions/2641)
-- optimized web for performance
+- optimized web for better performance by adding lazy loading and caching
+- internal system analog sensors (core_voltage, supply_voltage and gateway_temperature) cannot be accidentally removed
+- double click button reconnects EMS-ESP to AP
+- place system message command in side scheduler loop to reduce stack memory usage by 2KB
diff --git a/interface/package.json b/interface/package.json
index 5ddce35a0..cc5c7e633 100644
--- a/interface/package.json
+++ b/interface/package.json
@@ -63,7 +63,7 @@
"rollup-plugin-visualizer": "^6.0.5",
"terser": "^5.44.1",
"typescript-eslint": "^8.46.3",
- "vite": "^7.2.0",
+ "vite": "^7.2.2",
"vite-plugin-imagemin": "^0.6.1",
"vite-tsconfig-paths": "^5.1.4"
},
diff --git a/interface/pnpm-lock.yaml b/interface/pnpm-lock.yaml
index c177c8343..6a19b3e35 100644
--- a/interface/pnpm-lock.yaml
+++ b/interface/pnpm-lock.yaml
@@ -83,7 +83,7 @@ importers:
version: 9.39.1
'@preact/preset-vite':
specifier: ^2.10.2
- version: 2.10.2(@babel/core@7.28.5)(preact@10.27.2)(vite@7.2.0(@types/node@24.10.0)(terser@5.44.1))
+ version: 2.10.2(@babel/core@7.28.5)(preact@10.27.2)(vite@7.2.2(@types/node@24.10.0)(terser@5.44.1))
'@trivago/prettier-plugin-sort-imports':
specifier: ^6.0.0
version: 6.0.0(prettier@3.6.2)
@@ -113,7 +113,7 @@ importers:
version: 3.6.2
rollup-plugin-visualizer:
specifier: ^6.0.5
- version: 6.0.5(rollup@4.52.5)
+ version: 6.0.5(rollup@4.53.1)
terser:
specifier: ^5.44.1
version: 5.44.1
@@ -121,14 +121,14 @@ importers:
specifier: ^8.46.3
version: 8.46.3(eslint@9.39.1)(typescript@5.9.3)
vite:
- specifier: ^7.2.0
- version: 7.2.0(@types/node@24.10.0)(terser@5.44.1)
+ specifier: ^7.2.2
+ version: 7.2.2(@types/node@24.10.0)(terser@5.44.1)
vite-plugin-imagemin:
specifier: ^0.6.1
- version: 0.6.1(vite@7.2.0(@types/node@24.10.0)(terser@5.44.1))
+ version: 0.6.1(vite@7.2.2(@types/node@24.10.0)(terser@5.44.1))
vite-tsconfig-paths:
specifier: ^5.1.4
- version: 5.1.4(typescript@5.9.3)(vite@7.2.0(@types/node@24.10.0)(terser@5.44.1))
+ version: 5.1.4(typescript@5.9.3)(vite@7.2.2(@types/node@24.10.0)(terser@5.44.1))
packages:
@@ -679,113 +679,113 @@ packages:
resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==}
engines: {node: '>= 8.0.0'}
- '@rollup/rollup-android-arm-eabi@4.52.5':
- resolution: {integrity: sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ==}
+ '@rollup/rollup-android-arm-eabi@4.53.1':
+ resolution: {integrity: sha512-bxZtughE4VNVJlL1RdoSE545kc4JxL7op57KKoi59/gwuU5rV6jLWFXXc8jwgFoT6vtj+ZjO+Z2C5nrY0Cl6wA==}
cpu: [arm]
os: [android]
- '@rollup/rollup-android-arm64@4.52.5':
- resolution: {integrity: sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA==}
+ '@rollup/rollup-android-arm64@4.53.1':
+ resolution: {integrity: sha512-44a1hreb02cAAfAKmZfXVercPFaDjqXCK+iKeVOlJ9ltvnO6QqsBHgKVPTu+MJHSLLeMEUbeG2qiDYgbFPU48g==}
cpu: [arm64]
os: [android]
- '@rollup/rollup-darwin-arm64@4.52.5':
- resolution: {integrity: sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA==}
+ '@rollup/rollup-darwin-arm64@4.53.1':
+ resolution: {integrity: sha512-usmzIgD0rf1syoOZ2WZvy8YpXK5G1V3btm3QZddoGSa6mOgfXWkkv+642bfUUldomgrbiLQGrPryb7DXLovPWQ==}
cpu: [arm64]
os: [darwin]
- '@rollup/rollup-darwin-x64@4.52.5':
- resolution: {integrity: sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA==}
+ '@rollup/rollup-darwin-x64@4.53.1':
+ resolution: {integrity: sha512-is3r/k4vig2Gt8mKtTlzzyaSQ+hd87kDxiN3uDSDwggJLUV56Umli6OoL+/YZa/KvtdrdyNfMKHzL/P4siOOmg==}
cpu: [x64]
os: [darwin]
- '@rollup/rollup-freebsd-arm64@4.52.5':
- resolution: {integrity: sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA==}
+ '@rollup/rollup-freebsd-arm64@4.53.1':
+ resolution: {integrity: sha512-QJ1ksgp/bDJkZB4daldVmHaEQkG4r8PUXitCOC2WRmRaSaHx5RwPoI3DHVfXKwDkB+Sk6auFI/+JHacTekPRSw==}
cpu: [arm64]
os: [freebsd]
- '@rollup/rollup-freebsd-x64@4.52.5':
- resolution: {integrity: sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ==}
+ '@rollup/rollup-freebsd-x64@4.53.1':
+ resolution: {integrity: sha512-J6ma5xgAzvqsnU6a0+jgGX/gvoGokqpkx6zY4cWizRrm0ffhHDpJKQgC8dtDb3+MqfZDIqs64REbfHDMzxLMqQ==}
cpu: [x64]
os: [freebsd]
- '@rollup/rollup-linux-arm-gnueabihf@4.52.5':
- resolution: {integrity: sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ==}
+ '@rollup/rollup-linux-arm-gnueabihf@4.53.1':
+ resolution: {integrity: sha512-JzWRR41o2U3/KMNKRuZNsDUAcAVUYhsPuMlx5RUldw0E4lvSIXFUwejtYz1HJXohUmqs/M6BBJAUBzKXZVddbg==}
cpu: [arm]
os: [linux]
- '@rollup/rollup-linux-arm-musleabihf@4.52.5':
- resolution: {integrity: sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ==}
+ '@rollup/rollup-linux-arm-musleabihf@4.53.1':
+ resolution: {integrity: sha512-L8kRIrnfMrEoHLHtHn+4uYA52fiLDEDyezgxZtGUTiII/yb04Krq+vk3P2Try+Vya9LeCE9ZHU8CXD6J9EhzHQ==}
cpu: [arm]
os: [linux]
- '@rollup/rollup-linux-arm64-gnu@4.52.5':
- resolution: {integrity: sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg==}
+ '@rollup/rollup-linux-arm64-gnu@4.53.1':
+ resolution: {integrity: sha512-ysAc0MFRV+WtQ8li8hi3EoFi7us6d1UzaS/+Dp7FYZfg3NdDljGMoVyiIp6Ucz7uhlYDBZ/zt6XI0YEZbUO11Q==}
cpu: [arm64]
os: [linux]
- '@rollup/rollup-linux-arm64-musl@4.52.5':
- resolution: {integrity: sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q==}
+ '@rollup/rollup-linux-arm64-musl@4.53.1':
+ resolution: {integrity: sha512-UV6l9MJpDbDZZ/fJvqNcvO1PcivGEf1AvKuTcHoLjVZVFeAMygnamCTDikCVMRnA+qJe+B3pSbgX2+lBMqgBhA==}
cpu: [arm64]
os: [linux]
- '@rollup/rollup-linux-loong64-gnu@4.52.5':
- resolution: {integrity: sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA==}
+ '@rollup/rollup-linux-loong64-gnu@4.53.1':
+ resolution: {integrity: sha512-UDUtelEprkA85g95Q+nj3Xf0M4hHa4DiJ+3P3h4BuGliY4NReYYqwlc0Y8ICLjN4+uIgCEvaygYlpf0hUj90Yg==}
cpu: [loong64]
os: [linux]
- '@rollup/rollup-linux-ppc64-gnu@4.52.5':
- resolution: {integrity: sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw==}
+ '@rollup/rollup-linux-ppc64-gnu@4.53.1':
+ resolution: {integrity: sha512-vrRn+BYhEtNOte/zbc2wAUQReJXxEx2URfTol6OEfY2zFEUK92pkFBSXRylDM7aHi+YqEPJt9/ABYzmcrS4SgQ==}
cpu: [ppc64]
os: [linux]
- '@rollup/rollup-linux-riscv64-gnu@4.52.5':
- resolution: {integrity: sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw==}
+ '@rollup/rollup-linux-riscv64-gnu@4.53.1':
+ resolution: {integrity: sha512-gto/1CxHyi4A7YqZZNznQYrVlPSaodOBPKM+6xcDSCMVZN/Fzb4K+AIkNz/1yAYz9h3Ng+e2fY9H6bgawVq17w==}
cpu: [riscv64]
os: [linux]
- '@rollup/rollup-linux-riscv64-musl@4.52.5':
- resolution: {integrity: sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg==}
+ '@rollup/rollup-linux-riscv64-musl@4.53.1':
+ resolution: {integrity: sha512-KZ6Vx7jAw3aLNjFR8eYVcQVdFa/cvBzDNRFM3z7XhNNunWjA03eUrEwJYPk0G8V7Gs08IThFKcAPS4WY/ybIrQ==}
cpu: [riscv64]
os: [linux]
- '@rollup/rollup-linux-s390x-gnu@4.52.5':
- resolution: {integrity: sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ==}
+ '@rollup/rollup-linux-s390x-gnu@4.53.1':
+ resolution: {integrity: sha512-HvEixy2s/rWNgpwyKpXJcHmE7om1M89hxBTBi9Fs6zVuLU4gOrEMQNbNsN/tBVIMbLyysz/iwNiGtMOpLAOlvA==}
cpu: [s390x]
os: [linux]
- '@rollup/rollup-linux-x64-gnu@4.52.5':
- resolution: {integrity: sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q==}
+ '@rollup/rollup-linux-x64-gnu@4.53.1':
+ resolution: {integrity: sha512-E/n8x2MSjAQgjj9IixO4UeEUeqXLtiA7pyoXCFYLuXpBA/t2hnbIdxHfA7kK9BFsYAoNU4st1rHYdldl8dTqGA==}
cpu: [x64]
os: [linux]
- '@rollup/rollup-linux-x64-musl@4.52.5':
- resolution: {integrity: sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg==}
+ '@rollup/rollup-linux-x64-musl@4.53.1':
+ resolution: {integrity: sha512-IhJ087PbLOQXCN6Ui/3FUkI9pWNZe/Z7rEIVOzMsOs1/HSAECCvSZ7PkIbkNqL/AZn6WbZvnoVZw/qwqYMo4/w==}
cpu: [x64]
os: [linux]
- '@rollup/rollup-openharmony-arm64@4.52.5':
- resolution: {integrity: sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw==}
+ '@rollup/rollup-openharmony-arm64@4.53.1':
+ resolution: {integrity: sha512-0++oPNgLJHBblreu0SFM7b3mAsBJBTY0Ksrmu9N6ZVrPiTkRgda52mWR7TKhHAsUb9noCjFvAw9l6ZO1yzaVbA==}
cpu: [arm64]
os: [openharmony]
- '@rollup/rollup-win32-arm64-msvc@4.52.5':
- resolution: {integrity: sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w==}
+ '@rollup/rollup-win32-arm64-msvc@4.53.1':
+ resolution: {integrity: sha512-VJXivz61c5uVdbmitLkDlbcTk9Or43YC2QVLRkqp86QoeFSqI81bNgjhttqhKNMKnQMWnecOCm7lZz4s+WLGpQ==}
cpu: [arm64]
os: [win32]
- '@rollup/rollup-win32-ia32-msvc@4.52.5':
- resolution: {integrity: sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg==}
+ '@rollup/rollup-win32-ia32-msvc@4.53.1':
+ resolution: {integrity: sha512-NmZPVTUOitCXUH6erJDzTQ/jotYw4CnkMDjCYRxNHVD9bNyfrGoIse684F9okwzKCV4AIHRbUkeTBc9F2OOH5Q==}
cpu: [ia32]
os: [win32]
- '@rollup/rollup-win32-x64-gnu@4.52.5':
- resolution: {integrity: sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ==}
+ '@rollup/rollup-win32-x64-gnu@4.53.1':
+ resolution: {integrity: sha512-2SNj7COIdAf6yliSpLdLG8BEsp5lgzRehgfkP0Av8zKfQFKku6JcvbobvHASPJu4f3BFxej5g+HuQPvqPhHvpQ==}
cpu: [x64]
os: [win32]
- '@rollup/rollup-win32-x64-msvc@4.52.5':
- resolution: {integrity: sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg==}
+ '@rollup/rollup-win32-x64-msvc@4.53.1':
+ resolution: {integrity: sha512-rLarc1Ofcs3DHtgSzFO31pZsCh8g05R2azN1q3fF+H423Co87My0R+tazOEvYVKXSLh8C4LerMK41/K7wlklcg==}
cpu: [x64]
os: [win32]
@@ -1027,8 +1027,8 @@ packages:
base64-js@1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
- baseline-browser-mapping@2.8.24:
- resolution: {integrity: sha512-uUhTRDPXamakPyghwrUcjaGvvBqGrWvBHReoiULMIpOJVM9IYzQh83Xk2Onx5HlGI2o10NNCzcs9TG/S3TkwrQ==}
+ baseline-browser-mapping@2.8.25:
+ resolution: {integrity: sha512-2NovHVesVF5TXefsGX1yzx1xgr7+m9JQenvz6FQY3qd+YXkKkYiv+vTCc7OriP9mcDZpTC5mAOYN4ocd29+erA==}
hasBin: true
bin-build@3.0.0:
@@ -1117,8 +1117,8 @@ packages:
resolution: {integrity: sha512-DLIsRzJVBQu72meAKPkWQOLcujdXT32hwdfnkI1frSiSRMK1MofjKHf+MEx0SB6fjEFXL8fBDv1dKymBlOp4Qw==}
engines: {node: '>=0.10.0'}
- caniuse-lite@1.0.30001753:
- resolution: {integrity: sha512-Bj5H35MD/ebaOV4iDLqPEtiliTN29qkGtEHCwawWn4cYm+bPJM2NsaP30vtZcnERClMzp52J4+aw2UNbK4o+zw==}
+ caniuse-lite@1.0.30001754:
+ resolution: {integrity: sha512-x6OeBXueoAceOmotzx3PO4Zpt4rzpeIFsSr6AAePTZxSkXiYDUmpypEl7e2+8NCd9bD7bXjqyef8CJYPC1jfxg==}
caw@2.0.1:
resolution: {integrity: sha512-Cg8/ZSBEa8ZVY9HspcGUYaK63d/bN7rqS3CYCzEGUxuYv6UlmcjzDUz2fCFFHyTvUW5Pk0I+3hkA3iXlIj6guA==}
@@ -1337,8 +1337,8 @@ packages:
duplexer3@0.1.5:
resolution: {integrity: sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==}
- electron-to-chromium@1.5.245:
- resolution: {integrity: sha512-rdmGfW47ZhL/oWEJAY4qxRtdly2B98ooTJ0pdEI4jhVLZ6tNf8fPtov2wS1IRKwFJT92le3x4Knxiwzl7cPPpQ==}
+ electron-to-chromium@1.5.249:
+ resolution: {integrity: sha512-5vcfL3BBe++qZ5kuFhD/p8WOM1N9m3nwvJPULJx+4xf2usSlZFJ0qoNYO2fOX4hi3ocuDcmDobtA+5SFr4OmBg==}
emoji-regex@8.0.0:
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
@@ -2628,8 +2628,8 @@ packages:
rollup:
optional: true
- rollup@4.52.5:
- resolution: {integrity: sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw==}
+ rollup@4.53.1:
+ resolution: {integrity: sha512-n2I0V0lN3E9cxxMqBCT3opWOiQBzRN7UG60z/WDKqdX2zHUS/39lezBcsckZFsV6fUTSnfqI7kHf60jDAPGKug==}
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
hasBin: true
@@ -2999,8 +2999,8 @@ packages:
vite:
optional: true
- vite@7.2.0:
- resolution: {integrity: sha512-C/Naxf8H0pBx1PA4BdpT+c/5wdqI9ILMdwjSMILw7tVIh3JsxzZqdeTLmmdaoh5MYUEOyBnM9K3o0DzoZ/fe+w==}
+ vite@7.2.2:
+ resolution: {integrity: sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==}
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
peerDependencies:
@@ -3598,18 +3598,18 @@ snapshots:
dependencies:
preact: 10.27.2
- '@preact/preset-vite@2.10.2(@babel/core@7.28.5)(preact@10.27.2)(vite@7.2.0(@types/node@24.10.0)(terser@5.44.1))':
+ '@preact/preset-vite@2.10.2(@babel/core@7.28.5)(preact@10.27.2)(vite@7.2.2(@types/node@24.10.0)(terser@5.44.1))':
dependencies:
'@babel/core': 7.28.5
'@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.28.5)
'@babel/plugin-transform-react-jsx-development': 7.27.1(@babel/core@7.28.5)
- '@prefresh/vite': 2.4.11(preact@10.27.2)(vite@7.2.0(@types/node@24.10.0)(terser@5.44.1))
+ '@prefresh/vite': 2.4.11(preact@10.27.2)(vite@7.2.2(@types/node@24.10.0)(terser@5.44.1))
'@rollup/pluginutils': 4.2.1
babel-plugin-transform-hook-names: 1.0.2(@babel/core@7.28.5)
debug: 4.4.3
picocolors: 1.1.1
- vite: 7.2.0(@types/node@24.10.0)(terser@5.44.1)
- vite-prerender-plugin: 0.5.12(vite@7.2.0(@types/node@24.10.0)(terser@5.44.1))
+ vite: 7.2.2(@types/node@24.10.0)(terser@5.44.1)
+ vite-prerender-plugin: 0.5.12(vite@7.2.2(@types/node@24.10.0)(terser@5.44.1))
transitivePeerDependencies:
- preact
- supports-color
@@ -3622,7 +3622,7 @@ snapshots:
'@prefresh/utils@1.2.1': {}
- '@prefresh/vite@2.4.11(preact@10.27.2)(vite@7.2.0(@types/node@24.10.0)(terser@5.44.1))':
+ '@prefresh/vite@2.4.11(preact@10.27.2)(vite@7.2.2(@types/node@24.10.0)(terser@5.44.1))':
dependencies:
'@babel/core': 7.28.5
'@prefresh/babel-plugin': 0.5.2
@@ -3630,7 +3630,7 @@ snapshots:
'@prefresh/utils': 1.2.1
'@rollup/pluginutils': 4.2.1
preact: 10.27.2
- vite: 7.2.0(@types/node@24.10.0)(terser@5.44.1)
+ vite: 7.2.2(@types/node@24.10.0)(terser@5.44.1)
transitivePeerDependencies:
- supports-color
@@ -3639,70 +3639,70 @@ snapshots:
estree-walker: 2.0.2
picomatch: 2.3.1
- '@rollup/rollup-android-arm-eabi@4.52.5':
+ '@rollup/rollup-android-arm-eabi@4.53.1':
optional: true
- '@rollup/rollup-android-arm64@4.52.5':
+ '@rollup/rollup-android-arm64@4.53.1':
optional: true
- '@rollup/rollup-darwin-arm64@4.52.5':
+ '@rollup/rollup-darwin-arm64@4.53.1':
optional: true
- '@rollup/rollup-darwin-x64@4.52.5':
+ '@rollup/rollup-darwin-x64@4.53.1':
optional: true
- '@rollup/rollup-freebsd-arm64@4.52.5':
+ '@rollup/rollup-freebsd-arm64@4.53.1':
optional: true
- '@rollup/rollup-freebsd-x64@4.52.5':
+ '@rollup/rollup-freebsd-x64@4.53.1':
optional: true
- '@rollup/rollup-linux-arm-gnueabihf@4.52.5':
+ '@rollup/rollup-linux-arm-gnueabihf@4.53.1':
optional: true
- '@rollup/rollup-linux-arm-musleabihf@4.52.5':
+ '@rollup/rollup-linux-arm-musleabihf@4.53.1':
optional: true
- '@rollup/rollup-linux-arm64-gnu@4.52.5':
+ '@rollup/rollup-linux-arm64-gnu@4.53.1':
optional: true
- '@rollup/rollup-linux-arm64-musl@4.52.5':
+ '@rollup/rollup-linux-arm64-musl@4.53.1':
optional: true
- '@rollup/rollup-linux-loong64-gnu@4.52.5':
+ '@rollup/rollup-linux-loong64-gnu@4.53.1':
optional: true
- '@rollup/rollup-linux-ppc64-gnu@4.52.5':
+ '@rollup/rollup-linux-ppc64-gnu@4.53.1':
optional: true
- '@rollup/rollup-linux-riscv64-gnu@4.52.5':
+ '@rollup/rollup-linux-riscv64-gnu@4.53.1':
optional: true
- '@rollup/rollup-linux-riscv64-musl@4.52.5':
+ '@rollup/rollup-linux-riscv64-musl@4.53.1':
optional: true
- '@rollup/rollup-linux-s390x-gnu@4.52.5':
+ '@rollup/rollup-linux-s390x-gnu@4.53.1':
optional: true
- '@rollup/rollup-linux-x64-gnu@4.52.5':
+ '@rollup/rollup-linux-x64-gnu@4.53.1':
optional: true
- '@rollup/rollup-linux-x64-musl@4.52.5':
+ '@rollup/rollup-linux-x64-musl@4.53.1':
optional: true
- '@rollup/rollup-openharmony-arm64@4.52.5':
+ '@rollup/rollup-openharmony-arm64@4.53.1':
optional: true
- '@rollup/rollup-win32-arm64-msvc@4.52.5':
+ '@rollup/rollup-win32-arm64-msvc@4.53.1':
optional: true
- '@rollup/rollup-win32-ia32-msvc@4.52.5':
+ '@rollup/rollup-win32-ia32-msvc@4.53.1':
optional: true
- '@rollup/rollup-win32-x64-gnu@4.52.5':
+ '@rollup/rollup-win32-x64-gnu@4.53.1':
optional: true
- '@rollup/rollup-win32-x64-msvc@4.52.5':
+ '@rollup/rollup-win32-x64-msvc@4.53.1':
optional: true
'@sindresorhus/is@0.7.0': {}
@@ -3963,7 +3963,7 @@ snapshots:
base64-js@1.5.1: {}
- baseline-browser-mapping@2.8.24: {}
+ baseline-browser-mapping@2.8.25: {}
bin-build@3.0.0:
dependencies:
@@ -4020,9 +4020,9 @@ snapshots:
browserslist@4.27.0:
dependencies:
- baseline-browser-mapping: 2.8.24
- caniuse-lite: 1.0.30001753
- electron-to-chromium: 1.5.245
+ baseline-browser-mapping: 2.8.25
+ caniuse-lite: 1.0.30001754
+ electron-to-chromium: 1.5.249
node-releases: 2.0.27
update-browserslist-db: 1.1.4(browserslist@4.27.0)
@@ -4080,7 +4080,7 @@ snapshots:
camelcase@2.1.1: {}
- caniuse-lite@1.0.30001753: {}
+ caniuse-lite@1.0.30001754: {}
caw@2.0.1:
dependencies:
@@ -4367,7 +4367,7 @@ snapshots:
duplexer3@0.1.5: {}
- electron-to-chromium@1.5.245: {}
+ electron-to-chromium@1.5.249: {}
emoji-regex@8.0.0: {}
@@ -5621,41 +5621,41 @@ snapshots:
dependencies:
glob: 7.2.3
- rollup-plugin-visualizer@6.0.5(rollup@4.52.5):
+ rollup-plugin-visualizer@6.0.5(rollup@4.53.1):
dependencies:
open: 8.4.2
picomatch: 4.0.3
source-map: 0.7.6
yargs: 17.7.2
optionalDependencies:
- rollup: 4.52.5
+ rollup: 4.53.1
- rollup@4.52.5:
+ rollup@4.53.1:
dependencies:
'@types/estree': 1.0.8
optionalDependencies:
- '@rollup/rollup-android-arm-eabi': 4.52.5
- '@rollup/rollup-android-arm64': 4.52.5
- '@rollup/rollup-darwin-arm64': 4.52.5
- '@rollup/rollup-darwin-x64': 4.52.5
- '@rollup/rollup-freebsd-arm64': 4.52.5
- '@rollup/rollup-freebsd-x64': 4.52.5
- '@rollup/rollup-linux-arm-gnueabihf': 4.52.5
- '@rollup/rollup-linux-arm-musleabihf': 4.52.5
- '@rollup/rollup-linux-arm64-gnu': 4.52.5
- '@rollup/rollup-linux-arm64-musl': 4.52.5
- '@rollup/rollup-linux-loong64-gnu': 4.52.5
- '@rollup/rollup-linux-ppc64-gnu': 4.52.5
- '@rollup/rollup-linux-riscv64-gnu': 4.52.5
- '@rollup/rollup-linux-riscv64-musl': 4.52.5
- '@rollup/rollup-linux-s390x-gnu': 4.52.5
- '@rollup/rollup-linux-x64-gnu': 4.52.5
- '@rollup/rollup-linux-x64-musl': 4.52.5
- '@rollup/rollup-openharmony-arm64': 4.52.5
- '@rollup/rollup-win32-arm64-msvc': 4.52.5
- '@rollup/rollup-win32-ia32-msvc': 4.52.5
- '@rollup/rollup-win32-x64-gnu': 4.52.5
- '@rollup/rollup-win32-x64-msvc': 4.52.5
+ '@rollup/rollup-android-arm-eabi': 4.53.1
+ '@rollup/rollup-android-arm64': 4.53.1
+ '@rollup/rollup-darwin-arm64': 4.53.1
+ '@rollup/rollup-darwin-x64': 4.53.1
+ '@rollup/rollup-freebsd-arm64': 4.53.1
+ '@rollup/rollup-freebsd-x64': 4.53.1
+ '@rollup/rollup-linux-arm-gnueabihf': 4.53.1
+ '@rollup/rollup-linux-arm-musleabihf': 4.53.1
+ '@rollup/rollup-linux-arm64-gnu': 4.53.1
+ '@rollup/rollup-linux-arm64-musl': 4.53.1
+ '@rollup/rollup-linux-loong64-gnu': 4.53.1
+ '@rollup/rollup-linux-ppc64-gnu': 4.53.1
+ '@rollup/rollup-linux-riscv64-gnu': 4.53.1
+ '@rollup/rollup-linux-riscv64-musl': 4.53.1
+ '@rollup/rollup-linux-s390x-gnu': 4.53.1
+ '@rollup/rollup-linux-x64-gnu': 4.53.1
+ '@rollup/rollup-linux-x64-musl': 4.53.1
+ '@rollup/rollup-openharmony-arm64': 4.53.1
+ '@rollup/rollup-win32-arm64-msvc': 4.53.1
+ '@rollup/rollup-win32-ia32-msvc': 4.53.1
+ '@rollup/rollup-win32-x64-gnu': 4.53.1
+ '@rollup/rollup-win32-x64-msvc': 4.53.1
fsevents: 2.3.3
run-parallel@1.2.0:
@@ -5970,7 +5970,7 @@ snapshots:
spdx-correct: 3.2.0
spdx-expression-parse: 3.0.1
- vite-plugin-imagemin@0.6.1(vite@7.2.0(@types/node@24.10.0)(terser@5.44.1)):
+ vite-plugin-imagemin@0.6.1(vite@7.2.2(@types/node@24.10.0)(terser@5.44.1)):
dependencies:
'@types/imagemin': 7.0.1
'@types/imagemin-gifsicle': 7.0.4
@@ -5995,11 +5995,11 @@ snapshots:
imagemin-webp: 6.1.0
jpegtran-bin: 6.0.1
pathe: 0.2.0
- vite: 7.2.0(@types/node@24.10.0)(terser@5.44.1)
+ vite: 7.2.2(@types/node@24.10.0)(terser@5.44.1)
transitivePeerDependencies:
- supports-color
- vite-prerender-plugin@0.5.12(vite@7.2.0(@types/node@24.10.0)(terser@5.44.1)):
+ vite-prerender-plugin@0.5.12(vite@7.2.2(@types/node@24.10.0)(terser@5.44.1)):
dependencies:
kolorist: 1.8.0
magic-string: 0.30.21
@@ -6007,26 +6007,26 @@ snapshots:
simple-code-frame: 1.3.0
source-map: 0.7.6
stack-trace: 1.0.0-pre2
- vite: 7.2.0(@types/node@24.10.0)(terser@5.44.1)
+ vite: 7.2.2(@types/node@24.10.0)(terser@5.44.1)
- vite-tsconfig-paths@5.1.4(typescript@5.9.3)(vite@7.2.0(@types/node@24.10.0)(terser@5.44.1)):
+ vite-tsconfig-paths@5.1.4(typescript@5.9.3)(vite@7.2.2(@types/node@24.10.0)(terser@5.44.1)):
dependencies:
debug: 4.4.3
globrex: 0.1.2
tsconfck: 3.1.6(typescript@5.9.3)
optionalDependencies:
- vite: 7.2.0(@types/node@24.10.0)(terser@5.44.1)
+ vite: 7.2.2(@types/node@24.10.0)(terser@5.44.1)
transitivePeerDependencies:
- supports-color
- typescript
- vite@7.2.0(@types/node@24.10.0)(terser@5.44.1):
+ vite@7.2.2(@types/node@24.10.0)(terser@5.44.1):
dependencies:
esbuild: 0.25.12
fdir: 6.5.0(picomatch@4.0.3)
picomatch: 4.0.3
postcss: 8.5.6
- rollup: 4.52.5
+ rollup: 4.53.1
tinyglobby: 0.2.15
optionalDependencies:
'@types/node': 24.10.0
diff --git a/interface/src/app/main/SensorsAnalogDialog.tsx b/interface/src/app/main/SensorsAnalogDialog.tsx
index 2294a1991..fbbd35466 100644
--- a/interface/src/app/main/SensorsAnalogDialog.tsx
+++ b/interface/src/app/main/SensorsAnalogDialog.tsx
@@ -288,7 +288,7 @@ const SensorsAnalogDialog = ({
name="f"
label={LL.FACTOR()}
value={numberValue(editItem.f)}
- sx={{ width: '11ch' }}
+ sx={{ width: '14ch' }}
type="number"
variant="outlined"
onChange={updateFormValue}
@@ -447,6 +447,7 @@ const SensorsAnalogDialog = ({
}
+ disabled={editItem.s}
variant="outlined"
color="warning"
onClick={remove}
diff --git a/interface/src/app/main/types.ts b/interface/src/app/main/types.ts
index d13775568..92b476c96 100644
--- a/interface/src/app/main/types.ts
+++ b/interface/src/app/main/types.ts
@@ -95,6 +95,7 @@ export interface AnalogSensor {
f: number;
t: number;
d: boolean; // deleted flag
+ s: boolean; // system customization flag
o_n?: string;
}
diff --git a/interface/src/app/settings/ApplicationSettings.tsx b/interface/src/app/settings/ApplicationSettings.tsx
index 26692109d..854c347ca 100644
--- a/interface/src/app/settings/ApplicationSettings.tsx
+++ b/interface/src/app/settings/ApplicationSettings.tsx
@@ -37,13 +37,13 @@ import { validate } from 'validators';
import { API, getBoardProfile, readSettings, writeSettings } from '../../api/app';
import { BOARD_PROFILES } from '../main/types';
-import type { APIcall, Settings } from '../main/types';
+import type { APIcall, BoardProfileKey, Settings } from '../main/types';
import { createSettingsValidator } from '../main/validators';
export function boardProfileSelectItems() {
return Object.keys(BOARD_PROFILES).map((code) => (
));
}
diff --git a/interface/src/app/settings/Settings.tsx b/interface/src/app/settings/Settings.tsx
index 57472392d..608c37ee9 100644
--- a/interface/src/app/settings/Settings.tsx
+++ b/interface/src/app/settings/Settings.tsx
@@ -1,4 +1,4 @@
-import { useCallback, useState } from 'react';
+import { useCallback, useMemo, useState } from 'react';
import AccessTimeIcon from '@mui/icons-material/AccessTime';
import CancelIcon from '@mui/icons-material/Cancel';
@@ -30,11 +30,14 @@ import { SectionContent, useLayoutTitle } from 'components';
import ListMenuItem from 'components/layout/ListMenuItem';
import { useI18nContext } from 'i18n/i18n-react';
+import SystemMonitor from '../status/SystemMonitor';
+
const Settings = () => {
const { LL } = useI18nContext();
useLayoutTitle(LL.SETTINGS(0));
const [confirmFactoryReset, setConfirmFactoryReset] = useState(false);
+ const [restarting, setRestarting] = useState();
const { send: sendAPI } = useRequest((data: APIcall) => API(data), {
immediate: false
@@ -42,6 +45,7 @@ const Settings = () => {
const doFormat = useCallback(async () => {
await sendAPI({ device: 'system', cmd: 'format', id: 0 }).then(() => {
+ setRestarting(true);
setConfirmFactoryReset(false);
});
}, [sendAPI]);
@@ -54,120 +58,131 @@ const Settings = () => {
setConfirmFactoryReset(true);
}, []);
- return (
-
-
-
+ const content = useMemo(() => {
+ return (
+ <>
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
-
+
+
-
+
+ >
+ );
+ }, [
+ LL,
+ handleFactoryResetClick,
+ handleFactoryResetClose,
+ doFormat,
+ confirmFactoryReset,
+ restarting
+ ]);
-
-
-
- }
- variant="outlined"
- onClick={handleFactoryResetClick}
- color="error"
- >
- {LL.FACTORY_RESET()}
-
-
-
- );
+ return {restarting ? : content};
};
export default Settings;
diff --git a/interface/src/components/inputs/ValidatedTextField.tsx b/interface/src/components/inputs/ValidatedTextField.tsx
index b7ccd3ae6..ad1b87f37 100644
--- a/interface/src/components/inputs/ValidatedTextField.tsx
+++ b/interface/src/components/inputs/ValidatedTextField.tsx
@@ -23,7 +23,9 @@ const ValidatedTextField: FC = ({
<>
{errors?.map((e) => (
- {e.message}
+
+ {e.message}
+
))}
>
);
diff --git a/interface/src/i18n/cz/index.ts b/interface/src/i18n/cz/index.ts
index b7131532c..85f6f6f37 100644
--- a/interface/src/i18n/cz/index.ts
+++ b/interface/src/i18n/cz/index.ts
@@ -337,7 +337,7 @@ const cz: Translation = {
UPDATE_AVAILABLE: 'aktualizace dostupná',
LATEST_VERSION: 'Používáte nejnovější verzi {0}firmwaru',
PLEASE_WAIT: 'Prosím čekejte',
- RESTARTING_PRE: 'Inicializace',
+ RESTARTING_PRE: 'Bootování',
RESTARTING_POST: 'Příprava',
AUTO_SCROLL: 'Automatické rolování',
DASHBOARD: 'Dashboard',
diff --git a/interface/src/i18n/de/index.ts b/interface/src/i18n/de/index.ts
index f77904b6b..6cd8beb8e 100644
--- a/interface/src/i18n/de/index.ts
+++ b/interface/src/i18n/de/index.ts
@@ -337,7 +337,7 @@ const de: Translation = {
UPDATE_AVAILABLE: 'Firmware-Update verfügbar',
LATEST_VERSION: 'Sie verwenden die neueste {0} Firmware-Version',
PLEASE_WAIT: 'Bitte warten',
- RESTARTING_PRE: 'Initialisierung',
+ RESTARTING_PRE: 'Booten',
RESTARTING_POST: 'Vorbereitung',
AUTO_SCROLL: 'Automatisches Scrollen',
DASHBOARD: 'Dashboard',
diff --git a/interface/src/i18n/en/index.ts b/interface/src/i18n/en/index.ts
index d9d18b7d5..08cffa3df 100644
--- a/interface/src/i18n/en/index.ts
+++ b/interface/src/i18n/en/index.ts
@@ -337,7 +337,7 @@ const en: Translation = {
UPDATE_AVAILABLE: 'update available',
LATEST_VERSION: 'You are using the latest {0} firmware version',
PLEASE_WAIT: 'Please wait',
- RESTARTING_PRE: 'Initializing',
+ RESTARTING_PRE: 'Booting',
RESTARTING_POST: 'Preparing',
AUTO_SCROLL: 'Auto Scroll',
DASHBOARD: 'Dashboard',
diff --git a/interface/src/i18n/fr/index.ts b/interface/src/i18n/fr/index.ts
index f5a123b60..752f660fb 100644
--- a/interface/src/i18n/fr/index.ts
+++ b/interface/src/i18n/fr/index.ts
@@ -337,7 +337,7 @@ const fr: Translation = {
UPDATE_AVAILABLE: 'mise à jour disponible',
LATEST_VERSION: 'Vous utilisez la dernière version {0} du firmware',
PLEASE_WAIT: 'Veuillez patienter',
- RESTARTING_PRE: 'Initialisation',
+ RESTARTING_PRE: 'Démarrage',
RESTARTING_POST: 'Préparation',
AUTO_SCROLL: 'Défilement automatique',
DASHBOARD: 'Tableau de bord',
diff --git a/interface/src/i18n/it/index.ts b/interface/src/i18n/it/index.ts
index 44f60c691..279870901 100644
--- a/interface/src/i18n/it/index.ts
+++ b/interface/src/i18n/it/index.ts
@@ -337,7 +337,7 @@ const it: Translation = {
UPDATE_AVAILABLE: 'aggiornamento disponibile',
LATEST_VERSION: 'Stai usando la versione più recente del firmware {0}',
PLEASE_WAIT: 'Attendere',
- RESTARTING_PRE: 'Inizializzazione',
+ RESTARTING_PRE: 'Avviamento',
RESTARTING_POST: 'Preparazione',
AUTO_SCROLL: 'Scorrimento automatico',
DASHBOARD: 'Pannello di controllo',
diff --git a/interface/src/i18n/nl/index.ts b/interface/src/i18n/nl/index.ts
index acdf83c03..fbceaf4ee 100644
--- a/interface/src/i18n/nl/index.ts
+++ b/interface/src/i18n/nl/index.ts
@@ -337,7 +337,7 @@ const nl: Translation = {
UPDATE_AVAILABLE: 'update beschikbaar',
LATEST_VERSION: 'U gebruikt de nieuwste {0} firmwareversie',
PLEASE_WAIT: 'Een ogenblik geduld',
- RESTARTING_PRE: 'Initialiseren',
+ RESTARTING_PRE: 'Booten',
RESTARTING_POST: 'Voorbereiding',
AUTO_SCROLL: 'Automatisch Scrollen',
DASHBOARD: 'Dashboard',
diff --git a/interface/src/i18n/no/index.ts b/interface/src/i18n/no/index.ts
index e27d38089..1eb1dd3ba 100644
--- a/interface/src/i18n/no/index.ts
+++ b/interface/src/i18n/no/index.ts
@@ -337,7 +337,7 @@ const no: Translation = {
UPDATE_AVAILABLE: 'oppdatering tilgjengelig',
LATEST_VERSION: 'Du bruker den nyeste {0} firmware versjonen',
PLEASE_WAIT: 'Vennligst vent',
- RESTARTING_PRE: 'Initialiserer',
+ RESTARTING_PRE: 'Starte',
RESTARTING_POST: 'Forbereder',
AUTO_SCROLL: 'Automatisk rulling',
DASHBOARD: 'Dashboard',
diff --git a/interface/src/i18n/pl/index.ts b/interface/src/i18n/pl/index.ts
index 9fd1cbc02..8520e86bc 100644
--- a/interface/src/i18n/pl/index.ts
+++ b/interface/src/i18n/pl/index.ts
@@ -337,7 +337,7 @@ const pl: BaseTranslation = {
UPDATE_AVAILABLE: 'aktualizacja dostępna',
LATEST_VERSION: 'Jesteś używając najnowszej wersji firmware {0}',
PLEASE_WAIT: 'Proszę czekać',
- RESTARTING_PRE: 'Inicjalizacja',
+ RESTARTING_PRE: 'Uruchamianie',
RESTARTING_POST: 'Przygotowanie',
AUTO_SCROLL: 'Auto Scroll',
DASHBOARD: 'Pulpit',
diff --git a/interface/src/i18n/sk/index.ts b/interface/src/i18n/sk/index.ts
index 737113010..d9349bc2e 100644
--- a/interface/src/i18n/sk/index.ts
+++ b/interface/src/i18n/sk/index.ts
@@ -337,7 +337,7 @@ const sk: Translation = {
UPDATE_AVAILABLE: 'dostupná aktualizácia',
LATEST_VERSION: 'Používate poslednú {0} verziu firmvéru',
PLEASE_WAIT: 'Čakajte prosím',
- RESTARTING_PRE: 'Prebieha inicializácia',
+ RESTARTING_PRE: 'Bootovanie',
RESTARTING_POST: 'Príprava',
AUTO_SCROLL: 'Automatické rolovanie',
DASHBOARD: 'Panel',
diff --git a/interface/src/i18n/sv/index.ts b/interface/src/i18n/sv/index.ts
index 48f513017..af1681f5f 100644
--- a/interface/src/i18n/sv/index.ts
+++ b/interface/src/i18n/sv/index.ts
@@ -337,7 +337,7 @@ const sv: Translation = {
UPDATE_AVAILABLE: 'uppdatering tillgänglig',
LATEST_VERSION: 'Du använder den senaste {0} firmwareversionen.',
PLEASE_WAIT: 'Var god vänta',
- RESTARTING_PRE: 'Initialiserar',
+ RESTARTING_PRE: 'Bootar',
RESTARTING_POST: 'Förbereder',
AUTO_SCROLL: 'Autoskrolla',
DASHBOARD: 'Kontrollpanel',
diff --git a/interface/src/i18n/tr/index.ts b/interface/src/i18n/tr/index.ts
index d263176bb..2414a867d 100644
--- a/interface/src/i18n/tr/index.ts
+++ b/interface/src/i18n/tr/index.ts
@@ -337,7 +337,7 @@ const tr: Translation = {
UPDATE_AVAILABLE: 'güncellendi!',
LATEST_VERSION: 'En son {0} firmware sürümünü kullanıyorsunuz.',
PLEASE_WAIT: 'Lütfen bekleyin',
- RESTARTING_PRE: 'Başlatılıyor',
+ RESTARTING_PRE: 'Yükleniyor',
RESTARTING_POST: 'Hazırlanıyor',
AUTO_SCROLL: 'Otomatik kaydırma',
DASHBOARD: 'Kontrol Paneli',
diff --git a/lib/PButton/PButon.cpp b/lib/PButton/PButon.cpp
index 8324b7728..0b0ece9f1 100644
--- a/lib/PButton/PButon.cpp
+++ b/lib/PButton/PButon.cpp
@@ -23,10 +23,10 @@
// Constructor
PButton::PButton() {
// Initialization of default properties
- Debounce_ = 40; // Debounce period to prevent flickering when pressing or releasing the button (in ms)
- DblClickDelay_ = 250; // Max period between clicks for a double click event (in ms)
- LongPressDelay_ = 750; // Hold period for a long press event (in ms)
- VLongPressDelay_ = 3000; // Hold period for a very long press event (in ms)
+ Debounce_ = 40; // Debounce period to prevent flickering when pressing or releasing the button (in ms)
+ DblClickDelay_ = 250; // Max period between clicks for a double click event (in ms)
+ LongPressDelay_ = 9500; // Hold period for a long press event (in ms)
+ VLongPressDelay_ = 20000; // Hold period for a very long press event (in ms)
cb_onClick = nullptr;
cb_onDblClick = nullptr;
diff --git a/lib_standalone/ESP32React.h b/lib_standalone/ESP32React.h
index 115a42aae..4da6036a6 100644
--- a/lib_standalone/ESP32React.h
+++ b/lib_standalone/ESP32React.h
@@ -21,6 +21,7 @@
#define NTP_SETTINGS_FILE "/config/ntpSettings.json"
#define EMSESP_SETTINGS_FILE "/config/emsespSettings.json"
+#define AP_MODE_ALWAYS 0
class DummySettings {
public:
// SYSTEM
@@ -55,6 +56,7 @@ class DummySettings {
uint16_t publish_time_other = 10;
uint16_t publish_time_sensor = 10;
uint16_t publish_time_heartbeat = 60;
+ uint32_t publish_time_water = 0;
String hostname = "ems-esp";
String jwtSecret = "ems-esp";
@@ -72,11 +74,15 @@ class DummySettings {
String CORSOrigin = "*";
uint8_t tx_power = 0;
- uint8_t provisionMode = 0;
- uint32_t publish_time_water = 0;
+ // AP
+ uint8_t provisionMode = 0;
- static void read(DummySettings & settings, JsonObject root){};
- static void read(DummySettings & settings){};
+ // NTP
+ String server = "pool.ntp.org";
+ String tzLabel = "Europe/London";
+
+ static void read(DummySettings & settings, JsonObject root) {};
+ static void read(DummySettings & settings) {};
static StateUpdateResult update(JsonObject root, DummySettings & settings) {
return StateUpdateResult::CHANGED;
@@ -85,7 +91,7 @@ class DummySettings {
class DummySettingsService : public StatefulService {
public:
- DummySettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager){};
+ DummySettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager) {};
void begin();
void loop();
@@ -101,12 +107,12 @@ class ESP32React {
public:
ESP32React(AsyncWebServer * server, FS * fs)
: _settings(server, fs, nullptr)
- , _securitySettingsService(server, fs){};
+ , _securitySettingsService(server, fs) {};
void begin() {
_mqttClient = new espMqttClient();
};
- void loop(){};
+ void loop() {};
SecurityManager * getSecurityManager() {
return &_securitySettingsService;
diff --git a/mock-api/restServer.ts b/mock-api/restServer.ts
index cb3627521..c464166d3 100644
--- a/mock-api/restServer.ts
+++ b/mock-api/restServer.ts
@@ -965,14 +965,72 @@ const emsesp_sensordata = {
{ id: '28-233D-9497-0C03', n: 'Dallas 1', t: 25.7, o: 1.2, u: 1 },
{ id: '28-243D-7437-1E3A', n: 'Dallas 2 outside', t: 26.1, o: 0, u: 1 },
{ id: '28-243E-7437-1E3B', n: 'Zolder', t: 27.1, o: 0, u: 1 },
- { id: '28-183D-1892-0C33', n: 'Roof', o: 2, u: 1 } // no temperature
+ { id: '28-183D-1892-0C33', n: 'Roof', o: 2, u: 1 }, // no temperature
+ { id: '28_1767_7B13_2502', n: 'gateway_temperature', t: 28.1, o: 0, u: 1 } // internal system temp
],
// as: [],
as: [
- { id: 1, g: 36, n: 'motor', v: 0, u: 0, o: 17, f: 0, t: 0, d: false },
- { id: 2, g: 37, n: 'External switch', v: 13, u: 0, o: 17, f: 0, t: 1, d: false },
- { id: 3, g: 39, n: 'Pulse count', v: 144, u: 0, o: 0, f: 0, t: 2, d: false },
- { id: 4, g: 40, n: 'Pressure', v: 16, u: 17, o: 0, f: 0, t: 3, d: false }
+ { id: 1, g: 35, n: 'motor', v: 0, u: 0, o: 17, f: 0, t: 0, d: false, s: false },
+ {
+ id: 2,
+ g: 37,
+ n: 'External switch',
+ v: 13,
+ u: 0,
+ o: 17,
+ f: 0,
+ t: 1,
+ d: false,
+ s: false
+ },
+ {
+ id: 3,
+ g: 39,
+ n: 'Pulse count',
+ v: 144,
+ u: 0,
+ o: 0,
+ f: 0,
+ t: 2,
+ d: false,
+ s: false
+ },
+ {
+ id: 4,
+ g: 40,
+ n: 'Pressure',
+ v: 16,
+ u: 17,
+ o: 0,
+ f: 0,
+ t: 3,
+ d: false,
+ s: false
+ },
+ {
+ id: 6,
+ g: 39,
+ n: 'core_voltage',
+ v: 3.34,
+ u: 23,
+ o: 0,
+ f: 0.003771,
+ t: 3,
+ d: false,
+ s: true
+ },
+ {
+ id: 7,
+ g: 36,
+ n: 'supply_voltage',
+ v: 12.21,
+ u: 23,
+ o: 0,
+ f: 0.017,
+ t: 3,
+ d: false,
+ s: true
+ }
],
analog_enabled: true
};
@@ -4811,6 +4869,7 @@ router
u: as.uom,
t: as.type,
d: as.deleted,
+ s: as.is_system,
v: 0 // must be added for demo only
});
} else {
diff --git a/project-words.txt b/project-words.txt
index feffbe623..f5d4b01b6 100644
--- a/project-words.txt
+++ b/project-words.txt
@@ -1435,4 +1435,5 @@ teddybear
washingmachine
switchprogram
brotlin
-fanspd
\ No newline at end of file
+fanspd
+currhum
\ No newline at end of file
diff --git a/src/ESP32React/APSettingsService.h b/src/ESP32React/APSettingsService.h
index 3c76feeb5..67c9e03bf 100644
--- a/src/ESP32React/APSettingsService.h
+++ b/src/ESP32React/APSettingsService.h
@@ -58,7 +58,7 @@ enum APNetworkStatus { ACTIVE = 0, INACTIVE, LINGERING };
class APSettings {
public:
- uint8_t provisionMode;
+ uint8_t provisionMode; // 0 = on, 2 = off
String ssid;
String password;
uint8_t channel;
diff --git a/src/core/analogsensor.cpp b/src/core/analogsensor.cpp
index 8aa69c0cc..3b39df74e 100644
--- a/src/core/analogsensor.cpp
+++ b/src/core/analogsensor.cpp
@@ -52,18 +52,23 @@ void AnalogSensor::start(const bool factory_settings) {
// if (factory_settings && EMSESP::nvs_.getString("boot").equals("E32V2_2") && EMSESP::nvs_.getString("hwrevision").equals("3.0")) {
if (factory_settings && analogReadMilliVolts(39) > 700) { // core voltage > 2.6V
EMSESP::webCustomizationService.update([&](WebCustomization & settings) {
- auto newSensor = AnalogCustomization();
- newSensor.gpio = 39;
- newSensor.name = "core_voltage";
- newSensor.offset = 0;
- newSensor.factor = 0.00377136; // Divider 24k - 8,66k
- newSensor.uom = DeviceValueUOM::VOLTS;
- newSensor.type = AnalogType::ADC;
+ auto newSensor = AnalogCustomization();
+
+ newSensor.gpio = 39;
+ newSensor.name = "core_voltage";
+ newSensor.offset = 0;
+ newSensor.factor = 0.00377136; // Divider 24k - 8,66k
+ newSensor.uom = DeviceValueUOM::VOLTS;
+ newSensor.type = AnalogType::ADC;
+ newSensor.is_system = true;
settings.analogCustomizations.push_back(newSensor);
- newSensor.gpio = 36;
- newSensor.name = "supply_voltage";
- newSensor.factor = 0.017; // Divider 24k - 1,5k
+
+ newSensor.gpio = 36;
+ newSensor.name = "supply_voltage";
+ newSensor.factor = 0.017; // Divider 24k - 1,5k
+ newSensor.is_system = true;
settings.analogCustomizations.push_back(newSensor);
+
return StateUpdateResult::CHANGED; // persist the change
});
}
@@ -469,7 +474,7 @@ void AnalogSensor::loop() {
// update analog information name and offset
// a type value of -1 is used to delete the sensor
-bool AnalogSensor::update(uint8_t gpio, std::string & name, double offset, double factor, uint8_t uom, int8_t type, bool deleted) {
+bool AnalogSensor::update(uint8_t gpio, std::string & name, double offset, double factor, uint8_t uom, int8_t type, bool deleted, bool is_system) {
// first see if we can find the sensor in our customization list
bool found_sensor = false;
EMSESP::webCustomizationService.update([&](WebCustomization & settings) {
@@ -496,11 +501,12 @@ bool AnalogSensor::update(uint8_t gpio, std::string & name, double offset, doubl
if (name != AnalogCustomization.name) {
EMSESP::nvs_.remove(AnalogCustomization.name.c_str());
}
- AnalogCustomization.name = name;
- AnalogCustomization.offset = offset;
- AnalogCustomization.factor = factor;
- AnalogCustomization.uom = uom;
- AnalogCustomization.type = type;
+ AnalogCustomization.name = name;
+ AnalogCustomization.offset = offset;
+ AnalogCustomization.factor = factor;
+ AnalogCustomization.uom = uom;
+ AnalogCustomization.type = type;
+ AnalogCustomization.is_system = is_system;
LOG_DEBUG("Customizing existing analog GPIO %02d", gpio);
}
return StateUpdateResult::CHANGED; // persist the change
@@ -517,13 +523,14 @@ bool AnalogSensor::update(uint8_t gpio, std::string & name, double offset, doubl
// we didn't find it, it's new, so create and store it in the customization list
if (!found_sensor) {
EMSESP::webCustomizationService.update([&](WebCustomization & settings) {
- auto newSensor = AnalogCustomization();
- newSensor.gpio = gpio;
- newSensor.name = name;
- newSensor.offset = offset;
- newSensor.factor = factor;
- newSensor.uom = uom;
- newSensor.type = type;
+ auto newSensor = AnalogCustomization();
+ newSensor.gpio = gpio;
+ newSensor.name = name;
+ newSensor.offset = offset;
+ newSensor.factor = factor;
+ newSensor.uom = uom;
+ newSensor.type = type;
+ newSensor.is_system = false;
settings.analogCustomizations.push_back(newSensor);
LOG_DEBUG("Adding new customization for analog sensor GPIO %02d", gpio);
return StateUpdateResult::CHANGED; // persist the change
@@ -836,7 +843,8 @@ void AnalogSensor::get_value_json(JsonObject output, const Sensor & sensor) {
output["readable"] = true;
output["writeable"] = sensor.type() == AnalogType::COUNTER || sensor.type() == AnalogType::RGB || sensor.type() == AnalogType::PULSE
|| (sensor.type() >= AnalogType::DIGITAL_OUT && sensor.type() <= AnalogType::PWM_2);
- output["visible"] = true;
+ output["visible"] = true;
+ output["is_system"] = sensor.is_system();
if (sensor.type() == AnalogType::COUNTER) {
output["min"] = 0;
output["max"] = 4000000;
diff --git a/src/core/analogsensor.h b/src/core/analogsensor.h
index 7125a2b3b..83e8ed4a9 100644
--- a/src/core/analogsensor.h
+++ b/src/core/analogsensor.h
@@ -61,6 +61,10 @@ class AnalogSensor {
value_ = value;
}
+ bool is_system() const {
+ return is_system_;
+ }
+
double factor() const {
return factor_;
}
@@ -105,8 +109,9 @@ class AnalogSensor {
double offset_;
double factor_;
uint8_t uom_;
- double value_; // double because of the factor is a double
- int8_t type_; // one of the AnalogType enum
+ double value_; // double because of the factor is a double
+ int8_t type_; // one of the AnalogType enum
+ bool is_system_; // if true, the sensor is a system sensor
};
AnalogSensor() = default;
@@ -167,7 +172,7 @@ class AnalogSensor {
return sensors_.size();
}
- bool update(uint8_t gpio, std::string & name, double offset, double factor, uint8_t uom, int8_t type, bool deleted = false);
+ bool update(uint8_t gpio, std::string & name, double offset, double factor, uint8_t uom, int8_t type, bool deleted = false, bool is_system = false);
bool get_value_info(JsonObject output, const char * cmd, const int8_t id = -1);
void store_counters();
diff --git a/src/core/command.cpp b/src/core/command.cpp
index f5ab6fded..aee649dd6 100644
--- a/src/core/command.cpp
+++ b/src/core/command.cpp
@@ -592,6 +592,7 @@ bool Command::list(const uint8_t device_type, JsonObject output) {
output["ap/enabled"] = Helpers::translated_word(FL_(system_cmd));
output["syslog/enabled"] = Helpers::translated_word(FL_(system_cmd));
}
+
// create a list of commands we have registered, and sort them
std::list sorted_cmds;
for (const auto & cf : cmdfunctions_) {
diff --git a/src/core/common.h b/src/core/common.h
index aa49be717..89e354f8f 100644
--- a/src/core/common.h
+++ b/src/core/common.h
@@ -44,39 +44,39 @@ using string_vector = std::vector;
#endif
// clang-format off
+
+ #define FPSTR(pstr_pointer) pstr_pointer
+ #define MAKE_WORD_CUSTOM(string_name, string_literal) static const char __pstr__##string_name[] = string_literal;
+ #define MAKE_WORD(string_name) MAKE_WORD_CUSTOM(string_name, #string_name)
+
+ #define F_(string_name) (__pstr__##string_name)
+ #define FL_(list_name) (__pstr__L_##list_name)
+
+ // Translation counter - capture baseline before any MAKE_TRANSLATION calls
+ enum { EMSESP_TRANSLATION_COUNT_START = __COUNTER__ };
-#define FPSTR(pstr_pointer) pstr_pointer
-#define MAKE_WORD_CUSTOM(string_name, string_literal) static const char __pstr__##string_name[] = string_literal;
-#define MAKE_WORD(string_name) MAKE_WORD_CUSTOM(string_name, #string_name)
-
-#define F_(string_name) (__pstr__##string_name)
-#define FL_(list_name) (__pstr__L_##list_name)
-
-// The language settings below must match system.cpp
-#if defined(EMSESP_TEST)
-// in Test mode use two languages (en & de) to save flash memory needed for the tests
-#define MAKE_WORD_TRANSLATION(list_name, en, de, ...) static const char * const __pstr__L_##list_name[] = {en, de, nullptr};
-#define MAKE_TRANSLATION(list_name, shortname, en, de, ...) static const char * const __pstr__L_##list_name[] = {shortname, en, de, nullptr};
-#elif defined(EMSESP_EN_ONLY)
-// EN only
-#define MAKE_WORD_TRANSLATION(list_name, en, ...) static const char * const __pstr__L_##list_name[] = {en, nullptr};
-#define MAKE_TRANSLATION(list_name, shortname, en, ...) static const char * const __pstr__L_##list_name[] = {shortname, en, nullptr};
-#elif defined(EMSESP_DE_ONLY)
-// EN + DE
-#define MAKE_WORD_TRANSLATION(list_name, en, de, ...) static const char * const __pstr__L_##list_name[] = {en, de, nullptr};
-#define MAKE_TRANSLATION(list_name, shortname, en, de, ...) static const char * const __pstr__L_##list_name[] = {shortname, en, de, nullptr};
-#else
-#define MAKE_WORD_TRANSLATION(list_name, ...) static const char * const __pstr__L_##list_name[] = {__VA_ARGS__, nullptr};
-#define MAKE_TRANSLATION(list_name, ...) static const char * const __pstr__L_##list_name[] = {__VA_ARGS__, nullptr};
-#endif
-
-#define MAKE_NOTRANSLATION(list_name, ...) static const char * const __pstr__L_##list_name[] = {__VA_ARGS__, nullptr};
-
-// fixed strings, no translations
-#define MAKE_ENUM_FIXED(enum_name, ...) static const char * const __pstr__L_##enum_name[] = {__VA_ARGS__, nullptr};
-
-// with translations
-#define MAKE_ENUM(enum_name, ...) static const char * const * __pstr__L_##enum_name[] = {__VA_ARGS__, nullptr};
+ // The language settings below must match system.cpp
+ #if defined(EMSESP_EN_ONLY)
+ // EN only
+ #define MAKE_WORD_TRANSLATION(list_name, en, ...) static const char * const __pstr__L_##list_name[] = {en, nullptr};
+ #define MAKE_TRANSLATION(list_name, shortname, en, ...) static constexpr int __translation_counter_##list_name = __COUNTER__; static const char * const __pstr__L_##list_name[] = {shortname, en, nullptr};
+ #elif defined(EMSESP_TEST) || defined(EMSESP_DE_ONLY)
+ // EN + DE (Test mode uses two languages to save flash memory)
+ #define MAKE_WORD_TRANSLATION(list_name, en, de, ...) static const char * const __pstr__L_##list_name[] = {en, de, nullptr};
+ #define MAKE_TRANSLATION(list_name, shortname, en, de, ...) static constexpr int __translation_counter_##list_name = __COUNTER__; static const char * const __pstr__L_##list_name[] = {shortname, en, de, nullptr};
+ #else
+ // All languages
+ #define MAKE_WORD_TRANSLATION(list_name, ...) static const char * const __pstr__L_##list_name[] = {__VA_ARGS__, nullptr};
+ #define MAKE_TRANSLATION(list_name, ...) static constexpr int __translation_counter_##list_name = __COUNTER__; static const char * const __pstr__L_##list_name[] = {__VA_ARGS__, nullptr};
+ #endif
+
+ #define MAKE_NOTRANSLATION(list_name, ...) static const char * const __pstr__L_##list_name[] = {__VA_ARGS__, nullptr};
+
+ // fixed strings, no translations
+ #define MAKE_ENUM_FIXED(enum_name, ...) static const char * const __pstr__L_##enum_name[] = {__VA_ARGS__, nullptr};
+
+ // with translations
+ #define MAKE_ENUM(enum_name, ...) static const char * const * __pstr__L_##enum_name[] = {__VA_ARGS__, nullptr};
// clang-format on
@@ -84,4 +84,8 @@ using string_vector = std::vector;
#include "locale_translations.h"
#include "locale_common.h"
+// Translation count - dynamically calculated at compile-time
+enum { EMSESP_TRANSLATION_COUNT_END = __COUNTER__ };
+static constexpr uint16_t EMSESP_TRANSLATION_COUNT = EMSESP_TRANSLATION_COUNT_END - EMSESP_TRANSLATION_COUNT_START - 1;
+
#endif
diff --git a/src/core/emsesp.cpp b/src/core/emsesp.cpp
index 4ee114cea..e37c498c8 100644
--- a/src/core/emsesp.cpp
+++ b/src/core/emsesp.cpp
@@ -96,18 +96,35 @@ Preferences EMSESP::nvs_; // NV Storage
// for a specific EMS device go and request data values
// or if device_id is 0 it will fetch from all our known and active devices
void EMSESP::fetch_device_values(const uint8_t device_id) {
- for (const auto & emsdevice : emsdevices) {
- if ((device_id == 0) || emsdevice->is_device_id(device_id)) {
+ // Early return if no devices
+ if (emsdevices.empty()) {
+ return;
+ }
+
+ // If device_id is 0, fetch all
+ if (device_id == 0) {
+ for (const auto & emsdevice : emsdevices) {
emsdevice->fetch_values();
- if (device_id != 0) {
- return; // quit, we only want to return the selected device
- }
+ }
+ return;
+ }
+
+ // Fetch specific device
+ for (const auto & emsdevice : emsdevices) {
+ if (emsdevice->is_device_id(device_id)) {
+ emsdevice->fetch_values();
+ return; // quit, we only want to return the selected device
}
}
}
// see if the deviceID exists
bool EMSESP::valid_device(const uint8_t device_id) {
+ // Early return if devices list is empty
+ if (emsdevices.empty()) {
+ return false;
+ }
+
for (const auto & emsdevice : emsdevices) {
if (emsdevice && emsdevice->is_device_id(device_id)) {
return true;
@@ -118,6 +135,10 @@ bool EMSESP::valid_device(const uint8_t device_id) {
// for a specific EMS device type go and request data values
void EMSESP::fetch_device_values_type(const uint8_t device_type) {
+ if (emsdevices.empty()) {
+ return;
+ }
+
for (const auto & emsdevice : emsdevices) {
if (emsdevice && (emsdevice->device_type() == device_type)) {
emsdevice->fetch_values();
@@ -126,6 +147,10 @@ void EMSESP::fetch_device_values_type(const uint8_t device_type) {
}
bool EMSESP::cmd_is_readonly(const uint8_t device_type, const uint8_t device_id, const char * cmd, const int8_t id) {
+ if (emsdevices.empty()) {
+ return false;
+ }
+
for (const auto & emsdevice : emsdevices) {
if (emsdevice && (emsdevice->device_type() == device_type) && (!device_id || emsdevice->device_id() == device_id)) {
return emsdevice->is_readonly(cmd, id);
@@ -135,6 +160,10 @@ bool EMSESP::cmd_is_readonly(const uint8_t device_type, const uint8_t device_id,
}
uint8_t EMSESP::device_id_from_cmd(const uint8_t device_type, const char * cmd, const int8_t id) {
+ if (emsdevices.empty()) {
+ return 0;
+ }
+
for (const auto & emsdevice : emsdevices) {
if (emsdevice && emsdevice->device_type() == device_type && emsdevice->has_cmd(cmd, id)) {
return emsdevice->device_id();
@@ -151,10 +180,14 @@ void EMSESP::clear_all_devices() {
// return number of devices of a known type
uint8_t EMSESP::count_devices(const uint8_t device_type) {
+ if (emsdevices.empty()) {
+ return 0;
+ }
+
uint8_t count = 0;
for (const auto & emsdevice : emsdevices) {
- if (emsdevice) {
- count += (emsdevice->device_type() == device_type);
+ if (emsdevice && emsdevice->device_type() == device_type) {
+ count++;
}
}
return count;
@@ -162,10 +195,14 @@ uint8_t EMSESP::count_devices(const uint8_t device_type) {
// return total number of devices excluding the Controller
uint8_t EMSESP::count_devices() {
+ if (emsdevices.empty()) {
+ return 0;
+ }
+
uint8_t count = 0;
for (const auto & emsdevice : emsdevices) {
- if (emsdevice) {
- count += (emsdevice->device_type() != EMSdevice::DeviceType::CONTROLLER);
+ if (emsdevice && emsdevice->device_type() != EMSdevice::DeviceType::CONTROLLER) {
+ count++;
}
}
return count;
@@ -174,20 +211,22 @@ uint8_t EMSESP::count_devices() {
// returns the index of a device if there are more of the same type
// or 0 if there is only one or none
uint8_t EMSESP::device_index(const uint8_t device_type, const uint8_t unique_id) {
- if (count_devices(device_type) <= 1) {
- return 0; // none or only 1 device exists
- }
- uint8_t index = 1;
+ uint8_t count = 0;
+ uint8_t index = 0;
+ uint8_t current_index = 1;
+
for (const auto & emsdevice : emsdevices) {
if (emsdevice->device_type() == device_type) {
- // did we find it?
+ count++;
if (emsdevice->unique_id() == unique_id) {
- return index;
+ index = current_index;
}
- index++;
+ current_index++;
}
}
- return 0; // didn't find it
+
+ // Return 0 if only one device exists or not found
+ return (count <= 1) ? 0 : index;
}
// scans for new devices
@@ -242,13 +281,8 @@ uint8_t EMSESP::bus_status() {
uint32_t total_fail = txservice_.telegram_read_fail_count() + txservice_.telegram_write_fail_count();
// nothing sent and also no errors - must be ok
- if ((total_sent == 0) && (total_fail == 0)) {
- return BUS_STATUS_CONNECTED;
- }
-
- // nothing sent, but have Tx errors
- if ((total_sent == 0) && (total_fail != 0)) {
- return BUS_STATUS_TX_ERRORS;
+ if (total_sent == 0) {
+ return (total_fail == 0) ? BUS_STATUS_CONNECTED : BUS_STATUS_TX_ERRORS;
}
// Tx Failure rate > 10%
@@ -288,7 +322,7 @@ void EMSESP::show_ems(uuid::console::Shell & shell) {
shell.printfln(" #read fails (after %d retries): %d", TxService::MAXIMUM_TX_RETRIES, txservice_.telegram_read_fail_count());
shell.printfln(" #write fails (after %d retries): %d", TxService::MAXIMUM_TX_RETRIES, txservice_.telegram_write_fail_count());
shell.printfln(" Rx line quality: %d%%", rxservice_.quality());
- shell.printfln(" Tx line quality: %d%%", (txservice_.read_quality() + txservice_.read_quality()) / 2);
+ shell.printfln(" Tx line quality: %d%%", (txservice_.read_quality() + txservice_.write_quality()) / 2);
shell.println();
}
@@ -846,41 +880,58 @@ std::string EMSESP::pretty_telegram(std::shared_ptr telegram) {
std::string src_name("");
std::string dest_name("");
std::string type_name("");
+
+ // Single loop to find all device information
+ bool src_found = false;
+ bool dest_found = false;
+ bool type_found = false;
+
for (const auto & emsdevice : emsdevices) {
- // get src & dest
- if (emsdevice->is_device_id(src)) {
- src_name = emsdevice->device_type_name();
- } else if (emsdevice->is_device_id(dest)) {
- dest_name = emsdevice->device_type_name();
+ // get src name
+ if (!src_found && emsdevice->is_device_id(src)) {
+ src_name = emsdevice->device_type_name();
+ src_found = true;
}
- // get the type name
- if (type_name.empty()) {
+
+ // get dest name
+ if (!dest_found && emsdevice->is_device_id(dest)) {
+ dest_name = emsdevice->device_type_name();
+ dest_found = true;
+ }
+
+ // get the type name (try primary conditions first)
+ if (!type_found) {
if ((telegram->operation == Telegram::Operation::RX_READ && emsdevice->is_device_id(dest))
|| (telegram->operation != Telegram::Operation::RX_READ && dest == 0 && emsdevice->is_device_id(src))
|| (telegram->operation != Telegram::Operation::RX_READ && src == EMSbus::ems_bus_id() && emsdevice->is_device_id(dest))) {
type_name = emsdevice->telegram_type_name(telegram);
+ if (!type_name.empty()) {
+ type_found = true;
+ }
}
}
+
+ // Early exit if we found everything
+ if (src_found && dest_found && type_found) {
+ break;
+ }
}
- if (type_name.empty()) {
- // fallback, get the type name from src
- for (const auto & emsdevice : emsdevices) {
- if (telegram->operation != Telegram::Operation::RX_READ && emsdevice->is_device_id(src)) {
- type_name = emsdevice->telegram_type_name(telegram);
- break;
+
+ // Fallback for type name if not found - try src first, then dest
+ if (!type_found && telegram->operation != Telegram::Operation::RX_READ) {
+ for (int i = 0; i < 2 && type_name.empty(); ++i) {
+ uint8_t check_id = (i == 0) ? src : dest;
+ for (const auto & emsdevice : emsdevices) {
+ if (emsdevice->is_device_id(check_id)) {
+ type_name = emsdevice->telegram_type_name(telegram);
+ if (!type_name.empty()) {
+ break;
+ }
+ }
}
}
}
- if (type_name.empty()) {
- // 2nd fallback, get the type name from dest
- for (const auto & emsdevice : emsdevices) {
- if (telegram->operation != Telegram::Operation::RX_READ && emsdevice->is_device_id(dest)) {
- type_name = emsdevice->telegram_type_name(telegram);
- break;
- }
- }
- }
// if we can't find names for the devices, use their hex values
if (src_name.empty()) {
src_name = device_tostring(src);
@@ -1009,11 +1060,16 @@ void EMSESP::process_version(std::shared_ptr telegram) {
if (telegram->offset != 0) {
return;
}
+
+ const uint8_t msg_len = telegram->message_length;
+
// for empty telegram add device with empty product, version and brand
- if (telegram->message_length == 0) {
+ if (msg_len == 0) {
(void)add_device(telegram->src, 0, "00.00", 0);
return;
- } else if (telegram->message_length < 3) {
+ }
+
+ if (msg_len < 3) {
(void)add_device(telegram->src, telegram->message_data[0], "00.00", 0);
send_read_request(EMSdevice::EMS_TYPE_NAME, telegram->src, 27);
return;
@@ -1023,7 +1079,7 @@ void EMSESP::process_version(std::shared_ptr telegram) {
uint8_t offset = 0;
if (telegram->message_data[0] == 0x00) {
// see if we have a 2nd subscriber
- if (telegram->message_length > 5 && telegram->message_data[3] != 0x00) {
+ if (msg_len > 5 && telegram->message_data[3] != 0x00) {
offset = 3;
} else {
return; // ignore whole telegram
@@ -1040,7 +1096,7 @@ void EMSESP::process_version(std::shared_ptr telegram) {
// some devices store the protocol type (HT3, Buderus) in the last byte
uint8_t brand;
- if (telegram->message_length >= 10) {
+ if (msg_len >= 10) {
brand = EMSdevice::decode_brand(telegram->message_data[9]);
} else {
brand = EMSdevice::Brand::NO_BRAND; // unknown
@@ -1116,57 +1172,53 @@ bool EMSESP::process_telegram(std::shared_ptr telegram) {
// calls the associated process function for that EMS device
// returns false if the device_id doesn't recognize it
// after the telegram has been processed, see if there have been values changed and we need to do a MQTT publish
- bool telegram_found = false;
- uint8_t device_found = 0;
- // broadcast or send to us
+ bool telegram_found = false;
+ uint8_t device_found = 0;
+ EMSdevice * found_device = nullptr;
+
+ // Combined loop: check all conditions in a single pass
for (const auto & emsdevice : emsdevices) {
+ // broadcast or send to us
if (emsdevice->is_device_id(telegram->src) && (telegram->dest == 0 || telegram->dest == EMSbus::ems_bus_id())) {
telegram_found = emsdevice->handle_telegram(telegram);
- device_found = emsdevice->unique_id();
+ found_device = emsdevice.get();
break;
}
- }
- if (!telegram_found) {
// check for command to the device
- for (const auto & emsdevice : emsdevices) {
- if (emsdevice->is_device_id(telegram->dest) && telegram->src != EMSbus::ems_bus_id()) {
- telegram_found = emsdevice->handle_telegram(telegram);
- device_found = emsdevice->unique_id();
- break;
- }
- }
- }
- if (!telegram_found) {
- // check for sends to master thermostat
- for (const auto & emsdevice : emsdevices) {
- if (emsdevice->is_device_id(telegram->src) && telegram->dest == 0x10) {
- telegram_found = emsdevice->handle_telegram(telegram);
- device_found = emsdevice->unique_id();
- break;
- }
- }
- }
- for (const auto & emsdevice : emsdevices) {
- if (emsdevice->unique_id() == device_found) {
- if (!telegram_found && telegram->message_length > 0) {
- emsdevice->add_handlers_ignored(telegram->type_id);
- }
- if (wait_validate_ == telegram->type_id) {
- wait_validate_ = 0;
- }
- if (Mqtt::connected() && telegram_found
- && ((mqtt_.get_publish_onchange(emsdevice->device_type()) && emsdevice->has_update())
- || (telegram->type_id == publish_id_ && telegram->dest == EMSbus::ems_bus_id()))) {
- if (telegram->type_id == publish_id_) {
- publish_id_ = 0;
- }
- emsdevice->has_update(false); // reset flag
- if (!Mqtt::publish_single()) {
- publish_device_values(emsdevice->device_type()); // publish to MQTT if we explicitly have too
- }
- }
+ if (!telegram_found && emsdevice->is_device_id(telegram->dest) && telegram->src != EMSbus::ems_bus_id()) {
+ telegram_found = emsdevice->handle_telegram(telegram);
+ found_device = emsdevice.get();
break;
}
+ // check for sends to master thermostat
+ if (!telegram_found && emsdevice->is_device_id(telegram->src) && telegram->dest == 0x10) {
+ telegram_found = emsdevice->handle_telegram(telegram);
+ found_device = emsdevice.get();
+ break;
+ }
+ }
+
+ if (found_device) {
+ device_found = found_device->unique_id();
+
+ // Process the found device directly without another loop
+ if (!telegram_found && telegram->message_length > 0) {
+ found_device->add_handlers_ignored(telegram->type_id);
+ }
+ if (wait_validate_ == telegram->type_id) {
+ wait_validate_ = 0;
+ }
+ if (Mqtt::connected() && telegram_found
+ && ((mqtt_.get_publish_onchange(found_device->device_type()) && found_device->has_update())
+ || (telegram->type_id == publish_id_ && telegram->dest == EMSbus::ems_bus_id()))) {
+ if (telegram->type_id == publish_id_) {
+ publish_id_ = 0;
+ }
+ found_device->has_update(false); // reset flag
+ if (!Mqtt::publish_single()) {
+ publish_device_values(found_device->device_type()); // publish to MQTT if we explicitly have too
+ }
+ }
}
// handle unknown broadcasted telegrams (or send to us)
if (!telegram_found && (telegram->dest == 0 || telegram->dest == EMSbus::ems_bus_id())) {
@@ -1184,6 +1236,10 @@ bool EMSESP::process_telegram(std::shared_ptr telegram) {
// return true if we have this device already registered
bool EMSESP::device_exists(const uint8_t device_id) {
+ if (emsdevices.empty()) {
+ return false;
+ }
+
for (const auto & emsdevice : emsdevices) {
if (emsdevice && emsdevice->is_device_id(device_id)) {
return true;
@@ -1251,16 +1307,14 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, const
}
// first check to see if we already have it, if so update the record
- auto it = emsdevices.begin();
- for (const auto & emsdevice : emsdevices) {
- if (emsdevice && emsdevice->is_device_id(device_id)) {
- if (product_id == 0 || emsdevice->product_id() != 0) { // update only with valid product_id
+ for (auto it = emsdevices.begin(); it != emsdevices.end(); ++it) {
+ if ((*it) && (*it)->is_device_id(device_id)) {
+ if (product_id == 0 || (*it)->product_id() != 0) { // update only with valid product_id
return true;
}
emsdevices.erase(it); // erase the old device without product_id and re detect
break;
}
- it++;
}
// look up the rest of the details using the product_id and create the new device object
@@ -1691,7 +1745,7 @@ void EMSESP::start() {
device_library_ = {
#include "device_library.h"
};
- LOG_INFO("Loaded EMS device library (%d entries)", device_library_.size());
+ LOG_INFO("Library loaded: %d EMS devices, %d device entities, %s", device_library_.size(), EMSESP_TRANSLATION_COUNT, system_.languages_string().c_str());
system_.reload_settings(); // ... and store some of the settings locally
diff --git a/src/core/system.cpp b/src/core/system.cpp
index 5ca1e9a4d..ac0ce94ef 100644
--- a/src/core/system.cpp
+++ b/src/core/system.cpp
@@ -107,6 +107,19 @@ bool System::command_send(const char * value, const int8_t id) {
return EMSESP::txservice_.send_raw(value); // ignore id
}
+// return string of languages and count
+std::string System::languages_string() {
+ std::string languages_string = std::to_string(NUM_LANGUAGES) + " languages (";
+ for (uint8_t i = 0; i < NUM_LANGUAGES; i++) {
+ languages_string += languages[i];
+ if (i != NUM_LANGUAGES - 1) {
+ languages_string += ",";
+ }
+ }
+ languages_string += ")";
+ return languages_string;
+}
+
// returns last response from MQTT
bool System::command_response(const char * value, const int8_t id, JsonObject output) {
JsonDocument doc;
@@ -523,8 +536,18 @@ void System::button_OnClick(PButton & b) {
// button double click
void System::button_OnDblClick(PButton & b) {
- LOG_NOTICE("Button pressed - double click - wifi reconnect");
- EMSESP::system_.wifi_reconnect();
+ LOG_NOTICE("Button pressed - double click - wifi reconnect to AP");
+ // set AP mode to always so will join AP if wifi ssid fails to connect
+ EMSESP::esp32React.getAPSettingsService()->update([&](APSettings & apSettings) {
+ apSettings.provisionMode = AP_MODE_ALWAYS;
+ return StateUpdateResult::CHANGED;
+ });
+ // remove SSID from network settings
+ EMSESP::esp32React.getNetworkSettingsService()->update([&](NetworkSettings & networkSettings) {
+ networkSettings.ssid = "";
+ return StateUpdateResult::CHANGED;
+ });
+ EMSESP::esp32React.getNetworkSettingsService()->callUpdateHandlers(); // in case we've changed ssid or password
}
// button long press
@@ -1439,6 +1462,7 @@ bool System::command_service(const char * cmd, const char * value) {
ok = true;
}
}
+
int n;
if (!ok && Helpers::value2number(value, n)) {
#ifndef EMSESP_STANDALONE
@@ -1647,26 +1671,38 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output
}
});
-#ifndef EMSESP_STANDALONE
- EMSESP::esp32React.getAPSettingsService()->read([&](const APSettings & settings) {
- const char * pM[] = {"always", "disconnected", "never"};
- node["APProvisionMode"] = pM[settings.provisionMode];
- node["APSecurity"] = settings.password.length() ? "wpa2" : "open";
- node["APSSID"] = settings.ssid;
- });
-#endif
-
// NTP status
node = output["ntp"].to();
-#ifndef EMSESP_STANDALONE
- node["NTPStatus"] = EMSESP::system_.ntp_connected() ? "connected" : "disconnected";
EMSESP::esp32React.getNTPSettingsService()->read([&](const NTPSettings & settings) {
+#ifndef EMSESP_STANDALONE
node["enabled"] = settings.enabled;
+#else
+ node["enabled"] = true;
+#endif
node["server"] = settings.server;
node["tzLabel"] = settings.tzLabel;
});
+#ifndef EMSESP_STANDALONE
node["timestamp"] = time(nullptr);
#endif
+ node["NTPStatus"] = EMSESP::system_.ntp_connected() ? "connected" : "disconnected";
+
+ // AP Status
+ node = output["ap"].to();
+ EMSESP::esp32React.getAPSettingsService()->read([&](const APSettings & settings) {
+ const char * pM[] = {"always", "disconnected", "never"};
+ node["provisionMode"] = pM[settings.provisionMode];
+ node["ssid"] = settings.ssid;
+#ifndef EMSESP_STANDALONE
+ node["security"] = settings.password.length() ? "wpa2" : "open";
+ node["channel"] = settings.channel;
+ node["ssidHidden"] = settings.ssidHidden;
+ node["maxClients"] = settings.maxClients;
+ node["localIP"] = settings.localIP.toString();
+ node["gatewayIP"] = settings.gatewayIP.toString();
+ node["subnetMask"] = settings.subnetMask.toString();
+#endif
+ });
// MQTT Status
node = output["mqtt"].to();
@@ -1764,7 +1800,7 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output
node["busReadsFailed"] = EMSESP::txservice_.telegram_read_fail_count();
node["busWritesFailed"] = EMSESP::txservice_.telegram_write_fail_count();
node["busRxLineQuality"] = EMSESP::rxservice_.quality();
- node["busTxLineQuality"] = (EMSESP::txservice_.read_quality() + EMSESP::txservice_.read_quality()) / 2;
+ node["busTxLineQuality"] = (EMSESP::txservice_.read_quality() + EMSESP::txservice_.write_quality()) / 2;
// Settings
node = output["settings"].to();
@@ -1958,15 +1994,12 @@ bool System::load_board_profile(std::vector & data, const std::string &
// format command - factory reset, removing all config files
bool System::command_format(const char * value, const int8_t id) {
- LOG_INFO("Removing all config files");
+ LOG_INFO("Formatting FS, removing all config files");
#ifndef EMSESP_STANDALONE
- // TODO To replaced with LittleFS.rmdir(FS_CONFIG_DIRECTORY) now we're using IDF 4.2+
- File root = LittleFS.open(EMSESP_FS_CONFIG_DIRECTORY);
- File file;
- while ((file = root.openNextFile())) {
- String path = file.path();
- file.close();
- LittleFS.remove(path);
+ if (LittleFS.format()) {
+ LOG_INFO("FS formatted successfully");
+ } else {
+ LOG_ERROR("Format failed");
}
#endif
diff --git a/src/core/system.h b/src/core/system.h
index c151e2fd5..01bf97f88 100644
--- a/src/core/system.h
+++ b/src/core/system.h
@@ -291,6 +291,8 @@ class System {
void wifi_reconnect();
void show_users(uuid::console::Shell & shell);
+ static std::string languages_string();
+
uint32_t FStotal() {
return fstotal_;
}
diff --git a/src/emsesp_version.h b/src/emsesp_version.h
index caaa33454..cc360f69c 100644
--- a/src/emsesp_version.h
+++ b/src/emsesp_version.h
@@ -1 +1 @@
-#define EMSESP_APP_VERSION "3.7.3-dev.25"
+#define EMSESP_APP_VERSION "3.7.3-dev.26"
diff --git a/src/web/WebCustomizationService.cpp b/src/web/WebCustomizationService.cpp
index d1068861d..30a510376 100644
--- a/src/web/WebCustomizationService.cpp
+++ b/src/web/WebCustomizationService.cpp
@@ -63,13 +63,14 @@ void WebCustomization::read(WebCustomization & customizations, JsonObject root)
// Analog Sensor customization
JsonArray analogJson = root["as"].to();
for (const AnalogCustomization & sensor : customizations.analogCustomizations) {
- JsonObject sensorJson = analogJson.add();
- sensorJson["gpio"] = sensor.gpio; // g
- sensorJson["name"] = sensor.name; // n
- sensorJson["offset"] = sensor.offset; // o
- sensorJson["factor"] = sensor.factor; // f
- sensorJson["uom"] = sensor.uom; // u
- sensorJson["type"] = sensor.type; // t
+ JsonObject sensorJson = analogJson.add();
+ sensorJson["gpio"] = sensor.gpio; // g
+ sensorJson["name"] = sensor.name; // n
+ sensorJson["offset"] = sensor.offset; // o
+ sensorJson["factor"] = sensor.factor; // f
+ sensorJson["uom"] = sensor.uom; // u
+ sensorJson["type"] = sensor.type; // t
+ sensorJson["is_system"] = sensor.is_system; // s
}
// Masked entities customization and custom device name (optional)
@@ -115,13 +116,14 @@ StateUpdateResult WebCustomization::update(JsonObject root, WebCustomization & c
auto analogJsons = root["as"].as();
for (const JsonObject analogJson : analogJsons) {
// create each of the sensor, overwriting any previous settings
- auto analog = AnalogCustomization();
- analog.gpio = analogJson["gpio"];
- analog.name = analogJson["name"].as();
- analog.offset = analogJson["offset"];
- analog.factor = analogJson["factor"];
- analog.uom = analogJson["uom"];
- analog.type = analogJson["type"];
+ auto analog = AnalogCustomization();
+ analog.gpio = analogJson["gpio"];
+ analog.name = analogJson["name"].as();
+ analog.offset = analogJson["offset"];
+ analog.factor = analogJson["factor"];
+ analog.uom = analogJson["uom"];
+ analog.type = analogJson["type"];
+ analog.is_system = analogJson["is_system"];
if (_start && analog.type == EMSESP::analogsensor_.AnalogType::DIGITAL_OUT && analog.uom > DeviceValue::DeviceValueUOM::NONE) {
analog.offset = analog.uom - 1;
}
diff --git a/src/web/WebCustomizationService.h b/src/web/WebCustomizationService.h
index f12e5db33..9b416d213 100644
--- a/src/web/WebCustomizationService.h
+++ b/src/web/WebCustomizationService.h
@@ -45,8 +45,9 @@ class AnalogCustomization {
std::string name;
double offset;
double factor;
- uint8_t uom; // 0 is none
- int8_t type; // -1 is for deletion
+ uint8_t uom; // 0 is none
+ int8_t type; // -1 is for deletion
+ bool is_system = false; // if true, the customization is a system customization
// used for removing from a list
bool operator==(const AnalogCustomization & a) const {
diff --git a/src/web/WebDataService.cpp b/src/web/WebDataService.cpp
index 735a35182..df4db1f3f 100644
--- a/src/web/WebDataService.cpp
+++ b/src/web/WebDataService.cpp
@@ -143,6 +143,7 @@ void WebDataService::sensor_data(AsyncWebServerRequest * request) {
obj["o"] = sensor.offset();
obj["f"] = sensor.factor();
obj["t"] = sensor.type();
+ obj["s"] = sensor.is_system();
if (sensor.type() != AnalogSensor::AnalogType::NOTUSED) {
obj["v"] = Helpers::transformNumFloat(sensor.value()); // is optional and is a float
diff --git a/test/test_api/test_api.h b/test/test_api/test_api.h
index b3c7094c5..d4b3cc60f 100644
--- a/test/test_api/test_api.h
+++ b/test/test_api/test_api.h
@@ -1,4 +1,3 @@
-
// ---------- START - CUT HERE ----------
void test_1() {
@@ -162,22 +161,24 @@ void test_21() {
auto expected_response =
"[{\"system\":{\"version\":\"dev\",\"uptime\":\"000+00:00:00.000\",\"uptimeSec\":0,\"resetReason\":\"Unknown / "
"Unknown\"},\"network\":{\"network\":\"WiFi\",\"hostname\":\"ems-esp\",\"RSSI\":-23,\"TxPowerSetting\":0,\"staticIP\":false,\"lowBandwidth\":false,"
- "\"disableSleep\":true,\"enableMDNS\":true,\"enableCORS\":false},\"ntp\":{},\"mqtt\":{\"MQTTStatus\":\"disconnected\",\"MQTTPublishes\":0,"
- "\"MQTTQueued\":0,\"MQTTPublishFails\":0,\"MQTTReconnects\":0,\"enabled\":true,\"clientID\":\"ems-esp\",\"keepAlive\":60,\"cleanSession\":false,"
- "\"entityFormat\":1,\"base\":\"ems-esp\",\"discoveryPrefix\":\"homeassistant\",\"discoveryType\":0,\"nestedFormat\":1,\"haEnabled\":true,\"mqttQos\":0,"
- "\"mqttRetain\":false,\"publishTimeHeartbeat\":60,\"publishTimeBoiler\":10,\"publishTimeThermostat\":10,\"publishTimeSolar\":10,\"publishTimeMixer\":"
- "10,\"publishTimeWater\":0,\"publishTimeOther\":10,\"publishTimeSensor\":10,\"publishSingle\":false,\"publish2command\":false,\"sendResponse\":false},"
- "\"syslog\":{\"enabled\":false},\"sensor\":{\"temperatureSensors\":2,\"temperatureSensorReads\":0,\"temperatureSensorFails\":0,\"analogSensors\":4,"
- "\"analogSensorReads\":0,\"analogSensorFails\":0},\"api\":{\"APICalls\":0,\"APIFails\":0},\"bus\":{\"busStatus\":\"connected\",\"busProtocol\":"
- "\"Buderus\",\"busTelegramsReceived\":8,\"busReads\":0,\"busWrites\":0,\"busIncompleteTelegrams\":0,\"busReadsFailed\":0,\"busWritesFailed\":0,"
- "\"busRxLineQuality\":100,\"busTxLineQuality\":100},\"settings\":{\"boardProfile\":\"S32\",\"locale\":\"en\",\"txMode\":8,\"emsBusID\":11,"
- "\"showerTimer\":false,\"showerMinDuration\":180,\"showerAlert\":false,\"hideLed\":false,\"noTokenApi\":false,\"readonlyMode\":false,\"fahrenheit\":"
- "false,\"dallasParasite\":false,\"boolFormat\":1,\"boolDashboard\":1,\"enumFormat\":1,\"analogEnabled\":true,\"telnetEnabled\":true,"
- "\"maxWebLogBuffer\":25,\"modbusEnabled\":false,\"forceHeatingOff\":false,\"developerMode\":false},\"devices\":[{\"type\":\"boiler\",\"name\":\"My "
- "Custom "
+ "\"disableSleep\":true,\"enableMDNS\":true,\"enableCORS\":false},\"ntp\":{\"enabled\":true,\"server\":\"pool.ntp.org\",\"tzLabel\":\"Europe/"
+ "London\",\"NTPStatus\":\"disconnected\"},\"ap\":{\"provisionMode\":\"always\",\"ssid\":\"ems-esp\"},\"mqtt\":{\"MQTTStatus\":\"disconnected\","
+ "\"MQTTPublishes\":0,\"MQTTQueued\":0,\"MQTTPublishFails\":0,\"MQTTReconnects\":0,\"enabled\":true,\"clientID\":\"ems-esp\",\"keepAlive\":60,"
+ "\"cleanSession\":false,\"entityFormat\":1,\"base\":\"ems-esp\",\"discoveryPrefix\":\"homeassistant\",\"discoveryType\":0,\"nestedFormat\":1,"
+ "\"haEnabled\":true,\"mqttQos\":0,\"mqttRetain\":false,\"publishTimeHeartbeat\":60,\"publishTimeBoiler\":10,\"publishTimeThermostat\":10,"
+ "\"publishTimeSolar\":10,\"publishTimeMixer\":10,\"publishTimeWater\":0,\"publishTimeOther\":10,\"publishTimeSensor\":10,\"publishSingle\":false,"
+ "\"publish2command\":false,\"sendResponse\":false},\"syslog\":{\"enabled\":false},\"sensor\":{\"temperatureSensors\":2,\"temperatureSensorReads\":0,"
+ "\"temperatureSensorFails\":0,\"analogSensors\":4,\"analogSensorReads\":0,\"analogSensorFails\":0},\"api\":{\"APICalls\":0,\"APIFails\":0},\"bus\":{"
+ "\"busStatus\":\"connected\",\"busProtocol\":\"Buderus\",\"busTelegramsReceived\":8,\"busReads\":0,\"busWrites\":0,\"busIncompleteTelegrams\":0,"
+ "\"busReadsFailed\":0,\"busWritesFailed\":0,\"busRxLineQuality\":100,\"busTxLineQuality\":100},\"settings\":{\"boardProfile\":\"S32\",\"locale\":"
+ "\"en\",\"txMode\":8,\"emsBusID\":11,\"showerTimer\":false,\"showerMinDuration\":180,\"showerAlert\":false,\"hideLed\":false,\"noTokenApi\":false,"
+ "\"readonlyMode\":false,\"fahrenheit\":false,\"dallasParasite\":false,\"boolFormat\":1,\"boolDashboard\":1,\"enumFormat\":1,\"analogEnabled\":true,"
+ "\"telnetEnabled\":true,\"maxWebLogBuffer\":25,\"modbusEnabled\":false,\"forceHeatingOff\":false,\"developerMode\":false},\"devices\":[{\"type\":"
+ "\"boiler\",\"name\":\"My Custom "
"Boiler\",\"deviceID\":\"0x08\",\"productID\":123,\"brand\":\"\",\"version\":\"01.00\",\"entities\":38,\"handlersReceived\":\"0x18\","
"\"handlersFetched\":\"0x14 0x33\",\"handlersPending\":\"0xBF 0x10 0x11 0xC2 0xC6 0x15 0x1C 0x19 0x1A 0x35 0x34 0x2A 0xD1 0xE3 0xE4 0xE5 0xE9 0x02E0 "
- "0x2E 0x3B\"},{\"type\":\"thermostat\",\"name\":\"FW120\",\"deviceID\":\"0x10\",\"productID\":192,\"brand\":\"\",\"version\":\"01.00\",\"entities\":15,"
+ "0x2E "
+ "0x3B\"},{\"type\":\"thermostat\",\"name\":\"FW120\",\"deviceID\":\"0x10\",\"productID\":192,\"brand\":\"\",\"version\":\"01.00\",\"entities\":15,"
"\"handlersReceived\":\"0x016F\",\"handlersFetched\":\"0x0170 0x0171\",\"handlersPending\":\"0xA3 0x06 0xA2 0x12 0x13 0x0172 0x0165 "
"0x0168\"},{\"type\":\"temperaturesensor\",\"name\":\"temperaturesensor\",\"entities\":2},{\"type\":\"analogsensor\",\"name\":\"analogsensor\","
"\"entities\":4},{\"type\":\"scheduler\",\"name\":\"scheduler\",\"entities\":2},{\"type\":\"custom\",\"name\":\"custom\",\"entities\":4}]}]";
@@ -188,22 +189,24 @@ void test_22() {
auto expected_response =
"[{\"system\":{\"version\":\"dev\",\"uptime\":\"000+00:00:00.000\",\"uptimeSec\":0,\"resetReason\":\"Unknown / "
"Unknown\"},\"network\":{\"network\":\"WiFi\",\"hostname\":\"ems-esp\",\"RSSI\":-23,\"TxPowerSetting\":0,\"staticIP\":false,\"lowBandwidth\":false,"
- "\"disableSleep\":true,\"enableMDNS\":true,\"enableCORS\":false},\"ntp\":{},\"mqtt\":{\"MQTTStatus\":\"disconnected\",\"MQTTPublishes\":0,"
- "\"MQTTQueued\":0,\"MQTTPublishFails\":0,\"MQTTReconnects\":0,\"enabled\":true,\"clientID\":\"ems-esp\",\"keepAlive\":60,\"cleanSession\":false,"
- "\"entityFormat\":1,\"base\":\"ems-esp\",\"discoveryPrefix\":\"homeassistant\",\"discoveryType\":0,\"nestedFormat\":1,\"haEnabled\":true,\"mqttQos\":0,"
- "\"mqttRetain\":false,\"publishTimeHeartbeat\":60,\"publishTimeBoiler\":10,\"publishTimeThermostat\":10,\"publishTimeSolar\":10,\"publishTimeMixer\":"
- "10,\"publishTimeWater\":0,\"publishTimeOther\":10,\"publishTimeSensor\":10,\"publishSingle\":false,\"publish2command\":false,\"sendResponse\":false},"
- "\"syslog\":{\"enabled\":false},\"sensor\":{\"temperatureSensors\":2,\"temperatureSensorReads\":0,\"temperatureSensorFails\":0,\"analogSensors\":4,"
- "\"analogSensorReads\":0,\"analogSensorFails\":0},\"api\":{\"APICalls\":0,\"APIFails\":0},\"bus\":{\"busStatus\":\"connected\",\"busProtocol\":"
- "\"Buderus\",\"busTelegramsReceived\":8,\"busReads\":0,\"busWrites\":0,\"busIncompleteTelegrams\":0,\"busReadsFailed\":0,\"busWritesFailed\":0,"
- "\"busRxLineQuality\":100,\"busTxLineQuality\":100},\"settings\":{\"boardProfile\":\"S32\",\"locale\":\"en\",\"txMode\":8,\"emsBusID\":11,"
- "\"showerTimer\":false,\"showerMinDuration\":180,\"showerAlert\":false,\"hideLed\":false,\"noTokenApi\":false,\"readonlyMode\":false,\"fahrenheit\":"
- "false,\"dallasParasite\":false,\"boolFormat\":1,\"boolDashboard\":1,\"enumFormat\":1,\"analogEnabled\":true,\"telnetEnabled\":true,"
- "\"maxWebLogBuffer\":25,\"modbusEnabled\":false,\"forceHeatingOff\":false,\"developerMode\":false},\"devices\":[{\"type\":\"boiler\",\"name\":\"My "
- "Custom "
+ "\"disableSleep\":true,\"enableMDNS\":true,\"enableCORS\":false},\"ntp\":{\"enabled\":true,\"server\":\"pool.ntp.org\",\"tzLabel\":\"Europe/"
+ "London\",\"NTPStatus\":\"disconnected\"},\"ap\":{\"provisionMode\":\"always\",\"ssid\":\"ems-esp\"},\"mqtt\":{\"MQTTStatus\":\"disconnected\","
+ "\"MQTTPublishes\":0,\"MQTTQueued\":0,\"MQTTPublishFails\":0,\"MQTTReconnects\":0,\"enabled\":true,\"clientID\":\"ems-esp\",\"keepAlive\":60,"
+ "\"cleanSession\":false,\"entityFormat\":1,\"base\":\"ems-esp\",\"discoveryPrefix\":\"homeassistant\",\"discoveryType\":0,\"nestedFormat\":1,"
+ "\"haEnabled\":true,\"mqttQos\":0,\"mqttRetain\":false,\"publishTimeHeartbeat\":60,\"publishTimeBoiler\":10,\"publishTimeThermostat\":10,"
+ "\"publishTimeSolar\":10,\"publishTimeMixer\":10,\"publishTimeWater\":0,\"publishTimeOther\":10,\"publishTimeSensor\":10,\"publishSingle\":false,"
+ "\"publish2command\":false,\"sendResponse\":false},\"syslog\":{\"enabled\":false},\"sensor\":{\"temperatureSensors\":2,\"temperatureSensorReads\":0,"
+ "\"temperatureSensorFails\":0,\"analogSensors\":4,\"analogSensorReads\":0,\"analogSensorFails\":0},\"api\":{\"APICalls\":0,\"APIFails\":0},\"bus\":{"
+ "\"busStatus\":\"connected\",\"busProtocol\":\"Buderus\",\"busTelegramsReceived\":8,\"busReads\":0,\"busWrites\":0,\"busIncompleteTelegrams\":0,"
+ "\"busReadsFailed\":0,\"busWritesFailed\":0,\"busRxLineQuality\":100,\"busTxLineQuality\":100},\"settings\":{\"boardProfile\":\"S32\",\"locale\":"
+ "\"en\",\"txMode\":8,\"emsBusID\":11,\"showerTimer\":false,\"showerMinDuration\":180,\"showerAlert\":false,\"hideLed\":false,\"noTokenApi\":false,"
+ "\"readonlyMode\":false,\"fahrenheit\":false,\"dallasParasite\":false,\"boolFormat\":1,\"boolDashboard\":1,\"enumFormat\":1,\"analogEnabled\":true,"
+ "\"telnetEnabled\":true,\"maxWebLogBuffer\":25,\"modbusEnabled\":false,\"forceHeatingOff\":false,\"developerMode\":false},\"devices\":[{\"type\":"
+ "\"boiler\",\"name\":\"My Custom "
"Boiler\",\"deviceID\":\"0x08\",\"productID\":123,\"brand\":\"\",\"version\":\"01.00\",\"entities\":38,\"handlersReceived\":\"0x18\","
"\"handlersFetched\":\"0x14 0x33\",\"handlersPending\":\"0xBF 0x10 0x11 0xC2 0xC6 0x15 0x1C 0x19 0x1A 0x35 0x34 0x2A 0xD1 0xE3 0xE4 0xE5 0xE9 0x02E0 "
- "0x2E 0x3B\"},{\"type\":\"thermostat\",\"name\":\"FW120\",\"deviceID\":\"0x10\",\"productID\":192,\"brand\":\"\",\"version\":\"01.00\",\"entities\":15,"
+ "0x2E "
+ "0x3B\"},{\"type\":\"thermostat\",\"name\":\"FW120\",\"deviceID\":\"0x10\",\"productID\":192,\"brand\":\"\",\"version\":\"01.00\",\"entities\":15,"
"\"handlersReceived\":\"0x016F\",\"handlersFetched\":\"0x0170 0x0171\",\"handlersPending\":\"0xA3 0x06 0xA2 0x12 0x13 0x0172 0x0165 "
"0x0168\"},{\"type\":\"temperaturesensor\",\"name\":\"temperaturesensor\",\"entities\":2},{\"type\":\"analogsensor\",\"name\":\"analogsensor\","
"\"entities\":4},{\"type\":\"scheduler\",\"name\":\"scheduler\",\"entities\":2},{\"type\":\"custom\",\"name\":\"custom\",\"entities\":4}]}]";
@@ -281,8 +284,9 @@ void test_35() {
}
void test_36() {
- auto expected_response = "[{\"name\":\"test_analogsensor1\",\"fullname\":\"test_analogsensor1\",\"gpio\":36,\"type\":\"number\",\"analog\":\"adc\","
- "\"value\":0,\"readable\":true,\"writeable\":false,\"visible\":true,\"offset\":0,\"factor\":0.1,\"uom\":\"mV\"}]";
+ auto expected_response =
+ "[{\"name\":\"test_analogsensor1\",\"fullname\":\"test_analogsensor1\",\"gpio\":36,\"type\":\"number\",\"analog\":\"adc\",\"value\":0,\"readable\":"
+ "true,\"writeable\":false,\"visible\":true,\"is_system\":true,\"offset\":0,\"factor\":0.1,\"uom\":\"mV\"}]";
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/analogsensor/test_analogsensor1"));
}