From af19941a075cb5c54d474e623aaba0242ea29a47 Mon Sep 17 00:00:00 2001 From: proddy Date: Fri, 12 Dec 2025 21:58:06 +0100 Subject: [PATCH 01/12] https://github.com/emsesp/EMS-ESP32/discussions/2761 --- interface/src/i18n/pl/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/i18n/pl/index.ts b/interface/src/i18n/pl/index.ts index 6dd874a32..d50d6cefa 100644 --- a/interface/src/i18n/pl/index.ts +++ b/interface/src/i18n/pl/index.ts @@ -331,7 +331,7 @@ const pl: BaseTranslation = { SERVICES: 'Usługi', ALLVALUES: 'Wszystkie wartości', SPECIAL_FUNCTIONS: 'Specjalne funkcje', - WAIT_FIRMWARE: 'Firma jest wysyłana i instaluje się', + WAIT_FIRMWARE: 'Firmware ściąga się i instaluje', INSTALL_VERSION: 'To zainstaluje wersję {1} {0}. Jesteś pewny?', UPDATE_AVAILABLE: 'aktualizacja dostępna', LATEST_VERSION: 'Jesteś używając najnowszej wersji firmware {0}', From cdb592d744c1ed96e089c8afb1c7c04ed3202629 Mon Sep 17 00:00:00 2001 From: proddy Date: Fri, 12 Dec 2025 21:58:15 +0100 Subject: [PATCH 02/12] package update --- interface/package.json | 6 +- interface/pnpm-lock.yaml | 232 +++++++++++++++++++-------------------- 2 files changed, 119 insertions(+), 119 deletions(-) diff --git a/interface/package.json b/interface/package.json index 35a3bc339..438f1ab54 100644 --- a/interface/package.json +++ b/interface/package.json @@ -38,8 +38,8 @@ "magic-string": "^0.30.21", "mime-types": "^3.0.2", "preact": "^10.28.0", - "react": "^19.2.2", - "react-dom": "^19.2.2", + "react": "^19.2.3", + "react-dom": "^19.2.3", "react-icons": "^5.5.0", "react-router": "^7.10.1", "react-toastify": "^11.0.5", @@ -52,7 +52,7 @@ "@preact/compat": "^18.3.1", "@preact/preset-vite": "^2.10.2", "@trivago/prettier-plugin-sort-imports": "^6.0.0", - "@types/node": "^25.0.0", + "@types/node": "^25.0.1", "@types/react": "^19.2.7", "@types/react-dom": "^19.2.3", "axe-core": "^4.11.0", diff --git a/interface/pnpm-lock.yaml b/interface/pnpm-lock.yaml index 767321011..c3c32ae41 100644 --- a/interface/pnpm-lock.yaml +++ b/interface/pnpm-lock.yaml @@ -13,22 +13,22 @@ importers: version: 2.3.0(alova@3.4.0) '@emotion/react': specifier: ^11.14.0 - version: 11.14.0(@types/react@19.2.7)(react@19.2.2) + version: 11.14.0(@types/react@19.2.7)(react@19.2.3) '@emotion/styled': specifier: ^11.14.1 - version: 11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.2))(@types/react@19.2.7)(react@19.2.2) + version: 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) '@mui/icons-material': specifier: ^7.3.6 - version: 7.3.6(@mui/material@7.3.6(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.2))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.2))(@types/react@19.2.7)(react@19.2.2))(@types/react@19.2.7)(react-dom@19.2.2(react@19.2.2))(react@19.2.2))(@types/react@19.2.7)(react@19.2.2) + version: 7.3.6(@mui/material@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))(@types/react@19.2.7)(react@19.2.3) '@mui/material': specifier: ^7.3.6 - version: 7.3.6(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.2))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.2))(@types/react@19.2.7)(react@19.2.2))(@types/react@19.2.7)(react-dom@19.2.2(react@19.2.2))(react@19.2.2) + 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.0) '@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.2))(react-dom@19.2.2(react@19.2.2))(react@19.2.2) + 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) alova: specifier: 3.4.0 version: 3.4.0 @@ -54,20 +54,20 @@ importers: specifier: ^10.28.0 version: 10.28.0 react: - specifier: ^19.2.2 - version: 19.2.2 + specifier: ^19.2.3 + version: 19.2.3 react-dom: - specifier: ^19.2.2 - version: 19.2.2(react@19.2.2) + specifier: ^19.2.3 + version: 19.2.3(react@19.2.3) react-icons: specifier: ^5.5.0 - version: 5.5.0(react@19.2.2) + version: 5.5.0(react@19.2.3) react-router: specifier: ^7.10.1 - version: 7.10.1(react-dom@19.2.2(react@19.2.2))(react@19.2.2) + version: 7.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) react-toastify: specifier: ^11.0.5 - version: 11.0.5(react-dom@19.2.2(react@19.2.2))(react@19.2.2) + version: 11.0.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) typesafe-i18n: specifier: ^5.26.2 version: 5.26.2(typescript@5.9.3) @@ -83,13 +83,13 @@ importers: version: 9.39.1 '@preact/preset-vite': specifier: ^2.10.2 - version: 2.10.2(@babel/core@7.28.5)(preact@10.28.0)(vite@7.2.7(@types/node@25.0.0)(terser@5.44.1)) + version: 2.10.2(@babel/core@7.28.5)(preact@10.28.0)(vite@7.2.7(@types/node@25.0.1)(terser@5.44.1)) '@trivago/prettier-plugin-sort-imports': specifier: ^6.0.0 version: 6.0.0(prettier@3.7.4) '@types/node': - specifier: ^25.0.0 - version: 25.0.0 + specifier: ^25.0.1 + version: 25.0.1 '@types/react': specifier: ^19.2.7 version: 19.2.7 @@ -122,13 +122,13 @@ importers: version: 8.49.0(eslint@9.39.1)(typescript@5.9.3) vite: specifier: ^7.2.7 - version: 7.2.7(@types/node@25.0.0)(terser@5.44.1) + version: 7.2.7(@types/node@25.0.1)(terser@5.44.1) vite-plugin-imagemin: specifier: ^0.6.1 - version: 0.6.1(vite@7.2.7(@types/node@25.0.0)(terser@5.44.1)) + version: 0.6.1(vite@7.2.7(@types/node@25.0.1)(terser@5.44.1)) vite-tsconfig-paths: specifier: ^5.1.4 - version: 5.1.4(typescript@5.9.3)(vite@7.2.7(@types/node@25.0.0)(terser@5.44.1)) + version: 5.1.4(typescript@5.9.3)(vite@7.2.7(@types/node@25.0.1)(terser@5.44.1)) packages: @@ -860,8 +860,8 @@ packages: resolution: {integrity: sha512-zmPitbQ8+6zNutpwgcQuLcsEpn/Cj54Kbn7L5pX0Os5kdWplB7xPgEh/g+SWOB/qmows2gpuCaPyduq8ZZRnxA==} deprecated: This is a stub types definition. minimatch provides its own type definitions, so you do not need this installed. - '@types/node@25.0.0': - resolution: {integrity: sha512-rl78HwuZlaDIUSeUKkmogkhebA+8K1Hy7tddZuJ3D0xV8pZSfsYGTsliGUol1JPzu9EKnTxPC4L1fiWouStRew==} + '@types/node@25.0.1': + resolution: {integrity: sha512-czWPzKIAXucn9PtsttxmumiQ9N0ok9FrBwgRWrwmVLlp86BrMExzvXRLFYRJ+Ex3g6yqj+KuaxfX1JTgV2lpfg==} '@types/parse-json@4.0.2': resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} @@ -1027,8 +1027,8 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - baseline-browser-mapping@2.9.6: - resolution: {integrity: sha512-v9BVVpOTLB59C9E7aSnmIF8h7qRsFpx+A2nugVMTszEOMcfjlZMsXRm4LF23I3Z9AJxc8ANpIvzbzONoX9VJlg==} + baseline-browser-mapping@2.9.7: + resolution: {integrity: sha512-k9xFKplee6KIio3IDbwj+uaCLpqzOwakOgmqzPezM0sFJlFKcg30vk2wOiAJtkTSfx0SSQDSe8q+mWA/fSH5Zg==} hasBin: true bin-build@3.0.0: @@ -2509,10 +2509,10 @@ packages: rate-limiter-flexible@5.0.5: resolution: {integrity: sha512-+/dSQfo+3FYwYygUs/V2BBdwGa9nFtakDwKt4l0bnvNB53TNT++QSFewwHX9qXrZJuMe9j+TUaU21lm5ARgqdQ==} - react-dom@19.2.2: - resolution: {integrity: sha512-fhyD2BLrew6qYf4NNtHff1rLXvzR25rq49p+FeqByOazc6TcSi2n8EYulo5C1PbH+1uBW++5S1SG7FcUU6mlDg==} + react-dom@19.2.3: + resolution: {integrity: sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==} peerDependencies: - react: ^19.2.2 + react: ^19.2.3 react-icons@5.5.0: resolution: {integrity: sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==} @@ -2522,8 +2522,8 @@ packages: react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} - react-is@19.2.2: - resolution: {integrity: sha512-ADrk8mxPwhyj+IB8EnMMRzxnon5hJrOdEZl+mCrrHXfPGGPYHdRm1962fvXUiWAu/ZC1G+OTgBN3Puq57iS4Yg==} + react-is@19.2.3: + resolution: {integrity: sha512-qJNJfu81ByyabuG7hPFEbXqNcWSU3+eVus+KJs+0ncpGfMyYdvSmxiJxbWR65lYi1I+/0HBcliO029gc4F+PnA==} react-router@7.10.1: resolution: {integrity: sha512-gHL89dRa3kwlUYtRQ+m8NmxGI6CgqN+k4XyGjwcFoQwwCWF6xXpOCUlDovkXClS0d0XJN/5q7kc5W3kiFEd0Yw==} @@ -2560,8 +2560,8 @@ packages: react: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react@19.2.2: - resolution: {integrity: sha512-BdOGOY8OKRBcgoDkwqA8Q5XvOIhoNx/Sh6BnGJlet2Abt0X5BK0BDrqGyQgLhAVjD2nAg5f6o01u/OPUhG022Q==} + react@19.2.3: + resolution: {integrity: sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==} engines: {node: '>=0.10.0'} read-pkg-up@1.0.1: @@ -3265,17 +3265,17 @@ snapshots: '@emotion/memoize@0.9.0': {} - '@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.2)': + '@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.3)': dependencies: '@babel/runtime': 7.28.4 '@emotion/babel-plugin': 11.13.5 '@emotion/cache': 11.14.0 '@emotion/serialize': 1.3.3 - '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.2.2) + '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.2.3) '@emotion/utils': 1.4.2 '@emotion/weak-memoize': 0.4.0 hoist-non-react-statics: 3.3.2 - react: 19.2.2 + react: 19.2.3 optionalDependencies: '@types/react': 19.2.7 transitivePeerDependencies: @@ -3291,16 +3291,16 @@ snapshots: '@emotion/sheet@1.4.0': {} - '@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.2))(@types/react@19.2.7)(react@19.2.2)': + '@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)': dependencies: '@babel/runtime': 7.28.4 '@emotion/babel-plugin': 11.13.5 '@emotion/is-prop-valid': 1.4.0 - '@emotion/react': 11.14.0(@types/react@19.2.7)(react@19.2.2) + '@emotion/react': 11.14.0(@types/react@19.2.7)(react@19.2.3) '@emotion/serialize': 1.3.3 - '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.2.2) + '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.2.3) '@emotion/utils': 1.4.2 - react: 19.2.2 + react: 19.2.3 optionalDependencies: '@types/react': 19.2.7 transitivePeerDependencies: @@ -3308,9 +3308,9 @@ snapshots: '@emotion/unitless@0.10.0': {} - '@emotion/use-insertion-effect-with-fallbacks@1.2.0(react@19.2.2)': + '@emotion/use-insertion-effect-with-fallbacks@1.2.0(react@19.2.3)': dependencies: - react: 19.2.2 + react: 19.2.3 '@emotion/utils@1.4.2': {} @@ -3486,45 +3486,45 @@ snapshots: '@mui/core-downloads-tracker@7.3.6': {} - '@mui/icons-material@7.3.6(@mui/material@7.3.6(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.2))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.2))(@types/react@19.2.7)(react@19.2.2))(@types/react@19.2.7)(react-dom@19.2.2(react@19.2.2))(react@19.2.2))(@types/react@19.2.7)(react@19.2.2)': + '@mui/icons-material@7.3.6(@mui/material@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))(@types/react@19.2.7)(react@19.2.3)': dependencies: '@babel/runtime': 7.28.4 - '@mui/material': 7.3.6(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.2))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.2))(@types/react@19.2.7)(react@19.2.2))(@types/react@19.2.7)(react-dom@19.2.2(react@19.2.2))(react@19.2.2) - react: 19.2.2 + '@mui/material': 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) + react: 19.2.3 optionalDependencies: '@types/react': 19.2.7 - '@mui/material@7.3.6(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.2))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.2))(@types/react@19.2.7)(react@19.2.2))(@types/react@19.2.7)(react-dom@19.2.2(react@19.2.2))(react@19.2.2)': + '@mui/material@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)': dependencies: '@babel/runtime': 7.28.4 '@mui/core-downloads-tracker': 7.3.6 - '@mui/system': 7.3.6(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.2))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.2))(@types/react@19.2.7)(react@19.2.2))(@types/react@19.2.7)(react@19.2.2) + '@mui/system': 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@19.2.3) '@mui/types': 7.4.9(@types/react@19.2.7) - '@mui/utils': 7.3.6(@types/react@19.2.7)(react@19.2.2) + '@mui/utils': 7.3.6(@types/react@19.2.7)(react@19.2.3) '@popperjs/core': 2.11.8 '@types/react-transition-group': 4.4.12(@types/react@19.2.7) clsx: 2.1.1 csstype: 3.2.3 prop-types: 15.8.1 - react: 19.2.2 - react-dom: 19.2.2(react@19.2.2) - react-is: 19.2.2 - react-transition-group: 4.4.5(react-dom@19.2.2(react@19.2.2))(react@19.2.2) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + react-is: 19.2.3 + react-transition-group: 4.4.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) optionalDependencies: - '@emotion/react': 11.14.0(@types/react@19.2.7)(react@19.2.2) - '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.2))(@types/react@19.2.7)(react@19.2.2) + '@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 - '@mui/private-theming@7.3.6(@types/react@19.2.7)(react@19.2.2)': + '@mui/private-theming@7.3.6(@types/react@19.2.7)(react@19.2.3)': dependencies: '@babel/runtime': 7.28.4 - '@mui/utils': 7.3.6(@types/react@19.2.7)(react@19.2.2) + '@mui/utils': 7.3.6(@types/react@19.2.7)(react@19.2.3) prop-types: 15.8.1 - react: 19.2.2 + react: 19.2.3 optionalDependencies: '@types/react': 19.2.7 - '@mui/styled-engine@7.3.6(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.2))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.2))(@types/react@19.2.7)(react@19.2.2))(react@19.2.2)': + '@mui/styled-engine@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))(react@19.2.3)': dependencies: '@babel/runtime': 7.28.4 '@emotion/cache': 11.14.0 @@ -3532,25 +3532,25 @@ snapshots: '@emotion/sheet': 1.4.0 csstype: 3.2.3 prop-types: 15.8.1 - react: 19.2.2 + react: 19.2.3 optionalDependencies: - '@emotion/react': 11.14.0(@types/react@19.2.7)(react@19.2.2) - '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.2))(@types/react@19.2.7)(react@19.2.2) + '@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) - '@mui/system@7.3.6(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.2))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.2))(@types/react@19.2.7)(react@19.2.2))(@types/react@19.2.7)(react@19.2.2)': + '@mui/system@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@19.2.3)': dependencies: '@babel/runtime': 7.28.4 - '@mui/private-theming': 7.3.6(@types/react@19.2.7)(react@19.2.2) - '@mui/styled-engine': 7.3.6(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.2))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.2))(@types/react@19.2.7)(react@19.2.2))(react@19.2.2) + '@mui/private-theming': 7.3.6(@types/react@19.2.7)(react@19.2.3) + '@mui/styled-engine': 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))(react@19.2.3) '@mui/types': 7.4.9(@types/react@19.2.7) - '@mui/utils': 7.3.6(@types/react@19.2.7)(react@19.2.2) + '@mui/utils': 7.3.6(@types/react@19.2.7)(react@19.2.3) clsx: 2.1.1 csstype: 3.2.3 prop-types: 15.8.1 - react: 19.2.2 + react: 19.2.3 optionalDependencies: - '@emotion/react': 11.14.0(@types/react@19.2.7)(react@19.2.2) - '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.2))(@types/react@19.2.7)(react@19.2.2) + '@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 '@mui/types@7.4.9(@types/react@19.2.7)': @@ -3559,15 +3559,15 @@ snapshots: optionalDependencies: '@types/react': 19.2.7 - '@mui/utils@7.3.6(@types/react@19.2.7)(react@19.2.2)': + '@mui/utils@7.3.6(@types/react@19.2.7)(react@19.2.3)': dependencies: '@babel/runtime': 7.28.4 '@mui/types': 7.4.9(@types/react@19.2.7) '@types/prop-types': 15.7.15 clsx: 2.1.1 prop-types: 15.8.1 - react: 19.2.2 - react-is: 19.2.2 + react: 19.2.3 + react-is: 19.2.3 optionalDependencies: '@types/react': 19.2.7 @@ -3595,18 +3595,18 @@ snapshots: dependencies: preact: 10.28.0 - '@preact/preset-vite@2.10.2(@babel/core@7.28.5)(preact@10.28.0)(vite@7.2.7(@types/node@25.0.0)(terser@5.44.1))': + '@preact/preset-vite@2.10.2(@babel/core@7.28.5)(preact@10.28.0)(vite@7.2.7(@types/node@25.0.1)(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.0)(vite@7.2.7(@types/node@25.0.0)(terser@5.44.1)) + '@prefresh/vite': 2.4.11(preact@10.28.0)(vite@7.2.7(@types/node@25.0.1)(terser@5.44.1)) '@rollup/pluginutils': 4.2.1 babel-plugin-transform-hook-names: 1.0.2(@babel/core@7.28.5) debug: 4.4.3 picocolors: 1.1.1 - vite: 7.2.7(@types/node@25.0.0)(terser@5.44.1) - vite-prerender-plugin: 0.5.12(vite@7.2.7(@types/node@25.0.0)(terser@5.44.1)) + vite: 7.2.7(@types/node@25.0.1)(terser@5.44.1) + vite-prerender-plugin: 0.5.12(vite@7.2.7(@types/node@25.0.1)(terser@5.44.1)) transitivePeerDependencies: - preact - supports-color @@ -3619,7 +3619,7 @@ snapshots: '@prefresh/utils@1.2.1': {} - '@prefresh/vite@2.4.11(preact@10.28.0)(vite@7.2.7(@types/node@25.0.0)(terser@5.44.1))': + '@prefresh/vite@2.4.11(preact@10.28.0)(vite@7.2.7(@types/node@25.0.1)(terser@5.44.1))': dependencies: '@babel/core': 7.28.5 '@prefresh/babel-plugin': 0.5.2 @@ -3627,7 +3627,7 @@ snapshots: '@prefresh/utils': 1.2.1 '@rollup/pluginutils': 4.2.1 preact: 10.28.0 - vite: 7.2.7(@types/node@25.0.0)(terser@5.44.1) + vite: 7.2.7(@types/node@25.0.1)(terser@5.44.1) transitivePeerDependencies: - supports-color @@ -3704,14 +3704,14 @@ snapshots: '@sindresorhus/is@0.7.0': {} - '@table-library/react-table-library@4.1.15(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.2))(react-dom@19.2.2(react@19.2.2))(react@19.2.2)': + '@table-library/react-table-library@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)': dependencies: - '@emotion/react': 11.14.0(@types/react@19.2.7)(react@19.2.2) + '@emotion/react': 11.14.0(@types/react@19.2.7)(react@19.2.3) clsx: 1.1.1 - react: 19.2.2 - react-dom: 19.2.2(react@19.2.2) - react-virtualized-auto-sizer: 1.0.26(react-dom@19.2.2(react@19.2.2))(react@19.2.2) - react-window: 1.8.11(react-dom@19.2.2(react@19.2.2))(react@19.2.2) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + 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)': dependencies: @@ -3734,7 +3734,7 @@ snapshots: '@types/glob@7.2.0': dependencies: '@types/minimatch': 6.0.0 - '@types/node': 25.0.0 + '@types/node': 25.0.1 '@types/imagemin-gifsicle@7.0.4': dependencies: @@ -3763,19 +3763,19 @@ snapshots: '@types/imagemin@7.0.1': dependencies: - '@types/node': 25.0.0 + '@types/node': 25.0.1 '@types/json-schema@7.0.15': {} '@types/keyv@3.1.4': dependencies: - '@types/node': 25.0.0 + '@types/node': 25.0.1 '@types/minimatch@6.0.0': dependencies: minimatch: 10.1.1 - '@types/node@25.0.0': + '@types/node@25.0.1': dependencies: undici-types: 7.16.0 @@ -3797,11 +3797,11 @@ snapshots: '@types/responselike@1.0.3': dependencies: - '@types/node': 25.0.0 + '@types/node': 25.0.1 '@types/svgo@2.6.4': dependencies: - '@types/node': 25.0.0 + '@types/node': 25.0.1 '@typescript-eslint/eslint-plugin@8.49.0(@typescript-eslint/parser@8.49.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3)': dependencies: @@ -3958,7 +3958,7 @@ snapshots: base64-js@1.5.1: {} - baseline-browser-mapping@2.9.6: {} + baseline-browser-mapping@2.9.7: {} bin-build@3.0.0: dependencies: @@ -4015,7 +4015,7 @@ snapshots: browserslist@4.28.1: dependencies: - baseline-browser-mapping: 2.9.6 + baseline-browser-mapping: 2.9.7 caniuse-lite: 1.0.30001760 electron-to-chromium: 1.5.267 node-releases: 2.0.27 @@ -5512,55 +5512,55 @@ snapshots: rate-limiter-flexible@5.0.5: {} - react-dom@19.2.2(react@19.2.2): + react-dom@19.2.3(react@19.2.3): dependencies: - react: 19.2.2 + react: 19.2.3 scheduler: 0.27.0 - react-icons@5.5.0(react@19.2.2): + react-icons@5.5.0(react@19.2.3): dependencies: - react: 19.2.2 + react: 19.2.3 react-is@16.13.1: {} - react-is@19.2.2: {} + react-is@19.2.3: {} - react-router@7.10.1(react-dom@19.2.2(react@19.2.2))(react@19.2.2): + react-router@7.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3): dependencies: cookie: 1.1.1 - react: 19.2.2 + react: 19.2.3 set-cookie-parser: 2.7.2 optionalDependencies: - react-dom: 19.2.2(react@19.2.2) + react-dom: 19.2.3(react@19.2.3) - react-toastify@11.0.5(react-dom@19.2.2(react@19.2.2))(react@19.2.2): + react-toastify@11.0.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3): dependencies: clsx: 2.1.1 - react: 19.2.2 - react-dom: 19.2.2(react@19.2.2) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) - react-transition-group@4.4.5(react-dom@19.2.2(react@19.2.2))(react@19.2.2): + react-transition-group@4.4.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3): dependencies: '@babel/runtime': 7.28.4 dom-helpers: 5.2.1 loose-envify: 1.4.0 prop-types: 15.8.1 - react: 19.2.2 - react-dom: 19.2.2(react@19.2.2) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) - react-virtualized-auto-sizer@1.0.26(react-dom@19.2.2(react@19.2.2))(react@19.2.2): + react-virtualized-auto-sizer@1.0.26(react-dom@19.2.3(react@19.2.3))(react@19.2.3): dependencies: - react: 19.2.2 - react-dom: 19.2.2(react@19.2.2) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) - react-window@1.8.11(react-dom@19.2.2(react@19.2.2))(react@19.2.2): + react-window@1.8.11(react-dom@19.2.3(react@19.2.3))(react@19.2.3): dependencies: '@babel/runtime': 7.28.4 memoize-one: 5.2.1 - react: 19.2.2 - react-dom: 19.2.2(react@19.2.2) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) - react@19.2.2: {} + react@19.2.3: {} read-pkg-up@1.0.1: dependencies: @@ -5963,7 +5963,7 @@ snapshots: spdx-correct: 3.2.0 spdx-expression-parse: 3.0.1 - vite-plugin-imagemin@0.6.1(vite@7.2.7(@types/node@25.0.0)(terser@5.44.1)): + vite-plugin-imagemin@0.6.1(vite@7.2.7(@types/node@25.0.1)(terser@5.44.1)): dependencies: '@types/imagemin': 7.0.1 '@types/imagemin-gifsicle': 7.0.4 @@ -5988,11 +5988,11 @@ snapshots: imagemin-webp: 6.1.0 jpegtran-bin: 6.0.1 pathe: 0.2.0 - vite: 7.2.7(@types/node@25.0.0)(terser@5.44.1) + vite: 7.2.7(@types/node@25.0.1)(terser@5.44.1) transitivePeerDependencies: - supports-color - vite-prerender-plugin@0.5.12(vite@7.2.7(@types/node@25.0.0)(terser@5.44.1)): + vite-prerender-plugin@0.5.12(vite@7.2.7(@types/node@25.0.1)(terser@5.44.1)): dependencies: kolorist: 1.8.0 magic-string: 0.30.21 @@ -6000,20 +6000,20 @@ snapshots: simple-code-frame: 1.3.0 source-map: 0.7.6 stack-trace: 1.0.0-pre2 - vite: 7.2.7(@types/node@25.0.0)(terser@5.44.1) + vite: 7.2.7(@types/node@25.0.1)(terser@5.44.1) - vite-tsconfig-paths@5.1.4(typescript@5.9.3)(vite@7.2.7(@types/node@25.0.0)(terser@5.44.1)): + vite-tsconfig-paths@5.1.4(typescript@5.9.3)(vite@7.2.7(@types/node@25.0.1)(terser@5.44.1)): dependencies: debug: 4.4.3 globrex: 0.1.2 tsconfck: 3.1.6(typescript@5.9.3) optionalDependencies: - vite: 7.2.7(@types/node@25.0.0)(terser@5.44.1) + vite: 7.2.7(@types/node@25.0.1)(terser@5.44.1) transitivePeerDependencies: - supports-color - typescript - vite@7.2.7(@types/node@25.0.0)(terser@5.44.1): + vite@7.2.7(@types/node@25.0.1)(terser@5.44.1): dependencies: esbuild: 0.25.12 fdir: 6.5.0(picomatch@4.0.3) @@ -6022,7 +6022,7 @@ snapshots: rollup: 4.53.3 tinyglobby: 0.2.15 optionalDependencies: - '@types/node': 25.0.0 + '@types/node': 25.0.1 fsevents: 2.3.3 terser: 5.44.1 From 17a9b7eb0ab91a1ac1618f7116193e1afaad7905 Mon Sep 17 00:00:00 2001 From: proddy Date: Fri, 12 Dec 2025 21:58:35 +0100 Subject: [PATCH 03/12] send HA modes based on actual thermostat modes --- src/core/emsdevice.cpp | 6 +++--- src/core/mqtt.cpp | 35 +++++++++++++++++++++++++++++++---- src/core/mqtt.h | 2 +- src/emsesp_version.h | 2 +- 4 files changed, 36 insertions(+), 9 deletions(-) diff --git a/src/core/emsdevice.cpp b/src/core/emsdevice.cpp index 8b5ec063b..828c38e40 100644 --- a/src/core/emsdevice.cpp +++ b/src/core/emsdevice.cpp @@ -2050,14 +2050,14 @@ void EMSdevice::mqtt_ha_entity_config_create() { // create climate when we reach the haclimate entity if (!strcmp(dv.short_name, FL_(haclimate)[0]) && !dv.has_state(DeviceValueState::DV_API_MQTT_EXCLUDE) && dv.has_state(DeviceValueState::DV_ACTIVE)) { if (*(int8_t *)(dv.value_p) == 1 && (!dv.has_state(DeviceValueState::DV_HA_CONFIG_CREATED) || dv.has_state(DeviceValueState::DV_HA_CLIMATE_NO_RT))) { - if (Mqtt::publish_ha_climate_config(dv.tag, true, false, dv.min, dv.max)) { // roomTemp + if (Mqtt::publish_ha_climate_config(dv, true, false)) { // roomTemp dv.remove_state(DeviceValueState::DV_HA_CLIMATE_NO_RT); dv.add_state(DeviceValueState::DV_HA_CONFIG_CREATED); count++; } } else if (*(int8_t *)(dv.value_p) == 0 && (!dv.has_state(DeviceValueState::DV_HA_CONFIG_CREATED) || !dv.has_state(DeviceValueState::DV_HA_CLIMATE_NO_RT))) { - if (Mqtt::publish_ha_climate_config(dv.tag, false, false, dv.min, dv.max)) { // no roomTemp + if (Mqtt::publish_ha_climate_config(dv, false, false)) { // no roomTemp dv.add_state(DeviceValueState::DV_HA_CLIMATE_NO_RT); dv.add_state(DeviceValueState::DV_HA_CONFIG_CREATED); count++; @@ -2075,7 +2075,7 @@ void EMSdevice::mqtt_ha_entity_config_create() { } // SRC thermostats mapped to connect/src1/... if (dv.tag >= DeviceValueTAG::TAG_SRC1 && dv.tag <= DeviceValueTAG::TAG_SRC16 && !strcmp(dv.short_name, FL_(selRoomTemp)[0])) { - Mqtt::publish_ha_climate_config(dv.tag, true, false, dv.min, dv.max); + Mqtt::publish_ha_climate_config(dv, true, false); } #ifndef EMSESP_STANDALONE diff --git a/src/core/mqtt.cpp b/src/core/mqtt.cpp index 38342f7cb..6fecc5d3a 100644 --- a/src/core/mqtt.cpp +++ b/src/core/mqtt.cpp @@ -1236,7 +1236,10 @@ void Mqtt::add_ha_classes(JsonObject doc, const uint8_t device_type, const uint8 } } -bool Mqtt::publish_ha_climate_config(const int8_t tag, const bool has_roomtemp, const bool remove, const int16_t min, const uint32_t max) { +bool Mqtt::publish_ha_climate_config(const DeviceValue & dv, const bool has_roomtemp, const bool remove) { + int8_t tag = dv.tag; + int16_t min = dv.min; + uint32_t max = dv.max; uint8_t hc_num = tag < DeviceValueTAG::TAG_SRC1 ? tag : tag - DeviceValueTAG::TAG_SRC1 + 1; const char * tagname = tag < DeviceValueTAG::TAG_SRC1 ? "hc" : "src"; const uint8_t device_type = tag < DeviceValueTAG::TAG_SRC1 ? EMSdevice::DeviceType::THERMOSTAT : EMSdevice::DeviceType::CONNECT; @@ -1356,9 +1359,33 @@ bool Mqtt::publish_ha_climate_config(const int8_t tag, const bool has_roomtemp, // the HA climate component only responds to auto, heat and off JsonArray modes = doc["modes"].to(); - modes.add("auto"); - modes.add("heat"); - modes.add("off"); + // go through dv.options and map to HA climate modes + // https://www.home-assistant.io/integrations/climate.mqtt/ + // HA supports: ["auto", "off", "cool", "heat", "dry", "fan_only"] + if (dv.options != nullptr) { + bool found_auto = false; + bool found_heat = false; + bool found_off = false; + for (uint8_t i = 0; i < dv.options_size; i++) { + const char * option = dv.options[i][0]; + if (strcmp(option, "auto") == 0) { + found_auto = true; + } else if (strcmp(option, "heat") == 0) { + found_heat = true; + } else if (strcmp(option, "off") == 0) { + found_off = true; + } + } + if (found_auto) { + modes.add("auto"); + } + if (found_heat) { + modes.add("heat"); + } + if (found_off) { + modes.add("off"); + } + } add_ha_dev_section(doc.as(), devicename, nullptr, nullptr, nullptr, false); // add dev section add_ha_avty_section(doc.as(), topic_t, seltemp_cond, has_roomtemp ? currtemp_cond : nullptr, hc_mode_cond); // add availability section diff --git a/src/core/mqtt.h b/src/core/mqtt.h index cf86f1ee5..60b4f2a5e 100644 --- a/src/core/mqtt.h +++ b/src/core/mqtt.h @@ -113,7 +113,7 @@ class Mqtt { const bool create_device_config = false); static bool publish_system_ha_sensor_config(uint8_t type, const char * name, const char * entity, const uint8_t uom); - static bool publish_ha_climate_config(const int8_t tag, const bool has_roomtemp, const bool remove = false, const int16_t min = 5, const uint32_t max = 30); + static bool publish_ha_climate_config(const DeviceValue & dv, const bool has_roomtemp, const bool remove = false); static void show_topic_handlers(uuid::console::Shell & shell, const uint8_t device_type); static void show_mqtt(uuid::console::Shell & shell); diff --git a/src/emsesp_version.h b/src/emsesp_version.h index 830281e32..1b5f3d375 100644 --- a/src/emsesp_version.h +++ b/src/emsesp_version.h @@ -1 +1 @@ -#define EMSESP_APP_VERSION "3.7.3-dev.35" +#define EMSESP_APP_VERSION "3.7.3-dev.36" From 91a67def993b0535c1c4d6fbe2154aa6955463dc Mon Sep 17 00:00:00 2001 From: proddy Date: Sat, 13 Dec 2025 11:19:45 +0100 Subject: [PATCH 04/12] minor fix --- Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile b/Makefile index 7bbdc2383..51e990b4c 100644 --- a/Makefile +++ b/Makefile @@ -138,6 +138,7 @@ DEPFLAGS += -MF $(BUILD)/$*.d -MT $@ LINK.o = $(LD) $(LDFLAGS) $(LDLIBS) $^ -o $@ COMPILE.c = $(CC) $(C_STANDARD) $(CFLAGS) $(DEPFLAGS) -c $< -o $@ COMPILE.cpp = $(CXX) $(CXX_STANDARD) $(CXXFLAGS) $(DEPFLAGS) -c $< -o $@ +COMPILE.s = $(CC) $(CFLAGS) $(DEPFLAGS) -c $< -o $@ #---------------------------------------------------------------------- # Special Built-in Target @@ -181,6 +182,7 @@ $(BUILD)/%.o: %.cpp $(BUILD)/%.o: %.s @mkdir -p $(@D) + @$(ECHO) Compiling $@ @$(COMPILE.s) cppcheck: $(SOURCES) From 67d242d210082645d221efeaa8bae3674af0c600 Mon Sep 17 00:00:00 2001 From: proddy Date: Sat, 13 Dec 2025 11:19:52 +0100 Subject: [PATCH 05/12] package update --- interface/package.json | 4 +-- interface/pnpm-lock.yaml | 72 ++++++++++++++++++++-------------------- 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/interface/package.json b/interface/package.json index 438f1ab54..62309cf40 100644 --- a/interface/package.json +++ b/interface/package.json @@ -48,7 +48,7 @@ }, "devDependencies": { "@babel/core": "^7.28.5", - "@eslint/js": "^9.39.1", + "@eslint/js": "^9.39.2", "@preact/compat": "^18.3.1", "@preact/preset-vite": "^2.10.2", "@trivago/prettier-plugin-sort-imports": "^6.0.0", @@ -57,7 +57,7 @@ "@types/react-dom": "^19.2.3", "axe-core": "^4.11.0", "concurrently": "^9.2.1", - "eslint": "^9.39.1", + "eslint": "^9.39.2", "eslint-config-prettier": "^10.1.8", "prettier": "^3.7.4", "rollup-plugin-visualizer": "^6.0.5", diff --git a/interface/pnpm-lock.yaml b/interface/pnpm-lock.yaml index c3c32ae41..152aa4ab8 100644 --- a/interface/pnpm-lock.yaml +++ b/interface/pnpm-lock.yaml @@ -79,8 +79,8 @@ importers: specifier: ^7.28.5 version: 7.28.5 '@eslint/js': - specifier: ^9.39.1 - version: 9.39.1 + specifier: ^9.39.2 + version: 9.39.2 '@preact/preset-vite': specifier: ^2.10.2 version: 2.10.2(@babel/core@7.28.5)(preact@10.28.0)(vite@7.2.7(@types/node@25.0.1)(terser@5.44.1)) @@ -103,11 +103,11 @@ importers: specifier: ^9.2.1 version: 9.2.1 eslint: - specifier: ^9.39.1 - version: 9.39.1 + specifier: ^9.39.2 + version: 9.39.2 eslint-config-prettier: specifier: ^10.1.8 - version: 10.1.8(eslint@9.39.1) + version: 10.1.8(eslint@9.39.2) prettier: specifier: ^3.7.4 version: 3.7.4 @@ -119,7 +119,7 @@ importers: version: 5.44.1 typescript-eslint: specifier: ^8.49.0 - version: 8.49.0(eslint@9.39.1)(typescript@5.9.3) + version: 8.49.0(eslint@9.39.2)(typescript@5.9.3) vite: specifier: ^7.2.7 version: 7.2.7(@types/node@25.0.1)(terser@5.44.1) @@ -479,8 +479,8 @@ packages: resolution: {integrity: sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.39.1': - resolution: {integrity: sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==} + '@eslint/js@9.39.2': + resolution: {integrity: sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/object-schema@2.1.7': @@ -1528,8 +1528,8 @@ packages: resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint@9.39.1: - resolution: {integrity: sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==} + eslint@9.39.2: + resolution: {integrity: sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: @@ -3397,9 +3397,9 @@ snapshots: '@esbuild/win32-x64@0.25.12': optional: true - '@eslint-community/eslint-utils@4.9.0(eslint@9.39.1)': + '@eslint-community/eslint-utils@4.9.0(eslint@9.39.2)': dependencies: - eslint: 9.39.1 + eslint: 9.39.2 eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.2': {} @@ -3434,7 +3434,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@9.39.1': {} + '@eslint/js@9.39.2': {} '@eslint/object-schema@2.1.7': {} @@ -3803,15 +3803,15 @@ snapshots: dependencies: '@types/node': 25.0.1 - '@typescript-eslint/eslint-plugin@8.49.0(@typescript-eslint/parser@8.49.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3)': + '@typescript-eslint/eslint-plugin@8.49.0(@typescript-eslint/parser@8.49.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.49.0(eslint@9.39.1)(typescript@5.9.3) + '@typescript-eslint/parser': 8.49.0(eslint@9.39.2)(typescript@5.9.3) '@typescript-eslint/scope-manager': 8.49.0 - '@typescript-eslint/type-utils': 8.49.0(eslint@9.39.1)(typescript@5.9.3) - '@typescript-eslint/utils': 8.49.0(eslint@9.39.1)(typescript@5.9.3) + '@typescript-eslint/type-utils': 8.49.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/utils': 8.49.0(eslint@9.39.2)(typescript@5.9.3) '@typescript-eslint/visitor-keys': 8.49.0 - eslint: 9.39.1 + eslint: 9.39.2 ignore: 7.0.5 natural-compare: 1.4.0 ts-api-utils: 2.1.0(typescript@5.9.3) @@ -3819,14 +3819,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.49.0(eslint@9.39.1)(typescript@5.9.3)': + '@typescript-eslint/parser@8.49.0(eslint@9.39.2)(typescript@5.9.3)': dependencies: '@typescript-eslint/scope-manager': 8.49.0 '@typescript-eslint/types': 8.49.0 '@typescript-eslint/typescript-estree': 8.49.0(typescript@5.9.3) '@typescript-eslint/visitor-keys': 8.49.0 debug: 4.4.3 - eslint: 9.39.1 + eslint: 9.39.2 typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -3849,13 +3849,13 @@ snapshots: dependencies: typescript: 5.9.3 - '@typescript-eslint/type-utils@8.49.0(eslint@9.39.1)(typescript@5.9.3)': + '@typescript-eslint/type-utils@8.49.0(eslint@9.39.2)(typescript@5.9.3)': dependencies: '@typescript-eslint/types': 8.49.0 '@typescript-eslint/typescript-estree': 8.49.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.49.0(eslint@9.39.1)(typescript@5.9.3) + '@typescript-eslint/utils': 8.49.0(eslint@9.39.2)(typescript@5.9.3) debug: 4.4.3 - eslint: 9.39.1 + eslint: 9.39.2 ts-api-utils: 2.1.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: @@ -3878,13 +3878,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.49.0(eslint@9.39.1)(typescript@5.9.3)': + '@typescript-eslint/utils@8.49.0(eslint@9.39.2)(typescript@5.9.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2) '@typescript-eslint/scope-manager': 8.49.0 '@typescript-eslint/types': 8.49.0 '@typescript-eslint/typescript-estree': 8.49.0(typescript@5.9.3) - eslint: 9.39.1 + eslint: 9.39.2 typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -4505,9 +4505,9 @@ snapshots: escape-string-regexp@4.0.0: {} - eslint-config-prettier@10.1.8(eslint@9.39.1): + eslint-config-prettier@10.1.8(eslint@9.39.2): dependencies: - eslint: 9.39.1 + eslint: 9.39.2 eslint-scope@8.4.0: dependencies: @@ -4518,15 +4518,15 @@ snapshots: eslint-visitor-keys@4.2.1: {} - eslint@9.39.1: + eslint@9.39.2: dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2) '@eslint-community/regexpp': 4.12.2 '@eslint/config-array': 0.21.1 '@eslint/config-helpers': 0.4.2 '@eslint/core': 0.17.0 '@eslint/eslintrc': 3.3.3 - '@eslint/js': 9.39.1 + '@eslint/js': 9.39.2 '@eslint/plugin-kit': 0.4.1 '@humanfs/node': 0.16.7 '@humanwhocodes/module-importer': 1.0.1 @@ -5912,13 +5912,13 @@ snapshots: dependencies: typescript: 5.9.3 - typescript-eslint@8.49.0(eslint@9.39.1)(typescript@5.9.3): + typescript-eslint@8.49.0(eslint@9.39.2)(typescript@5.9.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.49.0(@typescript-eslint/parser@8.49.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3) - '@typescript-eslint/parser': 8.49.0(eslint@9.39.1)(typescript@5.9.3) + '@typescript-eslint/eslint-plugin': 8.49.0(@typescript-eslint/parser@8.49.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/parser': 8.49.0(eslint@9.39.2)(typescript@5.9.3) '@typescript-eslint/typescript-estree': 8.49.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.49.0(eslint@9.39.1)(typescript@5.9.3) - eslint: 9.39.1 + '@typescript-eslint/utils': 8.49.0(eslint@9.39.2)(typescript@5.9.3) + eslint: 9.39.2 typescript: 5.9.3 transitivePeerDependencies: - supports-color From 29037d19fb1c85446e308b5ceab20e76c8808292 Mon Sep 17 00:00:00 2001 From: proddy Date: Sat, 13 Dec 2025 11:20:24 +0100 Subject: [PATCH 06/12] auto-formatting --- src/core/temperaturesensor.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/temperaturesensor.h b/src/core/temperaturesensor.h index bd1af5459..f02a638ba 100644 --- a/src/core/temperaturesensor.h +++ b/src/core/temperaturesensor.h @@ -82,7 +82,7 @@ class TemperatureSensor { char name_[20]; int16_t offset_; - bool is_system_; + bool is_system_; }; TemperatureSensor() = default; @@ -124,7 +124,7 @@ class TemperatureSensor { return sensors_.size(); } - bool update(const char* id, const char* name, int16_t offset, bool hide = false, bool is_system = false); + bool update(const char * id, const char * name, int16_t offset, bool hide = false, bool is_system = false); #if defined(EMSESP_TEST) void load_test_data(); @@ -166,7 +166,7 @@ class TemperatureSensor { int16_t get_temperature_c(const uint8_t addr[]); uint64_t get_id(const uint8_t addr[]); void get_value_json(JsonObject output, const Sensor & sensor); - void remove_ha_topic(const char* id); + void remove_ha_topic(const char * id); std::vector> sensors_; // our list of active sensors From c2e8a6c73d776ce11337bd3429845e6fc3b7d59c Mon Sep 17 00:00:00 2001 From: proddy Date: Sat, 13 Dec 2025 11:20:48 +0100 Subject: [PATCH 07/12] added get_ip_or_hostname() --- src/core/system.cpp | 23 ++++++++++++++++++++++- src/core/system.h | 6 ++++-- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/core/system.cpp b/src/core/system.cpp index aa39b3b34..af70e6ee6 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -1789,6 +1789,28 @@ std::string System::get_metrics_prometheus() { return result; } +// return IP or hostname of the EMS-ESP device +String System::get_ip_or_hostname() { + String result = "ems-esp"; + EMSESP::esp32React.getNetworkSettingsService()->read([&](NetworkSettings & settings) { + if (settings.enableMDNS) { + if (EMSESP::system_.ethernet_connected()) { + result = ETH.getHostname(); + } else if (WiFi.status() == WL_CONNECTED) { + result = WiFi.getHostname(); + } + } else { + // no DNS, use the IP + if (EMSESP::system_.ethernet_connected()) { + result = ETH.localIP().toString(); + } else if (WiFi.status() == WL_CONNECTED) { + result = WiFi.localIP().toString(); + } + } + }); + return result; +} + // export status information including the device information // http://ems-esp/api/system/info bool System::command_info(const char * value, const int8_t id, JsonObject output) { @@ -2356,7 +2378,6 @@ String System::getBBQKeesGatewayDetails(uint8_t detail) { // This is to avoid timeouts in callback functions, like calling from a web hook. bool System::uploadFirmwareURL(const char * url) { #ifndef EMSESP_STANDALONE - static String saved_url; if (url && strlen(url) > 0) { diff --git a/src/core/system.h b/src/core/system.h index a265c65c5..c7f3adaa7 100644 --- a/src/core/system.h +++ b/src/core/system.h @@ -92,8 +92,8 @@ class System { static bool command_response(const char * value, const int8_t id, JsonObject output); static bool command_service(const char * cmd, const char * value); - static bool get_value_info(JsonObject root, const char * cmd); - static void get_value_json(JsonObject output, const std::string & circuit, const std::string & name, JsonVariant val); + static bool get_value_info(JsonObject root, const char * cmd); + static void get_value_json(JsonObject output, const std::string & circuit, const std::string & name, JsonVariant val); static std::string get_metrics_prometheus(); #if defined(EMSESP_TEST) @@ -151,6 +151,8 @@ class System { static bool readCommand(const char * data); + static String get_ip_or_hostname(); + void dallas_gpio(uint8_t gpio) { dallas_gpio_ = gpio; } From ecbdf01bdf8254103c02a16f8496a0d9751e676c Mon Sep 17 00:00:00 2001 From: proddy Date: Sat, 13 Dec 2025 11:21:45 +0100 Subject: [PATCH 08/12] add HA dev section linking all devices to ems-esp --- src/core/analogsensor.cpp | 2 +- src/core/shower.cpp | 4 ++-- src/core/temperaturesensor.cpp | 2 +- src/web/WebCustomEntityService.cpp | 2 +- src/web/WebSchedulerService.cpp | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/core/analogsensor.cpp b/src/core/analogsensor.cpp index f70e72f24..c5729d5b6 100644 --- a/src/core/analogsensor.cpp +++ b/src/core/analogsensor.cpp @@ -793,7 +793,7 @@ void AnalogSensor::publish_values(const bool force) { // see if we need to create the [devs] discovery section, as this needs only to be done once for all sensors if (std::none_of(sensors_.begin(), sensors_.end(), [](const auto & sensor) { return sensor.ha_registered; })) { - Mqtt::add_ha_dev_section(config.as(), "Analog Sensors", nullptr, nullptr, nullptr, false); + Mqtt::add_ha_dev_section(config.as(), "Analog Sensors", nullptr, "EMS-ESP", EMSESP_APP_VERSION, true); } // add default_entity_id diff --git a/src/core/shower.cpp b/src/core/shower.cpp index bb3b0f29c..0beebfd93 100644 --- a/src/core/shower.cpp +++ b/src/core/shower.cpp @@ -211,7 +211,7 @@ void Shower::create_ha_discovery() { doc["stat_t"] = "~/shower_active"; Mqtt::add_ha_bool(doc.as()); - Mqtt::add_ha_dev_section(doc.as(), "Shower Sensor", nullptr, nullptr, nullptr, false); + Mqtt::add_ha_dev_section(doc.as(), "Shower Sensors", nullptr, nullptr, nullptr, false); Mqtt::add_ha_avty_section(doc.as()); // no conditions snprintf(topic, sizeof(topic), "binary_sensor/%s/shower_active/config", Mqtt::basename().c_str()); @@ -239,7 +239,7 @@ void Shower::create_ha_discovery() { doc["dev_cla"] = "duration"; // doc["ent_cat"] = "diagnostic"; - Mqtt::add_ha_dev_section(doc.as(), "Shower Sensor", nullptr, nullptr, nullptr, false); + Mqtt::add_ha_dev_section(doc.as(), "Shower Sensors", nullptr, "EMS-ESP", EMSESP_APP_VERSION, true); Mqtt::add_ha_avty_section(doc.as(), "~/shower_data", "value_json.duration is defined"); snprintf(topic, sizeof(topic), "sensor/%s/shower_duration/config", Mqtt::basename().c_str()); diff --git a/src/core/temperaturesensor.cpp b/src/core/temperaturesensor.cpp index e50e0defc..cca9b289f 100644 --- a/src/core/temperaturesensor.cpp +++ b/src/core/temperaturesensor.cpp @@ -545,7 +545,7 @@ void TemperatureSensor::publish_values(const bool force) { // see if we need to create the [devs] discovery section, as this needs only to be done once for all sensors if (std::none_of(sensors_.begin(), sensors_.end(), [](const auto & sensor) { return sensor.ha_registered; })) { - Mqtt::add_ha_dev_section(config.as(), "Temperature Sensors", nullptr, nullptr, nullptr, false); + Mqtt::add_ha_dev_section(config.as(), "Temperature Sensors", nullptr, "EMS-ESP", EMSESP_APP_VERSION, true); } Mqtt::add_ha_avty_section(config.as(), stat_t, val_cond); diff --git a/src/web/WebCustomEntityService.cpp b/src/web/WebCustomEntityService.cpp index 807e7f3ba..22e7df99d 100644 --- a/src/web/WebCustomEntityService.cpp +++ b/src/web/WebCustomEntityService.cpp @@ -466,7 +466,7 @@ void WebCustomEntityService::publish(const bool force) { Mqtt::add_ha_classes(config.as(), EMSdevice::DeviceType::SYSTEM, entityItem.value_type, entityItem.uom); if (!ha_created) { - Mqtt::add_ha_dev_section(config.as(), "Custom Entities", nullptr, nullptr, nullptr, false); + Mqtt::add_ha_dev_section(config.as(), "Custom Entities", nullptr, "EMS-ESP", EMSESP_APP_VERSION, true); } Mqtt::add_ha_avty_section(config.as(), stat_t, val_cond); diff --git a/src/web/WebSchedulerService.cpp b/src/web/WebSchedulerService.cpp index 0e4c3c3b1..e732f6ea6 100644 --- a/src/web/WebSchedulerService.cpp +++ b/src/web/WebSchedulerService.cpp @@ -276,7 +276,7 @@ void WebSchedulerService::publish(const bool force) { Mqtt::add_ha_bool(config.as()); if (!ha_created) { - Mqtt::add_ha_dev_section(config.as(), F_(scheduler), nullptr, nullptr, nullptr, false); + Mqtt::add_ha_dev_section(config.as(), F_(scheduler), nullptr, "EMS-ESP", EMSESP_APP_VERSION, true); } Mqtt::add_ha_avty_section(config.as(), stat_t, val_cond); From c71b54fb5b18fe8bbb49518207f8d9540c3fe7e6 Mon Sep 17 00:00:00 2001 From: proddy Date: Sat, 13 Dec 2025 11:21:57 +0100 Subject: [PATCH 09/12] auto-formatting --- src/core/analogsensor.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/core/analogsensor.h b/src/core/analogsensor.h index 947094980..e7bda53b2 100644 --- a/src/core/analogsensor.h +++ b/src/core/analogsensor.h @@ -104,14 +104,14 @@ class AnalogSensor { uint32_t last_polltime_ = 0; // for timer private: - uint8_t gpio_; - char name_[20]; - double offset_; - double factor_; - uint8_t uom_; - double value_; // double because of the factor is a double - int8_t type_; // one of the AnalogType enum - bool is_system_; // if true, the sensor is a system sensor + uint8_t gpio_; + char name_[20]; + double offset_; + double factor_; + uint8_t uom_; + double value_; // double because of the factor is a double + int8_t type_; // one of the AnalogType enum + bool is_system_; // if true, the sensor is a system sensor }; AnalogSensor() = default; From d3b4bab40a71ce90779e2f48ceb301a96c9cebf2 Mon Sep 17 00:00:00 2001 From: proddy Date: Sat, 13 Dec 2025 11:22:22 +0100 Subject: [PATCH 10/12] dynamically add HA thermostat modes based on model --- src/core/emsdevice.cpp | 42 ++++++++++++++++++++--------- src/core/mqtt.cpp | 61 +++++++++++++++++++++++++++--------------- src/core/mqtt.h | 2 +- 3 files changed, 69 insertions(+), 36 deletions(-) diff --git a/src/core/emsdevice.cpp b/src/core/emsdevice.cpp index 828c38e40..fdf87b43d 100644 --- a/src/core/emsdevice.cpp +++ b/src/core/emsdevice.cpp @@ -2041,24 +2041,40 @@ bool EMSdevice::generate_values(JsonObject output, const int8_t tag_filter, cons // create the Home Assistant configs for each device value / entity // this is called when an MQTT publish is done via an EMS Device in emsesp.cpp::publish_device_values() void EMSdevice::mqtt_ha_entity_config_create() { - bool create_device_config = !ha_config_done(); // do we need to create the main Discovery device config with this entity? - uint16_t count = 0; + bool create_device_config = !ha_config_done(); // do we need to create the main Discovery device config with this entity? + uint16_t count = 0; + const char * const ** mode_options = nullptr; + + // if it's a thermostat go fetch the list of modes + if (device_type() == EMSdevice::DeviceType::THERMOSTAT) { + for (auto & dv : devicevalues_) { + // make sure it's a type DeviceValueType::ENUM + if ((dv.type == DeviceValueType::ENUM) && !strcmp(dv.short_name, FL_(mode)[0])) { + // get options + mode_options = dv.options; + break; + } + } + } // check the state of each of the device values // create the discovery topic if if hasn't already been created, not a command (like reset) and is active and visible for (auto & dv : devicevalues_) { // create climate when we reach the haclimate entity if (!strcmp(dv.short_name, FL_(haclimate)[0]) && !dv.has_state(DeviceValueState::DV_API_MQTT_EXCLUDE) && dv.has_state(DeviceValueState::DV_ACTIVE)) { - if (*(int8_t *)(dv.value_p) == 1 && (!dv.has_state(DeviceValueState::DV_HA_CONFIG_CREATED) || dv.has_state(DeviceValueState::DV_HA_CLIMATE_NO_RT))) { - if (Mqtt::publish_ha_climate_config(dv, true, false)) { // roomTemp - dv.remove_state(DeviceValueState::DV_HA_CLIMATE_NO_RT); - dv.add_state(DeviceValueState::DV_HA_CONFIG_CREATED); - count++; - } - } else if (*(int8_t *)(dv.value_p) == 0 - && (!dv.has_state(DeviceValueState::DV_HA_CONFIG_CREATED) || !dv.has_state(DeviceValueState::DV_HA_CLIMATE_NO_RT))) { - if (Mqtt::publish_ha_climate_config(dv, false, false)) { // no roomTemp - dv.add_state(DeviceValueState::DV_HA_CLIMATE_NO_RT); + int8_t haclimate_value = *(int8_t *)(dv.value_p); + bool has_config_created = dv.has_state(DeviceValueState::DV_HA_CONFIG_CREATED); + bool has_climate_no_rt = dv.has_state(DeviceValueState::DV_HA_CLIMATE_NO_RT); + bool needs_update = !has_config_created || (haclimate_value == 1 ? has_climate_no_rt : !has_climate_no_rt); + + if (needs_update) { + bool has_room_temp = (haclimate_value == 1); + if (Mqtt::publish_ha_climate_config(dv, has_room_temp, mode_options, false)) { + if (has_room_temp) { + dv.remove_state(DeviceValueState::DV_HA_CLIMATE_NO_RT); + } else { + dv.add_state(DeviceValueState::DV_HA_CLIMATE_NO_RT); + } dv.add_state(DeviceValueState::DV_HA_CONFIG_CREATED); count++; } @@ -2075,7 +2091,7 @@ void EMSdevice::mqtt_ha_entity_config_create() { } // SRC thermostats mapped to connect/src1/... if (dv.tag >= DeviceValueTAG::TAG_SRC1 && dv.tag <= DeviceValueTAG::TAG_SRC16 && !strcmp(dv.short_name, FL_(selRoomTemp)[0])) { - Mqtt::publish_ha_climate_config(dv, true, false); + Mqtt::publish_ha_climate_config(dv, true, mode_options, false); } #ifndef EMSESP_STANDALONE diff --git a/src/core/mqtt.cpp b/src/core/mqtt.cpp index 6fecc5d3a..d7e31aabb 100644 --- a/src/core/mqtt.cpp +++ b/src/core/mqtt.cpp @@ -546,7 +546,7 @@ void Mqtt::ha_status() { dev["mf"] = "EMS-ESP"; dev["mdl"] = "EMS-ESP"; #ifndef EMSESP_STANDALONE - dev["cu"] = "http://" + (EMSESP::system_.ethernet_connected() ? ETH.localIP().toString() : WiFi.localIP().toString()); + dev["cu"] = std::string("http://") + EMSESP::system_.get_ip_or_hostname().c_str(); #endif JsonArray ids = dev["ids"].to(); ids.add(Mqtt::basename()); @@ -1103,6 +1103,12 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev // https://github.com/emsesp/EMS-ESP32/discussions/1459#discussioncomment-7694873 add_ha_classes(doc.as(), device_type, type, uom, entity); + // add origin + JsonObject origin_json = doc["o"].to(); + origin_json["name"] = "EMS-ESP"; + origin_json["sw"] = EMSESP_APP_VERSION; + origin_json["url"] = "https://emsesp.org"; + // add dev section if (device_type == EMSdevice::DeviceType::SYSTEM) { add_ha_dev_section(doc.as(), nullptr, nullptr, nullptr, nullptr, false); @@ -1236,7 +1242,9 @@ void Mqtt::add_ha_classes(JsonObject doc, const uint8_t device_type, const uint8 } } -bool Mqtt::publish_ha_climate_config(const DeviceValue & dv, const bool has_roomtemp, const bool remove) { +// publish the HA climate config +// https://www.home-assistant.io/integrations/climate.mqtt/ +bool Mqtt::publish_ha_climate_config(const DeviceValue & dv, const bool has_roomtemp, const char * const ** mode_options, const bool remove) { int8_t tag = dv.tag; int16_t min = dv.min; uint32_t max = dv.max; @@ -1356,34 +1364,43 @@ bool Mqtt::publish_ha_climate_config(const DeviceValue & dv, const bool has_room doc["act_t"] = "~/boiler_data"; doc["act_tpl"] = "{% if value_json.hpactivity=='cooling'%}cooling{%elif value_json.heatingactive=='on'%}heating{%else%}idle{%endif%}"; - // the HA climate component only responds to auto, heat and off - JsonArray modes = doc["modes"].to(); - - // go through dv.options and map to HA climate modes - // https://www.home-assistant.io/integrations/climate.mqtt/ - // HA supports: ["auto", "off", "cool", "heat", "dry", "fan_only"] - if (dv.options != nullptr) { + // map EMS modes to HA climate modes + // EMS modes: auto, manual, heat, off, night, day, nofrost, eco, comfort, cool) + // HA supports: auto, off, cool, heat, dry, fan_only + if (mode_options != nullptr) { + // scan through mode_options and add to modes bool found_auto = false; bool found_heat = false; bool found_off = false; - for (uint8_t i = 0; i < dv.options_size; i++) { - const char * option = dv.options[i][0]; - if (strcmp(option, "auto") == 0) { + bool found_cool = false; + for (uint8_t i = 0; i < Helpers::count_items(mode_options); i++) { + const char * mode = mode_options[i][0]; // take EN + if (!strcmp(mode, FL_(auto)[0])) { found_auto = true; - } else if (strcmp(option, "heat") == 0) { + } else if (!strcmp(mode, FL_(heat)[0])) { found_heat = true; - } else if (strcmp(option, "off") == 0) { + } else if (!strcmp(mode, FL_(off)[0])) { found_off = true; + } else if (!strcmp(mode, FL_(cool)[0])) { + found_cool = true; } } - if (found_auto) { - modes.add("auto"); - } - if (found_heat) { - modes.add("heat"); - } - if (found_off) { - modes.add("off"); + + // only add modes if we found at least one + if (found_auto || found_heat || found_off || found_cool) { + JsonArray modes = doc["modes"].to(); + if (found_auto) { + modes.add("auto"); + } + if (found_heat) { + modes.add("heat"); + } + if (found_off) { + modes.add("off"); + } + if (found_cool) { + modes.add("cool"); + } } } diff --git a/src/core/mqtt.h b/src/core/mqtt.h index 60b4f2a5e..60e0fc1ea 100644 --- a/src/core/mqtt.h +++ b/src/core/mqtt.h @@ -113,7 +113,7 @@ class Mqtt { const bool create_device_config = false); static bool publish_system_ha_sensor_config(uint8_t type, const char * name, const char * entity, const uint8_t uom); - static bool publish_ha_climate_config(const DeviceValue & dv, const bool has_roomtemp, const bool remove = false); + static bool publish_ha_climate_config(const DeviceValue & dv, const bool has_roomtemp, const char * const ** mode_options, const bool remove = false); static void show_topic_handlers(uuid::console::Shell & shell, const uint8_t device_type); static void show_mqtt(uuid::console::Shell & shell); From 4ee743d3096daa38c24535778a700a5807e44901 Mon Sep 17 00:00:00 2001 From: proddy Date: Sat, 13 Dec 2025 11:29:01 +0100 Subject: [PATCH 11/12] fix standalone --- src/core/system.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/system.cpp b/src/core/system.cpp index af70e6ee6..8501f34f1 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -1792,6 +1792,7 @@ std::string System::get_metrics_prometheus() { // return IP or hostname of the EMS-ESP device String System::get_ip_or_hostname() { String result = "ems-esp"; +#ifndef EMSESP_STANDALONE EMSESP::esp32React.getNetworkSettingsService()->read([&](NetworkSettings & settings) { if (settings.enableMDNS) { if (EMSESP::system_.ethernet_connected()) { @@ -1808,6 +1809,7 @@ String System::get_ip_or_hostname() { } } }); +#endif return result; } From 4b3cc2e3ec400ef81db148c8d0c03f2a4e603f50 Mon Sep 17 00:00:00 2001 From: proddy Date: Sat, 13 Dec 2025 11:30:43 +0100 Subject: [PATCH 12/12] update --- CHANGELOG_LATEST.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/CHANGELOG_LATEST.md b/CHANGELOG_LATEST.md index 85df53c5a..d3cb3a10b 100644 --- a/CHANGELOG_LATEST.md +++ b/CHANGELOG_LATEST.md @@ -14,7 +14,7 @@ For more details go to [docs.emsesp.org](https://docs.emsesp.org/). - CS6800i changes [#2448](https://github.com/emsesp/EMS-ESP32/issues/2448), [#2449](https://github.com/emsesp/EMS-ESP32/issues/2449) - charging pump [#2544](https://github.com/emsesp/EMS-ESP32/issues/2544) - hybrid CSH5800iG [#2569](https://github.com/emsesp/EMS-ESP32/issues/2569) -- add EMS Device details to Home Assistant MQTT Discovery +- added EMS Device details to Home Assistant MQTT Discovery - disinfection command [#2601](https://github.com/emsesp/EMS-ESP32/issues/2601) - added new board profile for upcoming BBQKees E32V2.2 - set differential pressure entity in Mixer device @@ -34,11 +34,10 @@ For more details go to [docs.emsesp.org](https://docs.emsesp.org/). - pid settings [#2735](https://github.com/emsesp/EMS-ESP32/issues/2735) - refresh MQTT button added to MQTT Settings page - heating assistance, rounding custum settings [#2763](https://github.com/emsesp/EMS-ESP32/discussions/2763) -- add counter 0..2 for short pulses, high frequency [#2758](https://github.com/emsesp/EMS-ESP32/issues/2758) +- added counter 0..2 for short pulses, high frequency [#2758](https://github.com/emsesp/EMS-ESP32/issues/2758) - added LWT (Last Will and Testament) to MQTT entities in Home Assistant -- added api/metrics endpoint for prometheus integration by @gr3enk - [#2774](https://github.com/emsesp/EMS-ESP32/pull/2774) -- add RTL8201 to eth phy list [#2800](https://github.com/emsesp/EMS-ESP32/issues/2800) +- added api/metrics endpoint for prometheus integration by @gr3enk [#2774](https://github.com/emsesp/EMS-ESP32/pull/2774) +- added RTL8201 to eth phy list [#2800](https://github.com/emsesp/EMS-ESP32/issues/2800) ## Fixed @@ -64,7 +63,7 @@ For more details go to [docs.emsesp.org](https://docs.emsesp.org/). ## Changed - show console log with ISO date/time [#2533](https://github.com/emsesp/EMS-ESP32/discussions/2533) -- remove ESP32 CPU temperature +- removed ESP32 CPU temperature - updated core libraries like AsyncTCP, AsyncWebServer and Modbus - remove command `scan deep` - ignore repeated `forceheatingoff` commands [#2641](https://github.com/emsesp/EMS-ESP32/discussions/2641) @@ -80,4 +79,5 @@ For more details go to [docs.emsesp.org](https://docs.emsesp.org/). - show number on entities and supported languages in log on boot - on tx read fail delay the 3rd. retry 2 sec - move vectors and lists to PSRAM --remove unused last topic/payload echo-check +- removed unused last topic/payload echo-check +- added Home Assistant device details to MQTT Discovery for all devices