From 6c467703307192caf1a21ab8d0700e377ec6249b Mon Sep 17 00:00:00 2001 From: Proddy Date: Sat, 4 Feb 2023 14:25:29 +0100 Subject: [PATCH] merge PR 940 - https://github.com/emsesp/EMS-ESP32/pull/940 --- CHANGELOG_LATEST.md | 21 +- interface/.typesafe-i18n.json | 8 +- interface/package-lock.json | 162 ++++++------ interface/package.json | 10 +- interface/src/i18n/de/index.ts | 1 + interface/src/i18n/en/index.ts | 1 + interface/src/i18n/fr/index.ts | 1 + interface/src/i18n/nl/index.ts | 1 + interface/src/i18n/no/index.ts | 1 + interface/src/i18n/pl/index.ts | 1 + interface/src/i18n/sv/index.ts | 1 + interface/src/project/OptionIcon.tsx | 6 +- .../src/project/SettingsCustomization.tsx | 195 ++++++++------ interface/src/project/types.ts | 3 +- lib/framework/UploadFileService.cpp | 2 +- mock-api/server.js | 5 +- src/devices/thermostat.cpp | 247 ++++++------------ src/devices/thermostat.h | 104 ++++++-- src/emsdevice.cpp | 64 +++-- src/emsdevicevalue.cpp | 8 + src/emsdevicevalue.h | 11 +- src/locale_common.h | 4 +- src/mqtt.cpp | 5 +- src/version.h | 2 +- src/web/WebCustomizationService.cpp | 52 +++- 25 files changed, 506 insertions(+), 410 deletions(-) diff --git a/CHANGELOG_LATEST.md b/CHANGELOG_LATEST.md index d87aee0b3..b63843a33 100644 --- a/CHANGELOG_LATEST.md +++ b/CHANGELOG_LATEST.md @@ -5,7 +5,6 @@ ## **IMPORTANT! BREAKING CHANGES** - When upgrading to v3.5 for the first time from v3.4 on a BBQKees Gateway board you will need to use the [EMS-EPS Flasher](https://github.com/emsesp/EMS-ESP-Flasher/releases) to correctly re-partition the flash. Make sure you backup the settings and customizations from the WebUI (System->Upload/Download) and restore after the upgrade. -- Support for multiple EMS-ESPs [#759] has been added as an optional setting for MQTT. When enabled, which is now the default, all MQTT Discovery Entity IDs will include the MQTT base name and the shortname of the EMS-ESP device entity. For example what was previously `sensor.boiler_actual_boiler_temperature` will now become `sensor.ems_esp_boiler_boiltemp`. If you still want to use the old format and retain the history and script compatibility in Home Assistant then set this back to the old format. ## Added @@ -32,7 +31,8 @@ - Add Rego 3000, TR120RF thermostats [#917](https://github.com/emsesp/EMS-ESP32/issues/917) - Add config for ESP32-S3 - Add heatpump silent mode and other entities [#896](https://github.com/emsesp/EMS-ESP32/issues/896) -- Allow reboot to other partition (factory or asymmetric OTA) +- Allow reboot to other partition (factory or asymetric OTA) +- Blacklist entities to remove from memory [#891](https://github.com/emsesp/EMS-ESP32/issues/891) ## Fixed @@ -41,20 +41,21 @@ - Commands for multiple thermostats [#826](https://github.com/emsesp/EMS-ESP32/issues/826) - API queries for multiple devices [#865](https://github.com/emsesp/EMS-ESP32/issues/865) - Console crash when using call with command `hcx` only. [#841](https://github.com/emsesp/EMS-ESP32/issues/841) -- heatingPump2Mod was wrong, changed to absBurnPow [[#908](https://github.com/emsesp/EMS-ESP32/issues/908) -- rounding of web input values -- analog sensor with single gpio number [#915](https://github.com/emsesp/EMS-ESP32/issues/915) +- `heatingPump2Mod` was wrong, changed to absBurnPow [[#908](https://github.com/emsesp/EMS-ESP32/issues/908) +- Rounding of web input values +- Analog sensor with single gpio number [#915](https://github.com/emsesp/EMS-ESP32/issues/915) - HA dallas and analog configs: remove/rebuild on change [#888](https://github.com/emsesp/EMS-ESP32/issues/888) +- Modes and set seltemp for RC30 and RC20 [#932](https://github.com/emsesp/EMS-ESP32/issues/932) ## Changed - Discovery in HomeAssistant don't work with custom base topic. [#596](https://github.com/emsesp/EMS-ESP32/issues/596) Base topic containing `/` are changed to `_` - RF room temperature sensor are shown as thermostat -- render mqtt float json values with trailing zero -- removed flash strings, to increase available heap memory -- reload page after restart button is pressed -- analog/dallas values command as list like ems-devices -- analog/dallas HA-entities based on id +- Render mqtt float json values with trailing zero +- Removed flash strings, to increase available heap memory +- Reload page after restart button is pressed +- Analog/dallas values command as list like ems-devices +- Analog/dallas HA-entities based on id - MQTT Base is a mandatory field. Removed MQTT topic length from settings - HA duration class for time entities [[#822](https://github.com/emsesp/EMS-ESP32/issues/822) - AM200 alternative heatsource as class heatsource [[#857](https://github.com/emsesp/EMS-ESP32/issues/857) diff --git a/interface/.typesafe-i18n.json b/interface/.typesafe-i18n.json index e3832546d..d4c1b88f5 100644 --- a/interface/.typesafe-i18n.json +++ b/interface/.typesafe-i18n.json @@ -1,5 +1,5 @@ { - "adapter": "react", - "baseLocale": "pl", - "$schema": "https://unpkg.com/typesafe-i18n@5.21.0/schema/typesafe-i18n.json" -} \ No newline at end of file + "adapter": "react", + "baseLocale": "pl", + "$schema": "https://unpkg.com/typesafe-i18n@5.22.0/schema/typesafe-i18n.json" +} diff --git a/interface/package-lock.json b/interface/package-lock.json index d6557bba2..9593c3cd5 100644 --- a/interface/package-lock.json +++ b/interface/package-lock.json @@ -12,15 +12,15 @@ "@emotion/styled": "^11.10.5", "@msgpack/msgpack": "^2.8.0", "@mui/icons-material": "^5.11.0", - "@mui/material": "^5.11.6", - "@table-library/react-table-library": "4.0.23", + "@mui/material": "^5.11.7", + "@table-library/react-table-library": "4.0.24", "@types/lodash": "^4.14.191", "@types/node": "^18.11.18", "@types/react": "^18.0.27", "@types/react-dom": "^18.0.10", "@types/react-router-dom": "^5.3.3", "async-validator": "^4.2.5", - "axios": "^1.2.6", + "axios": "^1.3.2", "jwt-decode": "^3.1.2", "lodash": "^4.17.21", "notistack": "^2.0.8", @@ -32,8 +32,8 @@ "react-router-dom": "^6.8.0", "react-scripts": "^5.0.1", "sockette": "^2.0.6", - "typesafe-i18n": "^5.21.0", - "typescript": "^4.9.4" + "typesafe-i18n": "^5.22.0", + "typescript": "^4.9.5" }, "devDependencies": { "http-proxy-middleware": "^2.0.6", @@ -3082,14 +3082,14 @@ } }, "node_modules/@mui/base": { - "version": "5.0.0-alpha.115", - "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.115.tgz", - "integrity": "sha512-OGQ84whT/yNYd6xKCGGS6MxqEfjVjk5esXM7HP6bB2Rim7QICUapxZt4nm8q39fpT08rNDkv3xPVqDDwRdRg1g==", + "version": "5.0.0-alpha.116", + "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.116.tgz", + "integrity": "sha512-VwhifWdrfHc4/ZdqRZ4Gf+7P39sovNN24By1YVZdvJ9fvp0Sr8sNftGUCjYXXz+xCXVBQDXvhfxMwZrj2MvJvA==", "dependencies": { "@babel/runtime": "^7.20.7", "@emotion/is-prop-valid": "^1.2.0", "@mui/types": "^7.2.3", - "@mui/utils": "^5.11.2", + "@mui/utils": "^5.11.7", "@popperjs/core": "^2.11.6", "clsx": "^1.2.1", "prop-types": "^15.8.1", @@ -3114,9 +3114,9 @@ } }, "node_modules/@mui/core-downloads-tracker": { - "version": "5.11.6", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.11.6.tgz", - "integrity": "sha512-lbD3qdafBOf2dlqKhOcVRxaPAujX+9UlPC6v8iMugMeAXe0TCgU3QbGXY3zrJsu6ex64WYDpH4y1+WOOBmWMuA==", + "version": "5.11.7", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.11.7.tgz", + "integrity": "sha512-lZgX7XQTk0zVcpwEa80r+T4y09dosnUxWvFPSikU/2Hh5wnyNOek8WfJwGCNsaRiXJHMi5eHY+z8oku4u5lgNw==", "funding": { "type": "opencollective", "url": "https://opencollective.com/mui" @@ -3148,16 +3148,16 @@ } }, "node_modules/@mui/material": { - "version": "5.11.6", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.11.6.tgz", - "integrity": "sha512-MzkkL5KC2PCkFiv8cLpkzgLUPXSrAtnvJBR0emV7mLVWbkwV3n5832vjBx154B6R032fHjFTziTh7YEb50nK6Q==", + "version": "5.11.7", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.11.7.tgz", + "integrity": "sha512-wDv7Pc6kMe9jeWkmCLt4JChd1lPc2u23JQHpB35L2VwQowpNFoDfIwqi0sYCnZTMKlRc7lza8LqwSwHl2G52Rw==", "dependencies": { "@babel/runtime": "^7.20.7", - "@mui/base": "5.0.0-alpha.115", - "@mui/core-downloads-tracker": "^5.11.6", - "@mui/system": "^5.11.5", + "@mui/base": "5.0.0-alpha.116", + "@mui/core-downloads-tracker": "^5.11.7", + "@mui/system": "^5.11.7", "@mui/types": "^7.2.3", - "@mui/utils": "^5.11.2", + "@mui/utils": "^5.11.7", "@types/react-transition-group": "^4.4.5", "clsx": "^1.2.1", "csstype": "^3.1.1", @@ -3192,12 +3192,12 @@ } }, "node_modules/@mui/private-theming": { - "version": "5.11.2", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.11.2.tgz", - "integrity": "sha512-qZwMaqRFPwlYmqwVKblKBGKtIjJRAj3nsvX93pOmatsXyorW7N/0IPE/swPgz1VwChXhHO75DwBEx8tB+aRMNg==", + "version": "5.11.7", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.11.7.tgz", + "integrity": "sha512-XzRTSZdc8bhuUdjablTNv3kFkZ/XIMlKkOqqJCU0G8W3tWGXpau2DXkafPd1ddjPhF9zF3qLKNGgKCChYItjgA==", "dependencies": { "@babel/runtime": "^7.20.7", - "@mui/utils": "^5.11.2", + "@mui/utils": "^5.11.7", "prop-types": "^15.8.1" }, "engines": { @@ -3249,15 +3249,15 @@ } }, "node_modules/@mui/system": { - "version": "5.11.5", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.11.5.tgz", - "integrity": "sha512-KNVsJ0sgRRp2XBqhh4wPS5aacteqjwxgiYTVwVnll2fgkgunZKo3DsDiGMrFlCg25ZHA3Ax58txWGE9w58zp0w==", + "version": "5.11.7", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.11.7.tgz", + "integrity": "sha512-uGB6hBxGlAdlmbLdTtUZYNPXkgQGGnKxHdkRATqsu7UlCxNsc/yS5NCEWy/3c4pnelD1LDLD39WrntP9mwhfkQ==", "dependencies": { "@babel/runtime": "^7.20.7", - "@mui/private-theming": "^5.11.2", + "@mui/private-theming": "^5.11.7", "@mui/styled-engine": "^5.11.0", "@mui/types": "^7.2.3", - "@mui/utils": "^5.11.2", + "@mui/utils": "^5.11.7", "clsx": "^1.2.1", "csstype": "^3.1.1", "prop-types": "^15.8.1" @@ -3301,9 +3301,9 @@ } }, "node_modules/@mui/utils": { - "version": "5.11.2", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.11.2.tgz", - "integrity": "sha512-AyizuHHlGdAtH5hOOXBW3kriuIwUIKUIgg0P7LzMvzf6jPhoQbENYqY6zJqfoZ7fAWMNNYT8mgN5EftNGzwE2w==", + "version": "5.11.7", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.11.7.tgz", + "integrity": "sha512-8uyNDeVHZA804Ego20Erv8TpxlbqTe/EbhTI2H1UYr4/RiIbBprat8W4Qqr2UQIsC/b3DLz+0RQ6R/E5BxEcLA==", "dependencies": { "@babel/runtime": "^7.20.7", "@types/prop-types": "^15.7.5", @@ -3775,9 +3775,9 @@ } }, "node_modules/@table-library/react-table-library": { - "version": "4.0.23", - "resolved": "https://registry.npmjs.org/@table-library/react-table-library/-/react-table-library-4.0.23.tgz", - "integrity": "sha512-o2L/fqhwQNxsNbbm3LIiyZzEwaTslhG1tY9ArkYdS0xJyRhJxcOLfbJ3+dcnOOn+aIJpmPmQH+gr7RYJC0P8uw==", + "version": "4.0.24", + "resolved": "https://registry.npmjs.org/@table-library/react-table-library/-/react-table-library-4.0.24.tgz", + "integrity": "sha512-tOeKermNhSDKGVvzBlF8gtLVWl+lWbCHk0XH/7s1ybZ9XI8TXpogzXavyRfObGFAIyeazr7Qy5bWGWnE4uueUg==", "dependencies": { "clsx": "1.1.1", "react-virtualized-auto-sizer": "1.0.6", @@ -5076,9 +5076,9 @@ } }, "node_modules/axios": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.2.6.tgz", - "integrity": "sha512-rC/7F08XxZwjMV4iuWv+JpD3E0Ksqg9nac4IIg6RwNuF0JTeWoCo/mBNG54+tNhhI11G3/VDRbdDQTs9hGp4pQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.2.tgz", + "integrity": "sha512-1M3O703bYqYuPhbHeya5bnhpYVsDDRyQSabNja04mZtboLNSuZ4YrltestrLXfHgmzua4TpUqRiVKbiQuo2epw==", "dependencies": { "follow-redirects": "^1.15.0", "form-data": "^4.0.0", @@ -16789,9 +16789,9 @@ } }, "node_modules/typesafe-i18n": { - "version": "5.21.0", - "resolved": "https://registry.npmjs.org/typesafe-i18n/-/typesafe-i18n-5.21.0.tgz", - "integrity": "sha512-hfJQ7j0jvz21s2Iiv92Nx7rGyEQZJnVaQ64ek6Bn3IDaADiGFoUbSc7ZPYqeI9iTO8mOe35ACJ9MTn2cEp1ECA==", + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/typesafe-i18n/-/typesafe-i18n-5.22.0.tgz", + "integrity": "sha512-I6dH/lmBEOOMdLmHbxOZK4x7wQrf7ZrgT2ynTzcJZAIJIfj0QdU8ncKOfrMOuTf3ddpe/MLSH1NKLxfPS7l2dQ==", "bin": { "typesafe-i18n": "cli/typesafe-i18n.mjs" }, @@ -16804,9 +16804,9 @@ } }, "node_modules/typescript": { - "version": "4.9.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", - "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -20033,14 +20033,14 @@ "integrity": "sha512-h9u4u/jiIRKbq25PM+zymTyW6bhTzELvOoUd+AvYriWOAKpLGnIamaET3pnHYoI5iYphAHBI4ayx0MehR+VVPQ==" }, "@mui/base": { - "version": "5.0.0-alpha.115", - "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.115.tgz", - "integrity": "sha512-OGQ84whT/yNYd6xKCGGS6MxqEfjVjk5esXM7HP6bB2Rim7QICUapxZt4nm8q39fpT08rNDkv3xPVqDDwRdRg1g==", + "version": "5.0.0-alpha.116", + "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.116.tgz", + "integrity": "sha512-VwhifWdrfHc4/ZdqRZ4Gf+7P39sovNN24By1YVZdvJ9fvp0Sr8sNftGUCjYXXz+xCXVBQDXvhfxMwZrj2MvJvA==", "requires": { "@babel/runtime": "^7.20.7", "@emotion/is-prop-valid": "^1.2.0", "@mui/types": "^7.2.3", - "@mui/utils": "^5.11.2", + "@mui/utils": "^5.11.7", "@popperjs/core": "^2.11.6", "clsx": "^1.2.1", "prop-types": "^15.8.1", @@ -20048,9 +20048,9 @@ } }, "@mui/core-downloads-tracker": { - "version": "5.11.6", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.11.6.tgz", - "integrity": "sha512-lbD3qdafBOf2dlqKhOcVRxaPAujX+9UlPC6v8iMugMeAXe0TCgU3QbGXY3zrJsu6ex64WYDpH4y1+WOOBmWMuA==" + "version": "5.11.7", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.11.7.tgz", + "integrity": "sha512-lZgX7XQTk0zVcpwEa80r+T4y09dosnUxWvFPSikU/2Hh5wnyNOek8WfJwGCNsaRiXJHMi5eHY+z8oku4u5lgNw==" }, "@mui/icons-material": { "version": "5.11.0", @@ -20061,16 +20061,16 @@ } }, "@mui/material": { - "version": "5.11.6", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.11.6.tgz", - "integrity": "sha512-MzkkL5KC2PCkFiv8cLpkzgLUPXSrAtnvJBR0emV7mLVWbkwV3n5832vjBx154B6R032fHjFTziTh7YEb50nK6Q==", + "version": "5.11.7", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.11.7.tgz", + "integrity": "sha512-wDv7Pc6kMe9jeWkmCLt4JChd1lPc2u23JQHpB35L2VwQowpNFoDfIwqi0sYCnZTMKlRc7lza8LqwSwHl2G52Rw==", "requires": { "@babel/runtime": "^7.20.7", - "@mui/base": "5.0.0-alpha.115", - "@mui/core-downloads-tracker": "^5.11.6", - "@mui/system": "^5.11.5", + "@mui/base": "5.0.0-alpha.116", + "@mui/core-downloads-tracker": "^5.11.7", + "@mui/system": "^5.11.7", "@mui/types": "^7.2.3", - "@mui/utils": "^5.11.2", + "@mui/utils": "^5.11.7", "@types/react-transition-group": "^4.4.5", "clsx": "^1.2.1", "csstype": "^3.1.1", @@ -20080,12 +20080,12 @@ } }, "@mui/private-theming": { - "version": "5.11.2", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.11.2.tgz", - "integrity": "sha512-qZwMaqRFPwlYmqwVKblKBGKtIjJRAj3nsvX93pOmatsXyorW7N/0IPE/swPgz1VwChXhHO75DwBEx8tB+aRMNg==", + "version": "5.11.7", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.11.7.tgz", + "integrity": "sha512-XzRTSZdc8bhuUdjablTNv3kFkZ/XIMlKkOqqJCU0G8W3tWGXpau2DXkafPd1ddjPhF9zF3qLKNGgKCChYItjgA==", "requires": { "@babel/runtime": "^7.20.7", - "@mui/utils": "^5.11.2", + "@mui/utils": "^5.11.7", "prop-types": "^15.8.1" } }, @@ -20101,15 +20101,15 @@ } }, "@mui/system": { - "version": "5.11.5", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.11.5.tgz", - "integrity": "sha512-KNVsJ0sgRRp2XBqhh4wPS5aacteqjwxgiYTVwVnll2fgkgunZKo3DsDiGMrFlCg25ZHA3Ax58txWGE9w58zp0w==", + "version": "5.11.7", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.11.7.tgz", + "integrity": "sha512-uGB6hBxGlAdlmbLdTtUZYNPXkgQGGnKxHdkRATqsu7UlCxNsc/yS5NCEWy/3c4pnelD1LDLD39WrntP9mwhfkQ==", "requires": { "@babel/runtime": "^7.20.7", - "@mui/private-theming": "^5.11.2", + "@mui/private-theming": "^5.11.7", "@mui/styled-engine": "^5.11.0", "@mui/types": "^7.2.3", - "@mui/utils": "^5.11.2", + "@mui/utils": "^5.11.7", "clsx": "^1.2.1", "csstype": "^3.1.1", "prop-types": "^15.8.1" @@ -20122,9 +20122,9 @@ "requires": {} }, "@mui/utils": { - "version": "5.11.2", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.11.2.tgz", - "integrity": "sha512-AyizuHHlGdAtH5hOOXBW3kriuIwUIKUIgg0P7LzMvzf6jPhoQbENYqY6zJqfoZ7fAWMNNYT8mgN5EftNGzwE2w==", + "version": "5.11.7", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.11.7.tgz", + "integrity": "sha512-8uyNDeVHZA804Ego20Erv8TpxlbqTe/EbhTI2H1UYr4/RiIbBprat8W4Qqr2UQIsC/b3DLz+0RQ6R/E5BxEcLA==", "requires": { "@babel/runtime": "^7.20.7", "@types/prop-types": "^15.7.5", @@ -20408,9 +20408,9 @@ } }, "@table-library/react-table-library": { - "version": "4.0.23", - "resolved": "https://registry.npmjs.org/@table-library/react-table-library/-/react-table-library-4.0.23.tgz", - "integrity": "sha512-o2L/fqhwQNxsNbbm3LIiyZzEwaTslhG1tY9ArkYdS0xJyRhJxcOLfbJ3+dcnOOn+aIJpmPmQH+gr7RYJC0P8uw==", + "version": "4.0.24", + "resolved": "https://registry.npmjs.org/@table-library/react-table-library/-/react-table-library-4.0.24.tgz", + "integrity": "sha512-tOeKermNhSDKGVvzBlF8gtLVWl+lWbCHk0XH/7s1ybZ9XI8TXpogzXavyRfObGFAIyeazr7Qy5bWGWnE4uueUg==", "requires": { "clsx": "1.1.1", "react-virtualized-auto-sizer": "1.0.6", @@ -21434,9 +21434,9 @@ "integrity": "sha512-b1WlTV8+XKLj9gZy2DZXgQiyDp9xkkoe2a6U6UbYccScq2wgH/YwCeI2/Jq2mgo0HzQxqJOjWZBLeA/mqsk5Mg==" }, "axios": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.2.6.tgz", - "integrity": "sha512-rC/7F08XxZwjMV4iuWv+JpD3E0Ksqg9nac4IIg6RwNuF0JTeWoCo/mBNG54+tNhhI11G3/VDRbdDQTs9hGp4pQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.2.tgz", + "integrity": "sha512-1M3O703bYqYuPhbHeya5bnhpYVsDDRyQSabNja04mZtboLNSuZ4YrltestrLXfHgmzua4TpUqRiVKbiQuo2epw==", "requires": { "follow-redirects": "^1.15.0", "form-data": "^4.0.0", @@ -29785,15 +29785,15 @@ } }, "typesafe-i18n": { - "version": "5.21.0", - "resolved": "https://registry.npmjs.org/typesafe-i18n/-/typesafe-i18n-5.21.0.tgz", - "integrity": "sha512-hfJQ7j0jvz21s2Iiv92Nx7rGyEQZJnVaQ64ek6Bn3IDaADiGFoUbSc7ZPYqeI9iTO8mOe35ACJ9MTn2cEp1ECA==", + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/typesafe-i18n/-/typesafe-i18n-5.22.0.tgz", + "integrity": "sha512-I6dH/lmBEOOMdLmHbxOZK4x7wQrf7ZrgT2ynTzcJZAIJIfj0QdU8ncKOfrMOuTf3ddpe/MLSH1NKLxfPS7l2dQ==", "requires": {} }, "typescript": { - "version": "4.9.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", - "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==" + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==" }, "unbox-primitive": { "version": "1.0.2", diff --git a/interface/package.json b/interface/package.json index cfaca6f39..006d80030 100644 --- a/interface/package.json +++ b/interface/package.json @@ -8,8 +8,8 @@ "@emotion/styled": "^11.10.5", "@msgpack/msgpack": "^2.8.0", "@mui/icons-material": "^5.11.0", - "@mui/material": "^5.11.6", - "@table-library/react-table-library": "4.0.23", + "@mui/material": "^5.11.7", + "@table-library/react-table-library": "4.0.24", "@types/lodash": "^4.14.191", "@types/node": "^18.11.18", "@types/react": "^18.0.27", @@ -26,9 +26,9 @@ "react-icons": "^4.7.1", "react-scripts": "^5.0.1", "sockette": "^2.0.6", - "typesafe-i18n": "^5.21.0", - "typescript": "^4.9.4", - "axios": "^1.2.6", + "typesafe-i18n": "^5.22.0", + "typescript": "^4.9.5", + "axios": "^1.3.2", "react-router-dom": "^6.8.0" }, "scripts": { diff --git a/interface/src/i18n/de/index.ts b/interface/src/i18n/de/index.ts index b155fc59c..c7324ae72 100644 --- a/interface/src/i18n/de/index.ts +++ b/interface/src/i18n/de/index.ts @@ -157,6 +157,7 @@ const de: Translation = { CUSTOMIZATIONS_HELP_3: 'Schreibaktion deaktivieren', CUSTOMIZATIONS_HELP_4: 'von MQTT und API ausschließen', CUSTOMIZATIONS_HELP_5: 'Aus dem Kontrollzentrum ausblenden', + CUSTOMIZATIONS_HELP_6: 'Aus dem Speicher löschen', SELECT_DEVICE: 'Wählen Sie ein Gerät aus', SET_ALL: 'setzen Sie alle', OPTIONS: 'Optionen', diff --git a/interface/src/i18n/en/index.ts b/interface/src/i18n/en/index.ts index 4c63b9b07..b7b9d7c67 100644 --- a/interface/src/i18n/en/index.ts +++ b/interface/src/i18n/en/index.ts @@ -157,6 +157,7 @@ const en: Translation = { CUSTOMIZATIONS_HELP_3: 'disable write action', CUSTOMIZATIONS_HELP_4: 'exclude from MQTT and API', CUSTOMIZATIONS_HELP_5: 'hide from Dashboard', + CUSTOMIZATIONS_HELP_6: 'remove from memory', SELECT_DEVICE: 'Select a device', SET_ALL: 'set all', OPTIONS: 'Options', diff --git a/interface/src/i18n/fr/index.ts b/interface/src/i18n/fr/index.ts index a3de3057b..0a2fc65e8 100644 --- a/interface/src/i18n/fr/index.ts +++ b/interface/src/i18n/fr/index.ts @@ -157,6 +157,7 @@ const fr: Translation = { CUSTOMIZATIONS_HELP_3: 'désactiver l\'action d\'écriture', CUSTOMIZATIONS_HELP_4: 'exclure de MQTT et de l\'API', CUSTOMIZATIONS_HELP_5: 'cacher du Tableau de bord', + CUSTOMIZATIONS_HELP_6: 'remove from memory', // TODO translate SELECT_DEVICE: 'Sélectionnez un appareil', SET_ALL: 'tout régler', OPTIONS: 'Options', diff --git a/interface/src/i18n/nl/index.ts b/interface/src/i18n/nl/index.ts index ca4481f8b..e5c593e3a 100644 --- a/interface/src/i18n/nl/index.ts +++ b/interface/src/i18n/nl/index.ts @@ -157,6 +157,7 @@ const nl: Translation = { CUSTOMIZATIONS_HELP_3: 'Zet schrijfacties uit', CUSTOMIZATIONS_HELP_4: 'Uitsluiten van MQTT en API', CUSTOMIZATIONS_HELP_5: 'verberg van het Dashboard', + CUSTOMIZATIONS_HELP_6: 'remove from memory', // TODO translate SELECT_DEVICE: 'Selecteer een apparaat', SET_ALL: 'Alles aanzetten', OPTIONS: 'Opties', diff --git a/interface/src/i18n/no/index.ts b/interface/src/i18n/no/index.ts index cb80a638c..4e6c76525 100644 --- a/interface/src/i18n/no/index.ts +++ b/interface/src/i18n/no/index.ts @@ -157,6 +157,7 @@ const no: Translation = { CUSTOMIZATIONS_HELP_3: 'inaktiviser skriving', CUSTOMIZATIONS_HELP_4: 'ekskludere fra MQTT og API', CUSTOMIZATIONS_HELP_5: 'gjemme fra Dashboard', + CUSTOMIZATIONS_HELP_6: 'remove from memory', // TODO translate SELECT_DEVICE: 'Velg en enhet', SET_ALL: 'sett alle', OPTIONS: 'Alternativ', diff --git a/interface/src/i18n/pl/index.ts b/interface/src/i18n/pl/index.ts index a710c6267..a803a49ab 100644 --- a/interface/src/i18n/pl/index.ts +++ b/interface/src/i18n/pl/index.ts @@ -157,6 +157,7 @@ const pl: BaseTranslation = { CUSTOMIZATIONS_HELP_3: 'zablokuj akcje zapisu', CUSTOMIZATIONS_HELP_4: 'wyklucz z MQTT i API', CUSTOMIZATIONS_HELP_5: 'ukryj na pulpicie', + CUSTOMIZATIONS_HELP_6: 'remove from memory', // TODO translate SELECT_DEVICE: 'wybierz urządzenie', SET_ALL: 'Ustaw wszystko jako', OPTIONS: 'Opcje', diff --git a/interface/src/i18n/sv/index.ts b/interface/src/i18n/sv/index.ts index f9eea628f..3566ebeef 100644 --- a/interface/src/i18n/sv/index.ts +++ b/interface/src/i18n/sv/index.ts @@ -157,6 +157,7 @@ const sv: Translation = { CUSTOMIZATIONS_HELP_3: 'Inaktivera skrivningar', CUSTOMIZATIONS_HELP_4: 'Exkludera från MQTT & API', CUSTOMIZATIONS_HELP_5: 'Göm från Kontrollpanel', + CUSTOMIZATIONS_HELP_6: 'remove from memory', // TODO translate SELECT_DEVICE: 'Välj en enhet', SET_ALL: 'ställ in alla', OPTIONS: 'Alternativ', diff --git a/interface/src/project/OptionIcon.tsx b/interface/src/project/OptionIcon.tsx index 71be44969..220f331c4 100644 --- a/interface/src/project/OptionIcon.tsx +++ b/interface/src/project/OptionIcon.tsx @@ -13,9 +13,13 @@ import VisibilityOutlinedIcon from '@mui/icons-material/VisibilityOutlined'; import CommentsDisabledOutlinedIcon from '@mui/icons-material/CommentsDisabledOutlined'; import InsertCommentOutlinedIcon from '@mui/icons-material/InsertCommentOutlined'; -type OptionType = 'readonly' | 'web_exclude' | 'api_mqtt_exclude' | 'favorite'; +import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline'; +import DeleteForeverIcon from '@mui/icons-material/DeleteForever'; + +type OptionType = 'deleted' | 'readonly' | 'web_exclude' | 'api_mqtt_exclude' | 'favorite'; const OPTION_ICONS: { [type in OptionType]: [React.ComponentType, React.ComponentType] } = { + deleted: [DeleteForeverIcon, DeleteOutlineIcon], readonly: [EditOffOutlinedIcon, EditOutlinedIcon], web_exclude: [VisibilityOffOutlinedIcon, VisibilityOutlinedIcon], api_mqtt_exclude: [CommentsDisabledOutlinedIcon, InsertCommentOutlinedIcon], diff --git a/interface/src/project/SettingsCustomization.tsx b/interface/src/project/SettingsCustomization.tsx index ebd2cda5c..6f82248d6 100644 --- a/interface/src/project/SettingsCustomization.tsx +++ b/interface/src/project/SettingsCustomization.tsx @@ -25,14 +25,16 @@ import { useSnackbar } from 'notistack'; import WarningIcon from '@mui/icons-material/Warning'; import CancelIcon from '@mui/icons-material/Cancel'; +import DoneIcon from '@mui/icons-material/Done'; import SettingsBackupRestoreIcon from '@mui/icons-material/SettingsBackupRestore'; import SearchIcon from '@mui/icons-material/Search'; import FilterListIcon from '@mui/icons-material/FilterList'; +import PowerSettingsNewIcon from '@mui/icons-material/PowerSettingsNew'; import OptionIcon from './OptionIcon'; -import { ButtonRow, FormLoader, ValidatedTextField, SectionContent } from '../components'; +import { ButtonRow, FormLoader, ValidatedTextField, SectionContent, MessageBox } from '../components'; import * as EMSESP from './api'; @@ -42,15 +44,18 @@ import { DeviceShort, Devices, DeviceEntity, DeviceEntityMask } from './types'; import { useI18nContext } from '../i18n/i18n-react'; +import RestartMonitor from '../framework/system/RestartMonitor'; + export const APIURL = window.location.origin + '/api/'; const SettingsCustomization: FC = () => { const { LL } = useI18nContext(); - const { enqueueSnackbar } = useSnackbar(); const emptyDeviceEntity = { id: '', v: 0, n: '', cn: '', m: 0, w: false }; + const [restarting, setRestarting] = useState(false); + const [restartNeeded, setRestartNeeded] = useState(false); const [deviceEntities, setDeviceEntities] = useState([emptyDeviceEntity]); const [devices, setDevices] = useState(); const [errorMessage, setErrorMessage] = useState(); @@ -58,15 +63,13 @@ const SettingsCustomization: FC = () => { const [confirmReset, setConfirmReset] = useState(false); const [selectedFilters, setSelectedFilters] = useState(0); const [search, setSearch] = useState(''); - const [deviceEntity, setDeviceEntity] = useState(); - // eslint-disable-next-line const [masks, setMasks] = useState(() => ['']); const entities_theme = useTheme({ Table: ` - --data-table-library_grid-template-columns: 120px repeat(1, minmax(80px, 1fr)) 45px 45px 120px; + --data-table-library_grid-template-columns: 150px repeat(1, minmax(80px, 1fr)) 45px 45px 120px; `, BaseRow: ` font-size: 14px; @@ -210,6 +213,9 @@ const SettingsCustomization: FC = () => { if ((m & 8) === 8) { new_masks.push('8'); } + if ((m & 128) === 128) { + new_masks.push('128'); + } return new_masks; }; @@ -235,6 +241,7 @@ const SettingsCustomization: FC = () => { const selected_device = parseInt(event.target.value, 10); setSelectedDevice(selected_device); fetchDeviceEntities(devices?.devices[selected_device].i); + setRestartNeeded(false); } }; @@ -271,6 +278,15 @@ const SettingsCustomization: FC = () => { ); }; + const restart = async () => { + try { + await EMSESP.restart(); + setRestarting(true); + } catch (error) { + enqueueSnackbar(extractErrorMessage(error, LL.PROBLEM_UPDATING()), { variant: 'error' }); + } + }; + const saveCustomization = async () => { if (devices && deviceEntities && selectedDevice !== -1) { const masked_entities = getChanges(); @@ -289,6 +305,8 @@ const SettingsCustomization: FC = () => { }); if (response.status === 200) { enqueueSnackbar(LL.CUSTOMIZATIONS_SAVED(), { variant: 'success' }); + } else if (response.status === 201) { + setRestartNeeded(true); } else { enqueueSnackbar(LL.PROBLEM_UPDATING(), { variant: 'error' }); } @@ -312,7 +330,8 @@ const SettingsCustomization: FC = () => { ={LL.CUSTOMIZATIONS_HELP_2()}   ={LL.CUSTOMIZATIONS_HELP_3()}   ={LL.CUSTOMIZATIONS_HELP_4()}   - ={LL.CUSTOMIZATIONS_HELP_5()} + ={LL.CUSTOMIZATIONS_HELP_5()}   + ={LL.CUSTOMIZATIONS_HELP_6()} { + + + @@ -479,53 +501,63 @@ const SettingsCustomization: FC = () => { {tableList.map((de: DeviceEntity) => ( editEntity(de)}> - { - de.m = getMaskNumber(mask); - if (de.n === '' && de.m & DeviceEntityMask.DV_READONLY) { - de.m = de.m | DeviceEntityMask.DV_WEB_EXCLUDE; - } - if (de.m & DeviceEntityMask.DV_WEB_EXCLUDE) { - de.m = de.m & ~DeviceEntityMask.DV_FAVORITE; - } - setMasks(['']); - }} - > - - - - - - - - { + de.m = getMaskNumber(mask); + if (de.n === '' && de.m & DeviceEntityMask.DV_READONLY) { + de.m = de.m | DeviceEntityMask.DV_WEB_EXCLUDE; } - /> - - - - - + + if (de.m & DeviceEntityMask.DV_WEB_EXCLUDE) { + de.m = de.m & ~DeviceEntityMask.DV_FAVORITE; + } + setMasks(['']); + }} + > + + + + = 3}> + + + + + + + + + + + + + )} - {formatName(de)} - {!(de.m & DeviceEntityMask.DV_READONLY) && formatValue(de.mi)} - {!(de.m & DeviceEntityMask.DV_READONLY) && formatValue(de.ma)} - {formatValue(de.v)} + + {!deviceEntity && formatName(de)} + {!deviceEntity && !(de.m & DeviceEntityMask.DV_READONLY) && formatValue(de.mi)} + {!deviceEntity && !(de.m & DeviceEntityMask.DV_READONLY) && formatValue(de.ma)} + {!deviceEntity && formatValue(de.v)} ))} @@ -567,32 +599,41 @@ const SettingsCustomization: FC = () => { {renderDeviceList()} {renderDeviceData()} - - - + {restartNeeded && ( + + + + )} + {!restartNeeded && ( + + {num_changes !== 0 && ( - + + + )} + + + - - - - + )} {renderResetDialog()} ); @@ -655,11 +696,11 @@ const SettingsCustomization: FC = () => { {LL.CANCEL()} @@ -671,7 +712,7 @@ const SettingsCustomization: FC = () => { return ( - {renderContent()} + {restarting ? : renderContent()} {renderEditDialog()} ); diff --git a/interface/src/project/types.ts b/interface/src/project/types.ts index e2817af79..f769444d8 100644 --- a/interface/src/project/types.ts +++ b/interface/src/project/types.ts @@ -298,5 +298,6 @@ export enum DeviceEntityMask { DV_WEB_EXCLUDE = 1, DV_API_MQTT_EXCLUDE = 2, DV_READONLY = 4, - DV_FAVORITE = 8 + DV_FAVORITE = 8, + DV_DELETED = 128 } diff --git a/lib/framework/UploadFileService.cpp b/lib/framework/UploadFileService.cpp index 9ab97c683..49ebac21e 100644 --- a/lib/framework/UploadFileService.cpp +++ b/lib/framework/UploadFileService.cpp @@ -30,7 +30,7 @@ void UploadFileService::handleUpload(AsyncWebServerRequest * request, const Stri size_t fsize = request->contentLength(); is_firmware = false; - if ((extension == "bin") && (fsize > 1500000)) { + if ((extension == "bin") && (fsize > 1000000)) { is_firmware = true; } else if (extension == "json") { md5[0] = '\0'; // clear md5 diff --git a/mock-api/server.js b/mock-api/server.js index 39c8f6054..3ea2ca635 100644 --- a/mock-api/server.js +++ b/mock-api/server.js @@ -942,7 +942,10 @@ rest_server.post(EMSESP_DEVICEENTITIES_ENDPOINT, (req, res) => { function updateMask(entity, de, dd) { const current_mask = parseInt(entity.slice(0, 2), 16) - const shortname_with_customname = entity.slice(2) + + // strip of any min/max ranges + const shortname_with_customname = entity.slice(2).split('>')[0] + const shortname = shortname_with_customname.split('|')[0] const new_custom_name = shortname_with_customname.split('|')[1] diff --git a/src/devices/thermostat.cpp b/src/devices/thermostat.cpp index 1c2db6296..fdb4e1671 100644 --- a/src/devices/thermostat.cpp +++ b/src/devices/thermostat.cpp @@ -184,11 +184,6 @@ Thermostat::Thermostat(uint8_t device_type, uint8_t device_id, uint8_t product_i // register device values for common values (not heating circuit) register_device_values(); - if (System::test_set_all_active()) { - // if we're just dumping out values, create a single dummy hc1 - register_device_values_hc(std::make_shared(1, model)); // hc=1 - } - // query all the heating circuits. This is only done once. // The automatic fetch will from now on only update the active heating circuits for (uint8_t i = 0; i < monitor_typeids.size(); i++) { @@ -429,7 +424,7 @@ uint8_t Thermostat::HeatingCircuit::get_mode() const { } else if (mode == 0) { return HeatingCircuit::Mode::OFF; } - } else if (model == EMSdevice::EMS_DEVICE_FLAG_RC20) { + } else if (model == EMSdevice::EMS_DEVICE_FLAG_RC20 || model == EMSdevice::EMS_DEVICE_FLAG_RC30) { if (mode == 0) { return HeatingCircuit::Mode::OFF; } else if (mode == 1) { @@ -565,8 +560,8 @@ void Thermostat::process_RC20Set(std::shared_ptr telegram) { if (hc == nullptr) { return; } - has_update(telegram, hc->mode, 23); // 0:off, 1:manual, 2:auto - // has_update(telegram, hc->nofrosttemp, 24); // guess, not confirmed yet, maybe nighttemp? + has_update(telegram, hc->mode, 23); // 0:off, 1:manual, 2:auto + has_update(telegram, hc->nofrosttemp, 24); // for mode off // has_update(telegram, hc->tempautotemp, 28); // no need to read this has_update(telegram, hc->manualtemp, 29); } @@ -578,7 +573,7 @@ void Thermostat::process_RC20Temp(std::shared_ptr telegram) { if (hc == nullptr) { return; } - has_update(telegram, hc->nighttemp, 3); // 0:off, 1:manual, 2:auto + has_update(telegram, hc->nighttemp, 3); has_update(telegram, hc->daylowtemp, 4); has_update(telegram, hc->daymidtemp, 5); has_update(telegram, hc->daytemp, 6); @@ -1136,6 +1131,7 @@ void Thermostat::process_RC300Floordry(std::shared_ptr telegram) } // type 0x41 - data from the RC30 thermostat(0x10) - 14 bytes long +// RC30Monitor(0x41), data: 80 20 00 AC 00 00 00 02 00 05 09 00 AC 00 void Thermostat::process_RC30Monitor(std::shared_ptr telegram) { std::shared_ptr hc = heating_circuit(telegram); if (hc == nullptr) { @@ -1149,6 +1145,8 @@ void Thermostat::process_RC30Monitor(std::shared_ptr telegram) { } // type 0xA7 - for reading the mode from the RC30 thermostat (0x10) and all the installation settings +// RC30Set(0xA7), data: 01 00 FF F6 01 06 00 01 0D 00 00 FF FF 01 02 02 02 00 00 05 1F 05 1F 01 0E 00 FF +// RC30Set(0xA7), data: 00 00 20 02 (offset 27) void Thermostat::process_RC30Set(std::shared_ptr telegram) { std::shared_ptr hc = heating_circuit(telegram); if (hc == nullptr) { @@ -1163,12 +1161,15 @@ void Thermostat::process_RC30Set(std::shared_ptr telegram) { has_update(telegram, mixingvalves_, 17); // Number of Mixing Valves: (0x00=0, 0x01=1, 0x02=2) has_update(telegram, brightness_, 18); // Screen brightness 0F=dark F1=light has_update(telegram, hc->mode, 23); - has_update(telegram, offtemp_, 24); // Set Temperature when mode is Off / 10 (e.g.: 0x0F = 7.5 degrees Celsius) - has_update(telegram, heatingpid_, 25); // PID setting 00=1 01=2 02=3 - has_update(telegram, preheating_, 26); // Preheating in the clock program: (0x00 = off, 0xFF = on) + has_update(telegram, hc->nofrosttemp, 24); // Set Temperature when mode is Off / 10 (e.g.: 0x0F = 7.5 degrees Celsius) + has_update(telegram, heatingpid_, 25); // PID setting 00=1 01=2 02=3 + has_update(telegram, preheating_, 26); // Preheating in the clock program: (0x00 = off, 0xFF = on) + has_update(telegram, hc->tempautotemp, 28); // is * 2 + has_update(telegram, hc->manualtemp, 29); // manualtemp is * 2 } // type 0x40 (HC1) - for reading the operating mode from the RC30 thermostat (0x10) +// RC30Temp(0x40), data: 01 01 02 20 24 28 2A 1E 0E 00 01 5A 32 05 4B 2D 00 28 00 3C FF 11 00 05 00 void Thermostat::process_RC30Temp(std::shared_ptr telegram) { // check to see we have a valid type. heating: 1 radiator, 2 convectors, 3 floors if (telegram->offset == 0 && telegram->message_data[0] == 0x00) { @@ -2037,18 +2038,6 @@ bool Thermostat::set_preheating(const char * value, const int8_t id) { return true; } -bool Thermostat::set_offtemp(const char * value, const int8_t id) { - int ot; - if (!Helpers::value2temperature(value, ot, true)) { - return false; - } - - auto t = (int8_t)(ot * 2); - write_command(EMS_TYPE_RC30Settings, 24, t, EMS_TYPE_RC30Settings); - - return true; -} - bool Thermostat::set_mixingvalves(const char * value, const int8_t id) { int m; if (!Helpers::value2number(value, m, 0, 2)) { @@ -2261,11 +2250,11 @@ bool Thermostat::set_mode(const char * value, const int8_t id) { mode_list = FL_(enum_mode6); break; case EMSdevice::EMS_DEVICE_FLAG_RC20: - case EMSdevice::EMS_DEVICE_FLAG_RC20_N: + case EMSdevice::EMS_DEVICE_FLAG_RC30: mode_list = FL_(enum_mode2); break; + case EMSdevice::EMS_DEVICE_FLAG_RC20_N: case EMSdevice::EMS_DEVICE_FLAG_RC25: - case EMSdevice::EMS_DEVICE_FLAG_RC30: case EMSdevice::EMS_DEVICE_FLAG_RC35: case EMSdevice::EMS_DEVICE_FLAG_RC30_N: mode_list = FL_(enum_mode3); @@ -2286,20 +2275,12 @@ bool Thermostat::set_mode(const char * value, const int8_t id) { uint8_t enum_index = 0; - // check for a mode number as a string with a single digit (0..9) - if (value[0] >= '0' && value[0] <= '9') { - enum_index = value[0] - '0'; - if (enum_index >= Helpers::count_items(mode_list)) { - return false; // invalid number, not in enum - } - } else { - // check for the mode being a full string name - if (!Helpers::value2enum(value, enum_index, mode_list)) { - mode_list = FL_(enum_mode_ha); - if (Mqtt::ha_enabled() && !Helpers::value2enum(value, enum_index, mode_list)) { - LOG_WARNING("wrong mode: %s", value); - return false; // not found - } + // check for the mode being a full string name or single digit + if (!Helpers::value2enum(value, enum_index, mode_list)) { + mode_list = FL_(enum_mode_ha); + if (Mqtt::ha_enabled() && !Helpers::value2enum(value, enum_index, mode_list)) { + LOG_WARNING("wrong mode: %s", value); + return false; // not found } } @@ -2916,6 +2897,7 @@ bool Thermostat::set_temperature(const float temperature, const uint8_t mode, co default: break; } + } else if (model == EMS_DEVICE_FLAG_RC20) { switch (mode) { case HeatingCircuit::Mode::NIGHT: @@ -2939,10 +2921,17 @@ bool Thermostat::set_temperature(const float temperature, const uint8_t mode, co validate_typeid = set_typeid; break; case HeatingCircuit::Mode::MANUAL: - offset = EMS_OFFSET_RC20Set_temp_manual; + offset = EMS_OFFSET_RC20Set_temp_manual; + validate_typeid = set_typeid; + break; + case HeatingCircuit::Mode::OFF: + offset = EMS_OFFSET_RC20Set_temp_off; + validate_typeid = set_typeid; break; case HeatingCircuit::Mode::AUTO: - if (hc->get_mode() == HeatingCircuit::Mode::MANUAL) { + if (hc->get_mode() == HeatingCircuit::Mode::OFF) { + offset = EMS_OFFSET_RC20Set_temp_off; + } else if (hc->get_mode() == HeatingCircuit::Mode::MANUAL) { offset = EMS_OFFSET_RC20Set_temp_manual; } else { offset = EMS_OFFSET_RC20Set_temp_auto; @@ -2954,28 +2943,51 @@ bool Thermostat::set_temperature(const float temperature, const uint8_t mode, co } else if (model == EMS_DEVICE_FLAG_RC30) { switch (mode) { - case HeatingCircuit::Mode::NIGHT: // change the night temp - set_typeid = curve_typeids[hc->hc()]; - offset = EMS_OFFSET_RC30Temp_temp_night; + case HeatingCircuit::Mode::OFF: + offset = EMS_OFFSET_RC30Set_temp_off; + validate_typeid = set_typeid; break; - case HeatingCircuit::Mode::DAYLOW: // change the offset temp - set_typeid = curve_typeids[hc->hc()]; - offset = EMS_OFFSET_RC30Temp_temp_daylow; + case HeatingCircuit::Mode::MANUAL: + offset = EMS_OFFSET_RC30Set_temp_manual; + validate_typeid = set_typeid; break; - case HeatingCircuit::Mode::DAYMID: // change the offset of flowtemp - set_typeid = curve_typeids[hc->hc()]; - offset = EMS_OFFSET_RC30Temp_temp_daymid; + case HeatingCircuit::Mode::TEMPAUTO: + offset = EMS_OFFSET_RC30Set_temp_auto; + validate_typeid = set_typeid; break; - case HeatingCircuit::Mode::DAY: // change the day temp - set_typeid = curve_typeids[hc->hc()]; - offset = EMS_OFFSET_RC30Temp_temp_day; + case HeatingCircuit::Mode::NIGHT: + set_typeid = curve_typeids[hc->hc()]; + offset = EMS_OFFSET_RC30Temp_temp_night; + validate_typeid = set_typeid; break; - case HeatingCircuit::Mode::HOLIDAY: // change the holiday temp - set_typeid = curve_typeids[hc->hc()]; - offset = EMS_OFFSET_RC30Temp_temp_holiday; + case HeatingCircuit::Mode::DAYLOW: + set_typeid = curve_typeids[hc->hc()]; + offset = EMS_OFFSET_RC30Temp_temp_daylow; + validate_typeid = set_typeid; + break; + case HeatingCircuit::Mode::DAYMID: + set_typeid = curve_typeids[hc->hc()]; + offset = EMS_OFFSET_RC30Temp_temp_daymid; + validate_typeid = set_typeid; + break; + case HeatingCircuit::Mode::DAY: + set_typeid = curve_typeids[hc->hc()]; + offset = EMS_OFFSET_RC30Temp_temp_day; + validate_typeid = set_typeid; + break; + case HeatingCircuit::Mode::HOLIDAY: + set_typeid = curve_typeids[hc->hc()]; + offset = EMS_OFFSET_RC30Temp_temp_holiday; + validate_typeid = set_typeid; break; default: - offset = EMS_OFFSET_RC30Set_temp; + if (hc->get_mode() == HeatingCircuit::Mode::OFF) { + offset = EMS_OFFSET_RC30Set_temp_off; + } else if (hc->get_mode() == HeatingCircuit::Mode::MANUAL) { + offset = EMS_OFFSET_RC30Set_temp_manual; + } else { + offset = EMS_OFFSET_RC30Set_temp_auto; + } break; } @@ -3298,101 +3310,8 @@ bool Thermostat::set_temperature_value(const char * value, const int8_t id, cons uint8_t hc_num = (id == -1) ? AUTO_HEATING_CIRCUIT : id; if (Helpers::value2temperature(value, f, relative)) { return set_temperature(f, mode, hc_num); - } else { - return false; } -} - -bool Thermostat::set_temp(const char * value, const int8_t id) { - return set_temperature_value(value, id, HeatingCircuit::Mode::AUTO); -} - -bool Thermostat::set_nighttemp(const char * value, const int8_t id) { - return set_temperature_value(value, id, HeatingCircuit::Mode::NIGHT); -} - -bool Thermostat::set_daytemp(const char * value, const int8_t id) { - return set_temperature_value(value, id, HeatingCircuit::Mode::DAY); -} - -bool Thermostat::set_daylowtemp(const char * value, const int8_t id) { - return set_temperature_value(value, id, HeatingCircuit::Mode::DAYLOW); -} - -bool Thermostat::set_daymidtemp(const char * value, const int8_t id) { - return set_temperature_value(value, id, HeatingCircuit::Mode::DAYMID); -} - -bool Thermostat::set_comforttemp(const char * value, const int8_t id) { - return set_temperature_value(value, id, HeatingCircuit::Mode::COMFORT); -} - -bool Thermostat::set_nofrosttemp(const char * value, const int8_t id) { - return set_temperature_value(value, id, HeatingCircuit::Mode::NOFROST); -} - -bool Thermostat::set_ecotemp(const char * value, const int8_t id) { - return set_temperature_value(value, id, HeatingCircuit::Mode::ECO); -} - -bool Thermostat::set_heattemp(const char * value, const int8_t id) { - return set_temperature_value(value, id, HeatingCircuit::Mode::HEAT); -} - -bool Thermostat::set_summertemp(const char * value, const int8_t id) { - return set_temperature_value(value, id, HeatingCircuit::Mode::SUMMER); -} - -bool Thermostat::set_designtemp(const char * value, const int8_t id) { - return set_temperature_value(value, id, HeatingCircuit::Mode::DESIGN); -} - -bool Thermostat::set_offsettemp(const char * value, const int8_t id) { - return set_temperature_value(value, id, HeatingCircuit::Mode::OFFSET); -} - -bool Thermostat::set_holidaytemp(const char * value, const int8_t id) { - return set_temperature_value(value, id, HeatingCircuit::Mode::HOLIDAY); -} - -bool Thermostat::set_manualtemp(const char * value, const int8_t id) { - return set_temperature_value(value, id, HeatingCircuit::Mode::MANUAL); -} - -bool Thermostat::set_tempautotemp(const char * value, const int8_t id) { - return set_temperature_value(value, id, HeatingCircuit::Mode::TEMPAUTO); -} - -bool Thermostat::set_noreducetemp(const char * value, const int8_t id) { - return set_temperature_value(value, id, HeatingCircuit::Mode::NOREDUCE); -} - -bool Thermostat::set_reducetemp(const char * value, const int8_t id) { - return set_temperature_value(value, id, HeatingCircuit::Mode::REDUCE); -} - -bool Thermostat::set_vacreducetemp(const char * value, const int8_t id) { - return set_temperature_value(value, id, HeatingCircuit::Mode::VACREDUCE); -} - -bool Thermostat::set_flowtempoffset(const char * value, const int8_t id) { - return set_temperature_value(value, id, HeatingCircuit::Mode::FLOWOFFSET, true); -} - -bool Thermostat::set_maxflowtemp(const char * value, const int8_t id) { - return set_temperature_value(value, id, HeatingCircuit::Mode::MAXFLOW); -} - -bool Thermostat::set_minflowtemp(const char * value, const int8_t id) { - return set_temperature_value(value, id, HeatingCircuit::Mode::MINFLOW); -} - -bool Thermostat::set_roominfluence(const char * value, const int8_t id) { - return set_temperature_value(value, id, HeatingCircuit::Mode::ROOMINFLUENCE, true); -} - -bool Thermostat::set_remoteseltemp(const char * value, const int8_t id) { - return set_temperature_value(value, id, HeatingCircuit::Mode::REMOTESELTEMP); + return false; } // register main device values, top level for all thermostats (excluding heating circuits) @@ -3657,15 +3576,6 @@ void Thermostat::register_device_values() { FL_(ibaCalIntTemperature), DeviceValueUOM::DEGREES_R, MAKE_CF_CB(set_calinttemp)); - register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, - &offtemp_, - DeviceValueType::UINT, - DeviceValueNumOp::DV_NUMOP_DIV2, - FL_(offtemp), - DeviceValueUOM::DEGREES, - MAKE_CF_CB(set_offtemp), - 5, - 30); register_device_value( DeviceValueTAG::TAG_DEVICE_DATA_WW, &wwMode_, DeviceValueType::ENUM, FL_(enum_wwMode3), FL_(wwMode), DeviceValueUOM::NONE, MAKE_CF_CB(set_wwmode)); register_device_value(DeviceValueTAG::TAG_DEVICE_DATA_WW, @@ -4055,6 +3965,11 @@ void Thermostat::register_device_values() { register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &dateTime_, DeviceValueType::STRING, FL_(dateTime), DeviceValueUOM::NONE); // can't set datetime break; } + +#if defined(EMSESP_STANDALONE_DUMP) + // if we're just dumping out values, create a single dummy hc + register_device_values_hc(std::make_shared(1, this->model())); // hc=1 +#endif } // registers the values for a heating circuit @@ -4180,6 +4095,8 @@ void Thermostat::register_device_values_hc(std::shared_ptrmode, DeviceValueType::ENUM, FL_(enum_mode2), FL_(mode), DeviceValueUOM::NONE, MAKE_CF_CB(set_mode)); register_device_value( tag, &hc->manualtemp, DeviceValueType::UINT, DeviceValueNumOp::DV_NUMOP_DIV2, FL_(manualtemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_manualtemp)); + register_device_value( + tag, &hc->nofrosttemp, DeviceValueType::UINT, DeviceValueNumOp::DV_NUMOP_DIV2, FL_(offtemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_offtemp)); register_device_value( tag, &hc->daylowtemp, DeviceValueType::UINT, DeviceValueNumOp::DV_NUMOP_DIV2, FL_(daylowtemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_daylowtemp)); register_device_value( @@ -4192,7 +4109,7 @@ void Thermostat::register_device_values_hc(std::shared_ptrswitchtime1, DeviceValueType::STRING, FL_(tpl_switchtime1), FL_(switchtime), DeviceValueUOM::NONE, MAKE_CF_CB(set_switchtime1)); break; case EMS_DEVICE_FLAG_RC20_N: - register_device_value(tag, &hc->mode, DeviceValueType::ENUM, FL_(enum_mode2), FL_(mode), DeviceValueUOM::NONE, MAKE_CF_CB(set_mode)); + register_device_value(tag, &hc->mode, DeviceValueType::ENUM, FL_(enum_mode3), FL_(mode), DeviceValueUOM::NONE, MAKE_CF_CB(set_mode)); register_device_value(tag, &hc->modetype, DeviceValueType::ENUM, FL_(enum_modetype3), FL_(modetype), DeviceValueUOM::NONE); register_device_value( tag, &hc->daytemp, DeviceValueType::UINT, DeviceValueNumOp::DV_NUMOP_DIV2, FL_(daytemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_daytemp)); @@ -4237,7 +4154,7 @@ void Thermostat::register_device_values_hc(std::shared_ptrsummermode, DeviceValueType::ENUM, FL_(enum_summer), FL_(summermode), DeviceValueUOM::NONE); break; case EMS_DEVICE_FLAG_RC30: - register_device_value(tag, &hc->mode, DeviceValueType::ENUM, FL_(enum_mode3), FL_(mode), DeviceValueUOM::NONE, MAKE_CF_CB(set_mode)); + register_device_value(tag, &hc->mode, DeviceValueType::ENUM, FL_(enum_mode2), FL_(mode), DeviceValueUOM::NONE, MAKE_CF_CB(set_mode)); register_device_value(tag, &hc->holiday, DeviceValueType::STRING, FL_(tpl_holidays), FL_(holidays), DeviceValueUOM::NONE, MAKE_CF_CB(set_holiday)); register_device_value(tag, &hc->vacation, DeviceValueType::STRING, FL_(tpl_holidays), FL_(vacations), DeviceValueUOM::NONE, MAKE_CF_CB(set_vacation)); register_device_value(tag, &hc->program, DeviceValueType::ENUM, FL_(enum_progMode2), FL_(program), DeviceValueUOM::NONE, MAKE_CF_CB(set_program)); @@ -4264,6 +4181,10 @@ void Thermostat::register_device_values_hc(std::shared_ptrdaymidtemp, DeviceValueType::UINT, DeviceValueNumOp::DV_NUMOP_DIV2, FL_(daymidtemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_daymidtemp)); register_device_value( tag, &hc->daytemp, DeviceValueType::UINT, DeviceValueNumOp::DV_NUMOP_DIV2, FL_(dayhightemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_daytemp)); + register_device_value( + tag, &hc->manualtemp, DeviceValueType::UINT, DeviceValueNumOp::DV_NUMOP_DIV2, FL_(manualtemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_manualtemp)); + register_device_value( + tag, &hc->nofrosttemp, DeviceValueType::UINT, DeviceValueNumOp::DV_NUMOP_DIV2, FL_(offtemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_offtemp)); break; case EMS_DEVICE_FLAG_RC30_N: case EMS_DEVICE_FLAG_RC35: diff --git a/src/devices/thermostat.h b/src/devices/thermostat.h index 57ef89c4e..700668093 100644 --- a/src/devices/thermostat.h +++ b/src/devices/thermostat.h @@ -191,7 +191,6 @@ class Thermostat : public EMSdevice { int8_t brightness_; // Screen brightness 0F=dark F1=light uint8_t preheating_; // Preheating in the clock program: (0x00 = off, 0xFF = on) uint8_t autodst_; // Automatic change Daylight Saving time: (0x00 = off, 0xFF = on) - uint8_t offtemp_; // Set Temperature when mode is Off / 10 (e.g.: 0x0F = 7.5 degrees Celsius) uint8_t mixingvalves_; // Number of Mixing Valves: (0x00=0, 0x01=1, 0x02=2) int8_t dampedoutdoortemp_; @@ -255,6 +254,7 @@ class Thermostat : public EMSdevice { static constexpr uint8_t EMS_OFFSET_RC20StatusMessage_setpoint = 1; // setpoint temp static constexpr uint8_t EMS_OFFSET_RC20StatusMessage_curr = 2; // current temp static constexpr uint8_t EMS_OFFSET_RC20Set_mode = 23; // position of thermostat mode + static constexpr uint8_t EMS_OFFSET_RC20Set_temp_off = 24; // position of thermostat setpoint mode:off static constexpr uint8_t EMS_OFFSET_RC20Set_temp_auto = 28; // position of thermostat setpoint temperature static constexpr uint8_t EMS_OFFSET_RC20Set_temp_manual = 29; // position of thermostat setpoint temperature @@ -265,7 +265,9 @@ class Thermostat : public EMSdevice { static constexpr uint8_t EMS_OFFSET_RC30StatusMessage_setpoint = 1; // setpoint temp static constexpr uint8_t EMS_OFFSET_RC30StatusMessage_curr = 2; // current temp static constexpr uint8_t EMS_OFFSET_RC30Set_mode = 23; // position of thermostat mode - static constexpr uint8_t EMS_OFFSET_RC30Set_temp = 28; // position of thermostat setpoint temperature + static constexpr uint8_t EMS_OFFSET_RC30Set_temp_off = 24; // position of thermostat setpoint mode:off + static constexpr uint8_t EMS_OFFSET_RC30Set_temp_auto = 28; // position of thermostat setpoint temperature + static constexpr uint8_t EMS_OFFSET_RC30Set_temp_manual = 29; // position of thermostat setpoint temperature for manual mode static constexpr uint8_t EMS_OFFSET_RC30Temp_temp_night = 3; // position of thermostat setpoint temperature for night time (T1) static constexpr uint8_t EMS_OFFSET_RC30Temp_temp_daylow = 4; // position of thermostat setpoint temperature for daylow time (T2) static constexpr uint8_t EMS_OFFSET_RC30Temp_temp_daymid = 5; // position of thermostat setpoint temperature for daymid time (T3) @@ -408,33 +410,10 @@ class Thermostat : public EMSdevice { bool set_pause(const char * value, const int8_t id); bool set_party(const char * value, const int8_t id); bool set_summermode(const char * value, const int8_t id); - - bool set_temp(const char * value, const int8_t id); - bool set_nighttemp(const char * value, const int8_t id); - bool set_daytemp(const char * value, const int8_t id); - bool set_daylowtemp(const char * value, const int8_t id); - bool set_daymidtemp(const char * value, const int8_t id); - bool set_comforttemp(const char * value, const int8_t id); - bool set_nofrosttemp(const char * value, const int8_t id); - bool set_ecotemp(const char * value, const int8_t id); - bool set_heattemp(const char * value, const int8_t id); - bool set_summertemp(const char * value, const int8_t id); - bool set_designtemp(const char * value, const int8_t id); - bool set_offsettemp(const char * value, const int8_t id); - bool set_holidaytemp(const char * value, const int8_t id); - bool set_manualtemp(const char * value, const int8_t id); - bool set_tempautotemp(const char * value, const int8_t id); - bool set_noreducetemp(const char * value, const int8_t id); - bool set_reducetemp(const char * value, const int8_t id); - bool set_vacreducetemp(const char * value, const int8_t id); bool set_vacreducemode(const char * value, const int8_t id); bool set_nofrostmode(const char * value, const int8_t id); bool set_remotetemp(const char * value, const int8_t id); - bool set_roominfluence(const char * value, const int8_t id); bool set_roominfl_factor(const char * value, const int8_t id); - bool set_flowtempoffset(const char * value, const int8_t id); - bool set_minflowtemp(const char * value, const int8_t id); - bool set_maxflowtemp(const char * value, const int8_t id); bool set_reducemode(const char * value, const int8_t id); bool set_switchtime1(const char * value, const int8_t id); bool set_switchtime2(const char * value, const int8_t id); @@ -443,7 +422,79 @@ class Thermostat : public EMSdevice { bool set_wwprio(const char * value, const int8_t id); bool set_fastheatup(const char * value, const int8_t id); bool set_switchonoptimization(const char * value, const int8_t id); - bool set_remoteseltemp(const char * value, const int8_t id); + + inline bool set_temp(const char * value, const int8_t id) { + return set_temperature_value(value, id, HeatingCircuit::Mode::AUTO); + } + inline bool set_nighttemp(const char * value, const int8_t id) { + return set_temperature_value(value, id, HeatingCircuit::Mode::NIGHT); + } + inline bool set_daytemp(const char * value, const int8_t id) { + return set_temperature_value(value, id, HeatingCircuit::Mode::DAY); + } + inline bool set_daylowtemp(const char * value, const int8_t id) { + return set_temperature_value(value, id, HeatingCircuit::Mode::DAYLOW); + } + inline bool set_daymidtemp(const char * value, const int8_t id) { + return set_temperature_value(value, id, HeatingCircuit::Mode::DAYMID); + } + inline bool set_comforttemp(const char * value, const int8_t id) { + return set_temperature_value(value, id, HeatingCircuit::Mode::COMFORT); + } + inline bool set_nofrosttemp(const char * value, const int8_t id) { + return set_temperature_value(value, id, HeatingCircuit::Mode::NOFROST); + } + inline bool set_ecotemp(const char * value, const int8_t id) { + return set_temperature_value(value, id, HeatingCircuit::Mode::ECO); + } + inline bool set_heattemp(const char * value, const int8_t id) { + return set_temperature_value(value, id, HeatingCircuit::Mode::HEAT); + } + inline bool set_summertemp(const char * value, const int8_t id) { + return set_temperature_value(value, id, HeatingCircuit::Mode::SUMMER); + } + inline bool set_designtemp(const char * value, const int8_t id) { + return set_temperature_value(value, id, HeatingCircuit::Mode::DESIGN); + } + inline bool set_offsettemp(const char * value, const int8_t id) { + return set_temperature_value(value, id, HeatingCircuit::Mode::OFFSET); + } + inline bool set_holidaytemp(const char * value, const int8_t id) { + return set_temperature_value(value, id, HeatingCircuit::Mode::HOLIDAY); + } + inline bool set_offtemp(const char * value, const int8_t id) { + return set_temperature_value(value, id, HeatingCircuit::Mode::OFF); + } + inline bool set_manualtemp(const char * value, const int8_t id) { + return set_temperature_value(value, id, HeatingCircuit::Mode::MANUAL); + } + inline bool set_tempautotemp(const char * value, const int8_t id) { + return set_temperature_value(value, id, HeatingCircuit::Mode::TEMPAUTO); + } + inline bool set_noreducetemp(const char * value, const int8_t id) { + return set_temperature_value(value, id, HeatingCircuit::Mode::NOREDUCE); + } + inline bool set_reducetemp(const char * value, const int8_t id) { + return set_temperature_value(value, id, HeatingCircuit::Mode::REDUCE); + } + inline bool set_vacreducetemp(const char * value, const int8_t id) { + return set_temperature_value(value, id, HeatingCircuit::Mode::VACREDUCE); + } + inline bool set_flowtempoffset(const char * value, const int8_t id) { + return set_temperature_value(value, id, HeatingCircuit::Mode::FLOWOFFSET, true); + } + inline bool set_maxflowtemp(const char * value, const int8_t id) { + return set_temperature_value(value, id, HeatingCircuit::Mode::MAXFLOW); + } + inline bool set_minflowtemp(const char * value, const int8_t id) { + return set_temperature_value(value, id, HeatingCircuit::Mode::MINFLOW); + } + inline bool set_roominfluence(const char * value, const int8_t id) { + return set_temperature_value(value, id, HeatingCircuit::Mode::ROOMINFLUENCE, true); + } + inline bool set_remoteseltemp(const char * value, const int8_t id) { + return set_temperature_value(value, id, HeatingCircuit::Mode::REMOTESELTEMP); + } // set functions - these don't use the id/hc, the parameters are ignored bool set_wwmode(const char * value, const int8_t id); @@ -487,7 +538,6 @@ class Thermostat : public EMSdevice { bool set_autodst(const char * value, const int8_t id); bool set_preheating(const char * value, const int8_t id); bool set_mixingvalves(const char * value, const int8_t id); - bool set_offtemp(const char * value, const int8_t id); bool set_hybridStrategy(const char * value, const int8_t id); bool set_switchOverTemp(const char * value, const int8_t id); diff --git a/src/emsdevice.cpp b/src/emsdevice.cpp index 3f3965ddb..490512b38 100644 --- a/src/emsdevice.cpp +++ b/src/emsdevice.cpp @@ -40,7 +40,17 @@ bool EMSdevice::has_entities() const { return true; } } - return false; + + bool found = false; + EMSESP::webCustomizationService.read([&](WebCustomization & settings) { + for (EntityCustomization entityCustomization : settings.entityCustomizations) { + if (entityCustomization.device_id == device_id() && entityCustomization.entity_ids.size()) { + found = true; + break; + } + } + }); + return found; } const char * EMSdevice::tag_to_string(uint8_t tag, const bool translate) { @@ -521,14 +531,9 @@ void EMSdevice::add_device_value(uint8_t tag, for (std::string entity_id : entityCustomization.entity_ids) { // if there is an appended custom name, strip it to get the true entity name // and extract the new custom name - std::string shortname; auto custom_name_pos = entity_id.find('|'); bool has_custom_name = (custom_name_pos != std::string::npos); - if (has_custom_name) { - shortname = entity_id.substr(2, custom_name_pos - 2); - } else { - shortname = entity_id.substr(2); - } + std::string shortname = has_custom_name ? entity_id.substr(2, custom_name_pos - 2) : entity_id.substr(2); // we found the device entity if (shortname == entity) { @@ -1029,6 +1034,23 @@ void EMSdevice::generate_values_web_customization(JsonArray & output) { } } } + + EMSESP::webCustomizationService.read([&](WebCustomization & settings) { + for (EntityCustomization entityCustomization : settings.entityCustomizations) { + if ((entityCustomization.device_id == device_id())) { + for (std::string entity_id : entityCustomization.entity_ids) { + uint8_t mask = Helpers::hextoint(entity_id.substr(0, 2).c_str()); + if (mask & 0x80) { + JsonObject obj = output.createNestedObject(); + obj["id"] = DeviceValue::get_name(entity_id); + obj["m"] = mask; + obj["w"] = false; + } + } + break; + } + } + }); } void EMSdevice::set_climate_minmax(uint8_t tag, int16_t min, uint16_t max) { @@ -1057,14 +1079,9 @@ void EMSdevice::setCustomEntity(const std::string & entity_id) { } // extra shortname - std::string shortname; auto custom_name_pos = entity_id.find('|'); bool has_custom_name = (custom_name_pos != std::string::npos); - if (has_custom_name) { - shortname = entity_id.substr(2, custom_name_pos - 2); - } else { - shortname = entity_id.substr(2); - } + std::string shortname = has_custom_name ? entity_id.substr(2, custom_name_pos - 2) : entity_id.substr(2); if (entity_name == shortname) { // check the masks @@ -1106,10 +1123,25 @@ void EMSdevice::setCustomEntity(const std::string & entity_id) { // populate a string vector with entities that have masks set or have a custom name void EMSdevice::getCustomEntities(std::vector & entity_ids) { for (const auto & dv : devicevalues_) { - std::string entity_name = dv.tag < DeviceValueTAG::TAG_HC1 ? dv.short_name : std::string(tag_to_mqtt(dv.tag)) + "/" + dv.short_name; - uint8_t mask = dv.state >> 4; + char name[100]; + name[0] = '\0'; + if (dv.has_tag()) { + // prefix tag + strcpy(name, tag_to_mqtt(dv.tag)); + strcat(name, "/"); + } + strcat(name, dv.short_name); + std::string entity_name = name; - if (mask || !dv.custom_fullname.empty()) { + uint8_t mask = dv.state >> 4; + bool is_set = false; + for (auto & eid : entity_ids) { + if (DeviceValue::get_name(eid) == entity_name) { + is_set = true; + break; + } + } + if (!is_set && (mask || !dv.custom_fullname.empty())) { if (dv.custom_fullname.empty()) { entity_ids.push_back(Helpers::hextoa(mask, false) + entity_name); } else { diff --git a/src/emsdevicevalue.cpp b/src/emsdevicevalue.cpp index 4516618d3..0feb23ef9 100644 --- a/src/emsdevicevalue.cpp +++ b/src/emsdevicevalue.cpp @@ -379,4 +379,12 @@ std::string DeviceValue::get_fullname() const { return customname; } +std::string DeviceValue::get_name(std::string & entity) { + auto pos = entity.find('|'); + if (pos != std::string::npos) { + return entity.substr(2, pos - 2); + } + return entity.substr(2); +} + } // namespace emsesp diff --git a/src/emsdevicevalue.h b/src/emsdevicevalue.h index 3a8b07a4f..a9a87d9f7 100644 --- a/src/emsdevicevalue.h +++ b/src/emsdevicevalue.h @@ -186,11 +186,12 @@ class DeviceValue { bool get_min_max(int16_t & dv_set_min, uint16_t & dv_set_max); - void set_custom_minmax(); - bool get_custom_min(int16_t & val); - bool get_custom_max(uint16_t & val); - std::string get_custom_fullname() const; - std::string get_fullname() const; + void set_custom_minmax(); + bool get_custom_min(int16_t & val); + bool get_custom_max(uint16_t & val); + std::string get_custom_fullname() const; + std::string get_fullname() const; + static std::string get_name(std::string & entity); // dv state flags void add_state(uint8_t s) { diff --git a/src/locale_common.h b/src/locale_common.h index ecc91faa9..d0e3e3484 100644 --- a/src/locale_common.h +++ b/src/locale_common.h @@ -268,8 +268,8 @@ MAKE_PSTR_ENUM(enum_summer, FL_(winter), FL_(summer)) MAKE_PSTR_ENUM(enum_operatingstate, FL_(heating), FL_(off), FL_(cooling)) MAKE_PSTR_ENUM(enum_mode, FL_(manual), FL_(auto)) // RC100, RC300, RC310 -MAKE_PSTR_ENUM(enum_mode2, FL_(off), FL_(manual), FL_(auto)) // RC20 -MAKE_PSTR_ENUM(enum_mode3, FL_(night), FL_(day), FL_(auto)) // RC35, RC30, RC25 +MAKE_PSTR_ENUM(enum_mode2, FL_(off), FL_(manual), FL_(auto)) // RC20, RC30 +MAKE_PSTR_ENUM(enum_mode3, FL_(night), FL_(day), FL_(auto)) // RC35, RC30_N, RC25, RC20_N MAKE_PSTR_ENUM(enum_mode4, FL_(nofrost), FL_(eco), FL_(heat), FL_(auto)) // JUNKERS MAKE_PSTR_ENUM(enum_mode5, FL_(auto), FL_(off)) // CRF MAKE_PSTR_ENUM(enum_mode6, FL_(nofrost), FL_(night), FL_(day)) // RC10 diff --git a/src/mqtt.cpp b/src/mqtt.cpp index 5043703cd..8cdefedfb 100644 --- a/src/mqtt.cpp +++ b/src/mqtt.cpp @@ -635,10 +635,7 @@ void Mqtt::ha_status() { dev["mf"] = "proddy"; dev["mdl"] = "EMS-ESP"; #ifndef EMSESP_STANDALONE - if (EMSESP::system_.ethernet_connected()) - dev["cu"] = "http://" + ETH.localIP().toString(); - else - dev["cu"] = "http://" + WiFi.localIP().toString(); + dev["cu"] = "http://" + (EMSESP::system_.ethernet_connected() ? ETH.localIP().toString() : WiFi.localIP().toString()); #endif JsonArray ids = dev.createNestedArray("ids"); ids.add("ems-esp"); diff --git a/src/version.h b/src/version.h index 0a271aaf2..ff7ceafb2 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define EMSESP_APP_VERSION "3.5.0-tec.1" +#define EMSESP_APP_VERSION "3.5.0-tec.2" diff --git a/src/web/WebCustomizationService.cpp b/src/web/WebCustomizationService.cpp index 48a0809a9..ba2b828bf 100644 --- a/src/web/WebCustomizationService.cpp +++ b/src/web/WebCustomizationService.cpp @@ -235,6 +235,7 @@ void WebCustomizationService::device_entities(AsyncWebServerRequest * request, J // saves it in the customization service // and updates the entity list real-time void WebCustomizationService::custom_entities(AsyncWebServerRequest * request, JsonVariant & json) { + bool need_reboot = false; if (json.is()) { // find the device using the unique_id for (const auto & emsdevice : EMSESP::emsdevices) { @@ -245,10 +246,46 @@ void WebCustomizationService::custom_entities(AsyncWebServerRequest * request, J uint8_t device_id = emsdevice->device_id(); // and set the mask and custom names immediately for any listed entities - JsonArray entity_ids_json = json["entity_ids"]; + JsonArray entity_ids_json = json["entity_ids"]; + std::vector entity_ids; for (const JsonVariant id : entity_ids_json) { - emsdevice->setCustomEntity(id.as()); + std::string id_s = id.as(); + if (id_s[0] == '8') { + entity_ids.push_back(id_s); + need_reboot = true; + } else { + emsdevice->setCustomEntity(id_s); + } + // emsesp::EMSESP::logger().info(id.as()); } + // add deleted entities from file + EMSESP::webCustomizationService.read([&](WebCustomization & settings) { + for (EntityCustomization entityCustomization : settings.entityCustomizations) { + if (entityCustomization.device_id == device_id) { + for (std::string entity_id : entityCustomization.entity_ids) { + uint8_t mask = Helpers::hextoint(entity_id.substr(0, 2).c_str()); + std::string name = DeviceValue::get_name(entity_id); + if (mask & 0x80) { + bool is_set = false; + for (const JsonVariant id : entity_ids_json) { + std::string id_s = id.as(); + if (name == DeviceValue::get_name(id_s)) { + is_set = true; + need_reboot = true; + break; + } + } + if (!is_set) { + entity_ids.push_back(entity_id); + } + } + } + break; + } + } + }); + // get list of entities that have masks set or a custom fullname + emsdevice->getCustomEntities(entity_ids); // Save the list to the customization file EMSESP::webCustomizationService.update( @@ -263,18 +300,11 @@ void WebCustomizationService::custom_entities(AsyncWebServerRequest * request, J } } - if (!entity_ids_json.size()) { - return StateUpdateResult::UNCHANGED; // nothing to add - } - // create a new entry for this device if there are values EntityCustomization new_entry; new_entry.product_id = product_id; new_entry.device_id = device_id; - // get list of entities that have masks set or a custom fullname - std::vector entity_ids; - emsdevice->getCustomEntities(entity_ids); new_entry.entity_ids = entity_ids; // add the record and save @@ -289,7 +319,7 @@ void WebCustomizationService::custom_entities(AsyncWebServerRequest * request, J } } - AsyncWebServerResponse * response = request->beginResponse(200); // OK + AsyncWebServerResponse * response = request->beginResponse(need_reboot ? 201 : 200); // OK request->send(response); } @@ -298,4 +328,4 @@ void WebCustomizationService::begin() { _fsPersistence.readFromFS(); } -} // namespace emsesp +} // namespace emsesp \ No newline at end of file