diff --git a/CHANGELOG.md b/CHANGELOG.md index 110b43c9e..6314e7c64 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,8 +26,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - FW200 display options [#2610](https://github.com/emsesp/EMS-ESP32/discussions/2610) - CR11 mode settings OFF/MANUAL depends on selTemp [#2437](https://github.com/emsesp/EMS-ESP32/issues/2437) - implemented eFuse settings for BBQKees boards to store model type and ESP chipset -- Analogsensors for pulse output [#2624](https://github.com/emsesp/EMS-ESP32/discussions/2624) -- Analogsensors frequency input [#2631](https://github.com/emsesp/EMS-ESP32/discussions/2631) +- analogsensors for pulse output [#2624](https://github.com/emsesp/EMS-ESP32/discussions/2624) +- analogsensors frequency input [#2631](https://github.com/emsesp/EMS-ESP32/discussions/2631) - SRC plus thermostats [#2636](https://github.com/emsesp/EMS-ESP32/issues/2636) - Greenstar 2000 [#2645](https://github.com/emsesp/EMS-ESP32/issues/2645) - RC3xx `dhw modetype` [#2659](https://github.com/emsesp/EMS-ESP32/discussions/2659) diff --git a/CHANGELOG_LATEST.md b/CHANGELOG_LATEST.md index 24c3a9cb1..557966ecf 100644 --- a/CHANGELOG_LATEST.md +++ b/CHANGELOG_LATEST.md @@ -6,12 +6,19 @@ For more details go to [emsesp.org](https://emsesp.org/). ## Added -- update time safed in nvs +- update time saved in nvs ## Fixed -- selflowtemp [#2876](https://github.com/emsesp/EMS-ESP32/issues/2876) +- fix EMS bus disconnected errors on some systems [#2881](https://github.com/emsesp/EMS-ESP32/issues/2881) +- selflowtemp fix [#2876](https://github.com/emsesp/EMS-ESP32/issues/2876) +- updated valid GPIOs for ESP32S2, ESP32S3 and ESP32 that caused custom systems to block gpios [#2887](https://github.com/emsesp/EMS-ESP32/issues/2887) +- Junkers wwcharge offset [#2860](https://github.com/emsesp/EMS-ESP32/issues/2860) +- fixed minflowtemp [#2890](https://github.com/emsesp/EMS-ESP32/issues/2890) +- don't add HA uom/classes for bool values [#2885](https://github.com/emsesp/EMS-ESP32/issues/2885) +- fixed missing progress bar on web firmware uploads ## Changed -- snapshot gpios in temporarly ram +- snapshot gpios stored in temporary ram +- GPIOs stored along with the name and reported in log if conflicting diff --git a/Makefile b/Makefile index fdab1d661..c1ca05b90 100644 --- a/Makefile +++ b/Makefile @@ -67,7 +67,7 @@ DEFINES += -DARDUINOJSON_ENABLE -DARDUINOJSON_ENABLE_ARDUINO_STRING -DARDUINOJSO DEFINES += -DEMSESP_STANDALONE -DEMSESP_TEST -DEMSESP_DEBUG -DEMC_RX_BUFFER_SIZE=1500 DEFINES += $(ARGS) -DEFAULTS = -DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_TX_MODE=8 -DEMSESP_DEFAULT_VERSION=\"3.8.0-dev.0\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32S3\" +DEFAULTS = -DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32S3\" #---------------------------------------------------------------------- # Sources & Files diff --git a/boards/s2_4M_P.json b/boards/s2_4M_P.json index 349b065b1..3c313da37 100644 --- a/boards/s2_4M_P.json +++ b/boards/s2_4M_P.json @@ -37,8 +37,8 @@ "flash_size": "4MB", "maximum_ram_size": 327680, "maximum_size": 4194304, - "use_1200bps_touch": true, - "wait_for_upload_port": true, + "use_1200bps_touch": false, + "wait_for_upload_port": false, "require_upload_port": true, "speed": 921600 }, diff --git a/cspell.json b/cspell.json index 24df974df..880eedb9c 100644 --- a/cspell.json +++ b/cspell.json @@ -35,6 +35,7 @@ "managed_components/**", "pnpm-*.yaml", "vite.config.ts", - "lib/esp32-psram/**" + "lib/esp32-psram/**", + "test/test_api/test_api.h" ] } \ No newline at end of file diff --git a/interface/package.json b/interface/package.json index 3e242cb24..4bc3b941d 100644 --- a/interface/package.json +++ b/interface/package.json @@ -37,7 +37,7 @@ "jwt-decode": "^4.0.0", "magic-string": "^0.30.21", "mime-types": "^3.0.2", - "preact": "^10.28.1", + "preact": "^10.28.2", "react": "^19.2.3", "react-dom": "^19.2.3", "react-icons": "^5.5.0", @@ -51,18 +51,18 @@ "@eslint/js": "^9.39.2", "@preact/compat": "^18.3.1", "@preact/preset-vite": "^2.10.2", - "@trivago/prettier-plugin-sort-imports": "^6.0.0", + "@trivago/prettier-plugin-sort-imports": "^6.0.1", "@types/node": "^25.0.3", "@types/react": "^19.2.7", "@types/react-dom": "^19.2.3", - "axe-core": "^4.11.0", + "axe-core": "^4.11.1", "concurrently": "^9.2.1", "eslint": "^9.39.2", "eslint-config-prettier": "^10.1.8", "prettier": "^3.7.4", "rollup-plugin-visualizer": "^6.0.5", "terser": "^5.44.1", - "typescript-eslint": "^8.51.0", + "typescript-eslint": "^8.52.0", "vite": "^7.3.0", "vite-plugin-imagemin": "^0.6.1", "vite-tsconfig-paths": "^6.0.3" diff --git a/interface/pnpm-lock.yaml b/interface/pnpm-lock.yaml index e37fff143..091e63690 100644 --- a/interface/pnpm-lock.yaml +++ b/interface/pnpm-lock.yaml @@ -25,7 +25,7 @@ importers: version: 7.3.6(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react@19.2.3))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@preact/compat': specifier: ^18.3.1 - version: 18.3.1(preact@10.28.1) + version: 18.3.1(preact@10.28.2) '@table-library/react-table-library': specifier: 4.1.15 version: 4.1.15(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3) @@ -51,8 +51,8 @@ importers: specifier: ^3.0.2 version: 3.0.2 preact: - specifier: ^10.28.1 - version: 10.28.1 + specifier: ^10.28.2 + version: 10.28.2 react: specifier: ^19.2.3 version: 19.2.3 @@ -83,10 +83,10 @@ importers: version: 9.39.2 '@preact/preset-vite': specifier: ^2.10.2 - version: 2.10.2(@babel/core@7.28.5)(preact@10.28.1)(vite@7.3.0(@types/node@25.0.3)(terser@5.44.1)) + version: 2.10.2(@babel/core@7.28.5)(preact@10.28.2)(vite@7.3.0(@types/node@25.0.3)(terser@5.44.1)) '@trivago/prettier-plugin-sort-imports': - specifier: ^6.0.0 - version: 6.0.0(prettier@3.7.4) + specifier: ^6.0.1 + version: 6.0.1(prettier@3.7.4) '@types/node': specifier: ^25.0.3 version: 25.0.3 @@ -97,8 +97,8 @@ importers: specifier: ^19.2.3 version: 19.2.3(@types/react@19.2.7) axe-core: - specifier: ^4.11.0 - version: 4.11.0 + specifier: ^4.11.1 + version: 4.11.1 concurrently: specifier: ^9.2.1 version: 9.2.1 @@ -113,13 +113,13 @@ importers: version: 3.7.4 rollup-plugin-visualizer: specifier: ^6.0.5 - version: 6.0.5(rollup@4.54.0) + version: 6.0.5(rollup@4.55.1) terser: specifier: ^5.44.1 version: 5.44.1 typescript-eslint: - specifier: ^8.51.0 - version: 8.51.0(eslint@9.39.2)(typescript@5.9.3) + specifier: ^8.52.0 + version: 8.52.0(eslint@9.39.2)(typescript@5.9.3) vite: specifier: ^7.3.0 version: 7.3.0(@types/node@25.0.3)(terser@5.44.1) @@ -679,113 +679,128 @@ packages: resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==} engines: {node: '>= 8.0.0'} - '@rollup/rollup-android-arm-eabi@4.54.0': - resolution: {integrity: sha512-OywsdRHrFvCdvsewAInDKCNyR3laPA2mc9bRYJ6LBp5IyvF3fvXbbNR0bSzHlZVFtn6E0xw2oZlyjg4rKCVcng==} + '@rollup/rollup-android-arm-eabi@4.55.1': + resolution: {integrity: sha512-9R0DM/ykwfGIlNu6+2U09ga0WXeZ9MRC2Ter8jnz8415VbuIykVuc6bhdrbORFZANDmTDvq26mJrEVTl8TdnDg==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.54.0': - resolution: {integrity: sha512-Skx39Uv+u7H224Af+bDgNinitlmHyQX1K/atIA32JP3JQw6hVODX5tkbi2zof/E69M1qH2UoN3Xdxgs90mmNYw==} + '@rollup/rollup-android-arm64@4.55.1': + resolution: {integrity: sha512-eFZCb1YUqhTysgW3sj/55du5cG57S7UTNtdMjCW7LwVcj3dTTcowCsC8p7uBdzKsZYa8J7IDE8lhMI+HX1vQvg==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.54.0': - resolution: {integrity: sha512-k43D4qta/+6Fq+nCDhhv9yP2HdeKeP56QrUUTW7E6PhZP1US6NDqpJj4MY0jBHlJivVJD5P8NxrjuobZBJTCRw==} + '@rollup/rollup-darwin-arm64@4.55.1': + resolution: {integrity: sha512-p3grE2PHcQm2e8PSGZdzIhCKbMCw/xi9XvMPErPhwO17vxtvCN5FEA2mSLgmKlCjHGMQTP6phuQTYWUnKewwGg==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.54.0': - resolution: {integrity: sha512-cOo7biqwkpawslEfox5Vs8/qj83M/aZCSSNIWpVzfU2CYHa2G3P1UN5WF01RdTHSgCkri7XOlTdtk17BezlV3A==} + '@rollup/rollup-darwin-x64@4.55.1': + resolution: {integrity: sha512-rDUjG25C9qoTm+e02Esi+aqTKSBYwVTaoS1wxcN47/Luqef57Vgp96xNANwt5npq9GDxsH7kXxNkJVEsWEOEaQ==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.54.0': - resolution: {integrity: sha512-miSvuFkmvFbgJ1BevMa4CPCFt5MPGw094knM64W9I0giUIMMmRYcGW/JWZDriaw/k1kOBtsWh1z6nIFV1vPNtA==} + '@rollup/rollup-freebsd-arm64@4.55.1': + resolution: {integrity: sha512-+JiU7Jbp5cdxekIgdte0jfcu5oqw4GCKr6i3PJTlXTCU5H5Fvtkpbs4XJHRmWNXF+hKmn4v7ogI5OQPaupJgOg==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.54.0': - resolution: {integrity: sha512-KGXIs55+b/ZfZsq9aR026tmr/+7tq6VG6MsnrvF4H8VhwflTIuYh+LFUlIsRdQSgrgmtM3fVATzEAj4hBQlaqQ==} + '@rollup/rollup-freebsd-x64@4.55.1': + resolution: {integrity: sha512-V5xC1tOVWtLLmr3YUk2f6EJK4qksksOYiz/TCsFHu/R+woubcLWdC9nZQmwjOAbmExBIVKsm1/wKmEy4z4u4Bw==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.54.0': - resolution: {integrity: sha512-EHMUcDwhtdRGlXZsGSIuXSYwD5kOT9NVnx9sqzYiwAc91wfYOE1g1djOEDseZJKKqtHAHGwnGPQu3kytmfaXLQ==} + '@rollup/rollup-linux-arm-gnueabihf@4.55.1': + resolution: {integrity: sha512-Rn3n+FUk2J5VWx+ywrG/HGPTD9jXNbicRtTM11e/uorplArnXZYsVifnPPqNNP5BsO3roI4n8332ukpY/zN7rQ==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.54.0': - resolution: {integrity: sha512-+pBrqEjaakN2ySv5RVrj/qLytYhPKEUwk+e3SFU5jTLHIcAtqh2rLrd/OkbNuHJpsBgxsD8ccJt5ga/SeG0JmA==} + '@rollup/rollup-linux-arm-musleabihf@4.55.1': + resolution: {integrity: sha512-grPNWydeKtc1aEdrJDWk4opD7nFtQbMmV7769hiAaYyUKCT1faPRm2av8CX1YJsZ4TLAZcg9gTR1KvEzoLjXkg==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.54.0': - resolution: {integrity: sha512-NSqc7rE9wuUaRBsBp5ckQ5CVz5aIRKCwsoa6WMF7G01sX3/qHUw/z4pv+D+ahL1EIKy6Enpcnz1RY8pf7bjwng==} + '@rollup/rollup-linux-arm64-gnu@4.55.1': + resolution: {integrity: sha512-a59mwd1k6x8tXKcUxSyISiquLwB5pX+fJW9TkWU46lCqD/GRDe9uDN31jrMmVP3feI3mhAdvcCClhV8V5MhJFQ==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.54.0': - resolution: {integrity: sha512-gr5vDbg3Bakga5kbdpqx81m2n9IX8M6gIMlQQIXiLTNeQW6CucvuInJ91EuCJ/JYvc+rcLLsDFcfAD1K7fMofg==} + '@rollup/rollup-linux-arm64-musl@4.55.1': + resolution: {integrity: sha512-puS1MEgWX5GsHSoiAsF0TYrpomdvkaXm0CofIMG5uVkP6IBV+ZO9xhC5YEN49nsgYo1DuuMquF9+7EDBVYu4uA==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loong64-gnu@4.54.0': - resolution: {integrity: sha512-gsrtB1NA3ZYj2vq0Rzkylo9ylCtW/PhpLEivlgWe0bpgtX5+9j9EZa0wtZiCjgu6zmSeZWyI/e2YRX1URozpIw==} + '@rollup/rollup-linux-loong64-gnu@4.55.1': + resolution: {integrity: sha512-r3Wv40in+lTsULSb6nnoudVbARdOwb2u5fpeoOAZjFLznp6tDU8kd+GTHmJoqZ9lt6/Sys33KdIHUaQihFcu7g==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-ppc64-gnu@4.54.0': - resolution: {integrity: sha512-y3qNOfTBStmFNq+t4s7Tmc9hW2ENtPg8FeUD/VShI7rKxNW7O4fFeaYbMsd3tpFlIg1Q8IapFgy7Q9i2BqeBvA==} + '@rollup/rollup-linux-loong64-musl@4.55.1': + resolution: {integrity: sha512-MR8c0+UxAlB22Fq4R+aQSPBayvYa3+9DrwG/i1TKQXFYEaoW3B5b/rkSRIypcZDdWjWnpcvxbNaAJDcSbJU3Lw==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-ppc64-gnu@4.55.1': + resolution: {integrity: sha512-3KhoECe1BRlSYpMTeVrD4sh2Pw2xgt4jzNSZIIPLFEsnQn9gAnZagW9+VqDqAHgm1Xc77LzJOo2LdigS5qZ+gw==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.54.0': - resolution: {integrity: sha512-89sepv7h2lIVPsFma8iwmccN7Yjjtgz0Rj/Ou6fEqg3HDhpCa+Et+YSufy27i6b0Wav69Qv4WBNl3Rs6pwhebQ==} + '@rollup/rollup-linux-ppc64-musl@4.55.1': + resolution: {integrity: sha512-ziR1OuZx0vdYZZ30vueNZTg73alF59DicYrPViG0NEgDVN8/Jl87zkAPu4u6VjZST2llgEUjaiNl9JM6HH1Vdw==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.55.1': + resolution: {integrity: sha512-uW0Y12ih2XJRERZ4jAfKamTyIHVMPQnTZcQjme2HMVDAHY4amf5u414OqNYC+x+LzRdRcnIG1YodLrrtA8xsxw==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-musl@4.54.0': - resolution: {integrity: sha512-ZcU77ieh0M2Q8Ur7D5X7KvK+UxbXeDHwiOt/CPSBTI1fBmeDMivW0dPkdqkT4rOgDjrDDBUed9x4EgraIKoR2A==} + '@rollup/rollup-linux-riscv64-musl@4.55.1': + resolution: {integrity: sha512-u9yZ0jUkOED1BFrqu3BwMQoixvGHGZ+JhJNkNKY/hyoEgOwlqKb62qu+7UjbPSHYjiVy8kKJHvXKv5coH4wDeg==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.54.0': - resolution: {integrity: sha512-2AdWy5RdDF5+4YfG/YesGDDtbyJlC9LHmL6rZw6FurBJ5n4vFGupsOBGfwMRjBYH7qRQowT8D/U4LoSvVwOhSQ==} + '@rollup/rollup-linux-s390x-gnu@4.55.1': + resolution: {integrity: sha512-/0PenBCmqM4ZUd0190j7J0UsQ/1nsi735iPRakO8iPciE7BQ495Y6msPzaOmvx0/pn+eJVVlZrNrSh4WSYLxNg==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.54.0': - resolution: {integrity: sha512-WGt5J8Ij/rvyqpFexxk3ffKqqbLf9AqrTBbWDk7ApGUzaIs6V+s2s84kAxklFwmMF/vBNGrVdYgbblCOFFezMQ==} + '@rollup/rollup-linux-x64-gnu@4.55.1': + resolution: {integrity: sha512-a8G4wiQxQG2BAvo+gU6XrReRRqj+pLS2NGXKm8io19goR+K8lw269eTrPkSdDTALwMmJp4th2Uh0D8J9bEV1vg==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.54.0': - resolution: {integrity: sha512-JzQmb38ATzHjxlPHuTH6tE7ojnMKM2kYNzt44LO/jJi8BpceEC8QuXYA908n8r3CNuG/B3BV8VR3Hi1rYtmPiw==} + '@rollup/rollup-linux-x64-musl@4.55.1': + resolution: {integrity: sha512-bD+zjpFrMpP/hqkfEcnjXWHMw5BIghGisOKPj+2NaNDuVT+8Ds4mPf3XcPHuat1tz89WRL+1wbcxKY3WSbiT7w==} cpu: [x64] os: [linux] - '@rollup/rollup-openharmony-arm64@4.54.0': - resolution: {integrity: sha512-huT3fd0iC7jigGh7n3q/+lfPcXxBi+om/Rs3yiFxjvSxbSB6aohDFXbWvlspaqjeOh+hx7DDHS+5Es5qRkWkZg==} + '@rollup/rollup-openbsd-x64@4.55.1': + resolution: {integrity: sha512-eLXw0dOiqE4QmvikfQ6yjgkg/xDM+MdU9YJuP4ySTibXU0oAvnEWXt7UDJmD4UkYialMfOGFPJnIHSe/kdzPxg==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.55.1': + resolution: {integrity: sha512-xzm44KgEP11te3S2HCSyYf5zIzWmx3n8HDCc7EE59+lTcswEWNpvMLfd9uJvVX8LCg9QWG67Xt75AuHn4vgsXw==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.54.0': - resolution: {integrity: sha512-c2V0W1bsKIKfbLMBu/WGBz6Yci8nJ/ZJdheE0EwB73N3MvHYKiKGs3mVilX4Gs70eGeDaMqEob25Tw2Gb9Nqyw==} + '@rollup/rollup-win32-arm64-msvc@4.55.1': + resolution: {integrity: sha512-yR6Bl3tMC/gBok5cz/Qi0xYnVbIxGx5Fcf/ca0eB6/6JwOY+SRUcJfI0OpeTpPls7f194as62thCt/2BjxYN8g==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.54.0': - resolution: {integrity: sha512-woEHgqQqDCkAzrDhvDipnSirm5vxUXtSKDYTVpZG3nUdW/VVB5VdCYA2iReSj/u3yCZzXID4kuKG7OynPnB3WQ==} + '@rollup/rollup-win32-ia32-msvc@4.55.1': + resolution: {integrity: sha512-3fZBidchE0eY0oFZBnekYCfg+5wAB0mbpCBuofh5mZuzIU/4jIVkbESmd2dOsFNS78b53CYv3OAtwqkZZmU5nA==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.54.0': - resolution: {integrity: sha512-dzAc53LOuFvHwbCEOS0rPbXp6SIhAf2txMP5p6mGyOXXw5mWY8NGGbPMPrs4P1WItkfApDathBj/NzMLUZ9rtQ==} + '@rollup/rollup-win32-x64-gnu@4.55.1': + resolution: {integrity: sha512-xGGY5pXj69IxKb4yv/POoocPy/qmEGhimy/FoTpTSVju3FYXUQQMFCaZZXJVidsmGxRioZAwpThl/4zX41gRKg==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.54.0': - resolution: {integrity: sha512-hYT5d3YNdSh3mbCU1gwQyPgQd3T2ne0A3KG8KSBdav5TiBg6eInVmV+TeR5uHufiIgSFg0XsOWGW5/RhNcSvPg==} + '@rollup/rollup-win32-x64-msvc@4.55.1': + resolution: {integrity: sha512-SPEpaL6DX4rmcXtnhdrQYgzQ5W2uW3SCJch88lB2zImhJRhIIK44fkUrgIV/Q8yUNfw5oyZ5vkeQsZLhCb06lw==} cpu: [x64] os: [win32] @@ -800,8 +815,8 @@ packages: react: '>=16.8.0' react-dom: '>=16.8.0' - '@trivago/prettier-plugin-sort-imports@6.0.0': - resolution: {integrity: sha512-Xarx55ow0R8oC7ViL5fPmDsg1EBa1dVhyZFVbFXNtPPJyW2w9bJADIla8YFSaNG9N06XfcklA9O9vmw4noNxkQ==} + '@trivago/prettier-plugin-sort-imports@6.0.1': + resolution: {integrity: sha512-6B13DCWDfAfh4AEJ43gRgeCSAQmlKG5LHqHzHc0lbUwgBy0rX7o41US+46Fd4XiXBx+JDGEz3NBadCbUls0dUQ==} engines: {node: '>= 20'} peerDependencies: '@vue/compiler-sfc': 3.x @@ -888,63 +903,63 @@ packages: '@types/svgo@2.6.4': resolution: {integrity: sha512-l4cmyPEckf8moNYHdJ+4wkHvFxjyW6ulm9l4YGaOxeyBWPhBOT0gvni1InpFPdzx1dKf/2s62qGITwxNWnPQng==} - '@typescript-eslint/eslint-plugin@8.51.0': - resolution: {integrity: sha512-XtssGWJvypyM2ytBnSnKtHYOGT+4ZwTnBVl36TA4nRO2f4PRNGz5/1OszHzcZCvcBMh+qb7I06uoCmLTRdR9og==} + '@typescript-eslint/eslint-plugin@8.52.0': + resolution: {integrity: sha512-okqtOgqu2qmZJ5iN4TWlgfF171dZmx2FzdOv2K/ixL2LZWDStL8+JgQerI2sa8eAEfoydG9+0V96m7V+P8yE1Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.51.0 + '@typescript-eslint/parser': ^8.52.0 eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/parser@8.51.0': - resolution: {integrity: sha512-3xP4XzzDNQOIqBMWogftkwxhg5oMKApqY0BAflmLZiFYHqyhSOxv/cd/zPQLTcCXr4AkaKb25joocY0BD1WC6A==} + '@typescript-eslint/parser@8.52.0': + resolution: {integrity: sha512-iIACsx8pxRnguSYhHiMn2PvhvfpopO9FXHyn1mG5txZIsAaB6F0KwbFnUQN3KCiG3Jcuad/Cao2FAs1Wp7vAyg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/project-service@8.51.0': - resolution: {integrity: sha512-Luv/GafO07Z7HpiI7qeEW5NW8HUtZI/fo/kE0YbtQEFpJRUuR0ajcWfCE5bnMvL7QQFrmT/odMe8QZww8X2nfQ==} + '@typescript-eslint/project-service@8.52.0': + resolution: {integrity: sha512-xD0MfdSdEmeFa3OmVqonHi+Cciab96ls1UhIF/qX/O/gPu5KXD0bY9lu33jj04fjzrXHcuvjBcBC+D3SNSadaw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/scope-manager@8.51.0': - resolution: {integrity: sha512-JhhJDVwsSx4hiOEQPeajGhCWgBMBwVkxC/Pet53EpBVs7zHHtayKefw1jtPaNRXpI9RA2uocdmpdfE7T+NrizA==} + '@typescript-eslint/scope-manager@8.52.0': + resolution: {integrity: sha512-ixxqmmCcc1Nf8S0mS0TkJ/3LKcC8mruYJPOU6Ia2F/zUUR4pApW7LzrpU3JmtePbRUTes9bEqRc1Gg4iyRnDzA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.51.0': - resolution: {integrity: sha512-Qi5bSy/vuHeWyir2C8u/uqGMIlIDu8fuiYWv48ZGlZ/k+PRPHtaAu7erpc7p5bzw2WNNSniuxoMSO4Ar6V9OXw==} + '@typescript-eslint/tsconfig-utils@8.52.0': + resolution: {integrity: sha512-jl+8fzr/SdzdxWJznq5nvoI7qn2tNYV/ZBAEcaFMVXf+K6jmXvAFrgo/+5rxgnL152f//pDEAYAhhBAZGrVfwg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/type-utils@8.51.0': - resolution: {integrity: sha512-0XVtYzxnobc9K0VU7wRWg1yiUrw4oQzexCG2V2IDxxCxhqBMSMbjB+6o91A+Uc0GWtgjCa3Y8bi7hwI0Tu4n5Q==} + '@typescript-eslint/type-utils@8.52.0': + resolution: {integrity: sha512-JD3wKBRWglYRQkAtsyGz1AewDu3mTc7NtRjR/ceTyGoPqmdS5oCdx/oZMWD5Zuqmo6/MpsYs0wp6axNt88/2EQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/types@8.51.0': - resolution: {integrity: sha512-TizAvWYFM6sSscmEakjY3sPqGwxZRSywSsPEiuZF6d5GmGD9Gvlsv0f6N8FvAAA0CD06l3rIcWNbsN1e5F/9Ag==} + '@typescript-eslint/types@8.52.0': + resolution: {integrity: sha512-LWQV1V4q9V4cT4H5JCIx3481iIFxH1UkVk+ZkGGAV1ZGcjGI9IoFOfg3O6ywz8QqCDEp7Inlg6kovMofsNRaGg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.51.0': - resolution: {integrity: sha512-1qNjGqFRmlq0VW5iVlcyHBbCjPB7y6SxpBkrbhNWMy/65ZoncXCEPJxkRZL8McrseNH6lFhaxCIaX+vBuFnRng==} + '@typescript-eslint/typescript-estree@8.52.0': + resolution: {integrity: sha512-XP3LClsCc0FsTK5/frGjolyADTh3QmsLp6nKd476xNI9CsSsLnmn4f0jrzNoAulmxlmNIpeXuHYeEQv61Q6qeQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/utils@8.51.0': - resolution: {integrity: sha512-11rZYxSe0zabiKaCP2QAwRf/dnmgFgvTmeDTtZvUvXG3UuAdg/GU02NExmmIXzz3vLGgMdtrIosI84jITQOxUA==} + '@typescript-eslint/utils@8.52.0': + resolution: {integrity: sha512-wYndVMWkweqHpEpwPhwqE2lnD2DxC6WVLupU/DOt/0/v+/+iQbbzO3jOHjmBMnhu0DgLULvOaU4h4pwHYi2oRQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/visitor-keys@8.51.0': - resolution: {integrity: sha512-mM/JRQOzhVN1ykejrvwnBRV3+7yTKK8tVANVN3o1O0t0v7o+jqdVu9crPy5Y9dov15TJk/FTIgoUGHrTOVL3Zg==} + '@typescript-eslint/visitor-keys@8.52.0': + resolution: {integrity: sha512-ink3/Zofus34nmBsPjow63FP5M7IGff0RKAgqR6+CFpdk22M7aLwC9gOcLGYqr7MczLPzZVERW9hRog3O4n1sQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} acorn-jsx@5.3.2: @@ -1008,8 +1023,8 @@ packages: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} - axe-core@4.11.0: - resolution: {integrity: sha512-ilYanEU8vxxBexpJd8cWM4ElSQq4QctCLKih0TSfjIfCQTeyH/6zVrmIJfLPrKTKJRbiG+cfnZbQIjAlJmF1jQ==} + axe-core@4.11.1: + resolution: {integrity: sha512-BASOg+YwO2C+346x3LZOeoovTIoTrRqEsqMa6fmfAV0P+U9mFr9NsyOEpiYvFjbc64NMrSswhV50WdXzdb/Z5A==} engines: {node: '>=4'} babel-plugin-macros@3.1.0: @@ -2460,8 +2475,8 @@ packages: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} - preact@10.28.1: - resolution: {integrity: sha512-u1/ixq/lVQI0CakKNvLDEcW5zfCjUQfZdK9qqWuIJtsezuyG6pk9TWj75GMuI/EzRSZB/VAE43sNWWZfiy8psw==} + preact@10.28.2: + resolution: {integrity: sha512-lbteaWGzGHdlIuiJ0l2Jq454m6kcpI1zNje6d8MlGAFlYvP2GO4ibnat7P74Esfz4sPTdM6UxtTwh/d3pwM9JA==} prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} @@ -2625,8 +2640,8 @@ packages: rollup: optional: true - rollup@4.54.0: - resolution: {integrity: sha512-3nk8Y3a9Ea8szgKhinMlGMhGMw89mqule3KWczxhIzqudyHdCIOHw8WJlj/r329fACjKLEh13ZSk7oE22kyeIw==} + rollup@4.55.1: + resolution: {integrity: sha512-wDv/Ht1BNHB4upNbK74s9usvl7hObDnvVzknxqY/E/O3X6rW1U1rV1aENEfJ54eFZDTNo7zv1f5N4edCluH7+A==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -2884,8 +2899,8 @@ packages: resolution: {integrity: sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg==} engines: {node: '>=0.10.0'} - ts-api-utils@2.3.0: - resolution: {integrity: sha512-6eg3Y9SF7SsAvGzRHQvvc1skDAhwI4YQ32ui1scxD1Ccr0G5qIIbUBT3pFTKX8kmWIQClHobtUdNuaBgwdfdWg==} + ts-api-utils@2.4.0: + resolution: {integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==} engines: {node: '>=18.12'} peerDependencies: typescript: '>=4.8.4' @@ -2924,8 +2939,8 @@ packages: peerDependencies: typescript: '>=3.5.1' - typescript-eslint@8.51.0: - resolution: {integrity: sha512-jh8ZuM5oEh2PSdyQG9YAEM1TCGuWenLSuSUhf/irbVUNW9O5FhbFVONviN2TgMTBnUmyHv7E56rYnfLZK6TkiA==} + typescript-eslint@8.52.0: + resolution: {integrity: sha512-atlQQJ2YkO4pfTVQmQ+wvYQwexPDOIgo+RaVcD7gHgzy/IQA+XTyuxNM9M9TVXvttkF7koBHmcwisKdOAf2EcA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -3591,16 +3606,16 @@ snapshots: '@popperjs/core@2.11.8': {} - '@preact/compat@18.3.1(preact@10.28.1)': + '@preact/compat@18.3.1(preact@10.28.2)': dependencies: - preact: 10.28.1 + preact: 10.28.2 - '@preact/preset-vite@2.10.2(@babel/core@7.28.5)(preact@10.28.1)(vite@7.3.0(@types/node@25.0.3)(terser@5.44.1))': + '@preact/preset-vite@2.10.2(@babel/core@7.28.5)(preact@10.28.2)(vite@7.3.0(@types/node@25.0.3)(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.28.1)(vite@7.3.0(@types/node@25.0.3)(terser@5.44.1)) + '@prefresh/vite': 2.4.11(preact@10.28.2)(vite@7.3.0(@types/node@25.0.3)(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 @@ -3613,20 +3628,20 @@ snapshots: '@prefresh/babel-plugin@0.5.2': {} - '@prefresh/core@1.5.9(preact@10.28.1)': + '@prefresh/core@1.5.9(preact@10.28.2)': dependencies: - preact: 10.28.1 + preact: 10.28.2 '@prefresh/utils@1.2.1': {} - '@prefresh/vite@2.4.11(preact@10.28.1)(vite@7.3.0(@types/node@25.0.3)(terser@5.44.1))': + '@prefresh/vite@2.4.11(preact@10.28.2)(vite@7.3.0(@types/node@25.0.3)(terser@5.44.1))': dependencies: '@babel/core': 7.28.5 '@prefresh/babel-plugin': 0.5.2 - '@prefresh/core': 1.5.9(preact@10.28.1) + '@prefresh/core': 1.5.9(preact@10.28.2) '@prefresh/utils': 1.2.1 '@rollup/pluginutils': 4.2.1 - preact: 10.28.1 + preact: 10.28.2 vite: 7.3.0(@types/node@25.0.3)(terser@5.44.1) transitivePeerDependencies: - supports-color @@ -3636,70 +3651,79 @@ snapshots: estree-walker: 2.0.2 picomatch: 2.3.1 - '@rollup/rollup-android-arm-eabi@4.54.0': + '@rollup/rollup-android-arm-eabi@4.55.1': optional: true - '@rollup/rollup-android-arm64@4.54.0': + '@rollup/rollup-android-arm64@4.55.1': optional: true - '@rollup/rollup-darwin-arm64@4.54.0': + '@rollup/rollup-darwin-arm64@4.55.1': optional: true - '@rollup/rollup-darwin-x64@4.54.0': + '@rollup/rollup-darwin-x64@4.55.1': optional: true - '@rollup/rollup-freebsd-arm64@4.54.0': + '@rollup/rollup-freebsd-arm64@4.55.1': optional: true - '@rollup/rollup-freebsd-x64@4.54.0': + '@rollup/rollup-freebsd-x64@4.55.1': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.54.0': + '@rollup/rollup-linux-arm-gnueabihf@4.55.1': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.54.0': + '@rollup/rollup-linux-arm-musleabihf@4.55.1': optional: true - '@rollup/rollup-linux-arm64-gnu@4.54.0': + '@rollup/rollup-linux-arm64-gnu@4.55.1': optional: true - '@rollup/rollup-linux-arm64-musl@4.54.0': + '@rollup/rollup-linux-arm64-musl@4.55.1': optional: true - '@rollup/rollup-linux-loong64-gnu@4.54.0': + '@rollup/rollup-linux-loong64-gnu@4.55.1': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.54.0': + '@rollup/rollup-linux-loong64-musl@4.55.1': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.54.0': + '@rollup/rollup-linux-ppc64-gnu@4.55.1': optional: true - '@rollup/rollup-linux-riscv64-musl@4.54.0': + '@rollup/rollup-linux-ppc64-musl@4.55.1': optional: true - '@rollup/rollup-linux-s390x-gnu@4.54.0': + '@rollup/rollup-linux-riscv64-gnu@4.55.1': optional: true - '@rollup/rollup-linux-x64-gnu@4.54.0': + '@rollup/rollup-linux-riscv64-musl@4.55.1': optional: true - '@rollup/rollup-linux-x64-musl@4.54.0': + '@rollup/rollup-linux-s390x-gnu@4.55.1': optional: true - '@rollup/rollup-openharmony-arm64@4.54.0': + '@rollup/rollup-linux-x64-gnu@4.55.1': optional: true - '@rollup/rollup-win32-arm64-msvc@4.54.0': + '@rollup/rollup-linux-x64-musl@4.55.1': optional: true - '@rollup/rollup-win32-ia32-msvc@4.54.0': + '@rollup/rollup-openbsd-x64@4.55.1': optional: true - '@rollup/rollup-win32-x64-gnu@4.54.0': + '@rollup/rollup-openharmony-arm64@4.55.1': optional: true - '@rollup/rollup-win32-x64-msvc@4.54.0': + '@rollup/rollup-win32-arm64-msvc@4.55.1': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.55.1': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.55.1': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.55.1': optional: true '@sindresorhus/is@0.7.0': {} @@ -3713,7 +3737,7 @@ snapshots: react-virtualized-auto-sizer: 1.0.26(react-dom@19.2.3(react@19.2.3))(react@19.2.3) react-window: 1.8.11(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@trivago/prettier-plugin-sort-imports@6.0.0(prettier@3.7.4)': + '@trivago/prettier-plugin-sort-imports@6.0.1(prettier@3.7.4)': dependencies: '@babel/generator': 7.28.5 '@babel/parser': 7.28.5 @@ -3803,95 +3827,95 @@ snapshots: dependencies: '@types/node': 25.0.3 - '@typescript-eslint/eslint-plugin@8.51.0(@typescript-eslint/parser@8.51.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3)': + '@typescript-eslint/eslint-plugin@8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.51.0(eslint@9.39.2)(typescript@5.9.3) - '@typescript-eslint/scope-manager': 8.51.0 - '@typescript-eslint/type-utils': 8.51.0(eslint@9.39.2)(typescript@5.9.3) - '@typescript-eslint/utils': 8.51.0(eslint@9.39.2)(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.51.0 + '@typescript-eslint/parser': 8.52.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.52.0 + '@typescript-eslint/type-utils': 8.52.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/utils': 8.52.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.52.0 eslint: 9.39.2 ignore: 7.0.5 natural-compare: 1.4.0 - ts-api-utils: 2.3.0(typescript@5.9.3) + ts-api-utils: 2.4.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.51.0(eslint@9.39.2)(typescript@5.9.3)': + '@typescript-eslint/parser@8.52.0(eslint@9.39.2)(typescript@5.9.3)': dependencies: - '@typescript-eslint/scope-manager': 8.51.0 - '@typescript-eslint/types': 8.51.0 - '@typescript-eslint/typescript-estree': 8.51.0(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.51.0 + '@typescript-eslint/scope-manager': 8.52.0 + '@typescript-eslint/types': 8.52.0 + '@typescript-eslint/typescript-estree': 8.52.0(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.52.0 debug: 4.4.3 eslint: 9.39.2 typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.51.0(typescript@5.9.3)': + '@typescript-eslint/project-service@8.52.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.51.0(typescript@5.9.3) - '@typescript-eslint/types': 8.51.0 + '@typescript-eslint/tsconfig-utils': 8.52.0(typescript@5.9.3) + '@typescript-eslint/types': 8.52.0 debug: 4.4.3 typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.51.0': + '@typescript-eslint/scope-manager@8.52.0': dependencies: - '@typescript-eslint/types': 8.51.0 - '@typescript-eslint/visitor-keys': 8.51.0 + '@typescript-eslint/types': 8.52.0 + '@typescript-eslint/visitor-keys': 8.52.0 - '@typescript-eslint/tsconfig-utils@8.51.0(typescript@5.9.3)': + '@typescript-eslint/tsconfig-utils@8.52.0(typescript@5.9.3)': dependencies: typescript: 5.9.3 - '@typescript-eslint/type-utils@8.51.0(eslint@9.39.2)(typescript@5.9.3)': + '@typescript-eslint/type-utils@8.52.0(eslint@9.39.2)(typescript@5.9.3)': dependencies: - '@typescript-eslint/types': 8.51.0 - '@typescript-eslint/typescript-estree': 8.51.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.51.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/types': 8.52.0 + '@typescript-eslint/typescript-estree': 8.52.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.52.0(eslint@9.39.2)(typescript@5.9.3) debug: 4.4.3 eslint: 9.39.2 - ts-api-utils: 2.3.0(typescript@5.9.3) + ts-api-utils: 2.4.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.51.0': {} + '@typescript-eslint/types@8.52.0': {} - '@typescript-eslint/typescript-estree@8.51.0(typescript@5.9.3)': + '@typescript-eslint/typescript-estree@8.52.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/project-service': 8.51.0(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.51.0(typescript@5.9.3) - '@typescript-eslint/types': 8.51.0 - '@typescript-eslint/visitor-keys': 8.51.0 + '@typescript-eslint/project-service': 8.52.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.52.0(typescript@5.9.3) + '@typescript-eslint/types': 8.52.0 + '@typescript-eslint/visitor-keys': 8.52.0 debug: 4.4.3 minimatch: 9.0.5 semver: 7.7.3 tinyglobby: 0.2.15 - ts-api-utils: 2.3.0(typescript@5.9.3) + ts-api-utils: 2.4.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.51.0(eslint@9.39.2)(typescript@5.9.3)': + '@typescript-eslint/utils@8.52.0(eslint@9.39.2)(typescript@5.9.3)': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2) - '@typescript-eslint/scope-manager': 8.51.0 - '@typescript-eslint/types': 8.51.0 - '@typescript-eslint/typescript-estree': 8.51.0(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.52.0 + '@typescript-eslint/types': 8.52.0 + '@typescript-eslint/typescript-estree': 8.52.0(typescript@5.9.3) eslint: 9.39.2 typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.51.0': + '@typescript-eslint/visitor-keys@8.52.0': dependencies: - '@typescript-eslint/types': 8.51.0 + '@typescript-eslint/types': 8.52.0 eslint-visitor-keys: 4.2.1 acorn-jsx@5.3.2(acorn@8.15.0): @@ -3942,7 +3966,7 @@ snapshots: dependencies: possible-typed-array-names: 1.1.0 - axe-core@4.11.0: {} + axe-core@4.11.1: {} babel-plugin-macros@3.1.0: dependencies: @@ -5473,7 +5497,7 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 - preact@10.28.1: {} + preact@10.28.2: {} prelude-ls@1.2.1: {} @@ -5614,41 +5638,44 @@ snapshots: dependencies: glob: 7.2.3 - rollup-plugin-visualizer@6.0.5(rollup@4.54.0): + rollup-plugin-visualizer@6.0.5(rollup@4.55.1): dependencies: open: 8.4.2 picomatch: 4.0.3 source-map: 0.7.6 yargs: 17.7.2 optionalDependencies: - rollup: 4.54.0 + rollup: 4.55.1 - rollup@4.54.0: + rollup@4.55.1: dependencies: '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.54.0 - '@rollup/rollup-android-arm64': 4.54.0 - '@rollup/rollup-darwin-arm64': 4.54.0 - '@rollup/rollup-darwin-x64': 4.54.0 - '@rollup/rollup-freebsd-arm64': 4.54.0 - '@rollup/rollup-freebsd-x64': 4.54.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.54.0 - '@rollup/rollup-linux-arm-musleabihf': 4.54.0 - '@rollup/rollup-linux-arm64-gnu': 4.54.0 - '@rollup/rollup-linux-arm64-musl': 4.54.0 - '@rollup/rollup-linux-loong64-gnu': 4.54.0 - '@rollup/rollup-linux-ppc64-gnu': 4.54.0 - '@rollup/rollup-linux-riscv64-gnu': 4.54.0 - '@rollup/rollup-linux-riscv64-musl': 4.54.0 - '@rollup/rollup-linux-s390x-gnu': 4.54.0 - '@rollup/rollup-linux-x64-gnu': 4.54.0 - '@rollup/rollup-linux-x64-musl': 4.54.0 - '@rollup/rollup-openharmony-arm64': 4.54.0 - '@rollup/rollup-win32-arm64-msvc': 4.54.0 - '@rollup/rollup-win32-ia32-msvc': 4.54.0 - '@rollup/rollup-win32-x64-gnu': 4.54.0 - '@rollup/rollup-win32-x64-msvc': 4.54.0 + '@rollup/rollup-android-arm-eabi': 4.55.1 + '@rollup/rollup-android-arm64': 4.55.1 + '@rollup/rollup-darwin-arm64': 4.55.1 + '@rollup/rollup-darwin-x64': 4.55.1 + '@rollup/rollup-freebsd-arm64': 4.55.1 + '@rollup/rollup-freebsd-x64': 4.55.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.55.1 + '@rollup/rollup-linux-arm-musleabihf': 4.55.1 + '@rollup/rollup-linux-arm64-gnu': 4.55.1 + '@rollup/rollup-linux-arm64-musl': 4.55.1 + '@rollup/rollup-linux-loong64-gnu': 4.55.1 + '@rollup/rollup-linux-loong64-musl': 4.55.1 + '@rollup/rollup-linux-ppc64-gnu': 4.55.1 + '@rollup/rollup-linux-ppc64-musl': 4.55.1 + '@rollup/rollup-linux-riscv64-gnu': 4.55.1 + '@rollup/rollup-linux-riscv64-musl': 4.55.1 + '@rollup/rollup-linux-s390x-gnu': 4.55.1 + '@rollup/rollup-linux-x64-gnu': 4.55.1 + '@rollup/rollup-linux-x64-musl': 4.55.1 + '@rollup/rollup-openbsd-x64': 4.55.1 + '@rollup/rollup-openharmony-arm64': 4.55.1 + '@rollup/rollup-win32-arm64-msvc': 4.55.1 + '@rollup/rollup-win32-ia32-msvc': 4.55.1 + '@rollup/rollup-win32-x64-gnu': 4.55.1 + '@rollup/rollup-win32-x64-msvc': 4.55.1 fsevents: 2.3.3 run-parallel@1.2.0: @@ -5882,7 +5909,7 @@ snapshots: dependencies: escape-string-regexp: 1.0.5 - ts-api-utils@2.3.0(typescript@5.9.3): + ts-api-utils@2.4.0(typescript@5.9.3): dependencies: typescript: 5.9.3 @@ -5912,12 +5939,12 @@ snapshots: dependencies: typescript: 5.9.3 - typescript-eslint@8.51.0(eslint@9.39.2)(typescript@5.9.3): + typescript-eslint@8.52.0(eslint@9.39.2)(typescript@5.9.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.51.0(@typescript-eslint/parser@8.51.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3) - '@typescript-eslint/parser': 8.51.0(eslint@9.39.2)(typescript@5.9.3) - '@typescript-eslint/typescript-estree': 8.51.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.51.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/eslint-plugin': 8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/parser': 8.52.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.52.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.52.0(eslint@9.39.2)(typescript@5.9.3) eslint: 9.39.2 typescript: 5.9.3 transitivePeerDependencies: @@ -6019,7 +6046,7 @@ snapshots: fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 - rollup: 4.54.0 + rollup: 4.55.1 tinyglobby: 0.2.15 optionalDependencies: '@types/node': 25.0.3 diff --git a/interface/src/app/main/Customizations.tsx b/interface/src/app/main/Customizations.tsx index a387774cc..21711380e 100644 --- a/interface/src/app/main/Customizations.tsx +++ b/interface/src/app/main/Customizations.tsx @@ -788,10 +788,12 @@ const Customizations = () => { ); - return ( + return restarting ? ( + + ) : ( {blocker ? : null} - {restarting ? : renderContent()} + {renderContent()} {selectedDeviceEntity && ( { ); }; - return ( + return restarting ? ( + + ) : ( {blocker ? : null} - {restarting ? : content()} + {content()} ); }; diff --git a/interface/src/app/settings/Settings.tsx b/interface/src/app/settings/Settings.tsx index 608c37ee9..a8130bb24 100644 --- a/interface/src/app/settings/Settings.tsx +++ b/interface/src/app/settings/Settings.tsx @@ -182,7 +182,7 @@ const Settings = () => { restarting ]); - return {restarting ? : content}; + return restarting ? : {content}; }; export default Settings; diff --git a/interface/src/app/settings/network/NetworkSettings.tsx b/interface/src/app/settings/network/NetworkSettings.tsx index 5f525a992..92651c654 100644 --- a/interface/src/app/settings/network/NetworkSettings.tsx +++ b/interface/src/app/settings/network/NetworkSettings.tsx @@ -397,10 +397,12 @@ const NetworkSettings = () => { ); }; - return ( + return restarting ? ( + + ) : ( {blocker ? : null} - {restarting ? : content()} + {content()} ); }; diff --git a/interface/src/app/status/Status.tsx b/interface/src/app/status/Status.tsx index be5427785..1d87a5d4d 100644 --- a/interface/src/app/status/Status.tsx +++ b/interface/src/app/status/Status.tsx @@ -443,7 +443,7 @@ const SystemStatus = () => { renderRestartDialog ]); - return {restarting ? : content}; + return restarting ? : {content}; }; export default SystemStatus; diff --git a/interface/src/app/status/SystemMonitor.tsx b/interface/src/app/status/SystemMonitor.tsx index 492049074..7e7c289af 100644 --- a/interface/src/app/status/SystemMonitor.tsx +++ b/interface/src/app/status/SystemMonitor.tsx @@ -103,8 +103,8 @@ const SystemMonitor = () => { height: '100vh', display: 'flex', alignItems: 'center', - justifyContent: 'center', - backdropFilter: 'blur(8px)' + justifyContent: 'center' + // backdropFilter: 'blur(8px)' }} > { installPartitionFirmware ]); - return {restarting ? : content}; + return restarting ? : {content}; }; export default memo(Version); diff --git a/interface/src/components/layout/LayoutMenuItem.tsx b/interface/src/components/layout/LayoutMenuItem.tsx index b19652cca..a0dbc8354 100644 --- a/interface/src/components/layout/LayoutMenuItem.tsx +++ b/interface/src/components/layout/LayoutMenuItem.tsx @@ -39,9 +39,8 @@ const LayoutMenuItemComponent = ({ left: 0, top: 0, bottom: 0, - width: selected ? '4px' : '0px', + width: selected ? '3px' : '0px', backgroundColor: '#90caf9', - borderRadius: '0 2px 2px 0', transition: 'width 0.05s cubic-bezier(0.55, 0.085, 0.68, 0.53)' } }), diff --git a/interface/vite.config.ts b/interface/vite.config.ts index d73dc869f..2369cc845 100644 --- a/interface/vite.config.ts +++ b/interface/vite.config.ts @@ -136,9 +136,16 @@ const createManualChunks = (detailed = false) => { return 'vendor'; } if (detailed) { - if (id.includes('components/')) return 'components'; - if (id.includes('app/')) return 'app'; - if (id.includes('utils/')) return 'utils'; + // Group circularly dependent modules together to avoid circular chunk warnings + // components, app, and utils are tightly coupled, so combine them + if ( + id.includes('components/') || + id.includes('app/') || + id.includes('utils/') + ) { + return 'app'; + } + // Keep api separate as it's typically more independent if (id.includes('api/')) return 'api'; } return undefined; diff --git a/lib/eModbus/src/ModbusClientTCPasync.cpp b/lib/eModbus/src/ModbusClientTCPasync.cpp index 949de2c49..cebdd7a30 100644 --- a/lib/eModbus/src/ModbusClientTCPasync.cpp +++ b/lib/eModbus/src/ModbusClientTCPasync.cpp @@ -52,7 +52,7 @@ ModbusClientTCPasync::~ModbusClientTCPasync() { } } // force close client - MTA_client.close(true); + MTA_client.close(); } // optionally manually connect to modbus server. Otherwise connection will be made upon first request @@ -79,7 +79,7 @@ void ModbusClientTCPasync::connect(IPAddress host, uint16_t port) { // manually disconnect from modbus server. Connection will also auto close after idle time void ModbusClientTCPasync::disconnect(bool force) { LOG_D("disconnecting\n"); - MTA_client.close(force); + MTA_client.close(); } // Set timeout value diff --git a/lib/eModbus/src/ModbusServerTCPasync.cpp b/lib/eModbus/src/ModbusServerTCPasync.cpp index e8d0a43dc..be2b0cbf1 100644 --- a/lib/eModbus/src/ModbusServerTCPasync.cpp +++ b/lib/eModbus/src/ModbusServerTCPasync.cpp @@ -253,7 +253,7 @@ void ModbusServerTCPasync::onClientConnect(AsyncClient* client) { LOG_D("nr clients: %u\n", clients.size()); } else { LOG_D("max number of clients reached, closing new\n"); - client->close(true); + client->close(); delete client; } } diff --git a/lib/espMqttClient/src/Transport/ClientAsync.cpp b/lib/espMqttClient/src/Transport/ClientAsync.cpp index 4f8d69eeb..0ca91564f 100644 --- a/lib/espMqttClient/src/Transport/ClientAsync.cpp +++ b/lib/espMqttClient/src/Transport/ClientAsync.cpp @@ -42,7 +42,7 @@ int ClientAsync::read(uint8_t* buf, size_t size) { } void ClientAsync::stop() { - client.close(false); + client.close(); } bool ClientAsync::connected() { diff --git a/lib_standalone/Arduino.h b/lib_standalone/Arduino.h index 1ac2aea3b..d13411c24 100644 --- a/lib_standalone/Arduino.h +++ b/lib_standalone/Arduino.h @@ -76,6 +76,10 @@ void ledcWrite(uint8_t chan, uint32_t duty); void neopixelWrite(uint8_t pin, uint8_t red_val, uint8_t green_val, uint8_t blue_val); void rgbLedWrite(uint8_t pin, uint8_t red_val, uint8_t green_val, uint8_t blue_val); +inline int rtc_get_reset_reason(int cpu_no) { + return 0; +} + #define PROGMEM #define PGM_P const char * diff --git a/mock-api/package.json b/mock-api/package.json index 1934da897..8780817cc 100644 --- a/mock-api/package.json +++ b/mock-api/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@msgpack/msgpack": "^3.1.3", - "@trivago/prettier-plugin-sort-imports": "^6.0.0", + "@trivago/prettier-plugin-sort-imports": "^6.0.1", "formidable": "^3.5.4", "itty-router": "^5.0.22", "prettier": "^3.7.4" diff --git a/mock-api/pnpm-lock.yaml b/mock-api/pnpm-lock.yaml index 0151c466c..9586176a7 100644 --- a/mock-api/pnpm-lock.yaml +++ b/mock-api/pnpm-lock.yaml @@ -12,8 +12,8 @@ importers: specifier: ^3.1.3 version: 3.1.3 '@trivago/prettier-plugin-sort-imports': - specifier: ^6.0.0 - version: 6.0.0(prettier@3.7.4) + specifier: ^6.0.1 + version: 6.0.1(prettier@3.7.4) formidable: specifier: ^3.5.4 version: 3.5.4 @@ -87,8 +87,8 @@ packages: '@paralleldrive/cuid2@2.3.1': resolution: {integrity: sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==} - '@trivago/prettier-plugin-sort-imports@6.0.0': - resolution: {integrity: sha512-Xarx55ow0R8oC7ViL5fPmDsg1EBa1dVhyZFVbFXNtPPJyW2w9bJADIla8YFSaNG9N06XfcklA9O9vmw4noNxkQ==} + '@trivago/prettier-plugin-sort-imports@6.0.1': + resolution: {integrity: sha512-6B13DCWDfAfh4AEJ43gRgeCSAQmlKG5LHqHzHc0lbUwgBy0rX7o41US+46Fd4XiXBx+JDGEz3NBadCbUls0dUQ==} engines: {node: '>= 20'} peerDependencies: '@vue/compiler-sfc': 3.x @@ -246,7 +246,7 @@ snapshots: dependencies: '@noble/hashes': 1.8.0 - '@trivago/prettier-plugin-sort-imports@6.0.0(prettier@3.7.4)': + '@trivago/prettier-plugin-sort-imports@6.0.1(prettier@3.7.4)': dependencies: '@babel/generator': 7.28.5 '@babel/parser': 7.28.5 diff --git a/platformio.ini b/platformio.ini index 85ebc08bd..31ac85d60 100644 --- a/platformio.ini +++ b/platformio.ini @@ -106,7 +106,7 @@ board_build.filesystem = littlefs lib_deps = bblanchon/ArduinoJson @ 7.4.2 ESP32Async/AsyncTCP @ 3.4.10 - ESP32Async/ESPAsyncWebServer @ 3.9.3 + ESP32Async/ESPAsyncWebServer @ 3.9.4 https://github.com/emsesp/EMS-ESP-Modules.git @ 1.0.8 @@ -228,7 +228,7 @@ build_src_flags = -DEMSESP_STANDALONE -DEMSESP_TEST -DEMSESP_UNITY -DARDUINOJSON_ENABLE_ARDUINO_STRING=1 - -DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_TX_MODE=8 -DEMSESP_DEFAULT_VERSION=\"3.8.0-dev.0\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32\" + -DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32\" -std=gnu++17 -Og -ggdb -Wall -Wextra -Wno-unused-parameter -Wno-sign-compare -Wno-missing-braces @@ -284,7 +284,7 @@ build_flags = [env:standalone] extends = env:native build_flags = - -DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_TX_MODE=8 -DEMSESP_DEFAULT_VERSION=\"3.8.0-dev.0\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32\" + -DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32\" ; Modbus ; Creates the file modbus_entity_parameters.hpp diff --git a/src/ESP32React/APSettingsService.h b/src/ESP32React/APSettingsService.h index 67c9e03bf..fda384bc8 100644 --- a/src/ESP32React/APSettingsService.h +++ b/src/ESP32React/APSettingsService.h @@ -58,12 +58,12 @@ enum APNetworkStatus { ACTIVE = 0, INACTIVE, LINGERING }; class APSettings { public: - uint8_t provisionMode; // 0 = on, 2 = off - String ssid; - String password; - uint8_t channel; - bool ssidHidden; - uint8_t maxClients; + uint8_t provisionMode = FACTORY_AP_PROVISION_MODE; // 0 = on, 2 = off + String ssid = FACTORY_AP_SSID; + String password = FACTORY_AP_PASSWORD; + uint8_t channel = FACTORY_AP_CHANNEL; + bool ssidHidden = FACTORY_AP_SSID_HIDDEN; + uint8_t maxClients = FACTORY_AP_MAX_CLIENTS; IPAddress localIP; IPAddress gatewayIP; diff --git a/src/ESP32React/FSPersistence.h b/src/ESP32React/FSPersistence.h index 299d943c2..d41e6b53d 100644 --- a/src/ESP32React/FSPersistence.h +++ b/src/ESP32React/FSPersistence.h @@ -70,6 +70,10 @@ class FSPersistence { // failed to open file, return false if (!settingsFile || !jsonObject.size()) { +#if defined(EMSESP_DEBUG) + Serial.printf("Failed to write file %s", _filePath); + Serial.println(); +#endif return false; } diff --git a/src/ESP32React/MqttSettingsService.h b/src/ESP32React/MqttSettingsService.h index b844e2ea1..b31859201 100644 --- a/src/ESP32React/MqttSettingsService.h +++ b/src/ESP32React/MqttSettingsService.h @@ -8,6 +8,7 @@ #include #include +#include #define MQTT_RECONNECTION_DELAY 2000 // 2 seconds @@ -65,37 +66,37 @@ class MqttSettings { public: - bool enabled; - String host; - uint16_t port; - String rootCA; - bool enableTLS; - String username; - String password; + bool enabled = FACTORY_MQTT_ENABLED; + String host = FACTORY_MQTT_HOST; + uint16_t port = FACTORY_MQTT_PORT; + String rootCA = ""; + bool enableTLS = false; + String username = FACTORY_MQTT_USERNAME; + String password = FACTORY_MQTT_PASSWORD; String clientId; - uint16_t keepAlive; - bool cleanSession; + uint16_t keepAlive = FACTORY_MQTT_KEEP_ALIVE; + bool cleanSession = FACTORY_MQTT_CLEAN_SESSION; // EMS-ESP specific - String base; - uint16_t publish_time_boiler; - uint16_t publish_time_thermostat; - uint16_t publish_time_solar; - uint16_t publish_time_mixer; - uint16_t publish_time_water; - uint16_t publish_time_other; - uint16_t publish_time_sensor; - uint16_t publish_time_heartbeat; - uint8_t mqtt_qos; - bool mqtt_retain; - bool ha_enabled; - uint8_t nested_format; - String discovery_prefix; - uint8_t discovery_type; - bool publish_single; - bool publish_single2cmd; - bool send_response; - uint8_t entity_format; + String base = FACTORY_MQTT_BASE; + uint16_t publish_time_boiler = EMSESP_DEFAULT_PUBLISH_TIME; + uint16_t publish_time_thermostat = EMSESP_DEFAULT_PUBLISH_TIME; + uint16_t publish_time_solar = EMSESP_DEFAULT_PUBLISH_TIME; + uint16_t publish_time_mixer = EMSESP_DEFAULT_PUBLISH_TIME; + uint16_t publish_time_water = EMSESP_DEFAULT_PUBLISH_TIME; + uint16_t publish_time_other = EMSESP_DEFAULT_PUBLISH_TIME_OTHER; + uint16_t publish_time_sensor = EMSESP_DEFAULT_PUBLISH_TIME; + uint16_t publish_time_heartbeat = EMSESP_DEFAULT_PUBLISH_HEARTBEAT; + uint8_t mqtt_qos = EMSESP_DEFAULT_MQTT_QOS; + bool mqtt_retain = EMSESP_DEFAULT_MQTT_RETAIN; + bool ha_enabled = EMSESP_DEFAULT_HA_ENABLED; + uint8_t nested_format = EMSESP_DEFAULT_NESTED_FORMAT; + String discovery_prefix = EMSESP_DEFAULT_DISCOVERY_PREFIX; + uint8_t discovery_type = EMSESP_DEFAULT_DISCOVERY_TYPE; + bool publish_single = EMSESP_DEFAULT_PUBLISH_SINGLE; + bool publish_single2cmd = EMSESP_DEFAULT_PUBLISH_SINGLE2CMD; + bool send_response = EMSESP_DEFAULT_SEND_RESPONSE; + uint8_t entity_format = EMSESP_DEFAULT_ENTITY_FORMAT; static void read(MqttSettings & settings, JsonObject root); static StateUpdateResult update(JsonObject root, MqttSettings & settings); diff --git a/src/ESP32React/NTPSettingsService.h b/src/ESP32React/NTPSettingsService.h index 38f4ed5e2..4b3bbce69 100644 --- a/src/ESP32React/NTPSettingsService.h +++ b/src/ESP32React/NTPSettingsService.h @@ -30,10 +30,10 @@ class NTPSettings { public: - bool enabled; - String tzLabel; - String tzFormat; - String server; + bool enabled = FACTORY_NTP_ENABLED; + String tzLabel = FACTORY_NTP_TIME_ZONE_LABEL; + String tzFormat = FACTORY_NTP_TIME_ZONE_FORMAT; + String server = FACTORY_NTP_SERVER; static void read(NTPSettings & settings, JsonObject root); static StateUpdateResult update(JsonObject root, NTPSettings & settings); diff --git a/src/ESP32React/NetworkSettingsService.h b/src/ESP32React/NetworkSettingsService.h index a12536b78..f35186bf6 100644 --- a/src/ESP32React/NetworkSettingsService.h +++ b/src/ESP32React/NetworkSettingsService.h @@ -64,17 +64,17 @@ class NetworkSettings { public: // core wifi configuration - String ssid; - String bssid; - String password; - String hostname; - bool staticIPConfig; - bool bandwidth20; - uint8_t tx_power; - bool nosleep; - bool enableMDNS; - bool enableCORS; - String CORSOrigin; + String ssid = FACTORY_WIFI_SSID; + String bssid = ""; + String password = FACTORY_WIFI_PASSWORD; + String hostname = FACTORY_WIFI_HOSTNAME; + bool staticIPConfig = false; + bool bandwidth20 = false; + uint8_t tx_power = 0; + bool nosleep = true; + bool enableMDNS = true; + bool enableCORS = false; + String CORSOrigin = "*"; // optional configuration for static IP address IPAddress localIP; diff --git a/src/ESP32React/SecurityManager.h b/src/ESP32React/SecurityManager.h index 247f21019..b4dc89bb8 100644 --- a/src/ESP32React/SecurityManager.h +++ b/src/ESP32React/SecurityManager.h @@ -9,7 +9,6 @@ #include #define ACCESS_TOKEN_PARAMATER "access_token" - #define AUTHORIZATION_HEADER "Authorization" #define AUTHORIZATION_HEADER_PREFIX "Bearer " #define AUTHORIZATION_HEADER_PREFIX_LEN 7 diff --git a/src/ESP32React/UploadFileService.cpp b/src/ESP32React/UploadFileService.cpp index 8ef5bfc13..4d016dce4 100644 --- a/src/ESP32React/UploadFileService.cpp +++ b/src/ESP32React/UploadFileService.cpp @@ -172,7 +172,7 @@ void UploadFileService::handleError(AsyncWebServerRequest * request, int code) { // check for invalid extension and immediately kill the connection, which will throw an error // 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(true); + request->client()->close(); _is_firmware = false; Update.abort(); } diff --git a/src/core/analogsensor.cpp b/src/core/analogsensor.cpp index d62701ded..74532a876 100644 --- a/src/core/analogsensor.cpp +++ b/src/core/analogsensor.cpp @@ -576,7 +576,7 @@ bool AnalogSensor::update(uint8_t gpio, const char * org_name, double offset, do newSensor.is_system = is_system; settings.analogCustomizations.push_back(newSensor); // check the gpio again and add to used list - if (EMSESP::system_.add_gpio(gpio, "Analog Sensor")) { + if (EMSESP::system_.add_gpio(gpio, name)) { LOG_DEBUG("Adding customization for analog sensor GPIO %02d", gpio); return StateUpdateResult::CHANGED; // persist the change } else { diff --git a/src/core/default_settings.h b/src/core/default_settings.h index d8187b701..6ef742329 100644 --- a/src/core/default_settings.h +++ b/src/core/default_settings.h @@ -25,10 +25,6 @@ #define EMSESP_DEFAULT_LOCALE EMSESP_LOCALE_EN // English #endif -#ifndef EMSESP_DEFAULT_VERSION -#define EMSESP_DEFAULT_VERSION "" -#endif - #ifndef EMSESP_DEFAULT_TX_MODE #define EMSESP_DEFAULT_TX_MODE 1 // EMS1.0 #endif diff --git a/src/core/emsesp.cpp b/src/core/emsesp.cpp index 04b5b1e34..faa88331d 100644 --- a/src/core/emsesp.cpp +++ b/src/core/emsesp.cpp @@ -20,6 +20,7 @@ #ifndef EMSESP_STANDALONE #include "esp_ota_ops.h" +#include "rom/rtc.h" #endif static_assert(uuid::thread_safe, "uuid-common must be thread-safe"); @@ -1721,15 +1722,31 @@ void EMSESP::start() { LOG_INFO("EMS-ESP version %s", EMSESP_APP_VERSION); #endif - // check if the firmware is fresh + LOG_DEBUG("System is running in Debug mode"); + + // check if the firmware is fresh, i.e. a new install or a new version has been uploaded // this is set in UploadFileService::uploadComplete() // and reset in System::set_partition_install_date() - if (!EMSESP::nvs_.getBool(EMSESP_NVS_BOOT_NEW_FIRMWARE)) { - LOG_DEBUG("Firmware is fresh"); + if (EMSESP::nvs_.getBool(EMSESP_NVS_BOOT_NEW_FIRMWARE)) { + LOG_DEBUG("Firmware is a new install"); + } else { +// check if the firmware has been uploaded via Serial/USB +#if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) + if (rtc_get_reset_reason(0) == 11) { // ESP_RST_USB (Reset by USB peripheral, on CPU 0 only +#else + if ((rtc_get_reset_reason(0) == 14 || rtc_get_reset_reason(1) == 14)) { // APP CPU reset by PRO CPU, can be either CPUs +#endif + LOG_DEBUG("Firmware is a new install, uploaded via Serial/USB"); + EMSESP::nvs_.putBool(emsesp::EMSESP_NVS_BOOT_NEW_FIRMWARE, true); // set flag so it's picked up later to set the install date + } } - LOG_DEBUG("System is running in Debug mode"); +// S2 are C3 are both single core +#if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) + LOG_INFO("Last system reset reason Core0: %s", system_.reset_reason(0).c_str()); +#else LOG_INFO("Last system reset reason Core0: %s, Core1: %s", system_.reset_reason(0).c_str(), system_.reset_reason(1).c_str()); +#endif // see if we're restoring a settings file #ifndef EMSESP_STANDALONE @@ -1837,7 +1854,7 @@ void EMSESP::loop() { if (EMSESP::system_.systemStatus() == SYSTEM_STATUS::SYSTEM_STATUS_INVALID_GPIO) { static bool only_once = false; if (!only_once) { - LOG_ERROR("Invalid GPIOs used. Please check your settings and log"); + LOG_ERROR("Invalid GPIOs used. Please check your settings and the system log"); only_once = true; } } diff --git a/src/core/mqtt.cpp b/src/core/mqtt.cpp index 71f4e38de..e1e664d71 100644 --- a/src/core/mqtt.cpp +++ b/src/core/mqtt.cpp @@ -562,8 +562,8 @@ void Mqtt::ha_status() { // Note we don't use camelCase as it would change the HA entity_id and impact historic data #ifndef EMSESP_STANDALONE if (!EMSESP::system_.ethernet_connected() || WiFi.isConnected()) { - publish_system_ha_sensor_config(DeviceValueType::INT8, "WiFi RSSI", "rssi", DeviceValueUOM::DBM); - publish_system_ha_sensor_config(DeviceValueType::INT8, "WiFi strength", "wifistrength", DeviceValueUOM::PERCENT); + publish_system_ha_sensor_config(DeviceValueType::INT8, "RSSI", "rssi", DeviceValueUOM::DBM); + publish_system_ha_sensor_config(DeviceValueType::INT8, "Signal", "wifistrength", DeviceValueUOM::PERCENT); } #endif @@ -1628,4 +1628,4 @@ void Mqtt::add_value_bool(JsonObject doc, const char * name, bool value) { } } -} // namespace emsesp \ No newline at end of file +} // namespace emsesp diff --git a/src/core/system.cpp b/src/core/system.cpp index 6363a76da..12faa3af8 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -105,8 +105,8 @@ uint32_t System::led_flash_duration_ = 0; bool System::led_flash_timer_ = false; // GPIOs -std::vector> System::valid_system_gpios_; -std::vector> System::used_gpios_; +std::vector> System::valid_system_gpios_; +std::vector> System::used_gpios_; // find the index of the language // 0 = EN, 1 = DE, etc... @@ -316,11 +316,9 @@ void System::get_partition_info() { auto current_partition = (const char *)esp_ota_get_running_partition()->label; - // update the current version and partition name in NVS if not already set (saves on flash wearing) - if (EMSESP::nvs_.getString(current_partition) != EMSESP_APP_VERSION || emsesp::EMSESP::nvs_.getBool(emsesp::EMSESP_NVS_BOOT_NEW_FIRMWARE, false)) { - if (EMSESP::nvs_.getBool(emsesp::EMSESP_NVS_BOOT_NEW_FIRMWARE, false)) { - EMSESP::nvs_.putBool(emsesp::EMSESP_NVS_BOOT_NEW_FIRMWARE, false); - } + // update the current version and partition name in NVS if not already set + if (EMSESP::nvs_.getString(current_partition) != EMSESP_APP_VERSION || emsesp::EMSESP::nvs_.getBool(emsesp::EMSESP_NVS_BOOT_NEW_FIRMWARE, true)) { + EMSESP::nvs_.putBool(emsesp::EMSESP_NVS_BOOT_NEW_FIRMWARE, false); EMSESP::nvs_.putString(current_partition, EMSESP_APP_VERSION); char c[20]; snprintf(c, sizeof(c), "d_%s", current_partition); @@ -1216,16 +1214,16 @@ void System::show_system(uuid::console::Shell & shell) { // GPIOs shell.println(" GPIOs:"); shell.printf(" in use:"); - for (const auto & gpio : used_gpios_) { - shell.printf(" %d", gpio); + for (const auto & usage : used_gpios_) { + shell.printf(" %d(%s)", usage.pin, usage.source.c_str()); } - shell.printfln(" (total %d)", used_gpios_.size()); + shell.printfln(" [total %d]", used_gpios_.size()); auto available = available_gpios(); shell.printf(" available:"); for (const auto & gpio : available) { shell.printf(" %d", gpio); } - shell.printfln(" (total %d)", available.size()); + shell.printfln(" [total %d]", available.size()); // List all partitions and their version info shell.println(" Partitions:"); for (const auto & partition : partition_info_) { @@ -1410,7 +1408,7 @@ bool System::check_upgrade() { // see if we're missing a version, will be < 3.5.0b13 from Dec 23 2022 missing_version = (settingsVersion.empty() || (settingsVersion.length() < 5)); if (missing_version) { - LOG_WARNING("No version information found"); + LOG_WARNING("No version information found. Assuming version 3.5.0"); settingsVersion = "3.5.0"; // this was the last stable version without version info } @@ -1422,7 +1420,7 @@ bool System::check_upgrade() { bool save_version = true; bool reboot_required = false; - LOG_DEBUG("Checking for version upgrades (settings file is v%d.%d.%d%s)", + LOG_DEBUG("Checking for version upgrades from v%d.%d.%d%s", settings_version.major(), settings_version.minor(), settings_version.patch(), @@ -1432,7 +1430,7 @@ bool System::check_upgrade() { if (this_version > settings_version) { // we need to do an upgrade if (missing_version) { - LOG_NOTICE("Upgrading to version %d.%d.%d%s", this_version.major(), this_version.minor(), this_version.patch(), this_version_type); + LOG_NOTICE("Upgrading to version %d.%d.%d%s", this_version.major(), this_version.minor(), this_version.patch(), this_version_type.c_str()); } else { LOG_NOTICE("Upgrading from version %d.%d.%d%s to %d.%d.%d%s", settings_version.major(), @@ -1499,7 +1497,15 @@ bool System::check_upgrade() { }); } else if (this_version < settings_version) { // downgrading - LOG_NOTICE("Downgrading to version %d.%d.%d%s", this_version.major(), this_version.minor(), this_version.patch(), this_version_type.c_str()); + LOG_NOTICE("Downgrading from version %d.%d.%d%s to version %d.%d.%d%s", + settings_version.major(), + settings_version.minor(), + settings_version.patch(), + settings_version_type.c_str(), + this_version.major(), + this_version.minor(), + this_version.patch(), + this_version_type.c_str()); } else { save_version = false; // same version, do nothing } @@ -2031,8 +2037,9 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output #else node["version"] = EMSESP_APP_VERSION; #endif - node["uptime"] = uuid::log::format_timestamp_ms(uuid::get_uptime_ms(), 3); - node["uptimeSec"] = uuid::get_uptime_sec(); + node["uptime"] = uuid::log::format_timestamp_ms(uuid::get_uptime_ms(), 3); + node["uptimeSec"] = uuid::get_uptime_sec(); + node["resetReason"] = EMSESP::system_.reset_reason(0) + " / " + EMSESP::system_.reset_reason(1); #ifndef EMSESP_STANDALONE node["platform"] = EMSESP_PLATFORM; node["cpuType"] = ESP.getChipModel(); @@ -2045,10 +2052,7 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output node["freeApp"] = EMSESP::system_.appFree(); // kilobytes node["partition"] = (const char *)esp_ota_get_running_partition()->label; // active partition node["flash_chip_size"] = ESP.getFlashChipSize() / 1024; // kilobytes -#endif - node["resetReason"] = EMSESP::system_.reset_reason(0) + " / " + EMSESP::system_.reset_reason(1); -#ifndef EMSESP_STANDALONE - node["psram"] = (EMSESP::system_.PSram() > 0); // make boolean + node["psram"] = (EMSESP::system_.PSram() > 0); // make boolean if (EMSESP::system_.PSram()) { node["psramSize"] = EMSESP::system_.PSram(); node["freePsram"] = ESP.getFreePsram() / 1024; @@ -2057,9 +2061,27 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output #if CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2 node["temperature"] = EMSESP::system_.temperature(); #endif - - node["txpause"] = EMSbus::tx_mode() == EMS_TXMODE_OFF; #endif + node["txpause"] = EMSbus::tx_mode() == EMS_TXMODE_OFF; + + // GPIO information + std::string gpios_in_use_str; + for (const auto & usage : EMSESP::system_.used_gpios_) { + if (!gpios_in_use_str.empty()) { + gpios_in_use_str += ", "; + } + gpios_in_use_str += Helpers::itoa(usage.pin); + } + node["gpios_in_use"] = gpios_in_use_str; + + std::string gpios_available_str; + for (const auto & gpio : EMSESP::system_.available_gpios()) { + if (!gpios_available_str.empty()) { + gpios_available_str += ", "; + } + gpios_available_str += Helpers::itoa(gpio); + } + node["gpios_available"] = gpios_available_str; // Network Status node = output["network"].to(); @@ -2448,15 +2470,15 @@ bool System::command_txpause(const char * value, const int8_t id) { // format command - factory reset, removing all config files bool System::command_format(const char * value, const int8_t id) { -#if !defined(EMSESP_STANDALONE) && !defined(EMSESP_DEBUG) - // don't really format the filesystem in debug or standalone mode +#if !defined(EMSESP_STANDALONE) && !defined(EMSESP_TEST) + // don't really format the filesystem in test or standalone mode if (LittleFS.format()) { LOG_INFO("Filesystem formatted successfully. All config files removed."); } else { LOG_ERROR("Format failed"); } #else - LOG_INFO("Format command not available in standalone or debug mode"); + LOG_ERROR("Format command not available in standalone or test mode"); #endif // restart will be handled by the main loop @@ -2553,21 +2575,6 @@ bool System::ntp_connected() { // see if its a BBQKees Gateway by checking the nvs values String System::getBBQKeesGatewayDetails(uint8_t detail) { #ifndef EMSESP_STANDALONE - /* - if (!EMSESP::nvs_.isKey("mfg")) { - return ""; - } - - // mfg can be either "BBQKees" or "BBQKees Electronics" - auto mfg = EMSESP::nvs_.getString("mfg"); - if (mfg) { - if (!mfg.startsWith("BBQKees")) { - return ""; - } - } - - return "BBQKees Gateway Model " + EMSESP::nvs_.getString("model") + " v" + EMSESP::nvs_.getString("hwrevision") + "/" + EMSESP::nvs_.getString("batch"); -*/ union { struct { uint32_t no : 4; @@ -2580,14 +2587,17 @@ String System::getBBQKeesGatewayDetails(uint8_t detail) { }; uint32_t reg; } gw; + for (uint8_t reg = 0; reg < 8; reg++) { gw.reg = esp_efuse_read_reg(EFUSE_BLK3, reg); if (reg == 7 || esp_efuse_read_reg(EFUSE_BLK3, reg + 1) == 0) break; } + const char * mfg[] = {"unknown", "BBQKees Electronics", "", "", "", "", "", ""}; const char * model[] = {"unknown", "S3", "E32V2", "E32V2.2", "S32", "E32", "", "", ""}; const char * board[] = {"CUSTOM", "S32S3", "E32V2", "E32V2_2", "S32", "E32", "", "", ""}; + switch (detail) { case FUSE_VALUE::MFG: return gw.mfg < 2 ? String(mfg[gw.mfg]) : "unknown"; @@ -2605,9 +2615,11 @@ String System::getBBQKeesGatewayDetails(uint8_t detail) { default: break; } + if (!gw.reg || gw.mfg > 1 || gw.model > 5) { return ""; } + return String(mfg[gw.mfg]) + " " + String(model[gw.model]) + " rev." + String(gw.rev_major) + "." + String(gw.rev_minor) + "/" + String(2000 + gw.year) + (gw.month < 10 ? "0" : "") + String(gw.month) + String(gw.no); #else @@ -2684,6 +2696,9 @@ bool System::uploadFirmwareURL(const char * url) { // we're about to start the upload, set the status so the Web System Monitor spots it EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_UPLOADING); + // set a callback so we can monitor progress in the WebUI + Update.onProgress([](size_t progress, size_t total) { EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_UPLOADING + (progress * 100 / total)); }); + // get tcp stream and send it to Updater WiFiClient * stream = http.getStreamPtr(); if (Update.writeStream(*stream) != firmware_size) { @@ -2772,10 +2787,15 @@ bool System::command_read(const char * value, const int8_t id) { } // set the system status code - SYSTEM_STATUS in system.h +// this is also used in the SystemMonitor.tsx WebUI to show the progress of the firmware upload, start at 100 void System::systemStatus(uint8_t status_code) { if (systemStatus_ != status_code) { systemStatus_ = status_code; - LOG_DEBUG("Setting System status code %d", status_code); +#ifdef EMSESP_DEBUG + if (status_code < SYSTEM_STATUS::SYSTEM_STATUS_UPLOADING) { + LOG_DEBUG("Setting System status code %d", status_code); + } +#endif } } @@ -2783,8 +2803,11 @@ uint8_t System::systemStatus() { return systemStatus_; } -// takes a string range like "6-11, 1, 23, 24-48" which has optional ranges and single values and converts to a vector of ints -std::vector> System::string_range_to_vector(const std::string & range) { +// takes two arguments: +// the first is the full range of pins to consider +// the second is a string range of GPIOs to exclude, like "6-11, 1, 23, 24-48" +// returns a vector array of GPIOs that are valid for use +std::vector> System::string_range_to_vector(const std::string & range, const std::string & exclude) { std::vector> gpios; std::string::size_type pos = 0; std::string::size_type prev = 0; @@ -2816,41 +2839,122 @@ std::vector> System::string_range_to_vector(con // handle the last part process_part(range.substr(prev)); + // if exclude list is provided, parse it and remove excluded GPIOs + if (!exclude.empty()) { + std::vector> exclude_gpios; + pos = 0; + prev = 0; + + auto process_exclude = [&exclude_gpios](std::string part) { + // trim whitespace + part.erase(0, part.find_first_not_of(" \t")); + part.erase(part.find_last_not_of(" \t") + 1); + + // check if it's a range (contains '-') + std::string::size_type dash_pos = part.find('-'); + if (dash_pos != std::string::npos) { + // it's a range like "6-11" + int start = std::stoi(part.substr(0, dash_pos)); + int end = std::stoi(part.substr(dash_pos + 1)); + for (int i = start; i <= end; i++) { + exclude_gpios.push_back(static_cast(i)); + } + } else { + exclude_gpios.push_back(static_cast(std::stoi(part))); + } + }; + + while ((pos = exclude.find(',', prev)) != std::string::npos) { + process_exclude(exclude.substr(prev, pos - prev)); + prev = pos + 1; + } + + // handle the last part + process_exclude(exclude.substr(prev)); + + // remove excluded GPIOs from the main list + gpios.erase(std::remove_if(gpios.begin(), + gpios.end(), + [&exclude_gpios](uint8_t gpio) { return std::find(exclude_gpios.begin(), exclude_gpios.end(), gpio) != exclude_gpios.end(); }), + gpios.end()); + } + return gpios; } // initialize a list of valid GPIOs based on the ESP32 board -// note: we always allow 0, which is used to indicate Dallas or LED is disabled +// string_to_vector() take two strings, the first is the range of GPIOs to use, the second is a list of GPIOs to exclude +// notes: +// we always allow 0 (which is usually a strapping pin), because it's used to indicate whether EMS-ESP Dallas or the LED is disabled +// we allow UART0, 1 and 2 as they are configurable +// strapping pins are disabled as they can affect boot behaviour +// we accept GPIOs that are fixed on BBQKees boards +// void System::set_valid_system_gpios() { valid_system_gpios_.clear(); // reset system list used_gpios_.clear(); // reset used list // get free gpios based on board/platform type #if CONFIG_IDF_TARGET_ESP32C3 - // https://www.wemos.cc/en/latest/c3/c3_mini.html - valid_system_gpios_ = string_range_to_vector("0-10"); // UART0=20,21 + // https://docs.espressif.com/projects/esp-idf/en/stable/esp32c3/api-reference/peripherals/gpio.html + // excluded: + // GPIO2, GPIO8 - GPIO9 = strapping pins + // GPIO12 - GPIO17 = used for SPI flash and PSRAM + // GPIO18 - GPIO19 = USB-JTAG + // + // notes on what is allowed: + // GPIO10 = button on BOARD_C3_MINI_V1 + valid_system_gpios_ = string_range_to_vector("0-21", "2, 8-9, 12-17, 18-19"); + #elif CONFIG_IDF_TARGET_ESP32S2 - // 43 and 44 are UART0 pins - // 38 and 39 are strapping pins, input only - valid_system_gpios_ = string_range_to_vector("0-14, 19, 20, 21, 33-37, 45, 46"); + // https://docs.espressif.com/projects/esp-idf/en/stable/esp32s2/api-reference/peripherals/gpio.html + // excluded: + // GPIO26 - GPIO32 = SPI flash and PSRAM + // GPIO45 - GPIO46 = strapping pins + // GPIO39 - GPIO42 = USB-JTAG + // GPIO22 - GPIO25 = don't exist + // + // notes on what is allowed: + valid_system_gpios_ = string_range_to_vector("0-46", "26-32, 45-46, 39-42, 22-25"); + #elif CONFIG_IDF_TARGET_ESP32S3 - // 43 and 44 are UART0 pins - // 33-37 for Octal SPI (SPIIO4 through SPIIO7 and SPIDQS) - // 38 and 39 are input only - // 45 and 36 are strapping pins, input only - // 47 and 48 are valid on a Wemos S3 (https://github.com/emsesp/EMS-ESP32/issues/2874) - valid_system_gpios_ = string_range_to_vector("0-14, 17, 18, 21, 33-39, 45-48"); + // https://docs.espressif.com/projects/esp-idf/en/stable/esp32s3/api-reference/peripherals/gpio.html + // excluded: + // GPIO3, GPIO45 - GPIO46 = strapping pins + // GPIO26 - GPIO32 = SPI flash and PSRAM and not recommended + // GPIO33 - GPIO37 = Octal flash/PSRAM + // GPIO19 - GPIO20 = USB-JTAG + // GPIO22 - GPIO25 = don't exist + // + // notes on what is allowed: + // GPIO11 - GPIO19 = ADC analog input only pins + // GPIO47 - GPIO48 = valid on a Wemos S3 + // GPIO8 = used by Liligo S3 board profile for Rx + valid_system_gpios_ = string_range_to_vector("0-48", "3, 45-46, 26-32, 33-37, 19-20, 22-25"); + #elif CONFIG_IDF_TARGET_ESP32 - // 1 and 3 are UART0 pins, but used for some eth-boards (BBQKees-E32, OlimexPOE) - // 32-39 is ADC1, input only + // https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/peripherals/gpio.html + // excluded: + // GPIO6 - GPIO11, GPIO16 - GPIO17 = used for SPI flash and PSRAM + // GPIO12 - GPIO15 = USB-JTAG (but we allow GPIO14 for BBQKees) and GPIO12 & GPIO13 also reserved for BBQKees E32V2.2 + // GPIO20, GPIO24, GPIO28 - GPIO31 = don't exist + // + // notes on what is allowed: + // GPIO34, GPIO35, GPIO37 = input only + // GPIO2, GPIO4, GPIO5, GPIO14 = used on BBQKees boards for either LED, Dallas or Rx + // GPIO23 and GPIO18 are used by Ethernet + // GPIO25 - GPIO37 = ADC2 + // GPIO32 - GPIO39 = ADC1 + // GPIO36 = used on BBQKees boards for supply_voltage (E32V2.2) (note may conflict with WiFI on other boards) + // GPIO39 = used on BBQKees boards for core_voltage (E32V2.2) (note may conflict with WiFI on other boards) if (ESP.getPsramSize() > 0) { - // if psram is enabled remove pins 16 and 17 from the list - valid_system_gpios_ = string_range_to_vector("0-5, 12-15, 18-19, 23, 25-27, 32-39"); + // remove SPI0/1 PSRAM pins GPIO16 (CS) and GPIO17 (CLK) from the list + valid_system_gpios_ = string_range_to_vector("0-39", "6-11, 12, 13, 15, 16, 17, 20, 24, 28-31"); } else { - valid_system_gpios_ = string_range_to_vector("0-5, 12-19, 23, 25-27, 32-39"); + valid_system_gpios_ = string_range_to_vector("0-39", "6-11, 12, 13, 15, 20, 24, 28-31"); } #elif defined(EMSESP_STANDALONE) - valid_system_gpios_ = string_range_to_vector("0-5, 12-19, 23, 25-27, 32-39"); + valid_system_gpios_ = string_range_to_vector("0-39"); #endif } @@ -2860,8 +2964,9 @@ bool System::add_gpio(uint8_t pin, const char * source_name) { // check if this is a valid user GPIO if (std::find(valid_system_gpios_.begin(), valid_system_gpios_.end(), pin) != valid_system_gpios_.end()) { // It's valid now check if it's already in the used list - if (std::find(used_gpios_.begin(), used_gpios_.end(), pin) != used_gpios_.end()) { - LOG_WARNING("GPIO %d for %s is already in use", pin, source_name); + auto it = std::find_if(used_gpios_.begin(), used_gpios_.end(), [pin](const GpioUsage & usage) { return usage.pin == pin; }); + if (it != used_gpios_.end()) { + LOG_WARNING("GPIO %d for %s is already in use by %s", pin, source_name, it->source.c_str()); return false; // Pin is already used } } else { @@ -2874,24 +2979,24 @@ bool System::add_gpio(uint8_t pin, const char * source_name) { remove_gpio(pin); LOG_DEBUG("Adding GPIO %d for %s to used gpio list", pin, source_name); - used_gpios_.push_back(pin); // add to used list + used_gpios_.push_back({pin, source_name}); // add to used list return true; } // remove a gpio from both valid and used lists void System::remove_gpio(uint8_t pin, bool also_system) { - auto it = std::find(used_gpios_.begin(), used_gpios_.end(), pin); + auto it = std::find_if(used_gpios_.begin(), used_gpios_.end(), [pin](const GpioUsage & usage) { return usage.pin == pin; }); if (it != used_gpios_.end()) { LOG_DEBUG("GPIO %d removed from used gpio list", pin); used_gpios_.erase(it); } if (also_system) { - it = std::find(valid_system_gpios_.begin(), valid_system_gpios_.end(), pin); - if (it != valid_system_gpios_.end()) { + auto it_sys = std::find(valid_system_gpios_.begin(), valid_system_gpios_.end(), pin); + if (it_sys != valid_system_gpios_.end()) { LOG_DEBUG("GPIO %d removed from valid gpio list", pin); - valid_system_gpios_.erase(it); + valid_system_gpios_.erase(it_sys); } } } @@ -2900,7 +3005,7 @@ void System::remove_gpio(uint8_t pin, bool also_system) { std::vector System::available_gpios() { std::vector gpios; for (const auto & gpio : valid_system_gpios_) { - if (std::find(used_gpios_.begin(), used_gpios_.end(), gpio) == used_gpios_.end()) { + if (std::find_if(used_gpios_.begin(), used_gpios_.end(), [gpio](const GpioUsage & usage) { return usage.pin == gpio; }) == used_gpios_.end()) { gpios.push_back(gpio); // didn't find it in used_gpios_, so it's available } } @@ -2909,8 +3014,8 @@ std::vector System::available_gpios() { // make a snapshot of the current GPIOs void System::make_snapshot_gpios(std::vector & u_gpios, std::vector & s_gpios) { - for (const auto & gpio : used_gpios_) { - u_gpios.push_back(gpio); + for (const auto & usage : used_gpios_) { + u_gpios.push_back(usage.pin); } for (const auto & gpio : valid_system_gpios_) { s_gpios.push_back(gpio); @@ -2921,7 +3026,7 @@ void System::make_snapshot_gpios(std::vector & u_gpios, std::vector & u_gpios, std::vector & s_gpios) { used_gpios_.clear(); for (const auto & gpio : u_gpios) { - used_gpios_.push_back(gpio); + used_gpios_.push_back({static_cast(gpio), "restored"}); } valid_system_gpios_.clear(); diff --git a/src/core/system.h b/src/core/system.h index 1ed74833c..79e45a9c8 100644 --- a/src/core/system.h +++ b/src/core/system.h @@ -159,7 +159,6 @@ class System { static void extractSettings(const char * filename, const char * section, JsonObject output); static bool saveSettings(const char * filename, const char * section, JsonObject input); - // GPIOs static bool add_gpio(uint8_t pin, const char * source_name); static std::vector available_gpios(); static bool load_board_profile(std::vector & data, const std::string & board_profile); @@ -434,11 +433,15 @@ class System { void led_monitor(); void system_check(); - static std::vector> string_range_to_vector(const std::string & range); + static std::vector> string_range_to_vector(const std::string & range, const std::string & exclude = ""); // GPIOs - static std::vector> valid_system_gpios_; // list of valid GPIOs for the ESP32 board that can be used - static std::vector> used_gpios_; // list of GPIOs used by the application + struct GpioUsage { + uint8_t pin; + std::string source; + }; + static std::vector> valid_system_gpios_; // list of valid GPIOs for the ESP32 board that can be used + static std::vector> used_gpios_; // list of GPIOs used by the application int8_t wifi_quality(int8_t dBm); diff --git a/src/core/telegram.cpp b/src/core/telegram.cpp index 5d6b5d797..348f58c5c 100644 --- a/src/core/telegram.cpp +++ b/src/core/telegram.cpp @@ -221,14 +221,14 @@ void RxService::add(uint8_t * data, uint8_t length) { LOG_TRACE("Rx: %s", Helpers::data_to_hex(data, length).c_str()); } - LOG_DEBUG("New Rx telegram, message length %d", message_length); - - // if we don't have a type_id exit, - // do not exit on empty message, it is checked for toggle fetch + // if we don't have a type_id exit + // do not exit on empty message, it is checked later for toggle fetch if (type_id == 0) { return; } + LOG_DEBUG("New Rx telegram, message length %d", message_length); + // create the telegram auto telegram = std::make_shared(operation, src, dest, type_id, offset, message_data, message_length); diff --git a/src/emsesp_version.h b/src/emsesp_version.h index 7ae47456b..0afe1d82c 100644 --- a/src/emsesp_version.h +++ b/src/emsesp_version.h @@ -1 +1 @@ -#define EMSESP_APP_VERSION "3.8.1-dev.3" +#define EMSESP_APP_VERSION "3.8.1-dev.4" diff --git a/src/web/WebCustomizationService.cpp b/src/web/WebCustomizationService.cpp index 9490b8067..80e1108b9 100644 --- a/src/web/WebCustomizationService.cpp +++ b/src/web/WebCustomizationService.cpp @@ -120,10 +120,11 @@ StateUpdateResult WebCustomization::update(JsonObject root, WebCustomization & c for (const JsonObject analogJson : analogJsons) { // create each of the sensor, overwriting any previous settings // if the gpio is invalid skip the sensor - if (!EMSESP::system_.add_gpio(analogJson["gpio"].as(), "Analog Sensor")) { + auto analog_sensor_name = analogJson["name"].as(); + if (!EMSESP::system_.add_gpio(analogJson["gpio"].as(), analog_sensor_name)) { EMSESP::logger().warning("Analog sensor: Invalid GPIO %d for %s. Skipping.", analogJson["gpio"].as(), - analogJson["name"].as()); + analog_sensor_name); continue; } auto analog = AnalogCustomization(); diff --git a/src/web/WebLogService.cpp b/src/web/WebLogService.cpp index 15cdbc877..46e70ef14 100644 --- a/src/web/WebLogService.cpp +++ b/src/web/WebLogService.cpp @@ -97,7 +97,7 @@ void WebLogService::show(Shell & shell) { } shell.println(); - shell.printfln("Recent Log (level %s, max %d messages):", uuid::log::format_level_uppercase(level_), maximum_log_messages_); + shell.printfln("Recent Log:"); shell.println(); for (const auto & message : log_messages_) { diff --git a/src/web/WebSettingsService.cpp b/src/web/WebSettingsService.cpp index 7067516c9..fc2d4c3c6 100644 --- a/src/web/WebSettingsService.cpp +++ b/src/web/WebSettingsService.cpp @@ -96,9 +96,7 @@ StateUpdateResult WebSettings::update(JsonObject root, WebSettings & settings) { std::vector system_gpios; EMSESP::system_.make_snapshot_gpios(used_gpios, system_gpios); - reset_flags(); - - settings.version = root["version"] | EMSESP_DEFAULT_VERSION; // save the version, we use it later in System::check_upgrade() + settings.version = root["version"] | EMSESP_APP_VERSION; // save the version, we use it later in System::check_upgrade() settings.board_profile = root["board_profile"] | EMSESP_DEFAULT_BOARD_PROFILE; // get current values that are related to the board profile @@ -113,10 +111,13 @@ StateUpdateResult WebSettings::update(JsonObject root, WebSettings & settings) { settings.eth_clock_mode = root["eth_clock_mode"]; settings.led_type = root["led_type"]; // 1 = RGB-LED + reset_flags(); + // see if the user has changed the board profile // this will set: led_gpio, dallas_gpio, rx_gpio, tx_gpio, pbutton_gpio, phy_type, eth_power, eth_phy_addr, eth_clock_mode, led_type // this will always run when EMS-ESP starts since original_settings{} is empty - if (original_settings.board_profile != settings.board_profile) { + if (original_settings.board_profile != settings.board_profile || original_settings.board_profile == "default" + || original_settings.board_profile.length() == 0) { set_board_profile(settings); add_flags(ChangeFlags::RESTART); } @@ -306,10 +307,10 @@ StateUpdateResult WebSettings::update(JsonObject root, WebSettings & settings) { // save the settings if changed from the webUI // if we encountered an invalid GPIO, rollback changes and don't save settings, and report the error to WebUI + // without a restart if (!have_valid_gpios) { // replace settings with original settings - settings = original_settings; // the original settings are still valid - // restore the GPIOs from the snapshot + settings = original_settings; EMSESP::system_.restore_snapshot_gpios(used_gpios, system_gpios); // report the error to WebUI @@ -320,7 +321,8 @@ StateUpdateResult WebSettings::update(JsonObject root, WebSettings & settings) { // save the setting internally, for reference later EMSESP::system_.store_settings(settings); - if (has_flags(WebSettings::ChangeFlags::RESTART)) { + // and finally always write to the settings file + if (has_flags(ChangeFlags::RESTART)) { return StateUpdateResult::CHANGED_RESTART; } diff --git a/src/web/WebSettingsService.h b/src/web/WebSettingsService.h index bc66f024c..91a01f2cc 100644 --- a/src/web/WebSettingsService.h +++ b/src/web/WebSettingsService.h @@ -30,7 +30,7 @@ namespace emsesp { class WebSettings { public: - String version; + String version = EMSESP_APP_VERSION; String locale; uint8_t tx_mode; uint8_t ems_bus_id; diff --git a/test/test_api/test_api.cpp b/test/test_api/test_api.cpp index a003ecde1..67273e127 100644 --- a/test/test_api/test_api.cpp +++ b/test/test_api/test_api.cpp @@ -203,17 +203,40 @@ void capture(const char * url = nullptr) { } if (url) { - // call API, find and replace all double quotes with escaped quotes - std::string escaped_response = call_url(url); + // call API, store the response in a string + std::string response = call_url(url); - size_t pos = 0; - while ((pos = escaped_response.find("\"", pos)) != std::string::npos) { - escaped_response.replace(pos, 1, "\\\""); - pos += 2; + // escape all special characters for C++ string literal + std::string escaped_response; + escaped_response.reserve(response.length() * 2); // pre-allocate for efficiency + + for (char c : response) { + switch (c) { + case '\n': + escaped_response += "\\n"; + break; + case '\r': + escaped_response += "\\r"; + break; + case '\t': + escaped_response += "\\t"; + break; + case '\\': + escaped_response += "\\\\"; + break; + case '\"': + escaped_response += "\\\""; + break; + default: + escaped_response += c; + break; + } } Serial.printf("void test_%d() {\n", count++); - Serial.printf(" auto expected_response = \"%s\";\n", escaped_response.c_str()); + Serial.print(" auto expected_response = \""); + Serial.print(escaped_response.c_str()); + Serial.println("\";"); Serial.printf(" TEST_ASSERT_EQUAL_STRING(expected_response, call_url(\"%s\"));\n", url); Serial.println("}"); Serial.println(); diff --git a/test/test_api/test_api.h b/test/test_api/test_api.h index 1e2974d63..1696d48c9 100644 --- a/test/test_api/test_api.h +++ b/test/test_api/test_api.h @@ -67,103 +67,159 @@ void test_4() { } void test_5() { + auto expected_response = + "[{\"api_data\":\"# HELP emsesp_heatingoff force heating off, boolean, readable, writeable, visible\\n# TYPE emsesp_heatingoff " + "gauge\\nemsesp_heatingoff 0\\n# HELP emsesp_heatingactive is my heating on?, boolean, readable, visible\\n# TYPE emsesp_heatingactive " + "gauge\\nemsesp_heatingactive 0\\n# HELP emsesp_tapwateractive tapwater active, boolean, readable, visible\\n# TYPE emsesp_tapwateractive " + "gauge\\nemsesp_tapwateractive 1\\n# HELP emsesp_selflowtemp selected flow temperature, °C, readable, writeable, visible\\n# TYPE emsesp_selflowtemp " + "gauge\\nemsesp_selflowtemp 0\\n# HELP emsesp_curflowtemp current flow temperature, °C, readable, visible\\n# TYPE emsesp_curflowtemp " + "gauge\\nemsesp_curflowtemp 60.20\\n# HELP emsesp_rettemp return temperature, °C, readable, visible\\n# TYPE emsesp_rettemp gauge\\nemsesp_rettemp " + "48.10\\n# HELP emsesp_syspress system pressure, bar, readable, visible\\n# TYPE emsesp_syspress gauge\\nemsesp_syspress 1.40\\n# HELP emsesp_burngas " + "gas, boolean, readable, visible\\n# TYPE emsesp_burngas gauge\\nemsesp_burngas 1\\n# HELP emsesp_burngas2 gas stage 2, boolean, readable, visible\\n# " + "TYPE emsesp_burngas2 gauge\\nemsesp_burngas2 0\\n# HELP emsesp_flamecurr flame current, µA, readable, visible\\n# TYPE emsesp_flamecurr " + "gauge\\nemsesp_flamecurr 37.40\\n# HELP emsesp_fanwork fan, boolean, readable, visible\\n# TYPE emsesp_fanwork gauge\\nemsesp_fanwork 1\\n# HELP " + "emsesp_ignwork ignition, boolean, readable, visible\\n# TYPE emsesp_ignwork gauge\\nemsesp_ignwork 0\\n# HELP emsesp_oilpreheat oil preheating, " + "boolean, readable, visible\\n# TYPE emsesp_oilpreheat gauge\\nemsesp_oilpreheat 0\\n# HELP emsesp_heatingpump heating pump, boolean, readable, " + "visible\\n# TYPE emsesp_heatingpump gauge\\nemsesp_heatingpump 1\\n# HELP emsesp_selburnpow burner selected max power, %, readable, writeable, " + "visible\\n# TYPE emsesp_selburnpow gauge\\nemsesp_selburnpow 115\\n# HELP emsesp_curburnpow burner current power, %, readable, visible\\n# TYPE " + "emsesp_curburnpow gauge\\nemsesp_curburnpow 61\\n# HELP emsesp_ubauptime total UBA operating time, minutes, readable, visible\\n# TYPE " + "emsesp_ubauptime gauge\\nemsesp_ubauptime 3940268\\n# HELP emsesp_servicecodenumber service code number, readable, visible\\n# TYPE " + "emsesp_servicecodenumber gauge\\nemsesp_servicecodenumber 201\\n# HELP emsesp_seltemp selected temperature, °C, readable, writeable, visible\\n# TYPE " + "emsesp_seltemp gauge\\nemsesp_seltemp{circuit=\\\"dhw\\\"} 52\\n# HELP emsesp_comfort comfort, enum, (0: hot; 1: eco; 2: intelligent), readable, " + "writeable, visible\\n# TYPE emsesp_comfort gauge\\nemsesp_comfort{circuit=\\\"dhw\\\"} 0\\n# HELP emsesp_flowtempoffset flow temperature offset, °C, " + "readable, writeable, visible\\n# TYPE emsesp_flowtempoffset gauge\\nemsesp_flowtempoffset{circuit=\\\"dhw\\\"} 40\\n# HELP emsesp_chargeoptimization " + "charge optimization, boolean, readable, writeable, visible\\n# TYPE emsesp_chargeoptimization gauge\\nemsesp_chargeoptimization{circuit=\\\"dhw\\\"} " + "0\\n# HELP emsesp_circpump circulation pump available, boolean, readable, writeable, visible\\n# TYPE emsesp_circpump " + "gauge\\nemsesp_circpump{circuit=\\\"dhw\\\"} 0\\n# HELP emsesp_chargetype charging type, enum, (0: chargepump; 1: 3-way valve), readable, visible\\n# " + "TYPE emsesp_chargetype gauge\\nemsesp_chargetype{circuit=\\\"dhw\\\"} 1\\n# HELP emsesp_hyston hysteresis on temperature, °C, readable, writeable, " + "visible\\n# TYPE emsesp_hyston gauge\\nemsesp_hyston{circuit=\\\"dhw\\\"} -5\\n# HELP emsesp_disinfectiontemp disinfection temperature, °C, readable, " + "writeable, visible\\n# TYPE emsesp_disinfectiontemp gauge\\nemsesp_disinfectiontemp{circuit=\\\"dhw\\\"} 70\\n# HELP emsesp_circmode circulation pump " + "mode, enum, (0: off; 1: 1x3min; 2: 2x3min; 3: 3x3min; 4: 4x3min; 5: 5x3min; 6: 6x3min; 7: continuous), readable, writeable, visible\\n# TYPE " + "emsesp_circmode gauge\\nemsesp_circmode{circuit=\\\"dhw\\\"} 0\\n# HELP emsesp_circ circulation active, boolean, readable, writeable, visible\\n# " + "TYPE emsesp_circ gauge\\nemsesp_circ{circuit=\\\"dhw\\\"} 0\\n# HELP emsesp_storagetemp1 storage intern temperature, °C, readable, visible\\n# TYPE " + "emsesp_storagetemp1 gauge\\nemsesp_storagetemp1{circuit=\\\"dhw\\\"} 53.80\\n# HELP emsesp_activated activated, boolean, readable, writeable, " + "visible\\n# TYPE emsesp_activated gauge\\nemsesp_activated{circuit=\\\"dhw\\\"} 1\\n# HELP emsesp_3wayvalve 3-way valve active, boolean, readable, " + "visible\\n# TYPE emsesp_3wayvalve gauge\\nemsesp_3wayvalve{circuit=\\\"dhw\\\"} 1\\n# HELP emsesp_chargepump charge pump, boolean, readable, " + "visible\\n# TYPE emsesp_chargepump gauge\\nemsesp_chargepump{circuit=\\\"dhw\\\"} 0\\n# HELP emsesp_nompower nominal Power, kW, readable, writeable, " + "visible\\n# TYPE emsesp_nompower gauge\\nemsesp_nompower 0\\n# HELP emsesp_nrgtotal total energy, kWh, readable, visible\\n# TYPE emsesp_nrgtotal " + "gauge\\nemsesp_nrgtotal 0\\n# HELP emsesp_nrgheat energy heating, kWh, readable, writeable, visible\\n# TYPE emsesp_nrgheat gauge\\nemsesp_nrgheat " + "0\\n# HELP emsesp_nrg energy, kWh, readable, writeable, visible\\n# TYPE emsesp_nrg gauge\\nemsesp_nrg{circuit=\\\"dhw\\\"} 0\\n\"}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/boiler/metrics")); +} + +void test_6() { auto expected_response = "[{\"name\":\"comfort\",\"fullname\":\"dhw " "comfort\",\"circuit\":\"dhw\",\"index\":0,\"enum\":[\"hot\",\"eco\",\"intelligent\"],\"value\":\"hot\",\"type\":\"enum\"," "\"readable\":true,\"writeable\":true,\"visible\":true}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/boiler/comfort")); } -void test_6() { +void test_7() { auto expected_response = "[{\"api_data\":\"hot\"}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/boiler/comfort/value")); } -void test_7() { +void test_8() { auto expected_response = "[{\"api_data\":\"dhw comfort\"}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/boiler/comfort/fullname")); } -void test_8() { +void test_9() { auto expected_response = "[{\"name\":\"outdoortemp\",\"fullname\":\"outside " "temperature\",\"circuit\":\"\",\"type\":\"number\",\"uom\":\"°C\",\"state_class\":\"measurement\",\"device_class\":" "\"temperature\",\"readable\":true,\"writeable\":false,\"visible\":true}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/boiler/outdoortemp")); } -void test_9() { +void test_10() { auto expected_response = "[{\"name\":\"chargetype\",\"fullname\":\"dhw charging type\",\"circuit\":\"dhw\",\"index\":1,\"enum\":[\"chargepump\",\"3-way " "valve\"],\"value\":\"3-way valve\",\"type\":\"enum\",\"readable\":true,\"writeable\":false,\"visible\":true}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/boiler/dhw/chargetype")); } -void test_10() { +void test_11() { auto expected_response = "[{\"api_data\":\"false\"}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/boiler/dhw.chargetype/writeable")); } -void test_11() { +void test_12() { auto expected_response = "[{\"api_data\":\"37.4\"}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/boiler/flamecurr/value")); } -void test_12() { +void test_13() { auto expected_response = "[{\"hc1\":{\"seltemp\":20.5,\"currtemp\":22.8,\"haclimate\":\"roomTemp\",\"modetype\":\"heat\",\"remotetemp\":null},\"hc2\":{" "\"seltemp\":20.6,\"currtemp\":22.9,\"haclimate\":\"roomTemp\",\"modetype\":\"eco\",\"remotetemp\":null},\"hc3\":{\"seltemp\":20." "7,\"currtemp\":23.0,\"haclimate\":\"roomTemp\",\"modetype\":\"nofrost\",\"remotetemp\":null},\"dhw\":{}}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/thermostat")); } -void test_13() { +void test_14() { auto expected_response = "[{\"seltemp\":20.5,\"currtemp\":22.8,\"haclimate\":\"roomTemp\",\"modetype\":\"heat\",\"remotetemp\":null}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/thermostat/hc1/values")); } -void test_14() { +void test_15() { + auto expected_response = + "[{\"api_data\":\"# HELP emsesp_seltemp selected room temperature, °C, readable, writeable, visible\\n# TYPE emsesp_seltemp " + "gauge\\nemsesp_seltemp{circuit=\\\"hc1\\\"} 20.50\\n# HELP emsesp_currtemp current room temperature, °C, readable, visible\\n# TYPE emsesp_currtemp " + "gauge\\nemsesp_currtemp{circuit=\\\"hc1\\\"} 22.80\\n# HELP emsesp_haclimate mqtt discovery current room temperature, enum, (0: selTemp; 1: " + "roomTemp), readable, visible\\n# TYPE emsesp_haclimate gauge\\nemsesp_haclimate{circuit=\\\"hc1\\\"} 1\\n# HELP emsesp_modetype mode type, enum, (0: " + "nofrost; 1: eco; 2: heat), readable, visible\\n# TYPE emsesp_modetype gauge\\nemsesp_modetype{circuit=\\\"hc1\\\"} " + "2\\nemsesp_seltemp{circuit=\\\"hc2\\\"} 20.60\\nemsesp_currtemp{circuit=\\\"hc2\\\"} 22.90\\nemsesp_haclimate{circuit=\\\"hc2\\\"} " + "1\\nemsesp_modetype{circuit=\\\"hc2\\\"} 1\\nemsesp_seltemp{circuit=\\\"hc3\\\"} 20.70\\nemsesp_currtemp{circuit=\\\"hc3\\\"} " + "23\\nemsesp_haclimate{circuit=\\\"hc3\\\"} 1\\nemsesp_modetype{circuit=\\\"hc3\\\"} 0\\n\"}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/thermostat/metrics")); +} + +void test_16() { auto expected_response = "[{\"name\":\"seltemp\",\"fullname\":\"hc1 selected room " "temperature\",\"circuit\":\"hc1\",\"value\":20.5,\"type\":\"number\",\"min\":0,\"max\":30,\"uom\":\"°C\",\"state_class\":" "\"measurement\",\"device_class\":\"temperature\",\"readable\":true,\"writeable\":true,\"visible\":true}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/thermostat/hc1/seltemp")); } -void test_15() { +void test_17() { auto expected_response = "[{\"name\":\"seltemp\",\"fullname\":\"hc2 selected room " "temperature\",\"circuit\":\"hc2\",\"value\":20.6,\"type\":\"number\",\"min\":0,\"max\":30,\"uom\":\"°C\",\"state_class\":" "\"measurement\",\"device_class\":\"temperature\",\"readable\":true,\"writeable\":true,\"visible\":true}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/thermostat/hc2/seltemp")); } -void test_16() { +void test_18() { auto expected_response = "[{\"test_custom\":0.00,\"test_read_only\":70.00,\"test_ram\":\"14\",\"test_seltemp\":\"14\"}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/custom")); } -void test_17() { +void test_19() { auto expected_response = "[{\"test_custom\":0.00,\"test_read_only\":70.00,\"test_ram\":\"14\",\"test_seltemp\":\"14\"}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/custom/info")); } -void test_18() { +void test_20() { auto expected_response = "[{\"name\":\"test_seltemp\",\"fullname\":\"test_seltemp\",\"storage\":\"ram\",\"type\":\"number\",\"readable\":true," "\"writeable\":true,\"visible\":true,\"ent_cat\":\"diagnostic\",\"value\":\"14\"}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/custom/test_seltemp")); } -void test_19() { +void test_21() { auto expected_response = "[{\"api_data\":\"14\"}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/custom/test_seltemp/value")); } -void test_20() { +void test_22() { auto expected_response = "[{\"name\":\"test_custom\",\"fullname\":\"test_custom\",\"storage\":\"ems\",\"type\":\"number\",\"readable\":true,\"writeable\":" "true,\"visible\":true,\"device_id\":\"0x08\",\"type_id\":\"0x18\",\"offset\":0,\"factor\":1,\"ent_cat\":\"diagnostic\",\"uom\":" "\"°C\",\"state_class\":\"measurement\",\"device_class\":\"temperature\",\"value\":0.00}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/custom/test_custom")); } -void test_21() { +void test_23() { 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," + "Unknown\",\"txpause\":false,\"gpios_in_use\":\"23, 5, 2, 18, 0\",\"gpios_available\":\"1, 3, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 19, 20, " + "21, 22, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, " + "39\"},\"network\":{\"network\":\"WiFi\",\"hostname\":\"ems-esp\",\"RSSI\":-23,\"TxPowerSetting\":0,\"staticIP\":false,\"lowBandwidth\":false," "\"disableSleep\":true,\"enableMDNS\":true,\"enableCORS\":false},\"ntp\":{\"NTPstatus\":\"disconnected\",\"enabled\":true,\"server\":\"pool.ntp.org\"," "\"tzLabel\":\"Europe/" "London\",\"NTPStatus\":\"disconnected\"},\"ap\":{\"provisionMode\":\"always\",\"ssid\":\"ems-esp\"},\"mqtt\":{\"MQTTStatus\":\"disconnected\"," @@ -175,7 +231,7 @@ void test_21() { "\"temperatureSensorFails\":0,\"analogSensors\":5,\"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," + "\"en\",\"txMode\":1,\"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 " @@ -189,10 +245,12 @@ void test_21() { TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/system")); } -void test_22() { +void test_24() { 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," + "Unknown\",\"txpause\":false,\"gpios_in_use\":\"23, 5, 2, 18, 0\",\"gpios_available\":\"1, 3, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 19, 20, " + "21, 22, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, " + "39\"},\"network\":{\"network\":\"WiFi\",\"hostname\":\"ems-esp\",\"RSSI\":-23,\"TxPowerSetting\":0,\"staticIP\":false,\"lowBandwidth\":false," "\"disableSleep\":true,\"enableMDNS\":true,\"enableCORS\":false},\"ntp\":{\"NTPstatus\":\"disconnected\",\"enabled\":true,\"server\":\"pool.ntp.org\"," "\"tzLabel\":\"Europe/" "London\",\"NTPStatus\":\"disconnected\"},\"ap\":{\"provisionMode\":\"always\",\"ssid\":\"ems-esp\"},\"mqtt\":{\"MQTTStatus\":\"disconnected\"," @@ -204,7 +262,7 @@ void test_22() { "\"temperatureSensorFails\":0,\"analogSensors\":5,\"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," + "\"en\",\"txMode\":1,\"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 " @@ -218,169 +276,255 @@ void test_22() { TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/system/info")); } -void test_23() { +void test_25() { + auto expected_response = + "[{\"api_data\":\"# HELP emsesp_system_uptimesec uptimeSec\\n# TYPE emsesp_system_uptimesec gauge\\nemsesp_system_uptimesec 0\\n# HELP " + "emsesp_system_txpause txpause\\n# TYPE emsesp_system_txpause gauge\\nemsesp_system_txpause 0\\n# HELP emsesp_system_info info\\n# TYPE " + "emsesp_system_info gauge\\nemsesp_system_info{version=\\\"dev\\\", resetreason=\\\"Unknown / Unknown\\\", gpios_in_use=\\\"23, 5, 2, 18, 0\\\", " + "gpios_available=\\\"1, 3, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 19, 20, 21, 22, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, " + "39\\\"} 1\\n# HELP emsesp_network_rssi RSSI\\n# TYPE emsesp_network_rssi gauge\\nemsesp_network_rssi -23\\n# HELP emsesp_network_txpowersetting " + "TxPowerSetting\\n# TYPE emsesp_network_txpowersetting gauge\\nemsesp_network_txpowersetting 0\\n# HELP emsesp_network_staticip staticIP\\n# TYPE " + "emsesp_network_staticip gauge\\nemsesp_network_staticip 0\\n# HELP emsesp_network_lowbandwidth lowBandwidth\\n# TYPE emsesp_network_lowbandwidth " + "gauge\\nemsesp_network_lowbandwidth 0\\n# HELP emsesp_network_disablesleep disableSleep\\n# TYPE emsesp_network_disablesleep " + "gauge\\nemsesp_network_disablesleep 1\\n# HELP emsesp_network_enablemdns enableMDNS\\n# TYPE emsesp_network_enablemdns " + "gauge\\nemsesp_network_enablemdns 1\\n# HELP emsesp_network_enablecors enableCORS\\n# TYPE emsesp_network_enablecors " + "gauge\\nemsesp_network_enablecors 0\\n# HELP emsesp_network_info info\\n# TYPE emsesp_network_info gauge\\nemsesp_network_info{network=\\\"WiFi\\\", " + "hostname=\\\"ems-esp\\\"} 1\\n# HELP emsesp_ntp_enabled enabled\\n# TYPE emsesp_ntp_enabled gauge\\nemsesp_ntp_enabled 1\\n# HELP emsesp_ntp_info " + "info\\n# TYPE emsesp_ntp_info gauge\\nemsesp_ntp_info{ntpstatus=\\\"disconnected\\\", server=\\\"pool.ntp.org\\\", tzlabel=\\\"Europe/London\\\"} " + "1\\n# HELP emsesp_ap_info info\\n# TYPE emsesp_ap_info gauge\\nemsesp_ap_info{provisionmode=\\\"always\\\", ssid=\\\"ems-esp\\\"} 1\\n# HELP " + "emsesp_mqtt_mqttpublishes MQTTPublishes\\n# TYPE emsesp_mqtt_mqttpublishes gauge\\nemsesp_mqtt_mqttpublishes 0\\n# HELP emsesp_mqtt_mqttqueued " + "MQTTQueued\\n# TYPE emsesp_mqtt_mqttqueued gauge\\nemsesp_mqtt_mqttqueued 0\\n# HELP emsesp_mqtt_mqttpublishfails MQTTPublishFails\\n# TYPE " + "emsesp_mqtt_mqttpublishfails gauge\\nemsesp_mqtt_mqttpublishfails 0\\n# HELP emsesp_mqtt_mqttreconnects MQTTReconnects\\n# TYPE " + "emsesp_mqtt_mqttreconnects gauge\\nemsesp_mqtt_mqttreconnects 0\\n# HELP emsesp_mqtt_enabled enabled\\n# TYPE emsesp_mqtt_enabled " + "gauge\\nemsesp_mqtt_enabled 1\\n# HELP emsesp_mqtt_keepalive keepAlive\\n# TYPE emsesp_mqtt_keepalive gauge\\nemsesp_mqtt_keepalive 60\\n# HELP " + "emsesp_mqtt_cleansession cleanSession\\n# TYPE emsesp_mqtt_cleansession gauge\\nemsesp_mqtt_cleansession 0\\n# HELP emsesp_mqtt_entityformat " + "entityFormat\\n# TYPE emsesp_mqtt_entityformat gauge\\nemsesp_mqtt_entityformat 1\\n# HELP emsesp_mqtt_discoverytype discoveryType\\n# TYPE " + "emsesp_mqtt_discoverytype gauge\\nemsesp_mqtt_discoverytype 0\\n# HELP emsesp_mqtt_nestedformat nestedFormat\\n# TYPE emsesp_mqtt_nestedformat " + "gauge\\nemsesp_mqtt_nestedformat 1\\n# HELP emsesp_mqtt_haenabled haEnabled\\n# TYPE emsesp_mqtt_haenabled gauge\\nemsesp_mqtt_haenabled 1\\n# HELP " + "emsesp_mqtt_mqttqos mqttQos\\n# TYPE emsesp_mqtt_mqttqos gauge\\nemsesp_mqtt_mqttqos 0\\n# HELP emsesp_mqtt_mqttretain mqttRetain\\n# TYPE " + "emsesp_mqtt_mqttretain gauge\\nemsesp_mqtt_mqttretain 0\\n# HELP emsesp_mqtt_publishtimeheartbeat publishTimeHeartbeat\\n# TYPE " + "emsesp_mqtt_publishtimeheartbeat gauge\\nemsesp_mqtt_publishtimeheartbeat 60\\n# HELP emsesp_mqtt_publishtimeboiler publishTimeBoiler\\n# TYPE " + "emsesp_mqtt_publishtimeboiler gauge\\nemsesp_mqtt_publishtimeboiler 10\\n# HELP emsesp_mqtt_publishtimethermostat publishTimeThermostat\\n# TYPE " + "emsesp_mqtt_publishtimethermostat gauge\\nemsesp_mqtt_publishtimethermostat 10\\n# HELP emsesp_mqtt_publishtimesolar publishTimeSolar\\n# TYPE " + "emsesp_mqtt_publishtimesolar gauge\\nemsesp_mqtt_publishtimesolar 10\\n# HELP emsesp_mqtt_publishtimemixer publishTimeMixer\\n# TYPE " + "emsesp_mqtt_publishtimemixer gauge\\nemsesp_mqtt_publishtimemixer 10\\n# HELP emsesp_mqtt_publishtimewater publishTimeWater\\n# TYPE " + "emsesp_mqtt_publishtimewater gauge\\nemsesp_mqtt_publishtimewater 0\\n# HELP emsesp_mqtt_publishtimeother publishTimeOther\\n# TYPE " + "emsesp_mqtt_publishtimeother gauge\\nemsesp_mqtt_publishtimeother 10\\n# HELP emsesp_mqtt_publishtimesensor publishTimeSensor\\n# TYPE " + "emsesp_mqtt_publishtimesensor gauge\\nemsesp_mqtt_publishtimesensor 10\\n# HELP emsesp_mqtt_publishsingle publishSingle\\n# TYPE " + "emsesp_mqtt_publishsingle gauge\\nemsesp_mqtt_publishsingle 0\\n# HELP emsesp_mqtt_publish2command publish2command\\n# TYPE " + "emsesp_mqtt_publish2command gauge\\nemsesp_mqtt_publish2command 0\\n# HELP emsesp_mqtt_sendresponse sendResponse\\n# TYPE emsesp_mqtt_sendresponse " + "gauge\\nemsesp_mqtt_sendresponse 0\\n# HELP emsesp_mqtt_info info\\n# TYPE emsesp_mqtt_info gauge\\nemsesp_mqtt_info{mqttstatus=\\\"disconnected\\\", " + "clientid=\\\"ems-esp\\\", base=\\\"ems-esp\\\", discoveryprefix=\\\"homeassistant\\\"} 1\\n# HELP emsesp_syslog_enabled enabled\\n# TYPE " + "emsesp_syslog_enabled gauge\\nemsesp_syslog_enabled 0\\n# HELP emsesp_sensor_temperaturesensors temperatureSensors\\n# TYPE " + "emsesp_sensor_temperaturesensors gauge\\nemsesp_sensor_temperaturesensors 3\\n# HELP emsesp_sensor_temperaturesensorreads temperatureSensorReads\\n# " + "TYPE emsesp_sensor_temperaturesensorreads gauge\\nemsesp_sensor_temperaturesensorreads 0\\n# HELP emsesp_sensor_temperaturesensorfails " + "temperatureSensorFails\\n# TYPE emsesp_sensor_temperaturesensorfails gauge\\nemsesp_sensor_temperaturesensorfails 0\\n# HELP " + "emsesp_sensor_analogsensors analogSensors\\n# TYPE emsesp_sensor_analogsensors gauge\\nemsesp_sensor_analogsensors 5\\n# HELP " + "emsesp_sensor_analogsensorreads analogSensorReads\\n# TYPE emsesp_sensor_analogsensorreads gauge\\nemsesp_sensor_analogsensorreads 0\\n# HELP " + "emsesp_sensor_analogsensorfails analogSensorFails\\n# TYPE emsesp_sensor_analogsensorfails gauge\\nemsesp_sensor_analogsensorfails 0\\n# HELP " + "emsesp_api_apicalls APICalls\\n# TYPE emsesp_api_apicalls gauge\\nemsesp_api_apicalls 0\\n# HELP emsesp_api_apifails APIFails\\n# TYPE " + "emsesp_api_apifails gauge\\nemsesp_api_apifails 0\\n# HELP emsesp_bus_bustelegramsreceived busTelegramsReceived\\n# TYPE " + "emsesp_bus_bustelegramsreceived gauge\\nemsesp_bus_bustelegramsreceived 8\\n# HELP emsesp_bus_busreads busReads\\n# TYPE emsesp_bus_busreads " + "gauge\\nemsesp_bus_busreads 0\\n# HELP emsesp_bus_buswrites busWrites\\n# TYPE emsesp_bus_buswrites gauge\\nemsesp_bus_buswrites 0\\n# HELP " + "emsesp_bus_busincompletetelegrams busIncompleteTelegrams\\n# TYPE emsesp_bus_busincompletetelegrams gauge\\nemsesp_bus_busincompletetelegrams 0\\n# " + "HELP emsesp_bus_busreadsfailed busReadsFailed\\n# TYPE emsesp_bus_busreadsfailed gauge\\nemsesp_bus_busreadsfailed 0\\n# HELP " + "emsesp_bus_buswritesfailed busWritesFailed\\n# TYPE emsesp_bus_buswritesfailed gauge\\nemsesp_bus_buswritesfailed 0\\n# HELP " + "emsesp_bus_busrxlinequality busRxLineQuality\\n# TYPE emsesp_bus_busrxlinequality gauge\\nemsesp_bus_busrxlinequality 100\\n# HELP " + "emsesp_bus_bustxlinequality busTxLineQuality\\n# TYPE emsesp_bus_bustxlinequality gauge\\nemsesp_bus_bustxlinequality 100\\n# HELP emsesp_bus_info " + "info\\n# TYPE emsesp_bus_info gauge\\nemsesp_bus_info{busstatus=\\\"connected\\\", busprotocol=\\\"Buderus\\\"} 1\\n# HELP emsesp_settings_txmode " + "txMode\\n# TYPE emsesp_settings_txmode gauge\\nemsesp_settings_txmode 1\\n# HELP emsesp_settings_emsbusid emsBusID\\n# TYPE emsesp_settings_emsbusid " + "gauge\\nemsesp_settings_emsbusid 11\\n# HELP emsesp_settings_showertimer showerTimer\\n# TYPE emsesp_settings_showertimer " + "gauge\\nemsesp_settings_showertimer 0\\n# HELP emsesp_settings_showerminduration showerMinDuration\\n# TYPE emsesp_settings_showerminduration " + "gauge\\nemsesp_settings_showerminduration 180\\n# HELP emsesp_settings_showeralert showerAlert\\n# TYPE emsesp_settings_showeralert " + "gauge\\nemsesp_settings_showeralert 0\\n# HELP emsesp_settings_hideled hideLed\\n# TYPE emsesp_settings_hideled gauge\\nemsesp_settings_hideled 0\\n# " + "HELP emsesp_settings_notokenapi noTokenApi\\n# TYPE emsesp_settings_notokenapi gauge\\nemsesp_settings_notokenapi 0\\n# HELP " + "emsesp_settings_readonlymode readonlyMode\\n# TYPE emsesp_settings_readonlymode gauge\\nemsesp_settings_readonlymode 0\\n# HELP " + "emsesp_settings_fahrenheit fahrenheit\\n# TYPE emsesp_settings_fahrenheit gauge\\nemsesp_settings_fahrenheit 0\\n# HELP " + "emsesp_settings_dallasparasite dallasParasite\\n# TYPE emsesp_settings_dallasparasite gauge\\nemsesp_settings_dallasparasite 0\\n# HELP " + "emsesp_settings_boolformat boolFormat\\n# TYPE emsesp_settings_boolformat gauge\\nemsesp_settings_boolformat 1\\n# HELP emsesp_settings_booldashboard " + "boolDashboard\\n# TYPE emsesp_settings_booldashboard gauge\\nemsesp_settings_booldashboard 1\\n# HELP emsesp_settings_enumformat enumFormat\\n# TYPE " + "emsesp_settings_enumformat gauge\\nemsesp_settings_enumformat 1\\n# HELP emsesp_settings_analogenabled analogEnabled\\n# TYPE " + "emsesp_settings_analogenabled gauge\\nemsesp_settings_analogenabled 1\\n# HELP emsesp_settings_telnetenabled telnetEnabled\\n# TYPE " + "emsesp_settings_telnetenabled gauge\\nemsesp_settings_telnetenabled 1\\n# HELP emsesp_settings_maxweblogbuffer maxWebLogBuffer\\n# TYPE " + "emsesp_settings_maxweblogbuffer gauge\\nemsesp_settings_maxweblogbuffer 25\\n# HELP emsesp_settings_modbusenabled modbusEnabled\\n# TYPE " + "emsesp_settings_modbusenabled gauge\\nemsesp_settings_modbusenabled 0\\n# HELP emsesp_settings_forceheatingoff forceHeatingOff\\n# TYPE " + "emsesp_settings_forceheatingoff gauge\\nemsesp_settings_forceheatingoff 0\\n# HELP emsesp_settings_developermode developerMode\\n# TYPE " + "emsesp_settings_developermode gauge\\nemsesp_settings_developermode 0\\n# HELP emsesp_settings_info info\\n# TYPE emsesp_settings_info " + "gauge\\nemsesp_settings_info{boardprofile=\\\"S32\\\", locale=\\\"en\\\"} 1\\n# HELP emsesp_device_productid productID\\n# TYPE " + "emsesp_device_productid gauge\\nemsesp_device_productid{type=\\\"boiler\\\", name=\\\"My Custom Boiler\\\", deviceid=\\\"0x08\\\", " + "version=\\\"01.00\\\"} 123\\n# HELP emsesp_device_entities entities\\n# TYPE emsesp_device_entities " + "gauge\\nemsesp_device_entities{type=\\\"boiler\\\", name=\\\"My Custom Boiler\\\", deviceid=\\\"0x08\\\", version=\\\"01.00\\\"} " + "39\\nemsesp_device_productid{type=\\\"thermostat\\\", name=\\\"FW120\\\", deviceid=\\\"0x10\\\", version=\\\"01.00\\\"} " + "192\\nemsesp_device_entities{type=\\\"thermostat\\\", name=\\\"FW120\\\", deviceid=\\\"0x10\\\", version=\\\"01.00\\\"} " + "15\\nemsesp_device_entities{type=\\\"temperaturesensor\\\", name=\\\"temperaturesensor\\\"} 3\\nemsesp_device_entities{type=\\\"analogsensor\\\", " + "name=\\\"analogsensor\\\"} 5\\nemsesp_device_entities{type=\\\"scheduler\\\", name=\\\"scheduler\\\"} 2\\nemsesp_device_entities{type=\\\"custom\\\", " + "name=\\\"custom\\\"} 4\\n\"}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/system/metrics")); +} + +void test_26() { auto expected_response = "[{\"name\":\"locale\",\"circuit\":\"settings\",\"readable\":true,\"writeable\":false,\"visible\":true,\"value\":\"en\",\"type\":\"string\"}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/system/settings/locale")); } -void test_24() { +void test_27() { auto expected_response = "[{}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/system/fetch")); } -void test_25() { +void test_28() { auto expected_response = "[{\"network\":\"WiFi\",\"hostname\":\"ems-esp\",\"RSSI\":\"-23\",\"TxPowerSetting\":\"0\",\"staticIP\":\"false\"," "\"lowBandwidth\":\"false\",\"disableSleep\":\"true\",\"enableMDNS\":\"true\",\"enableCORS\":\"false\"}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/system/network/values")); } -void test_26() { +void test_29() { auto expected_response = "[{\"test_scheduler\":\"on\"}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/scheduler")); } -void test_27() { +void test_30() { auto expected_response = "[{\"test_scheduler\":\"on\"}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/scheduler/info")); } -void test_28() { +void test_31() { auto expected_response = "[{\"name\":\"test_scheduler\",\"fullname\":\"test_scheduler\",\"type\":\"boolean\",\"value\":\"on\",\"time\":\"12:00\"," "\"command\":\"system/fetch\",\"cmd_data\":\"10\",\"readable\":true,\"writeable\":true,\"visible\":true}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/scheduler/test_scheduler")); } -void test_29() { +void test_32() { auto expected_response = "[{\"test_tempsensor1\":12.3,\"test_tempsensor2\":45.6,\"gateway_temperature\":28.1}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/temperaturesensor")); } -void test_30() { +void test_33() { auto expected_response = "[{\"test_tempsensor1\":12.3,\"test_tempsensor2\":45.6,\"gateway_temperature\":28.1}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/temperaturesensor/info")); } -void test_31() { +void test_34() { auto expected_response = "[{\"id\":\"0B_0C0D_0E0F_1011\",\"name\":\"test_tempsensor2\",\"fullname\":\"test_tempsensor2\",\"value\":45.6,\"type\":" "\"number\",\"uom\":\"°C\",\"readable\":true,\"writeable\":false,\"visible\":true,\"is_system\":false}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/temperaturesensor/test_tempsensor2")); } -void test_32() { +void test_35() { auto expected_response = "[{\"id\":\"0B_0C0D_0E0F_1011\",\"name\":\"test_tempsensor2\",\"fullname\":\"test_tempsensor2\",\"value\":45.6,\"type\":" "\"number\",\"uom\":\"°C\",\"readable\":true,\"writeable\":false,\"visible\":true,\"is_system\":false}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/temperaturesensor/0B_0C0D_0E0F_1011")); } -void test_33() { +void test_36() { auto expected_response = "[{\"api_data\":\"45.6\"}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/temperaturesensor/test_tempsensor2/value")); } -void test_34() { +void test_37() { auto expected_response = "[{\"test_analogsensor1\":0,\"test_analogsensor2\":1,\"test_analogsensor3\":0,\"test_analogsensor4\":0,\"test_analogsensor5\":0}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/analogsensor")); } -void test_35() { +void test_38() { auto expected_response = "[{\"test_analogsensor1\":0,\"test_analogsensor2\":1,\"test_analogsensor3\":0,\"test_analogsensor4\":0,\"test_analogsensor5\":0}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/analogsensor/info")); } -void test_36() { +void test_39() { 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\":false,\"offset\":0,\"factor\":0.2,\"uom\":\"mV\"}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/analogsensor/test_analogsensor1")); } -void test_37() { +void test_40() { auto expected_response = "[{\"api_data\":\"0\"}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/analogsensor/test_analogsensor1/offset")); } -void test_38() { +void test_41() { auto expected_response = "[{\"message\":\"unknown device boiler2\"}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/boiler2")); } -void test_39() { +void test_42() { auto expected_response = "[{}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/boiler/bad/value")); } -void test_40() { +void test_43() { auto expected_response = "[{\"message\":\"no attribute 'valu' in comfort\"}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/boiler/comfort/valu")); } -void test_41() { +void test_44() { auto expected_response = "[{\"message\":\"no 'settings' in system\"}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/system/settings/locale2")); } -void test_42() { +void test_45() { auto expected_response = "[{\"message\":\"no 'settings2' in system\"}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/system/settings2")); } -void test_43() { +void test_46() { auto expected_response = "[{\"message\":\"no 'settings2' in system\"}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/system/settings2/locale2")); } -void test_44() { +void test_47() { auto expected_response = "[{\"message\":\"no 'test_scheduler2' in scheduler\"}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/scheduler/test_scheduler2")); } -void test_45() { +void test_48() { auto expected_response = "[{\"message\":\"no attribute 'val' in test_scheduler\"}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/scheduler/test_scheduler/val")); } -void test_46() { +void test_49() { auto expected_response = "[{\"message\":\"no 'test_scheduler2' in scheduler\"}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/scheduler/test_scheduler2/val2")); } -void test_47() { +void test_50() { auto expected_response = "[{\"message\":\"no 'test_seltemp2' in custom\"}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/custom/test_seltemp2")); } -void test_48() { +void test_51() { auto expected_response = "[{\"message\":\"Command test_seltemp failed (Error)\"}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/custom/test_seltemp/val")); } -void test_49() { +void test_52() { auto expected_response = "[{\"message\":\"no 'test_sensor20' in temperaturesensor\"}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/temperaturesensor/test_sensor20")); } -void test_50() { +void test_53() { auto expected_response = "[{\"message\":\"no '0b_0c0d_0e0f_xxxx' in temperaturesensor\"}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/temperaturesensor/0B_0C0D_0E0F_XXXX")); } -void test_51() { +void test_54() { auto expected_response = "[{\"message\":\"no attribute 'bad' in test_tempsensor2\"}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/temperaturesensor/test_tempsensor2/bad")); } -void test_52() { +void test_55() { auto expected_response = "[{\"message\":\"no attribute 'bad' in test_analogsensor1\"}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/analogsensor/test_analogsensor1/bad")); } -void test_53() { +void test_56() { auto expected_response = "[{\"message\":\"no 'test_analog10' in analogsensor\"}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/analogsensor/test_analog10")); } -void test_54() { +void test_57() { auto expected_response = "[{\"message\":\"no 'test_analog10' in analogsensor\"}]"; TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/analogsensor/test_analog10/bad2")); } @@ -440,6 +584,9 @@ void run_tests() { RUN_TEST(test_52); RUN_TEST(test_53); RUN_TEST(test_54); + RUN_TEST(test_55); + RUN_TEST(test_56); + RUN_TEST(test_57); } -// ---------- END - CUT HERE ---------- \ No newline at end of file +// ---------- END - CUT HERE ----------