Compare commits

...
161 Commits
Author SHA1 Message Date
proddy ba0f4cd008 3.8.3 2026-08-02 09:56:04 +02:00
proddy 45f7d91b43 Merge remote-tracking branch 'origin/dev' 2026-08-02 09:55:48 +02:00
Proddy e0f59a4c31 Merge pull request #3176 from proddy/dev
pkg update and getting ready for stable
2026-08-01 17:05:18 +02:00
proddy 7eba8acbb4 3.8.3-dev.11 2026-08-01 17:04:13 +02:00
proddy d0e6a603bb auto-formatting 2026-08-01 16:57:41 +02:00
proddy 36b4dd55a7 pkg update 2026-08-01 16:44:16 +02:00
proddy 341a4d4e1d update version 2026-08-01 16:44:11 +02:00
proddy a61745b710 formatting 2026-08-01 16:44:03 +02:00
proddy b1ebd81ca2 update 2026-08-01 16:43:55 +02:00
Proddy 1ff53e6ac7 Merge pull request #3171 from MichaelDvP/dev
fix water SM100
2026-07-28 19:02:52 +02:00
MichaelDvP 9cab3b480e chore: update generated files for v3.8.3-dev.10 2026-07-22 15:37:09 +00:00
MichaelDvP edfbbe68b0 language code "cs" 2026-07-22 17:28:16 +02:00
MichaelDvP 96e8a0d916 fix water module #3164 2026-07-22 17:27:08 +02:00
MichaelDvP 8c90d10601 dump 2026-07-20 15:35:48 +02:00
Proddy bacd3faac2 Merge pull request #3159 from MichaelDvP/dev
dev9
2026-07-15 08:44:31 +02:00
MichaelDvP 2b187fb848 dev8f->dev9 2026-07-14 21:56:41 +02:00
MichaelDvP 4a12066e37 update pkg 2026-07-13 15:08:19 +02:00
MichaelDvP b82f8c979f process message from scheduler 2026-07-13 12:32:21 +02:00
MichaelDvP 342878f628 fix standalone 2026-07-12 17:25:51 +02:00
MichaelDvP 4e47be7fa6 Merge remote-tracking branch 'mdvp/dev' into dev 2026-07-12 17:17:42 +02:00
MichaelDvP 2b25ae18d1 set 10 sectimeout for http requests 2026-07-12 17:17:07 +02:00
MichaelDvP 98c9e011bc chore: update generated files for v3.8.3-dev.8d 2026-07-12 09:16:46 +00:00
MichaelDvP cb1cb356ce prevent errorlogs for nonexisting nvs values, version numbering to dev.8d 2026-07-12 11:05:28 +02:00
MichaelDvP 20fc5ede78 changelog 2026-07-12 10:31:51 +02:00
MichaelDvP a20d1d8970 fix typo 2026-07-12 10:17:02 +02:00
MichaelDvP a647e16f68 update asyncwebserver, dev.11 2026-07-12 09:57:05 +02:00
MichaelDvP 49ab5e3d72 delay scheduler loop after startup 2026-07-12 09:46:43 +02:00
MichaelDvP 9c905c8bab donot fetch system_info for reset command 2026-07-12 09:46:18 +02:00
MichaelDvP 7ab343a216 fix crashes from #3146 2026-07-12 08:17:40 +02:00
MichaelDvP 188cc7b666 check for "http" before allocating a http-client 2026-07-11 19:12:34 +02:00
MichaelDvP 193e5d591c block too many api GET requests 2026-07-11 14:48:19 +02:00
MichaelDvP ff3f4d3a6d update pkg 2026-07-11 14:47:39 +02:00
MichaelDvP 5129b475c5 NTP sync enabled by default 2026-07-11 14:47:19 +02:00
MichaelDvP f31dbd44a5 rollback regex 2026-07-10 12:13:31 +02:00
MichaelDvP 5c55c2216e rollback regex
crashes
2026-07-10 10:41:43 +02:00
MichaelDvP 5a1d06d39e fix possible memory leaks 2026-07-10 09:33:15 +02:00
MichaelDvP 9443cf3c87 Merge remote-tracking branch 'mdvp/dev' into dev 2026-07-10 08:35:03 +02:00
MichaelDvP fa31dcdf24 use F7 only for auto mode 2026-07-10 08:33:19 +02:00
MichaelDvP 2c80cc597e SRC modes from set-telegram 2026-07-10 08:17:53 +02:00
MichaelDvP d7641fbb2f use regex for isnum 2026-07-10 08:16:58 +02:00
MichaelDvP 5742c03eee chore: update generated files for v3.8.3-dev.10 2026-07-09 18:14:31 +00:00
MichaelDvP b2858ab3e7 Merge remote-tracking branch 'mdvp/dev' into dev 2026-07-09 19:59:37 +02:00
MichaelDvP 2255ad0b17 update modbus 2026-07-09 19:46:45 +02:00
MichaelDvP ed8adf2617 SRC modes from MenuConfig F7 2026-07-09 19:42:32 +02:00
MichaelDvP 5c2205cf65 calc_dew to helpers 2026-07-09 19:41:54 +02:00
MichaelDvP f6cb695635 direct call compute 2026-07-09 19:41:13 +02:00
MichaelDvP 1450e79b64 chore: update generated files for v3.8.3-dev.10 2026-07-06 15:24:45 +00:00
MichaelDvP fae0f9fb49 changelog 2026-07-06 17:12:41 +02:00
MichaelDvP 8638424d5b fix water circulation for dhw circuits 1/2 2026-07-06 17:00:12 +02:00
MichaelDvP 1d016ec356 formatting, manual set time 2026-07-06 16:55:23 +02:00
MichaelDvP f16b10f544 fix different timezones 2026-07-06 16:11:14 +02:00
MichaelDvP dd0c8a5ca8 SRC cooling mode, guess 2026-07-06 10:24:27 +02:00
MichaelDvP 7175191c66 Merge remote-tracking branch 'emsesp/dev' into dev 2026-07-06 08:25:11 +02:00
MichaelDvP f7a81c50b0 fix standalone, formatting 2026-07-06 08:24:43 +02:00
Proddy cfbcaa6fce Merge pull request #3149 from Cali1205/dev
update water.cpp: modify set_wwCircTc to use new command values
2026-07-05 22:02:16 +02:00
MichaelDvP abd1c36ec9 option to sync thermostat clock 2026-07-05 21:20:54 +02:00
Rainer ebd6d15c48 update water.cpp: modify set_wwCircTc to use new command values 2026-07-05 17:34:20 +02:00
MichaelDvP f4979d905d hpminflowtemp telegramoffset depends on control 2026-06-29 21:17:51 +02:00
MichaelDvP 496884c78a dev.9, changelog 2026-06-29 10:02:58 +02:00
MichaelDvP 696c82364e fix offset of hpminflowtemp #3144 2026-06-29 10:01:34 +02:00
Proddy c55655c0c5 Merge pull request #3143 from proddy/dev
fix thermostat date/time for HT3
2026-06-28 16:35:39 +02:00
proddy 55a0e4b2b5 Revert "Reduce DeviceValue heap usage with on-demand custom name"
This reverts commit 5aadf7234f.
2026-06-28 16:30:24 +02:00
proddy eceb097cc7 fix thermostat date/time for HT3 2026-06-28 16:08:31 +02:00
Proddy 0e18645ba5 Merge branch 'emsesp:dev' into dev 2026-06-27 14:32:48 +02:00
Proddy 63f49a347b Merge pull request #3136 from MichaelDvP/dev
fixes for #3127 and #3135
2026-06-27 14:32:04 +02:00
MichaelDvP 943048a920 dev.7 2026-06-27 13:14:57 +02:00
MichaelDvP 2b8a0bd8b7 Merge remote-tracking branch 'emsesp/dev' into dev 2026-06-27 13:06:19 +02:00
MichaelDvP d83b56a13c calcutae dewtemp for Easycontrol, #3135 2026-06-27 13:03:10 +02:00
Proddy 1f34a41d27 Merge branch 'emsesp:dev' into dev 2026-06-25 21:14:59 +02:00
Proddy 343ac5880c Merge pull request #3134 from s-schnitzer/fix-negative-dew-point-calculation
fix: negative dew point calculation (emulated RC100H)
2026-06-25 21:05:17 +02:00
s-schnitzer 855edace20 fix: negative dew point calc. (emulated RC100H) 2026-06-25 18:10:52 +02:00
Proddy aabc4f021c Merge pull request #3128 from soderdaen/fix-wifi-scan
Fix: AP crash and infinite loop during WiFi scan.
2026-06-23 21:01:21 +02:00
MichaelDvP 0aaa566e73 Merge remote-tracking branch 'emsesp/dev' into dev 2026-06-23 08:02:32 +02:00
MichaelDvP 20e3d6a19b dev.7, fix crash 2026-06-23 07:57:55 +02:00
MichaelDvP 67953fccd6 from PR #3128 2026-06-22 11:59:06 +02:00
MichaelDvP 0c910bc822 fix mock-api 2026-06-22 11:58:05 +02:00
soderdaen b6a7fb0bf0 Update changelog_latest.md and emsesp_version 2026-06-21 21:15:47 +02:00
MichaelDvP 95f475ab6f command message calsl compute direct,#3127 2026-06-21 10:53:10 +02:00
soderdaen f8fa092ac3 Fix: AP crash and infinite loop during WiFi scan. 2026-06-20 10:42:38 +02:00
proddyandCursor 5aadf7234f Reduce DeviceValue heap usage with on-demand custom name
Store the per-entity custom_fullname as a heap-allocated string that is
only created when a custom name is actually set, instead of an inline
std::string on every DeviceValue. Saves ~20 bytes per entity (hundreds to
~1000 entities), reclaiming internal heap on no-PSRAM boards.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-15 19:53:14 +02:00
Proddy b8a63aeac6 Merge pull request #3125 from proddy/dev
remove old S3 binaries #3123
2026-06-15 18:24:46 +02:00
proddy 908a9ebbfc remove old S3 binaries #3123 2026-06-15 18:23:58 +02:00
Proddy dbf970d869 Merge branch 'emsesp:dev' into dev 2026-06-07 12:52:09 +02:00
Proddy 9cb2455af2 Merge pull request #3111 from MichaelDvP/dev
reset system status
2026-06-07 12:51:51 +02:00
MichaelDvP f99ca7f013 seltemp write in cooling mode #1781 2026-06-04 12:21:16 +02:00
MichaelDvP 7349bc9ebf reset system status 2026-06-04 12:20:12 +02:00
MichaelDvP ccd3a7b223 Merge remote branch “emsesp/dev” 2026-06-04 11:52:53 +02:00
Proddy 33602f65f3 Merge branch 'emsesp:dev' into dev 2026-05-28 20:55:54 +01:00
Proddy 1ae547a710 Merge branch 'emsesp:dev' into dev 2026-05-19 22:33:59 +02:00
Proddy 70cfbd96e2 Merge branch 'emsesp:dev' into dev 2026-05-19 12:09:53 +02:00
proddy b441fd83dd chore: update generated files for v3.8.3-dev.1 2026-05-14 10:25:29 +00:00
proddy ee3a5d231a Merge remote-tracking branch 'origin/dev' 2026-05-12 20:15:32 +02:00
proddy e8f57b6343 v3.8.1 2026-01-11 19:28:38 +01:00
proddy a1ab81de66 Merge branch 'dev' 2026-01-11 19:14:11 +01:00
proddy 28135c225b Merge branch 'dev' 2025-12-31 21:26:15 +01:00
proddy eaa277fef0 v3.7.2 2025-03-22 10:32:03 +01:00
proddy e418b7d8e7 fix routing in tabs - #2264 2024-11-30 21:31:42 +01:00
proddy a4e3be6a69 3.7.1 changelog 2024-11-29 10:30:14 +01:00
proddy a1bc5bb055 3.7.1 2024-11-29 10:30:05 +01:00
proddy f444ca31e0 Merge remote-tracking branch 'origin/dev' 2024-10-27 11:41:47 +01:00
proddy 1b70b55989 update demo url 2024-08-10 14:58:48 +02:00
proddy 1487f30c43 Merge remote-tracking branch 'origin/dev' for 3.6.5 2024-03-23 17:56:05 +01:00
Proddy e00eb8e64f 3.6.4 2023-11-26 20:11:36 +01:00
Proddy f41bb3671c 3.6.4 2023-11-24 07:36:36 +01:00
Proddy 22c75e6df3 3.6.4 2023-11-24 07:36:29 +01:00
proddy 5ab10b7aa6 fixes #1450 2023-11-22 09:48:09 +01:00
Proddy ee5fd4d0eb 3.6.3 2023-11-21 14:40:47 +01:00
Proddy 46f35bc67c another patch on 3.6.3 2023-11-21 14:40:32 +01:00
Proddy ec85a7ec24 3.6.3 (refershed) 2023-11-19 21:24:01 +01:00
Proddy 02f2389587 add workflow_dispatch: 2023-11-18 18:51:12 +01:00
Proddy 7f140021aa quick fix - https://github.com/emsesp/EMS-ESP32/pull/1438 2023-11-18 18:47:28 +01:00
Proddy 6796962c1e 3.6.3 2023-11-18 13:35:20 +01:00
Proddy df6de21cf4 Merge remote-tracking branch 'origin/dev' 2023-11-18 13:35:04 +01:00
Proddy df9f75a5c9 updated yarn for 3.6.2 2023-10-01 17:42:54 +02:00
Proddy 7bd8710eb6 3.6.2 2023-10-01 17:40:09 +02:00
Proddy 32f2c6d341 Merge remote-tracking branch 'origin/dev' 2023-10-01 17:40:02 +02:00
Proddy 86919c1684 Merge branch 'origin/dev' 2023-09-09 14:12:07 +02:00
Proddy 86e29515e7 build s3 2023-08-15 18:44:24 +02:00
Proddy 46eb4185d7 update with 3.6.0 2023-08-13 14:37:13 +02:00
Proddy 8da6761a48 Merge branch 'dev' 2023-08-13 14:32:41 +02:00
Proddy 9233f0dfcc v3.5.1 - merge with patch 2023-03-11 16:06:05 +01:00
Proddy 292f743b14 Update bug_report.md 2023-02-19 11:18:08 +01:00
Proddy dd6dfffd57 Delete questions---troubleshooting.md 2023-02-19 10:49:52 +01:00
Proddy ec705a5307 Delete feature_request.md 2023-02-19 10:49:45 +01:00
Proddy f45f071710 Create config.yml 2023-02-19 10:49:32 +01:00
Proddy f3858546de Merge branch 'origin/dev' 2023-02-06 21:58:27 +01:00
Proddy d0ac0b7804 update with version on dev 2022-10-30 16:58:37 +01:00
Proddy d8284ec09f Merge pull request #705 from MichaelDvP/main
v3.4.4 Fix for new installations with filesystem not initializing
2022-10-29 10:46:41 +02:00
MichaelDvP 6e982acde8 v3.4.4 Fix for new installations with filesystem not initializing 2022-10-28 10:50:51 +02:00
Proddy 8c94ce99b2 quick fix for filesystem initialization 2022-10-08 09:23:00 +02:00
proddy fc057d18c9 Merge remote-tracking branch 'origin/dev' 2022-09-18 14:33:23 +02:00
Proddy 18e9b99413 Merge remote-tracking branch 'origin/dev' into main 2022-05-29 16:16:38 +02:00
Proddy a47e0e8266 update for 3.4.0 2022-05-23 21:20:45 +02:00
Proddy f412ddc716 Merge remote-tracking branch 'origin/dev' into main 2022-05-23 21:20:36 +02:00
proddy 29110e96e5 Merge remote-tracking branch 'origin/dev' 2022-01-20 10:51:40 +01:00
proddy b65866217a 3.4.0 2021-11-28 23:03:28 +01:00
proddy 611e3b1243 Merge remote-tracking branch 'origin/dev' 2021-11-28 23:03:15 +01:00
proddy 2ca0a0c634 v3.2.1 merged from dev 2021-08-08 14:46:14 +02:00
proddy 7eb1f061b7 Merge remote-tracking branch 'origin/dev' for 3.2.0 release 2021-08-06 12:06:08 +02:00
proddy 50459a23fe force v16 of nodejs 2021-06-26 11:13:07 +02:00
proddy 5bf53c3389 3.1.1 2021-06-26 11:03:03 +02:00
proddy 4b7aa95be3 Merge remote-tracking branch 'origin/dev' 2021-06-26 11:02:55 +02:00
Proddy 70943f5758 Update pre_release.yml 2021-05-16 15:52:09 +02:00
Proddy 3bc280b817 Delete check_code.yml 2021-05-16 15:51:56 +02:00
Proddy 62b15a5319 Update pre_release.yml 2021-05-16 15:35:06 +02:00
Proddy 8dd18802d6 Update tagged_release.yml 2021-05-16 15:34:45 +02:00
proddy 57a516a83a updated README and images 2021-05-09 15:13:16 +02:00
proddy a57fdaa4b3 Merge remote-tracking branch 'origin/dev' into main 2021-05-04 12:21:51 +02:00
proddy 4841e42286 Merge remote-tracking branch 'origin/dev' into main 2021-03-30 16:35:18 +02:00
proddy 8c2d2b06ed cleaned up old changelog 2021-03-18 20:59:09 +01:00
proddy 38c8b1b7f0 3.0.0 2021-03-18 20:58:21 +01:00
proddy 6fb5933a02 Merge remote-tracking branch 'origin/dev' into main 2021-03-18 20:58:12 +01:00
proddy c0944433be remove workspace.code-workspace 2021-03-16 17:41:42 +01:00
Proddy 478e6362c9 Merge pull request #27 from FauthD:main
Add global names to Dallas sensors to avoid ugly <unknown> and other …
2021-03-16 17:39:00 +01:00
fauthd 4d6354db78 Add stuff to gitignore, add vscode workspace 2021-03-16 16:47:09 +01:00
fauthd beab0f0c77 Add global names to Dallas sensors to avoid ugly <unknown> and other issues in HA 2021-03-16 16:00:23 +01:00
Proddy c17749bd22 Update README.md 2021-03-14 23:43:33 +01:00
proddy 2bad769c5c build: include assets 2021-03-14 21:20:51 +01:00
proddy 8ad89ca64b move repo 2021-03-14 21:05:15 +01:00
proddy 9244d8daec Semantic Commit Messages 2021-03-14 18:10:57 +01:00
proddy 02d01334b2 update new build 2021-03-14 17:37:18 +01:00
48 changed files with 1409 additions and 1726 deletions
+31 -1
View File
@@ -5,6 +5,37 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [3.8.3] 1 August 2026
## Added
- KM300 at address 0x4A [#3084](https://github.com/emsesp/EMS-ESP32/issues/3084)
## Fixed
- signed value for solarInfuence [#3077](https://github.com/emsesp/EMS-ESP32/issues/3077)
- set bin file upload limit to 1M again [#3086](https://github.com/emsesp/EMS-ESP32/issues/3086)
- check arithmetric operations on strings [#3127](https://github.com/emsesp/EMS-ESP32/discussions/3127)
- prevent system crash during WiFi network discovery on ESP32-S3 hardware [#3128](https://github.com/emsesp/EMS-ESP32/pull/3128)
- fix setting date/time on Junkers thermostats
- offset of hpminflowtemp [#3144](https://github.com/emsesp/EMS-ESP32/issues/3144)
- water circulation command [#3149](https://github.com/emsesp/EMS-ESP32/pull/3149)
- start scheduler after customEntities, delay scheduler loop after startup, handling of `system/restart` command [#3146](https://github.com/emsesp/EMS-ESP32/issues/3146)
- possible memory leaks fixed.
- repeated startup schedules compute value
- call `system/message` called from scheduler processes compute twice
- water module SM100/200 as dhw1/dhw2 [#3164](https://github.com/emsesp/EMS-ESP32/issues/3164)
## Changed
- call compute value direct, enlarge TCP stack [#3127](https://github.com/emsesp/EMS-ESP32/discussions/3127)
- Dewtemperature for Easycontrol calculated by ems-esp [#3135](https://github.com/emsesp/EMS-ESP32/issues/3135)
- add option for sync thermostat to ntp, allow different time zones [#3148](https://github.com/emsesp/EMS-ESP32/discussions/3148), **defaults to sync**.
- block too many GET requests [mentioned in #3104](https://github.com/emsesp/EMS-ESP32/issues/3104)
- set 10 sec timeout for http requests [#3104](https://github.com/emsesp/EMS-ESP32/issues/3104)
- language code "cs" [#3161](https://github.com/emsesp/EMS-ESP32/issues/3161)
## [3.8.2] 12 May 2026 ## [3.8.2] 12 May 2026
## Added ## Added
@@ -71,7 +102,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- board profile `CUSTOM` can only be selected in developer mode - board profile `CUSTOM` can only be selected in developer mode
- mqtt sends round values without decimals (`28` instead of `28.0`) - mqtt sends round values without decimals (`28` instead of `28.0`)
## [3.8.0] 31 December 2025 ## [3.8.0] 31 December 2025
## Added ## Added
-13
View File
@@ -1,16 +1,3 @@
# Changelog # Changelog
For more details go to [emsesp.org](https://emsesp.org/). For more details go to [emsesp.org](https://emsesp.org/).
## [3.8.3]
## Added
- KM300 at address 0x4A [#3084](https://github.com/emsesp/EMS-ESP32/issues/3084)
## Fixed
- signed value for solarInfuence [#3077](https://github.com/emsesp/EMS-ESP32/issues/3077)
- set bin file upload limit to 1M again [3086](https://github.com/emsesp/EMS-ESP32/issues/3086)
## Changed
+11 -41
View File
@@ -8146,7 +8146,7 @@ uint8
| datetime | date/time | string | | false | DEVICE_DATA | 0 | 13 | 1 | | datetime | date/time | string | | false | DEVICE_DATA | 0 | 13 | 1 |
| outdoortemp | outside temperature | int16 | C | false | DEVICE_DATA | 13 | 1 | 1/10 | | outdoortemp | outside temperature | int16 | C | false | DEVICE_DATA | 13 | 1 | 1/10 |
| src1.currtemp | current room temperature | int16 | C | false | SRC | 0 | 1 | 1/10 | | src1.currtemp | current room temperature | int16 | C | false | SRC | 0 | 1 | 1/10 |
| src1.airhumidity | relative air humidity | int8 | % | false | SRC | 1 | 1 | 1 | | src1.airhumidity | relative air humidity | uint8 | % | false | SRC | 1 | 1 | 1 |
| src1.dewtemperature | dew point temperature | int16 | C | false | SRC | 2 | 1 | 1/10 | | src1.dewtemperature | dew point temperature | int16 | C | false | SRC | 2 | 1 | 1/10 |
uint8 uint8
| src1.seltemp | selected room temperature | uint8 (&gt;=5&lt;=30) | C | true | SRC | 3 | 1 | 1/2 | | src1.seltemp | selected room temperature | uint8 (&gt;=5&lt;=30) | C | true | SRC | 3 | 1 | 1/2 |
@@ -8356,70 +8356,40 @@ uint8
| shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor | | shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor |
|-|-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-|-|
| dhw.temp | current temperature | uint16 | C | false | DHW | 1 | 1 | 1/10 | | dhw.temp | current temperature | uint16 | C | false | DHW | 1 | 1 | 1/10 |
| dhw.storagetemp1 | storage intern temperature | uint16 | C | false | DHW | 16 | 1 | 1/10 | | dhw.tempstatus | temperature switch in assigned dhw (MC1) | int8 | | false | DHW | 11 | 1 | 1 |
| dhw.coldtemp | cold water | uint16 | C | false | DHW | 17 | 1 | 1/10 |
| dhw.temp5 | temperature 5 | uint16 | C | false | DHW | 18 | 1 | 1/10 |
| dhw.rettemp | return temperature | uint16 | C | false | DHW | 19 | 1 | 1/10 |
| dhw.pump | pump | boolean | | false | DHW | 4 | 1 | 1 | | dhw.pump | pump | boolean | | false | DHW | 4 | 1 | 1 |
uint8 uint8
| dhw.maxtemp | maximum temperature | uint8 (&gt;=0&lt;=0) | C | true | DHW | 12 | 1 | 1 | | dhw.maxtemp | maximum temperature | uint8 (&gt;=0&lt;=0) | C | true | DHW | 12 | 1 | 1 |
int8
| dhw.difftemp | start differential temperature | int8 (&gt;=0&lt;=0) | C | true | DHW | 13 | 1 | 1 |
uint8 uint8
| dhw.seltemp | selected temperature | uint8 (&gt;=0&lt;=0) | C | true | DHW | 0 | 1 | 1 | | dhw.disinfectiontemp | disinfection temperature | uint8 (&gt;=0&lt;=0) | C | true | DHW | 8 | 1 | 1 |
uint8 uint8
| dhw.redtemp | reduced temperature | uint8 (&gt;=0&lt;=0) | C | true | DHW | 14 | 1 | 1 | | dhw.redtemp | reduced temperature | uint8 (&gt;=0&lt;=0) | C | true | DHW | 14 | 1 | 1 |
uint8 uint8
| dhw.hottemp | extra hot temperature | uint8 (&gt;=0&lt;=0) | C | true | DHW | 20 | 1 | 1 | | dhw.requiredtemp | required temperature | uint8 (&gt;=0&lt;=0) | C | true | DHW | 15 | 1 | 1 |
uint8
| dhw.dailytemp | daily temperature | uint8 (&gt;=0&lt;=0) | C | true | DHW | 21 | 1 | 1 |
uint8
| dhw.disinfectiontemp | disinfection temperature | uint8 (&gt;=0&lt;=0) | C | true | DHW | 8 | 1 | 1 |
| dhw.circ | circulation active | boolean | | true | DHW | 9 | 1 | 1 | | dhw.circ | circulation active | boolean | | true | DHW | 9 | 1 | 1 |
| dhw.circmode | circulation pump mode | enum | | true | DHW | 10 | 1 | 1 | | dhw.circmode | circulation pump mode | enum | | true | DHW | 10 | 1 | 1 |
| dhw.circtc | circulation time controlled | boolean | | true | DHW | 22 | 1 | 1 |
| dhw.keepwarm | keep warm | boolean | | true | DHW | 23 | 1 | 1 |
| dhw.status2 | status 2 | enum | | false | DHW | 24 | 1 | 1 |
| dhw.pumpmod | pump modulation | uint8 | % | false | DHW | 25 | 1 | 1 |
| dhw.flow | flow rate | uint16 | l/min | false | DHW | 26 | 1 | 1/10 |
uint8
| dhw.valvereturn | return valve | uint8 (&gt;=0&lt;=0) | % | true | DHW | 27 | 1 | 1 |
uint8
| dhw.deltatret | temp. diff. return valve | uint8 (&gt;=0&lt;=0) | K | true | DHW | 28 | 1 | 1 |
| dhw.errordisp | error display | enum | | true | DHW | 29 | 1 | 1 |
### SM200, MS200 ### SM200, MS200
| shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor | | shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor |
|-|-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-|-|
| dhw.temp | current temperature | uint16 | C | false | DHW | 1 | 1 | 1/10 | | dhw.temp | current temperature | uint16 | C | false | DHW | 1 | 1 | 1/10 |
| dhw.storagetemp1 | storage intern temperature | uint16 | C | false | DHW | 16 | 1 | 1/10 | | dhw.tempstatus | temperature switch in assigned dhw (MC1) | int8 | | false | DHW | 11 | 1 | 1 |
| dhw.coldtemp | cold water | uint16 | C | false | DHW | 17 | 1 | 1/10 |
| dhw.temp5 | temperature 5 | uint16 | C | false | DHW | 18 | 1 | 1/10 |
| dhw.rettemp | return temperature | uint16 | C | false | DHW | 19 | 1 | 1/10 |
| dhw.pump | pump | boolean | | false | DHW | 4 | 1 | 1 | | dhw.pump | pump | boolean | | false | DHW | 4 | 1 | 1 |
uint8 uint8
| dhw.maxtemp | maximum temperature | uint8 (&gt;=0&lt;=0) | C | true | DHW | 12 | 1 | 1 | | dhw.maxtemp | maximum temperature | uint8 (&gt;=0&lt;=0) | C | true | DHW | 12 | 1 | 1 |
int8
| dhw.difftemp | start differential temperature | int8 (&gt;=0&lt;=0) | C | true | DHW | 13 | 1 | 1 |
uint8 uint8
| dhw.seltemp | selected temperature | uint8 (&gt;=0&lt;=0) | C | true | DHW | 0 | 1 | 1 | | dhw.disinfectiontemp | disinfection temperature | uint8 (&gt;=0&lt;=0) | C | true | DHW | 8 | 1 | 1 |
uint8 uint8
| dhw.redtemp | reduced temperature | uint8 (&gt;=0&lt;=0) | C | true | DHW | 14 | 1 | 1 | | dhw.redtemp | reduced temperature | uint8 (&gt;=0&lt;=0) | C | true | DHW | 14 | 1 | 1 |
uint8 uint8
| dhw.hottemp | extra hot temperature | uint8 (&gt;=0&lt;=0) | C | true | DHW | 20 | 1 | 1 | | dhw.requiredtemp | required temperature | uint8 (&gt;=0&lt;=0) | C | true | DHW | 15 | 1 | 1 |
uint8
| dhw.dailytemp | daily temperature | uint8 (&gt;=0&lt;=0) | C | true | DHW | 21 | 1 | 1 |
uint8
| dhw.disinfectiontemp | disinfection temperature | uint8 (&gt;=0&lt;=0) | C | true | DHW | 8 | 1 | 1 |
| dhw.circ | circulation active | boolean | | true | DHW | 9 | 1 | 1 | | dhw.circ | circulation active | boolean | | true | DHW | 9 | 1 | 1 |
| dhw.circmode | circulation pump mode | enum | | true | DHW | 10 | 1 | 1 | | dhw.circmode | circulation pump mode | enum | | true | DHW | 10 | 1 | 1 |
| dhw.circtc | circulation time controlled | boolean | | true | DHW | 22 | 1 | 1 |
| dhw.keepwarm | keep warm | boolean | | true | DHW | 23 | 1 | 1 |
| dhw.status2 | status 2 | enum | | false | DHW | 24 | 1 | 1 |
| dhw.pumpmod | pump modulation | uint8 | % | false | DHW | 25 | 1 | 1 |
| dhw.flow | flow rate | uint16 | l/min | false | DHW | 26 | 1 | 1/10 |
uint8
| dhw.valvereturn | return valve | uint8 (&gt;=0&lt;=0) | % | true | DHW | 27 | 1 | 1 |
uint8
| dhw.deltatret | temp. diff. return valve | uint8 (&gt;=0&lt;=0) | K | true | DHW | 28 | 1 | 1 |
| dhw.errordisp | error display | enum | | true | DHW | 29 | 1 | 1 |
## Devices of type \_pool ## Devices of type \_pool
+11 -35
View File
@@ -5861,7 +5861,7 @@ device name,device type,product id,shortname,fullname,type [options...] \| (min/
"MX400",connect,17,datetime,date/time,string, ,false,sensor.connect_date/time,sensor.connect_datetime,13,0,1,0,13 "MX400",connect,17,datetime,date/time,string, ,false,sensor.connect_date/time,sensor.connect_datetime,13,0,1,0,13
"MX400",connect,17,outdoortemp,outside temperature,int16,C,false,sensor.connect_outside_temperature,sensor.connect_outdoortemp,13,0,1/10,13,1 "MX400",connect,17,outdoortemp,outside temperature,int16,C,false,sensor.connect_outside_temperature,sensor.connect_outdoortemp,13,0,1/10,13,1
"MX400",connect,17,src1.currtemp,current room temperature,int16,C,false,sensor.connect_src1_current_room_temperature,sensor.connect_src1_currtemp,13,36,1/10,0,1 "MX400",connect,17,src1.currtemp,current room temperature,int16,C,false,sensor.connect_src1_current_room_temperature,sensor.connect_src1_currtemp,13,36,1/10,0,1
"MX400",connect,17,src1.airhumidity,relative air humidity,int8,%,false,sensor.connect_src1_relative_air_humidity,sensor.connect_src1_airhumidity,13,36,1,1,1 "MX400",connect,17,src1.airhumidity,relative air humidity,uint8,%,false,sensor.connect_src1_relative_air_humidity,sensor.connect_src1_airhumidity,13,36,1,1,1
"MX400",connect,17,src1.dewtemperature,dew point temperature,int16,C,false,sensor.connect_src1_dew_point_temperature,sensor.connect_src1_dewtemperature,13,36,1/10,2,1 "MX400",connect,17,src1.dewtemperature,dew point temperature,int16,C,false,sensor.connect_src1_dew_point_temperature,sensor.connect_src1_dewtemperature,13,36,1/10,2,1
"MX400",connect,17,src1.seltemp,selected room temperature,uint8 (>=5<=30),C,true,number.connect_src1_selected_room_temperature,number.connect_src1_seltemp,13,36,1/2,3,1 "MX400",connect,17,src1.seltemp,selected room temperature,uint8 (>=5<=30),C,true,number.connect_src1_selected_room_temperature,number.connect_src1_seltemp,13,36,1/2,3,1
"MX400",connect,17,src1.mode,operating mode,enum [off\|manual\|auto] (>=0<=0), ,true,select.connect_src1_operating_mode,select.connect_src1_mode,13,36,1,4,1 "MX400",connect,17,src1.mode,operating mode,enum [off\|manual\|auto] (>=0<=0), ,true,select.connect_src1_operating_mode,select.connect_src1_mode,13,36,1,4,1
@@ -5975,49 +5975,25 @@ device name,device type,product id,shortname,fullname,type [options...] \| (min/
"MM200",water,161,dhw.circ,circulation active,boolean (>=0<=0), ,true,switch.water_dhw_circulation_active,switch.water_dhw_circ,19,9,1,9,1 "MM200",water,161,dhw.circ,circulation active,boolean (>=0<=0), ,true,switch.water_dhw_circulation_active,switch.water_dhw_circ,19,9,1,9,1
"MM200",water,161,dhw.circmode,circulation pump mode,enum [off\|on\|auto\|own prog] (>=0<=0), ,true,select.water_dhw_circulation_pump_mode,select.water_dhw_circmode,19,9,1,10,1 "MM200",water,161,dhw.circmode,circulation pump mode,enum [off\|on\|auto\|own prog] (>=0<=0), ,true,select.water_dhw_circulation_pump_mode,select.water_dhw_circmode,19,9,1,10,1
"SM100, MS100",water,163,dhw.temp,current temperature,uint16,C,false,sensor.water_dhw_current_temperature,sensor.water_dhw_temp,19,9,1/10,1,1 "SM100, MS100",water,163,dhw.temp,current temperature,uint16,C,false,sensor.water_dhw_current_temperature,sensor.water_dhw_temp,19,9,1/10,1,1
"SM100, MS100",water,163,dhw.storagetemp1,storage intern temperature,uint16,C,false,sensor.water_dhw_storage_intern_temperature,sensor.water_dhw_storagetemp1,19,9,1/10,16,1 "SM100, MS100",water,163,dhw.tempstatus,temperature switch in assigned dhw (MC1),int8, ,false,sensor.water_dhw_temperature_switch_in_assigned_dhw_(MC1),sensor.water_dhw_tempstatus,19,9,1,11,1
"SM100, MS100",water,163,dhw.coldtemp,cold water,uint16,C,false,sensor.water_dhw_cold_water,sensor.water_dhw_coldtemp,19,9,1/10,17,1
"SM100, MS100",water,163,dhw.temp5,temperature 5,uint16,C,false,sensor.water_dhw_temperature_5,sensor.water_dhw_temp5,19,9,1/10,18,1
"SM100, MS100",water,163,dhw.rettemp,return temperature,uint16,C,false,sensor.water_dhw_return_temperature,sensor.water_dhw_rettemp,19,9,1/10,19,1
"SM100, MS100",water,163,dhw.pump,pump,boolean, ,false,binary_sensor.water_dhw_pump,binary_sensor.water_dhw_pump,19,9,1,4,1 "SM100, MS100",water,163,dhw.pump,pump,boolean, ,false,binary_sensor.water_dhw_pump,binary_sensor.water_dhw_pump,19,9,1,4,1
"SM100, MS100",water,163,dhw.maxtemp,maximum temperature,uint8 (>=0<=0),C,true,number.water_dhw_maximum_temperature,number.water_dhw_maxtemp,19,9,1,12,1 "SM100, MS100",water,163,dhw.maxtemp,maximum temperature,uint8 (>=0<=0),C,true,number.water_dhw_maximum_temperature,number.water_dhw_maxtemp,19,9,1,12,1
"SM100, MS100",water,163,dhw.seltemp,selected temperature,uint8 (>=0<=0),C,true,number.water_dhw_selected_temperature,number.water_dhw_seltemp,19,9,1,0,1 "SM100, MS100",water,163,dhw.difftemp,start differential temperature,int8 (>=0<=0),C,true,number.water_dhw_start_differential_temperature,number.water_dhw_difftemp,19,9,1,13,1
"SM100, MS100",water,163,dhw.redtemp,reduced temperature,uint8 (>=0<=0),C,true,number.water_dhw_reduced_temperature,number.water_dhw_redtemp,19,9,1,14,1
"SM100, MS100",water,163,dhw.hottemp,extra hot temperature,uint8 (>=0<=0),C,true,number.water_dhw_extra_hot_temperature,number.water_dhw_hottemp,19,9,1,20,1
"SM100, MS100",water,163,dhw.dailytemp,daily temperature,uint8 (>=0<=0),C,true,number.water_dhw_daily_temperature,number.water_dhw_dailytemp,19,9,1,21,1
"SM100, MS100",water,163,dhw.disinfectiontemp,disinfection temperature,uint8 (>=0<=0),C,true,number.water_dhw_disinfection_temperature,number.water_dhw_disinfectiontemp,19,9,1,8,1 "SM100, MS100",water,163,dhw.disinfectiontemp,disinfection temperature,uint8 (>=0<=0),C,true,number.water_dhw_disinfection_temperature,number.water_dhw_disinfectiontemp,19,9,1,8,1
"SM100, MS100",water,163,dhw.redtemp,reduced temperature,uint8 (>=0<=0),C,true,number.water_dhw_reduced_temperature,number.water_dhw_redtemp,19,9,1,14,1
"SM100, MS100",water,163,dhw.requiredtemp,required temperature,uint8 (>=0<=0),C,true,number.water_dhw_required_temperature,number.water_dhw_requiredtemp,19,9,1,15,1
"SM100, MS100",water,163,dhw.circ,circulation active,boolean (>=0<=0), ,true,switch.water_dhw_circulation_active,switch.water_dhw_circ,19,9,1,9,1 "SM100, MS100",water,163,dhw.circ,circulation active,boolean (>=0<=0), ,true,switch.water_dhw_circulation_active,switch.water_dhw_circ,19,9,1,9,1
"SM100, MS100",water,163,dhw.circmode,circulation pump mode,enum [off\|1x3min\|2x3min\|3x3min\|4x3min\|5x3min\|6x3min\|continuous] (>=0<=0), ,true,select.water_dhw_circulation_pump_mode,select.water_dhw_circmode,19,9,1,10,1 "SM100, MS100",water,163,dhw.circmode,circulation pump mode,enum [off\|on\|auto\|own prog] (>=0<=0), ,true,select.water_dhw_circulation_pump_mode,select.water_dhw_circmode,19,9,1,10,1
"SM100, MS100",water,163,dhw.circtc,circulation time controlled,boolean (>=0<=0), ,true,switch.water_dhw_circulation_time_controlled,switch.water_dhw_circtc,19,9,1,22,1
"SM100, MS100",water,163,dhw.keepwarm,keep warm,boolean (>=0<=0), ,true,switch.water_dhw_keep_warm,switch.water_dhw_keepwarm,19,9,1,23,1
"SM100, MS100",water,163,dhw.status2,status 2,enum [\|\|\|no heat\|\|\|heat request\|\|disinfecting\|hold], ,false,sensor.water_dhw_status_2,sensor.water_dhw_status2,19,9,1,24,1
"SM100, MS100",water,163,dhw.pumpmod,pump modulation,uint8,%,false,sensor.water_dhw_pump_modulation,sensor.water_dhw_pumpmod,19,9,1,25,1
"SM100, MS100",water,163,dhw.flow,flow rate,uint16,l/min,false,sensor.water_dhw_flow_rate,sensor.water_dhw_flow,19,9,1/10,26,1
"SM100, MS100",water,163,dhw.valvereturn,return valve,uint8 (>=0<=0),%,true,number.water_dhw_return_valve,number.water_dhw_valvereturn,19,9,1,27,1
"SM100, MS100",water,163,dhw.deltatret,temp. diff. return valve,uint8 (>=0<=0),K,true,number.water_dhw_temp._diff._return_valve,number.water_dhw_deltatret,19,9,1,28,1
"SM100, MS100",water,163,dhw.errordisp,error display,enum [off\|normal\|inverted] (>=0<=0), ,true,select.water_dhw_error_display,select.water_dhw_errordisp,19,9,1,29,1
"SM200, MS200",water,164,dhw.temp,current temperature,uint16,C,false,sensor.water_dhw_current_temperature,sensor.water_dhw_temp,19,9,1/10,1,1 "SM200, MS200",water,164,dhw.temp,current temperature,uint16,C,false,sensor.water_dhw_current_temperature,sensor.water_dhw_temp,19,9,1/10,1,1
"SM200, MS200",water,164,dhw.storagetemp1,storage intern temperature,uint16,C,false,sensor.water_dhw_storage_intern_temperature,sensor.water_dhw_storagetemp1,19,9,1/10,16,1 "SM200, MS200",water,164,dhw.tempstatus,temperature switch in assigned dhw (MC1),int8, ,false,sensor.water_dhw_temperature_switch_in_assigned_dhw_(MC1),sensor.water_dhw_tempstatus,19,9,1,11,1
"SM200, MS200",water,164,dhw.coldtemp,cold water,uint16,C,false,sensor.water_dhw_cold_water,sensor.water_dhw_coldtemp,19,9,1/10,17,1
"SM200, MS200",water,164,dhw.temp5,temperature 5,uint16,C,false,sensor.water_dhw_temperature_5,sensor.water_dhw_temp5,19,9,1/10,18,1
"SM200, MS200",water,164,dhw.rettemp,return temperature,uint16,C,false,sensor.water_dhw_return_temperature,sensor.water_dhw_rettemp,19,9,1/10,19,1
"SM200, MS200",water,164,dhw.pump,pump,boolean, ,false,binary_sensor.water_dhw_pump,binary_sensor.water_dhw_pump,19,9,1,4,1 "SM200, MS200",water,164,dhw.pump,pump,boolean, ,false,binary_sensor.water_dhw_pump,binary_sensor.water_dhw_pump,19,9,1,4,1
"SM200, MS200",water,164,dhw.maxtemp,maximum temperature,uint8 (>=0<=0),C,true,number.water_dhw_maximum_temperature,number.water_dhw_maxtemp,19,9,1,12,1 "SM200, MS200",water,164,dhw.maxtemp,maximum temperature,uint8 (>=0<=0),C,true,number.water_dhw_maximum_temperature,number.water_dhw_maxtemp,19,9,1,12,1
"SM200, MS200",water,164,dhw.seltemp,selected temperature,uint8 (>=0<=0),C,true,number.water_dhw_selected_temperature,number.water_dhw_seltemp,19,9,1,0,1 "SM200, MS200",water,164,dhw.difftemp,start differential temperature,int8 (>=0<=0),C,true,number.water_dhw_start_differential_temperature,number.water_dhw_difftemp,19,9,1,13,1
"SM200, MS200",water,164,dhw.redtemp,reduced temperature,uint8 (>=0<=0),C,true,number.water_dhw_reduced_temperature,number.water_dhw_redtemp,19,9,1,14,1
"SM200, MS200",water,164,dhw.hottemp,extra hot temperature,uint8 (>=0<=0),C,true,number.water_dhw_extra_hot_temperature,number.water_dhw_hottemp,19,9,1,20,1
"SM200, MS200",water,164,dhw.dailytemp,daily temperature,uint8 (>=0<=0),C,true,number.water_dhw_daily_temperature,number.water_dhw_dailytemp,19,9,1,21,1
"SM200, MS200",water,164,dhw.disinfectiontemp,disinfection temperature,uint8 (>=0<=0),C,true,number.water_dhw_disinfection_temperature,number.water_dhw_disinfectiontemp,19,9,1,8,1 "SM200, MS200",water,164,dhw.disinfectiontemp,disinfection temperature,uint8 (>=0<=0),C,true,number.water_dhw_disinfection_temperature,number.water_dhw_disinfectiontemp,19,9,1,8,1
"SM200, MS200",water,164,dhw.redtemp,reduced temperature,uint8 (>=0<=0),C,true,number.water_dhw_reduced_temperature,number.water_dhw_redtemp,19,9,1,14,1
"SM200, MS200",water,164,dhw.requiredtemp,required temperature,uint8 (>=0<=0),C,true,number.water_dhw_required_temperature,number.water_dhw_requiredtemp,19,9,1,15,1
"SM200, MS200",water,164,dhw.circ,circulation active,boolean (>=0<=0), ,true,switch.water_dhw_circulation_active,switch.water_dhw_circ,19,9,1,9,1 "SM200, MS200",water,164,dhw.circ,circulation active,boolean (>=0<=0), ,true,switch.water_dhw_circulation_active,switch.water_dhw_circ,19,9,1,9,1
"SM200, MS200",water,164,dhw.circmode,circulation pump mode,enum [off\|1x3min\|2x3min\|3x3min\|4x3min\|5x3min\|6x3min\|continuous] (>=0<=0), ,true,select.water_dhw_circulation_pump_mode,select.water_dhw_circmode,19,9,1,10,1 "SM200, MS200",water,164,dhw.circmode,circulation pump mode,enum [off\|on\|auto\|own prog] (>=0<=0), ,true,select.water_dhw_circulation_pump_mode,select.water_dhw_circmode,19,9,1,10,1
"SM200, MS200",water,164,dhw.circtc,circulation time controlled,boolean (>=0<=0), ,true,switch.water_dhw_circulation_time_controlled,switch.water_dhw_circtc,19,9,1,22,1
"SM200, MS200",water,164,dhw.keepwarm,keep warm,boolean (>=0<=0), ,true,switch.water_dhw_keep_warm,switch.water_dhw_keepwarm,19,9,1,23,1
"SM200, MS200",water,164,dhw.status2,status 2,enum [\|\|\|no heat\|\|\|heat request\|\|disinfecting\|hold], ,false,sensor.water_dhw_status_2,sensor.water_dhw_status2,19,9,1,24,1
"SM200, MS200",water,164,dhw.pumpmod,pump modulation,uint8,%,false,sensor.water_dhw_pump_modulation,sensor.water_dhw_pumpmod,19,9,1,25,1
"SM200, MS200",water,164,dhw.flow,flow rate,uint16,l/min,false,sensor.water_dhw_flow_rate,sensor.water_dhw_flow,19,9,1/10,26,1
"SM200, MS200",water,164,dhw.valvereturn,return valve,uint8 (>=0<=0),%,true,number.water_dhw_return_valve,number.water_dhw_valvereturn,19,9,1,27,1
"SM200, MS200",water,164,dhw.deltatret,temp. diff. return valve,uint8 (>=0<=0),K,true,number.water_dhw_temp._diff._return_valve,number.water_dhw_deltatret,19,9,1,28,1
"SM200, MS200",water,164,dhw.errordisp,error display,enum [off\|normal\|inverted] (>=0<=0), ,true,select.water_dhw_error_display,select.water_dhw_errordisp,19,9,1,29,1
"MP100",pool,204,pooltemp,pool temperature,int16,C,false,sensor.pool_pool_temperature,sensor.pool_pooltemp,20,0,1/10,-1,1 "MP100",pool,204,pooltemp,pool temperature,int16,C,false,sensor.pool_pool_temperature,sensor.pool_pooltemp,20,0,1/10,-1,1
"MP100",pool,204,poolshuntstatus,pool shunt status opening/closing,enum [stopped\|opening\|closing\|open\|close], ,false,sensor.pool_pool_shunt_status_opening/closing,sensor.pool_poolshuntstatus,20,0,1,-1,1 "MP100",pool,204,poolshuntstatus,pool shunt status opening/closing,enum [stopped\|opening\|closing\|open\|close], ,false,sensor.pool_pool_shunt_status_opening/closing,sensor.pool_poolshuntstatus,20,0,1,-1,1
"MP100",pool,204,poolshunt,pool shunt open/close (0% = pool / 100% = heat),uint8,%,false,sensor.pool_pool_shunt_open/close_(0%_=_pool_/_100%_=_heat),sensor.pool_poolshunt,20,0,1,-1,1 "MP100",pool,204,poolshunt,pool shunt open/close (0% = pool / 100% = heat),uint8,%,false,sensor.pool_pool_shunt_open/close_(0%_=_pool_/_100%_=_heat),sensor.pool_poolshunt,20,0,1,-1,1
Can't render this file because it is too large.
+1
View File
@@ -24,6 +24,7 @@ build_flags =
-D FACTORY_NTP_TIME_ZONE_LABEL=\"Europe/Amsterdam\" -D FACTORY_NTP_TIME_ZONE_LABEL=\"Europe/Amsterdam\"
-D FACTORY_NTP_TIME_ZONE_FORMAT=\"CET-1CEST,M3.5.0,M10.5.0/3\" -D FACTORY_NTP_TIME_ZONE_FORMAT=\"CET-1CEST,M3.5.0,M10.5.0/3\"
-D FACTORY_NTP_SERVER=\"time.google.com\" -D FACTORY_NTP_SERVER=\"time.google.com\"
-D FACTORY_NTP_THERMOSTAT_SYNC=0
; MQTT settings ; MQTT settings
-D FACTORY_MQTT_ENABLED=false -D FACTORY_MQTT_ENABLED=false
+12 -12
View File
@@ -26,8 +26,8 @@
"@alova/adapter-xhr": "2.3.1", "@alova/adapter-xhr": "2.3.1",
"@emotion/react": "^11.14.0", "@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.1", "@emotion/styled": "^11.14.1",
"@mui/icons-material": "^9.0.1", "@mui/icons-material": "^9.2.0",
"@mui/material": "^9.0.1", "@mui/material": "^9.2.0",
"@preact/compat": "^18.3.2", "@preact/compat": "^18.3.2",
"@table-library/react-table-library": "4.1.15", "@table-library/react-table-library": "4.1.15",
"alova": "3.5.1", "alova": "3.5.1",
@@ -37,17 +37,17 @@
"jwt-decode": "^4.0.0", "jwt-decode": "^4.0.0",
"magic-string": "^0.30.21", "magic-string": "^0.30.21",
"mime-types": "^3.0.2", "mime-types": "^3.0.2",
"preact": "^10.29.1", "preact": "^10.29.7",
"react": "^19.2.6", "react": "^19.2.7",
"react-dom": "^19.2.6", "react-dom": "^19.2.7",
"react-icons": "^5.6.0", "react-icons": "^5.7.0",
"react-router": "^7.15.0", "react-router": "^7.18.1",
"react-toastify": "^11.1.0", "react-toastify": "^11.1.0",
"typesafe-i18n": "^5.27.1", "typesafe-i18n": "^5.27.1",
"typescript": "^6.0.3" "typescript": "^6.0.3"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.29.0", "@babel/core": "^7.29.7",
"@eslint/js": "^10.0.1", "@eslint/js": "^10.0.1",
"@preact/compat": "^18.3.2", "@preact/compat": "^18.3.2",
"@preact/preset-vite": "^2.10.5", "@preact/preset-vite": "^2.10.5",
@@ -55,11 +55,11 @@
"@types/node": "^25.7.0", "@types/node": "^25.7.0",
"@types/react": "^19.2.14", "@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3", "@types/react-dom": "^19.2.3",
"axe-core": "^4.11.4", "axe-core": "^4.12.1",
"concurrently": "^9.2.1", "concurrently": "^9.2.4",
"eslint": "^10.3.0", "eslint": "^10.7.0",
"eslint-config-prettier": "^10.1.8", "eslint-config-prettier": "^10.1.8",
"prettier": "^3.8.3", "prettier": "^3.9.5",
"rollup-plugin-visualizer": "^7.0.1", "rollup-plugin-visualizer": "^7.0.1",
"terser": "^5.47.1", "terser": "^5.47.1",
"typescript-eslint": "^8.59.3", "typescript-eslint": "^8.59.3",
+730 -1243
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -19,7 +19,7 @@ const AVAILABLE_LOCALES = [
'sk', 'sk',
'sv', 'sv',
'tr', 'tr',
'cz' 'cs'
] as Locales[]; ] as Locales[];
// Static toast configuration - no need to recreate on every render // Static toast configuration - no need to recreate on every render
+1 -5
View File
@@ -13,11 +13,7 @@ import VisibilityOutlinedIcon from '@mui/icons-material/VisibilityOutlined';
import type { SvgIconProps } from '@mui/material'; import type { SvgIconProps } from '@mui/material';
export type OptionType = export type OptionType =
| 'deleted' 'deleted' | 'readonly' | 'web_exclude' | 'api_mqtt_exclude' | 'favorite';
| 'readonly'
| 'web_exclude'
| 'api_mqtt_exclude'
| 'favorite';
type IconPair = [ type IconPair = [
React.ComponentType<SvgIconProps>, React.ComponentType<SvgIconProps>,
@@ -396,7 +396,7 @@ const ApplicationSettings = () => {
margin="normal" margin="normal"
select select
> >
<MenuItem value="cz">Česky (CZ)</MenuItem> <MenuItem value="cs">Česky (CS)</MenuItem>
<MenuItem value="de">Deutsch (DE)</MenuItem> <MenuItem value="de">Deutsch (DE)</MenuItem>
<MenuItem value="en">English (EN)</MenuItem> <MenuItem value="en">English (EN)</MenuItem>
<MenuItem value="fr">Français (FR)</MenuItem> <MenuItem value="fr">Français (FR)</MenuItem>
+67 -3
View File
@@ -12,6 +12,7 @@ import {
DialogActions, DialogActions,
DialogContent, DialogContent,
DialogTitle, DialogTitle,
Grid,
MenuItem, MenuItem,
TextField, TextField,
Typography Typography
@@ -39,7 +40,12 @@ import { formatLocalDateTime, updateValueDirty, useRest } from 'utils';
import { ValidationError, validate } from 'validators'; import { ValidationError, validate } from 'validators';
import { NTP_SETTINGS_VALIDATOR } from 'validators/ntp'; import { NTP_SETTINGS_VALIDATOR } from 'validators/ntp';
import { TIME_ZONES, selectedTimeZone, useTimeZoneSelectItems } from './TZ'; import {
TIME_ZONES,
selectedTimeZone,
timeZoneSelectItemsT,
useTimeZoneSelectItems
} from './TZ';
const NTPSettings = () => { const NTPSettings = () => {
const { const {
@@ -63,13 +69,17 @@ const NTPSettings = () => {
// Memoized timezone select items for better performance // Memoized timezone select items for better performance
const timeZoneItems = useTimeZoneSelectItems(); const timeZoneItems = useTimeZoneSelectItems();
const timeZoneItemsT = timeZoneSelectItemsT();
// Memoized selected timezone value // Memoized selected timezone value
const selectedTzValue = useMemo( const selectedTzValue = useMemo(
() => (data ? selectedTimeZone(data.tz_label, data.tz_format) : undefined), () => (data ? selectedTimeZone(data.tz_label, data.tz_format) : undefined),
[data?.tz_label, data?.tz_format] [data?.tz_label, data?.tz_format]
); );
const selectedTzValueT = useMemo(
() => (data ? selectedTimeZone(data.tz_label_t, data.tz_format_t) : undefined),
[data?.tz_label_t, data?.tz_format_t]
);
const [localTime, setLocalTime] = useState<string>(''); const [localTime, setLocalTime] = useState<string>('');
const [settingTime, setSettingTime] = useState<boolean>(false); const [settingTime, setSettingTime] = useState<boolean>(false);
const [processing, setProcessing] = useState<boolean>(false); const [processing, setProcessing] = useState<boolean>(false);
@@ -150,6 +160,18 @@ const NTPSettings = () => {
[updateFormValue] [updateFormValue]
); );
const changeTimeZoneT = useCallback(
(event: React.ChangeEvent<HTMLInputElement>) => {
void updateState(readNTPSettings(), (settings: NTPSettingsType) => ({
...settings,
tz_label_t: event.target.value,
tz_format_t: TIME_ZONES[event.target.value]
}));
updateFormValue(event);
},
[updateFormValue]
);
// Memoize render content to prevent unnecessary re-renders // Memoize render content to prevent unnecessary re-renders
const renderContent = useMemo(() => { const renderContent = useMemo(() => {
if (!data) { if (!data) {
@@ -174,6 +196,7 @@ const NTPSettings = () => {
label={LL.NTP_SERVER()} label={LL.NTP_SERVER()}
fullWidth fullWidth
variant="outlined" variant="outlined"
disabled={!data.enabled}
value={data.server} value={data.server}
onChange={updateFormValue} onChange={updateFormValue}
margin="normal" margin="normal"
@@ -192,7 +215,47 @@ const NTPSettings = () => {
<MenuItem disabled>{LL.TIME_ZONE()}...</MenuItem> <MenuItem disabled>{LL.TIME_ZONE()}...</MenuItem>
{timeZoneItems} {timeZoneItems}
</ValidatedTextField> </ValidatedTextField>
{data.enabled && (
<Grid container spacing={2} rowSpacing={0}>
<Grid>
<ValidatedTextField
fieldErrors={fieldErrors || {}}
name="thermostat_sync"
label="Sync EMS-Thermostat"
variant="outlined"
sx={{ width: '30ch' }}
value={data.thermostat_sync}
onChange={updateFormValue}
margin="normal"
select
>
<MenuItem value={0}>{LL.OFF()}</MenuItem>
<MenuItem value={1}>{LL.ON()}</MenuItem>
<MenuItem value={2}>
{LL.USE()} {LL.TIME_ZONE()}
</MenuItem>
</ValidatedTextField>
</Grid>
<Grid>
{data.thermostat_sync === 2 && (
<ValidatedTextField
fieldErrors={fieldErrors || {}}
name="tz_label_t"
label={LL.TIME_ZONE() + ' Thermostat'}
sx={{ width: '30ch' }}
variant="outlined"
value={selectedTzValueT}
onChange={changeTimeZoneT}
margin="normal"
select
>
<MenuItem disabled>{LL.TIME_ZONE()}...</MenuItem>
{timeZoneItemsT}
</ValidatedTextField>
)}
</Grid>
</Grid>
)}
<Box sx={{ display: 'flex', flexWrap: 'wrap' }}> <Box sx={{ display: 'flex', flexWrap: 'wrap' }}>
{!data.enabled && !dirtyFlags.length && ( {!data.enabled && !dirtyFlags.length && (
<Box sx={{ flexWrap: 'nowrap', whiteSpace: 'nowrap' }}> <Box sx={{ flexWrap: 'nowrap', whiteSpace: 'nowrap' }}>
@@ -243,6 +306,7 @@ const NTPSettings = () => {
updateFormValue, updateFormValue,
fieldErrors, fieldErrors,
selectedTzValue, selectedTzValue,
selectedTzValueT,
changeTimeZone, changeTimeZone,
timeZoneItems, timeZoneItems,
dirtyFlags, dirtyFlags,
+4
View File
@@ -495,3 +495,7 @@ const precomputedTimeZoneItems = TIME_ZONE_LABELS.map((label) => (
export function timeZoneSelectItems() { export function timeZoneSelectItems() {
return precomputedTimeZoneItems; return precomputedTimeZoneItems;
} }
export function timeZoneSelectItemsT() {
return precomputedTimeZoneItems;
}
@@ -28,7 +28,7 @@ interface LanguageOption {
} }
const LANGUAGE_OPTIONS: LanguageOption[] = [ const LANGUAGE_OPTIONS: LanguageOption[] = [
{ key: 'cz', flag: CZflag, label: 'CZ' }, { key: 'cs', flag: CZflag, label: 'CS' },
{ key: 'de', flag: DEflag, label: 'DE' }, { key: 'de', flag: DEflag, label: 'DE' },
{ key: 'en', flag: GBflag, label: 'EN' }, { key: 'en', flag: GBflag, label: 'EN' },
{ key: 'fr', flag: FRflag, label: 'FR' }, { key: 'fr', flag: FRflag, label: 'FR' },
@@ -1,6 +1,6 @@
import type { Translation } from '../i18n-types'; import type { Translation } from '../i18n-types';
const cz: Translation = { const cs: Translation = {
LANGUAGE: 'Jazyk', LANGUAGE: 'Jazyk',
RETRY: 'Zkusit znovu', RETRY: 'Zkusit znovu',
LOADING: 'Načítání', LOADING: 'Načítání',
@@ -368,4 +368,4 @@ const cz: Translation = {
}; };
export default cz; export default cs;
+3
View File
@@ -16,6 +16,9 @@ export interface NTPSettingsType {
server: string; server: string;
tz_label: string; tz_label: string;
tz_format: string; tz_format: string;
thermostat_sync: number;
tz_label_t: string;
tz_format_t: string;
} }
export interface Time { export interface Time {
+6 -2
View File
@@ -79,8 +79,12 @@ class DummySettings {
uint8_t provisionMode = 0; uint8_t provisionMode = 0;
// NTP // NTP
String server = "pool.ntp.org"; String server = "pool.ntp.org";
String tzLabel = "Europe/London"; String tzLabel = "Europe/London";
String tzFormat = "GMT0BST,M3.5.0/1,M10.5.0";
String tzLabelT = "Europe/Berlin";
String tzFormatT = "CET-1CEST,M3.5.0,M10.5.0/3";
uint8_t thermostat_sync = 0;
static void read(DummySettings & settings, JsonObject root) {}; static void read(DummySettings & settings, JsonObject root) {};
static void read(DummySettings & settings) {}; static void read(DummySettings & settings) {};
+3 -2
View File
@@ -2,6 +2,7 @@
"name": "mock-api", "name": "mock-api",
"version": "3.8.2", "version": "3.8.2",
"description": "mock api for EMS-ESP", "description": "mock api for EMS-ESP",
"type": "module",
"author": "proddy, emsesp.org", "author": "proddy, emsesp.org",
"license": "MIT", "license": "MIT",
"scripts": { "scripts": {
@@ -12,8 +13,8 @@
"@msgpack/msgpack": "^3.1.3", "@msgpack/msgpack": "^3.1.3",
"@trivago/prettier-plugin-sort-imports": "^6.0.2", "@trivago/prettier-plugin-sort-imports": "^6.0.2",
"formidable": "^3.5.4", "formidable": "^3.5.4",
"itty-router": "^5.0.23", "itty-router": "^5.0.24",
"prettier": "^3.8.3" "prettier": "^3.9.6"
}, },
"packageManager": "pnpm@10.33.4+sha512.1c67b3b359b2d408119ba1ed289f34b8fc3c6873412bec6fd264fbdc82489e510fcbecb9ce9d22dae7f3b76269d8441046014bdca53b9979cd7a561ad631b800" "packageManager": "pnpm@10.33.4+sha512.1c67b3b359b2d408119ba1ed289f34b8fc3c6873412bec6fd264fbdc82489e510fcbecb9ce9d22dae7f3b76269d8441046014bdca53b9979cd7a561ad631b800"
} }
+63 -63
View File
@@ -13,54 +13,54 @@ importers:
version: 3.1.3 version: 3.1.3
'@trivago/prettier-plugin-sort-imports': '@trivago/prettier-plugin-sort-imports':
specifier: ^6.0.2 specifier: ^6.0.2
version: 6.0.2(prettier@3.8.3) version: 6.0.2(prettier@3.9.6)
formidable: formidable:
specifier: ^3.5.4 specifier: ^3.5.4
version: 3.5.4 version: 3.5.4
itty-router: itty-router:
specifier: ^5.0.23 specifier: ^5.0.24
version: 5.0.23 version: 5.0.24
prettier: prettier:
specifier: ^3.8.3 specifier: ^3.9.6
version: 3.8.3 version: 3.9.6
packages: packages:
'@babel/code-frame@7.29.0': '@babel/code-frame@7.29.7':
resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
'@babel/generator@7.29.1': '@babel/generator@7.29.8':
resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} resolution: {integrity: sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
'@babel/helper-globals@7.28.0': '@babel/helper-globals@7.29.7':
resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
'@babel/helper-string-parser@7.27.1': '@babel/helper-string-parser@7.29.7':
resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
'@babel/helper-validator-identifier@7.28.5': '@babel/helper-validator-identifier@7.29.7':
resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
'@babel/parser@7.29.3': '@babel/parser@7.29.8':
resolution: {integrity: sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==} resolution: {integrity: sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==}
engines: {node: '>=6.0.0'} engines: {node: '>=6.0.0'}
hasBin: true hasBin: true
'@babel/template@7.28.6': '@babel/template@7.29.7':
resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
'@babel/traverse@7.29.0': '@babel/traverse@7.29.8':
resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} resolution: {integrity: sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
'@babel/types@7.29.0': '@babel/types@7.29.8':
resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} resolution: {integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
'@jridgewell/gen-mapping@0.3.13': '@jridgewell/gen-mapping@0.3.13':
@@ -112,8 +112,8 @@ packages:
balanced-match@1.0.2: balanced-match@1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
brace-expansion@2.1.0: brace-expansion@2.1.4:
resolution: {integrity: sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==} resolution: {integrity: sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==}
debug@4.4.3: debug@4.4.3:
resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
@@ -131,8 +131,8 @@ packages:
resolution: {integrity: sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==} resolution: {integrity: sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==}
engines: {node: '>=14.0.0'} engines: {node: '>=14.0.0'}
itty-router@5.0.23: itty-router@5.0.24:
resolution: {integrity: sha512-i49WU+SNPrwOZA4Z61En1RYd5h2Lcqa+5IvCpMrNi4dxymzJK15ozUUnRrWIUAv95Zamd4eJPAot2UvHRrQg7w==} resolution: {integrity: sha512-PXij1qGKtE6jg3dLgrTRdh/Frovl5Wc4Av67sD2xfhDPrgNt4u79++/il8/c/OfZGkhD/8uYJub8tjo+H165rA==}
javascript-natural-sort@0.7.1: javascript-natural-sort@0.7.1:
resolution: {integrity: sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==} resolution: {integrity: sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==}
@@ -167,8 +167,8 @@ packages:
picocolors@1.1.1: picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
prettier@3.8.3: prettier@3.9.6:
resolution: {integrity: sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==} resolution: {integrity: sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==}
engines: {node: '>=14'} engines: {node: '>=14'}
hasBin: true hasBin: true
@@ -177,52 +177,52 @@ packages:
snapshots: snapshots:
'@babel/code-frame@7.29.0': '@babel/code-frame@7.29.7':
dependencies: dependencies:
'@babel/helper-validator-identifier': 7.28.5 '@babel/helper-validator-identifier': 7.29.7
js-tokens: 4.0.0 js-tokens: 4.0.0
picocolors: 1.1.1 picocolors: 1.1.1
'@babel/generator@7.29.1': '@babel/generator@7.29.8':
dependencies: dependencies:
'@babel/parser': 7.29.3 '@babel/parser': 7.29.8
'@babel/types': 7.29.0 '@babel/types': 7.29.8
'@jridgewell/gen-mapping': 0.3.13 '@jridgewell/gen-mapping': 0.3.13
'@jridgewell/trace-mapping': 0.3.31 '@jridgewell/trace-mapping': 0.3.31
jsesc: 3.1.0 jsesc: 3.1.0
'@babel/helper-globals@7.28.0': {} '@babel/helper-globals@7.29.7': {}
'@babel/helper-string-parser@7.27.1': {} '@babel/helper-string-parser@7.29.7': {}
'@babel/helper-validator-identifier@7.28.5': {} '@babel/helper-validator-identifier@7.29.7': {}
'@babel/parser@7.29.3': '@babel/parser@7.29.8':
dependencies: dependencies:
'@babel/types': 7.29.0 '@babel/types': 7.29.8
'@babel/template@7.28.6': '@babel/template@7.29.7':
dependencies: dependencies:
'@babel/code-frame': 7.29.0 '@babel/code-frame': 7.29.7
'@babel/parser': 7.29.3 '@babel/parser': 7.29.8
'@babel/types': 7.29.0 '@babel/types': 7.29.8
'@babel/traverse@7.29.0': '@babel/traverse@7.29.8':
dependencies: dependencies:
'@babel/code-frame': 7.29.0 '@babel/code-frame': 7.29.7
'@babel/generator': 7.29.1 '@babel/generator': 7.29.8
'@babel/helper-globals': 7.28.0 '@babel/helper-globals': 7.29.7
'@babel/parser': 7.29.3 '@babel/parser': 7.29.8
'@babel/template': 7.28.6 '@babel/template': 7.29.7
'@babel/types': 7.29.0 '@babel/types': 7.29.8
debug: 4.4.3 debug: 4.4.3
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@babel/types@7.29.0': '@babel/types@7.29.8':
dependencies: dependencies:
'@babel/helper-string-parser': 7.27.1 '@babel/helper-string-parser': 7.29.7
'@babel/helper-validator-identifier': 7.28.5 '@babel/helper-validator-identifier': 7.29.7
'@jridgewell/gen-mapping@0.3.13': '@jridgewell/gen-mapping@0.3.13':
dependencies: dependencies:
@@ -246,17 +246,17 @@ snapshots:
dependencies: dependencies:
'@noble/hashes': 1.8.0 '@noble/hashes': 1.8.0
'@trivago/prettier-plugin-sort-imports@6.0.2(prettier@3.8.3)': '@trivago/prettier-plugin-sort-imports@6.0.2(prettier@3.9.6)':
dependencies: dependencies:
'@babel/generator': 7.29.1 '@babel/generator': 7.29.8
'@babel/parser': 7.29.3 '@babel/parser': 7.29.8
'@babel/traverse': 7.29.0 '@babel/traverse': 7.29.8
'@babel/types': 7.29.0 '@babel/types': 7.29.8
javascript-natural-sort: 0.7.1 javascript-natural-sort: 0.7.1
lodash-es: 4.18.1 lodash-es: 4.18.1
minimatch: 9.0.9 minimatch: 9.0.9
parse-imports-exports: 0.2.4 parse-imports-exports: 0.2.4
prettier: 3.8.3 prettier: 3.9.6
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@@ -264,7 +264,7 @@ snapshots:
balanced-match@1.0.2: {} balanced-match@1.0.2: {}
brace-expansion@2.1.0: brace-expansion@2.1.4:
dependencies: dependencies:
balanced-match: 1.0.2 balanced-match: 1.0.2
@@ -283,7 +283,7 @@ snapshots:
dezalgo: 1.0.4 dezalgo: 1.0.4
once: 1.4.0 once: 1.4.0
itty-router@5.0.23: {} itty-router@5.0.24: {}
javascript-natural-sort@0.7.1: {} javascript-natural-sort@0.7.1: {}
@@ -295,7 +295,7 @@ snapshots:
minimatch@9.0.9: minimatch@9.0.9:
dependencies: dependencies:
brace-expansion: 2.1.0 brace-expansion: 2.1.4
ms@2.1.3: {} ms@2.1.3: {}
@@ -311,6 +311,6 @@ snapshots:
picocolors@1.1.1: {} picocolors@1.1.1: {}
prettier@3.8.3: {} prettier@3.9.6: {}
wrappy@1.0.2: {} wrappy@1.0.2: {}
+3 -3
View File
@@ -141,8 +141,8 @@ let system_status = {
let DEV_VERSION_IS_UPGRADEABLE: boolean; let DEV_VERSION_IS_UPGRADEABLE: boolean;
let STABLE_VERSION_IS_UPGRADEABLE: boolean; let STABLE_VERSION_IS_UPGRADEABLE: boolean;
let THIS_VERSION: string; let THIS_VERSION: string;
let LATEST_STABLE_VERSION = '3.8.2'; let LATEST_STABLE_VERSION = '3.8.3';
let LATEST_DEV_VERSION = '3.8.3-dev.2'; let LATEST_DEV_VERSION = '3.9.0-dev.0';
// scenarios for testing versioning // scenarios for testing versioning
let version_test = 0; // on latest stable, or switch to dev let version_test = 0; // on latest stable, or switch to dev
@@ -4139,7 +4139,7 @@ let emsesp_schedule = {
id: 5, id: 5,
active: false, active: false,
flags: 130, flags: 130,
time: 'system/network info/rssi < -70', time: 'system/network/rssi < -70',
cmd: 'system/restart', cmd: 'system/restart',
value: '', value: '',
name: 'bad_wifi' name: 'bad_wifi'
+2 -2
View File
@@ -39,7 +39,7 @@ build_flags =
-D CONFIG_ASYNC_TCP_PRIORITY=10 ; default -D CONFIG_ASYNC_TCP_PRIORITY=10 ; default
-D CONFIG_ASYNC_TCP_QUEUE_SIZE=64 ; default -D CONFIG_ASYNC_TCP_QUEUE_SIZE=64 ; default
-D CONFIG_ASYNC_TCP_RUNNING_CORE=1 ; force async_tcp task to be on same core as Arduino app (default is any core) -D CONFIG_ASYNC_TCP_RUNNING_CORE=1 ; force async_tcp task to be on same core as Arduino app (default is any core)
-D CONFIG_ASYNC_TCP_STACK_SIZE=6144 ; default is 16KB/8192*2 -D CONFIG_ASYNC_TCP_STACK_SIZE=8192 ; default is 16KB/8192*2
; ESPAsyncWebServer ; ESPAsyncWebServer
; -D WS_MAX_QUEUED_MESSAGES=0 ; not used, default 8 ; -D WS_MAX_QUEUED_MESSAGES=0 ; not used, default 8
; -D SSE_MAX_QUEUED_MESSAGES=1 ; for log messages, default 32 ; -D SSE_MAX_QUEUED_MESSAGES=1 ; for log messages, default 32
@@ -105,7 +105,7 @@ board_build.filesystem = littlefs
lib_deps = lib_deps =
bblanchon/ArduinoJson @ 7.4.3 bblanchon/ArduinoJson @ 7.4.3
ESP32Async/AsyncTCP @ 3.4.10 ESP32Async/AsyncTCP @ 3.4.10
ESP32Async/ESPAsyncWebServer @ 3.11.0 ESP32Async/ESPAsyncWebServer @ 3.11.2
; https://github.com/emsesp/EMS-ESP-Modules.git @ 1.0.8 ; https://github.com/emsesp/EMS-ESP-Modules.git @ 1.0.8
; builds the web interface only, not the firmware ; builds the web interface only, not the firmware
-28
View File
@@ -88,34 +88,6 @@ def bin_copy(source, target, env):
print(f"MD5: {md5_hash}") print(f"MD5: {md5_hash}")
md5_file.write_text(md5_hash) md5_file.write_text(md5_hash)
# Make a copy using the old 3.6.x filename format for backwards compatibility
# Note: there is a chance newer E32V2s (which use the 16MB partition table and PSRAM)
# are running a custom build of the 3.6.5 firmware as 3.6.5 was released before
# production of the gateway board. Updating via the WebUI will break the system
# and require a manual update.
pio_env = env.get('PIOENV', '')
extra_variant = None
if pio_env == "s3_16M_P":
extra_variant = f"EMS-ESP-{app_version.replace('.', '_')}-ESP32_S3"
elif pio_env == "s_4M":
extra_variant = f"EMS-ESP-{app_version.replace('.', '_')}-ESP32"
if extra_variant:
extra_bin_file = firmware_dir / f"{extra_variant}.bin"
extra_md5_file = firmware_dir / f"{extra_variant}.md5"
# Remove existing files if they exist
for file_path in [extra_bin_file, extra_md5_file]:
if file_path.exists():
file_path.unlink()
# Copy files
shutil.copy2(str(bin_file), str(extra_bin_file))
shutil.copy2(str(md5_file), str(extra_md5_file))
print(f"Filename copy for 3.6.x: {extra_bin_file}")
print("=" * 90) print("=" * 90)
+17 -11
View File
@@ -3,28 +3,34 @@
# run from root folder, like `sh ./scripts/update_all.sh` # run from root folder, like `sh ./scripts/update_all.sh`
# as well as GNUMake (make) and python3 # as well as GNUMake (make) and python3
# abort on first error so a failed lint/build isn't silently swallowed
set -e
cd interface cd interface
rm -rf node_modules rm -rf node_modules
corepack use pnpm@latest-10 corepack use pnpm@latest
pnpm update --latest pnpm update
pnpm install pnpm install
pnpm format pnpm format
pnpm lint pnpm lint
pnpm build-webUI
cd ../mock-api cd ../mock-api
rm -rf node_modules rm -rf node_modules
corepack use pnpm@latest-10 corepack use pnpm@latest
pnpm update --latest pnpm update
pnpm install pnpm install
pnpm format pnpm format
cd ..
cd interface
pnpm build-webUI
cd .. cd ..
npx cspell "**" npx cspell "**"
# platformio run -e build_modbus # build files that go into docs folder
# platformio run -e build_standalone
# platformio run -e native-test -t exec platformio run -e build_modbus -t clean
platformio run -e build_modbus
platformio run -e build_standalone -t clean
platformio run -e build_standalone
# run tests
platformio run -e native-test -t exec
+15 -9
View File
@@ -86,16 +86,22 @@ void NTPSettingsService::ntp_received(struct timeval * tv) {
} }
void NTPSettings::read(NTPSettings & settings, JsonObject root) { void NTPSettings::read(NTPSettings & settings, JsonObject root) {
root["enabled"] = settings.enabled; root["enabled"] = settings.enabled;
root["server"] = settings.server; root["server"] = settings.server;
root["tz_label"] = settings.tzLabel; root["tz_label"] = settings.tzLabel;
root["tz_format"] = settings.tzFormat; root["tz_format"] = settings.tzFormat;
root["thermostat_sync"] = settings.thermostat_sync;
root["tz_label_t"] = settings.tzLabelT;
root["tz_format_t"] = settings.tzFormatT;
} }
StateUpdateResult NTPSettings::update(JsonObject root, NTPSettings & settings) { StateUpdateResult NTPSettings::update(JsonObject root, NTPSettings & settings) {
settings.enabled = root["enabled"] | FACTORY_NTP_ENABLED; settings.enabled = root["enabled"] | FACTORY_NTP_ENABLED;
settings.server = root["server"] | FACTORY_NTP_SERVER; settings.server = root["server"] | FACTORY_NTP_SERVER;
settings.tzLabel = root["tz_label"] | FACTORY_NTP_TIME_ZONE_LABEL; settings.tzLabel = root["tz_label"] | FACTORY_NTP_TIME_ZONE_LABEL;
settings.tzFormat = root["tz_format"] | FACTORY_NTP_TIME_ZONE_FORMAT; settings.tzFormat = root["tz_format"] | FACTORY_NTP_TIME_ZONE_FORMAT;
settings.thermostat_sync = root["thermostat_sync"] | FACTORY_NTP_THERMOSTAT_SYNC;
settings.tzLabelT = root["tz_label_t"] | FACTORY_NTP_TIME_ZONE_LABEL;
settings.tzFormatT = root["tz_format_t"] | FACTORY_NTP_TIME_ZONE_FORMAT;
return StateUpdateResult::CHANGED; return StateUpdateResult::CHANGED;
} }
+11 -4
View File
@@ -19,6 +19,10 @@
#define FACTORY_NTP_TIME_ZONE_FORMAT "GMT0BST,M3.5.0/1,M10.5.0" #define FACTORY_NTP_TIME_ZONE_FORMAT "GMT0BST,M3.5.0/1,M10.5.0"
#endif #endif
#ifndef FACTORY_NTP_THERMOSTAT_SYNC
#define FACTORY_NTP_THERMOSTAT_SYNC 1
#endif
#ifndef FACTORY_NTP_SERVER #ifndef FACTORY_NTP_SERVER
#define FACTORY_NTP_SERVER "time.google.com" #define FACTORY_NTP_SERVER "time.google.com"
#endif #endif
@@ -30,10 +34,13 @@
class NTPSettings { class NTPSettings {
public: public:
bool enabled = FACTORY_NTP_ENABLED; bool enabled = FACTORY_NTP_ENABLED;
String tzLabel = FACTORY_NTP_TIME_ZONE_LABEL; String tzLabel = FACTORY_NTP_TIME_ZONE_LABEL;
String tzFormat = FACTORY_NTP_TIME_ZONE_FORMAT; String tzFormat = FACTORY_NTP_TIME_ZONE_FORMAT;
String server = FACTORY_NTP_SERVER; String server = FACTORY_NTP_SERVER;
uint8_t thermostat_sync = FACTORY_NTP_THERMOSTAT_SYNC;
String tzLabelT = FACTORY_NTP_TIME_ZONE_LABEL;
String tzFormatT = FACTORY_NTP_TIME_ZONE_FORMAT;
static void read(NTPSettings & settings, JsonObject root); static void read(NTPSettings & settings, JsonObject root);
static StateUpdateResult update(JsonObject root, NTPSettings & settings); static StateUpdateResult update(JsonObject root, NTPSettings & settings);
+6 -2
View File
@@ -15,7 +15,7 @@ void WiFiScanner::scanNetworks(AsyncWebServerRequest * request) {
if (WiFi.scanComplete() != -1) { if (WiFi.scanComplete() != -1) {
WiFi.scanDelete(); WiFi.scanDelete();
WiFi.scanNetworks(true); WiFi.scanNetworks(true, false, true, 250);
} }
} }
@@ -38,6 +38,10 @@ void WiFiScanner::listNetworks(AsyncWebServerRequest * request) {
} else if (numNetworks == -1) { } else if (numNetworks == -1) {
request->send(202); // special code to indicate scan in progress request->send(202); // special code to indicate scan in progress
} else { } else {
scanNetworks(request); auto * response = new AsyncJsonResponse(false);
JsonObject root = response->getRoot();
root["networks"].to<JsonArray>();
response->setLength();
request->send(response);
} }
} }
+4 -2
View File
@@ -546,11 +546,13 @@ bool AnalogSensor::update(uint8_t gpio, const char * org_name, double offset, do
if (deleted) { if (deleted) {
LOG_DEBUG("Removing analog sensor GPIO %02d", gpio); LOG_DEBUG("Removing analog sensor GPIO %02d", gpio);
EMSESP::system_.remove_gpio(gpio); // remove from used list only EMSESP::system_.remove_gpio(gpio); // remove from used list only
EMSESP::nvs_.remove(AnalogCustomization.name); if (EMSESP::nvs_.isKey(AnalogCustomization.name)) {
EMSESP::nvs_.remove(AnalogCustomization.name);
}
settings.analogCustomizations.remove(AnalogCustomization); settings.analogCustomizations.remove(AnalogCustomization);
} else { } else {
// update existing record // update existing record
if (!strcmp(name, AnalogCustomization.name)) { if (!strcmp(name, AnalogCustomization.name) && EMSESP::nvs_.isKey(AnalogCustomization.name)) {
EMSESP::nvs_.remove(AnalogCustomization.name); EMSESP::nvs_.remove(AnalogCustomization.name);
} }
strlcpy(AnalogCustomization.name, name, sizeof(AnalogCustomization.name)); strlcpy(AnalogCustomization.name, name, sizeof(AnalogCustomization.name));
+23 -11
View File
@@ -1800,8 +1800,8 @@ void EMSESP::start() {
LOG_INFO("Library loaded: %d EMS devices, %d device entities, %s", device_library_.size(), EMSESP_TRANSLATION_COUNT, system_.languages_string().c_str()); LOG_INFO("Library loaded: %d EMS devices, %d device entities, %s", device_library_.size(), EMSESP_TRANSLATION_COUNT, system_.languages_string().c_str());
webCustomizationService.begin(); // load the customizations webCustomizationService.begin(); // load the customizations
webSchedulerService.begin(); // load the scheduler events
webCustomEntityService.begin(); // load the custom telegram reads webCustomEntityService.begin(); // load the custom telegram reads
webSchedulerService.begin(); // load the scheduler events
// start telnet service if it's enabled // start telnet service if it's enabled
// default idle is 10 minutes, default write timeout is 0 (automatic) // default idle is 10 minutes, default write timeout is 0 (automatic)
@@ -1864,16 +1864,9 @@ void EMSESP::loop() {
webLogService.loop(); // log in Web UI webLogService.loop(); // log in Web UI
// run the loop, unless we're in the middle of an OTA upload // run the loop, unless we're in the middle of an OTA upload
if (EMSESP::system_.systemStatus() == SYSTEM_STATUS::SYSTEM_STATUS_NORMAL || EMSESP::system_.systemStatus() == SYSTEM_STATUS::SYSTEM_STATUS_INVALID_GPIO) { // if (EMSESP::system_.systemStatus() == SYSTEM_STATUS::SYSTEM_STATUS_NORMAL || EMSESP::system_.systemStatus() == SYSTEM_STATUS::SYSTEM_STATUS_INVALID_GPIO) {
// check for GPIO Errors - this is called once when booting if (EMSESP::system_.systemStatus() != SYSTEM_STATUS::SYSTEM_STATUS_PENDING_UPLOAD
if (EMSESP::system_.systemStatus() == SYSTEM_STATUS::SYSTEM_STATUS_INVALID_GPIO) { && EMSESP::system_.systemStatus() != SYSTEM_STATUS::SYSTEM_STATUS_UPLOADING) {
static bool only_once = false;
if (!only_once) {
LOG_ERROR("Invalid GPIOs used. Please check your settings and the system log");
only_once = true;
}
}
// loop through the services // loop through the services
rxservice_.loop(); // process any incoming Rx telegrams rxservice_.loop(); // process any incoming Rx telegrams
shower_.loop(); // check for shower on/off shower_.loop(); // check for shower on/off
@@ -1887,6 +1880,14 @@ void EMSESP::loop() {
} }
scheduled_fetch_values(); // force a query on the EMS devices to fetch latest data at a set interval (1 min) scheduled_fetch_values(); // force a query on the EMS devices to fetch latest data at a set interval (1 min)
} }
// check for GPIO Errors - this is called once when booting
if (EMSESP::system_.systemStatus() == SYSTEM_STATUS::SYSTEM_STATUS_INVALID_GPIO) {
static bool only_once = false;
if (!only_once) {
LOG_ERROR("Invalid GPIOs used. Please check your settings and the system log");
only_once = true;
}
}
if (EMSESP::system_.systemStatus() == SYSTEM_STATUS::SYSTEM_STATUS_PENDING_UPLOAD) { if (EMSESP::system_.systemStatus() == SYSTEM_STATUS::SYSTEM_STATUS_PENDING_UPLOAD) {
// start an upload from a URL, assuming the URL exists and set from a previous pass // start an upload from a URL, assuming the URL exists and set from a previous pass
@@ -1899,6 +1900,17 @@ void EMSESP::loop() {
} }
} }
// reset status after 5 Minutes
if (EMSESP::system_.systemStatus() != SYSTEM_STATUS::SYSTEM_STATUS_NORMAL) {
static uint32_t starttime = 0;
if (starttime == 0) {
starttime = uuid::get_uptime_ms();
} else if (uuid::get_uptime_ms() - starttime > 300000) {
starttime = 0;
EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_NORMAL);
}
}
// telnet service // telnet service
#ifndef EMSESP_STANDALONE #ifndef EMSESP_STANDALONE
if (system_.telnet_enabled()) { if (system_.telnet_enabled()) {
+14 -1
View File
@@ -817,7 +817,7 @@ std::string Helpers::toUpper(std::string const & s) {
// capitalizes one UTF-8 character in char array // capitalizes one UTF-8 character in char array
// works with Latin1 (1 byte), Polish and other (2 bytes) characters // works with Latin1 (1 byte), Polish and other (2 bytes) characters
// supports special characters for all 11 supported languages: EN, DE, NL, SV, PL, NO, FR, TR, IT, SK, CZ // supports special characters for all 11 supported languages: EN, DE, NL, SV, PL, NO, FR, TR, IT, SK, CS
#if defined(EMSESP_STANDALONE) #if defined(EMSESP_STANDALONE)
#pragma GCC diagnostic push #pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wtype-limits" #pragma GCC diagnostic ignored "-Wtype-limits"
@@ -1033,4 +1033,17 @@ float Helpers::numericoperator2scalefactor(int8_t numeric_operator) {
return -numeric_operator; return -numeric_operator;
} }
int16_t Helpers::calc_dew(int16_t temp, uint8_t humi) {
if (humi == EMS_VALUE_UINT8_NOTSET || temp == EMS_VALUE_INT16_NOTSET) {
return EMS_VALUE_INT16_NOTSET;
}
const float k2 = 17.62;
const float k3 = 243.12;
const float t = (float)temp / 10;
const float h = (float)humi / 100;
int16_t dt = (10 * k3 * (((k2 * t) / (k3 + t)) + log(h)) / (((k2 * k3) / (k3 + t)) - log(h)));
return dt;
}
} // namespace emsesp } // namespace emsesp
+1
View File
@@ -84,6 +84,7 @@ class Helpers {
static uint8_t count_items(const char * const * list); static uint8_t count_items(const char * const * list);
static const char * translated_word(const char * const * strings, const bool force_en = false); static const char * translated_word(const char * const * strings, const bool force_en = false);
static int16_t calc_dew(int16_t temp, uint8_t hum);
#ifdef EMSESP_STANDALONE #ifdef EMSESP_STANDALONE
static char * ultostr(char * ptr, uint32_t value, const uint8_t base); static char * ultostr(char * ptr, uint32_t value, const uint8_t base);
+3 -3
View File
@@ -30,9 +30,9 @@
#define EMSESP_LOCALE_TR "tr" #define EMSESP_LOCALE_TR "tr"
#define EMSESP_LOCALE_IT "it" #define EMSESP_LOCALE_IT "it"
#define EMSESP_LOCALE_SK "sk" #define EMSESP_LOCALE_SK "sk"
#define EMSESP_LOCALE_CZ "cz" #define EMSESP_LOCALE_CS "cs"
// IMPORTANT! translations are in the order: en, de, nl, sv, pl, no, fr, tr, it, sk, cz // IMPORTANT! translations are in the order: en, de, nl, sv, pl, no, fr, tr, it, sk, cs
// //
// if there is no translation, it will default to en // if there is no translation, it will default to en
@@ -367,7 +367,7 @@ MAKE_WORD_TRANSLATION(bookshelf, "mdi:bookshelf", "mdi:bookshelf")
// MQTT Discovery - this is special device entity for 'climate' // MQTT Discovery - this is special device entity for 'climate'
MAKE_TRANSLATION(haclimate, "haclimate", "mqtt discovery current room temperature", "Discovery aktuelle Raumtemperatur", "Discovery huidige kamertemperatuur", "MQTT Discovery för aktuell rumstemperatur", "termostat w HA", "HA Avlest temp", "découverte mqtt température actuelle", "Güncel osa sıcaklığı", "verifica temperatura ambiente attuale", "mqtt discovery aktuálna teplota v miestnosti", "mqtt discovery aktuální pokojová teplota") MAKE_TRANSLATION(haclimate, "haclimate", "mqtt discovery current room temperature", "Discovery aktuelle Raumtemperatur", "Discovery huidige kamertemperatuur", "MQTT Discovery för aktuell rumstemperatur", "termostat w HA", "HA Avlest temp", "découverte mqtt température actuelle", "Güncel osa sıcaklığı", "verifica temperatura ambiente attuale", "mqtt discovery aktuálna teplota v miestnosti", "mqtt discovery aktuální pokojová teplota")
// Entity translations: tag, mqtt, en, de, nl, sv, pl, no, fr, tr, it, sk, cz // Entity translations: tag, mqtt, en, de, nl, sv, pl, no, fr, tr, it, sk, cs
// Boiler // Boiler
MAKE_TRANSLATION(chimneysweeper, "chimneysweeper", "chimney sweeper", "Schornsteinfeger", "schoorsteenveger", "Sotare", "tryb kominiarza", "feier", "ramoneur", "baca temizleyici", "spazzacamino", "kominár", "kominík") MAKE_TRANSLATION(chimneysweeper, "chimneysweeper", "chimney sweeper", "Schornsteinfeger", "schoorsteenveger", "Sotare", "tryb kominiarza", "feier", "ramoneur", "baca temizleyici", "spazzacamino", "kominár", "kominík")
MAKE_TRANSLATION(reset, "reset", "reset", "Reset", "Reset", "Återställ", "kasowanie komunikatu", "nullstill", "reset", "Sıfırla", "Reset", "reset", "reset") MAKE_TRANSLATION(reset, "reset", "reset", "Reset", "Reset", "Återställ", "kasowanie komunikatu", "nullstill", "reset", "Sıfırla", "Reset", "reset", "reset")
-14
View File
@@ -637,20 +637,6 @@ const std::initializer_list<Modbus::EntityModbusInfo> Modbus::modbus_register_ma
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwDiffTemp), 13, 1), // difftemp REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwDiffTemp), 13, 1), // difftemp
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwRedTemp), 14, 1), // redtemp REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwRedTemp), 14, 1), // redtemp
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwRequiredTemp), 15, 1), // requiredtemp REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwRequiredTemp), 15, 1), // requiredtemp
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwStorageTemp1), 16, 1), // storagetemp1
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwColdTemp), 17, 1), // coldtemp
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwTemp5), 18, 1), // temp5
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(retTemp), 19, 1), // rettemp
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwHotTemp), 20, 1), // hottemp
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwDailyTemp), 21, 1), // dailytemp
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwCircTc), 22, 1), // circtc
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwKeepWarm), 23, 1), // keepwarm
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwStatus2), 24, 1), // status2
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwPumpMod), 25, 1), // pumpmod
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwFlow), 26, 1), // flow
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(valveReturn), 27, 1), // valvereturn
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(deltaTRet), 28, 1), // deltatret
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(errorDisp), 29, 1), // errordisp
}; };
} // namespace emsesp } // namespace emsesp
+16 -25
View File
@@ -377,19 +377,23 @@ void Roomctrl::temperature(uint8_t addr, uint8_t dst, uint8_t hc) {
// send telegram 0x047B only for RC100H // send telegram 0x047B only for RC100H
void Roomctrl::humidity(uint8_t addr, uint8_t dst, uint8_t hc) { void Roomctrl::humidity(uint8_t addr, uint8_t dst, uint8_t hc) {
int16_t dew = Helpers::calc_dew(remotetemp_[hc], remotehum_[hc]);
int8_t dew8 = EMS_VALUE_INT8_NOTSET;
if (dew != EMS_VALUE_INT16_NOTSET) {
dew8 = static_cast<int8_t>((dew >= 0 ? dew + 5 : dew - 5) / 10);
}
uint8_t data[11]; uint8_t data[11];
data[0] = addr | EMSbus::ems_mask(); data[0] = addr | EMSbus::ems_mask();
data[1] = dst & 0x7F; data[1] = dst & 0x7F;
uint16_t dew = calc_dew(remotetemp_[hc], remotehum_[hc]); data[2] = 0xFF;
data[2] = 0xFF; data[3] = 0;
data[3] = 0; data[4] = 3;
data[4] = 3; data[5] = 0x7B + hc;
data[5] = 0x7B + hc; data[6] = static_cast<uint8_t>(dew8);
data[6] = dew == EMS_VALUE_INT16_NOTSET ? EMS_VALUE_INT8_NOTSET : (uint8_t)((dew + 5) / 10); data[7] = remotehum_[hc];
data[7] = remotehum_[hc]; data[8] = static_cast<uint8_t>(dew >> 8);
data[8] = (uint8_t)(dew << 8); data[9] = static_cast<uint8_t>(dew & 0x00FF);
data[9] = (uint8_t)(dew & 0xFF); data[10] = EMSbus::calculate_crc(data, 10); // append CRC
data[10] = EMSbus::calculate_crc(data, 10); // append CRC
EMSuart::transmit(data, 11); EMSuart::transmit(data, 11);
} }
@@ -436,17 +440,4 @@ void Roomctrl::replyF7(uint8_t addr, uint8_t dst, uint8_t offset, uint8_t typehh
EMSuart::transmit(data, 10); EMSuart::transmit(data, 10);
} }
int16_t Roomctrl::calc_dew(int16_t temp, uint8_t humi) {
if (humi == EMS_VALUE_UINT8_NOTSET || temp == EMS_VALUE_INT16_NOTSET) {
return EMS_VALUE_INT16_NOTSET;
}
const float k2 = 17.62;
const float k3 = 243.12;
const float t = (float)temp / 10;
const float h = (float)humi / 100;
int16_t dt = (10 * k3 * (((k2 * t) / (k3 + t)) + log(h)) / (((k2 * k3) / (k3 + t)) - log(h)));
return dt;
}
} // namespace emsesp } // namespace emsesp
-1
View File
@@ -50,7 +50,6 @@ class Roomctrl {
static void nack_write(); static void nack_write();
static void ack_write(); static void ack_write();
static void replyF7(uint8_t addr, uint8_t dst, uint8_t offset, uint8_t typehh, uint8_t typeh, uint8_t typel, uint8_t hc); static void replyF7(uint8_t addr, uint8_t dst, uint8_t offset, uint8_t typehh, uint8_t typeh, uint8_t typel, uint8_t hc);
static int16_t calc_dew(int16_t temp, uint8_t hum);
static bool switch_off_[HCS]; static bool switch_off_[HCS];
static uint32_t send_time_[HCS]; static uint32_t send_time_[HCS];
+59 -55
View File
@@ -39,17 +39,8 @@ std::deque<Token> exprToTokens(const std::string & expr) {
i += (*p == '{') ? 1 : (*p == '}') ? -1 : 0; i += (*p == '{') ? 1 : (*p == '}') ? -1 : 0;
++p; ++p;
} }
if (*p) { const auto s = std::string(b, p);
++p; tokens.emplace_back(Token::Type::String, s, -3);
}
// Use string_view to avoid unnecessary string copies
std::string_view s(b, p - b);
auto n = s.find("\"\"");
while (n != std::string_view::npos) {
s.remove_prefix(n + 2);
n = s.find("\"\"");
}
tokens.emplace_back(Token::Type::String, std::string(s), -3);
if (*p == '\0') { if (*p == '\0') {
--p; --p;
} }
@@ -333,7 +324,10 @@ std::deque<Token> shuntingYard(const std::deque<Token> & tokens) {
// check if string is a number // check if string is a number
bool isnum(const std::string & s) { bool isnum(const std::string & s) {
if (!s.empty() && (s.find_first_not_of("0123456789.") == std::string::npos || (s[0] == '-' && s.find_first_not_of("0123456789.", 1) == std::string::npos))) { if (s.empty() || s.find_first_of("0123456789") == std::string::npos) {
return false;
}
if (s.find_first_not_of("0123456789.") == std::string::npos || (s[0] == '-' && s.find_first_not_of("0123456789.", 1) == std::string::npos)) {
return true; return true;
} }
return false; return false;
@@ -636,6 +630,10 @@ std::string calculate(const std::string & expr) {
stack.push_back(lhs + rhs); stack.push_back(lhs + rhs);
break; break;
} }
if (!isnum(rhs) || !isnum(lhs)) {
stack.push_back(lhs + token.str + rhs);
break;
}
auto lhd = std::stod(lhs); auto lhd = std::stod(lhs);
auto rhd = std::stod(rhs); auto rhd = std::stod(rhs);
switch (token.str[0]) { switch (token.str[0]) {
@@ -700,8 +698,7 @@ std::string compute(const std::string & expr) {
std::string cmd = expr_new.substr(f, e - f).c_str(); std::string cmd = expr_new.substr(f, e - f).c_str();
JsonDocument doc; JsonDocument doc;
if (DeserializationError::Ok == deserializeJson(doc, cmd)) { if (DeserializationError::Ok == deserializeJson(doc, cmd)) {
HTTPClient * http = new HTTPClient; std::string url, header_s, value_s, method_s, key_s, keys_s;
std::string url, header_s, value_s, method_s, key_s, keys_s;
// search keys lower case // search keys lower case
for (JsonPair p : doc.as<JsonObject>()) { for (JsonPair p : doc.as<JsonObject>()) {
if (Helpers::toLower(p.key().c_str()) == "url") { if (Helpers::toLower(p.key().c_str()) == "url") {
@@ -720,56 +717,63 @@ std::string compute(const std::string & expr) {
keys_s = p.key().c_str(); keys_s = p.key().c_str();
} }
} }
if (http->begin(url.c_str())) { if (url.substr(0, 4) == "http") { // match http and https
int httpResult = 0; HTTPClient * http = new HTTPClient;
for (JsonPair p : doc[header_s].as<JsonObject>()) { #ifndef EMSESP_STANDALONE
http->addHeader(p.key().c_str(), p.value().as<std::string>().c_str()); http->setConnectTimeout(10000);
} http->setTimeout(10000);
std::string value = doc[value_s] | ""; #endif
std::string method = doc[method_s] | "get"; if (http->begin(url.c_str())) {
int httpResult = 0;
// if there is data, force a POST for (JsonPair p : doc[header_s].as<JsonObject>()) {
if (value.length() || Helpers::toLower(method) == "post") { http->addHeader(p.key().c_str(), p.value().as<std::string>().c_str());
if (value.find_first_of('{') != std::string::npos) {
http->addHeader(asyncsrv::T_Content_Type, asyncsrv::T_application_json, false); // auto-set to JSON
} }
httpResult = http->POST(value.c_str()); std::string value = doc[value_s] | "";
} else { std::string method = doc[method_s] | "get";
httpResult = http->GET(); // normal GET
}
if (httpResult > 0) { // if there is data, force a POST
std::string result = http->getString().c_str(); if (value.length() || Helpers::toLower(method) == "post") {
std::string key = doc[key_s] | ""; if (value.find_first_of('{') != std::string::npos) {
JsonDocument keys_doc; // JsonDocument to hold "keys" after doc is parsed with HTTP body http->addHeader(asyncsrv::T_Content_Type, asyncsrv::T_application_json, false); // auto-set to JSON
if (doc[keys_s].is<JsonArray>()) { }
keys_doc.set(doc[keys_s].as<JsonArray>()); httpResult = http->POST(value.c_str());
} else {
httpResult = http->GET(); // normal GET
} }
JsonArray keys = keys_doc.as<JsonArray>();
if (key.length() || !keys.isNull()) { if (httpResult > 0) {
doc.clear(); std::string result = http->getString().c_str();
if (DeserializationError::Ok == deserializeJson(doc, result)) { std::string key = doc[key_s] | "";
if (key.length()) { JsonDocument keys_doc; // JsonDocument to hold "keys" after doc is parsed with HTTP body
result = doc[key.c_str()].as<std::string>(); if (doc[keys_s].is<JsonArray>()) {
} else { keys_doc.set(doc[keys_s].as<JsonArray>());
JsonVariant json = doc.as<JsonVariant>(); }
for (JsonVariant keys_key : keys) { JsonArray keys = keys_doc.as<JsonArray>();
if (keys_key.is<std::string>() && json.is<JsonObject>()) {
json = json[keys_key.as<std::string>()].as<JsonVariant>(); if (key.length() || !keys.isNull()) {
} else if (keys_key.is<int>() && json.is<JsonArray>()) { doc.clear();
json = json[keys_key.as<int>()].as<JsonVariant>(); if (DeserializationError::Ok == deserializeJson(doc, result)) {
} else { if (key.length()) {
break; // type mismatch result = doc[key.c_str()].as<std::string>();
} else {
JsonVariant json = doc.as<JsonVariant>();
for (JsonVariant keys_key : keys) {
if (keys_key.is<std::string>() && json.is<JsonObject>()) {
json = json[keys_key.as<std::string>()].as<JsonVariant>();
} else if (keys_key.is<int>() && json.is<JsonArray>()) {
json = json[keys_key.as<int>()].as<JsonVariant>();
} else {
break; // type mismatch
}
} }
result = json.as<std::string>();
} }
result = json.as<std::string>();
} }
} }
expr_new.replace(f, e - f, result.c_str());
} }
expr_new.replace(f, e - f, result.c_str()); http->end();
} }
http->end();
delete http; delete http;
} }
} }
@@ -821,4 +825,4 @@ std::string compute(const std::string & expr) {
return calculate(expr_new); return calculate(expr_new);
} }
} // namespace emsesp } // namespace emsesp
+9 -19
View File
@@ -30,7 +30,7 @@
#include <HTTPClient.h> #include <HTTPClient.h>
#include <map> #include <map>
#include "shuntingYard.h" // for compute() used by the message
#include "EMSESP_Version.h" #include "EMSESP_Version.h"
#if defined(EMSESP_TEST) #if defined(EMSESP_TEST)
@@ -61,7 +61,7 @@ const char * const languages[] = {EMSESP_LOCALE_EN,
EMSESP_LOCALE_TR, EMSESP_LOCALE_TR,
EMSESP_LOCALE_IT, EMSESP_LOCALE_IT,
EMSESP_LOCALE_SK, EMSESP_LOCALE_SK,
EMSESP_LOCALE_CZ}; EMSESP_LOCALE_CS};
#endif #endif
static constexpr uint8_t NUM_LANGUAGES = sizeof(languages) / sizeof(const char *); static constexpr uint8_t NUM_LANGUAGES = sizeof(languages) / sizeof(const char *);
@@ -222,22 +222,10 @@ bool System::command_message(const char * value, const int8_t id, JsonObject out
LOG_WARNING("Message is empty"); LOG_WARNING("Message is empty");
return false; // must have a string value return false; // must have a string value
} }
std::string computed_value = compute(value);
EMSESP::webSchedulerService.computed_value.clear(); LOG_INFO("Message: %s", computed_value.c_str()); // send to log
EMSESP::webSchedulerService.raw_value = value; Mqtt::queue_publish(F_(message), computed_value); // send to MQTT if enabled
for (uint16_t wait = 0; wait < 2000 && !EMSESP::webSchedulerService.raw_value.empty(); wait++) { output["api_data"] = computed_value; // send to API
delay(1);
}
if (EMSESP::webSchedulerService.computed_value.empty()) {
LOG_WARNING("Message result is empty");
return false;
}
LOG_INFO("Message: %s", EMSESP::webSchedulerService.computed_value.c_str()); // send to log
Mqtt::queue_publish(F_(message), EMSESP::webSchedulerService.computed_value); // send to MQTT if enabled
output["api_data"] = EMSESP::webSchedulerService.computed_value; // send to API
return true; return true;
} }
@@ -1964,7 +1952,9 @@ bool System::get_value_info(JsonObject output, const char * cmd) {
LOG_ERROR("empty system command"); LOG_ERROR("empty system command");
return false; return false;
} }
if (!strcmp(cmd, "restart")) { // restart is a command, not an entity
return false;
}
// check for hardcoded "info"/"value" // check for hardcoded "info"/"value"
if (!strcmp(cmd, F_(info)) || !strcmp(cmd, F_(values))) { if (!strcmp(cmd, F_(info)) || !strcmp(cmd, F_(values))) {
return command_info("", 0, output); return command_info("", 0, output);
+95 -16
View File
@@ -45,7 +45,8 @@ Connect::Connect(uint8_t device_type, uint8_t device_id, uint8_t product_id, con
register_telegram_type(0x1230 + i, "Roomparams", false, MAKE_PF_CB(process_roomThermostatParam)); // fetch for active circuits register_telegram_type(0x1230 + i, "Roomparams", false, MAKE_PF_CB(process_roomThermostatParam)); // fetch for active circuits
register_telegram_type(0x1244 + i, "Roomdata", false, MAKE_PF_CB(process_roomThermostatData)); // broadcasted register_telegram_type(0x1244 + i, "Roomdata", false, MAKE_PF_CB(process_roomThermostatData)); // broadcasted
} }
register_telegram_type(0xDB65, "Roomschedule", true, MAKE_PF_CB(process_roomSchedule)); register_telegram_type(0x0B65, "Roomschedule", true, MAKE_PF_CB(process_roomSchedule));
register_telegram_type(0xF7, "MenuConfig", false, MAKE_PF_CB(process_roomConfig));
// 0x2040, broadcast 36 bytes: // 0x2040, broadcast 36 bytes:
// data: 0E 60 00 DF 0D AF 0A 46 0A 46 02 9A 1C 53 1C 53 12 AD 12 AD 00 00 13 C2 // data: 0E 60 00 DF 0D AF 0A 46 0A 46 02 9A 1C 53 1C 53 12 AD 12 AD 00 00 13 C2
// data: 1F 37 1F 37 00 00 00 00 18 97 11 27 (offset 24) // data: 1F 37 1F 37 00 00 00 00 18 97 11 27 (offset 24)
@@ -88,11 +89,12 @@ void Connect::process_RCTime(std::shared_ptr<const Telegram> telegram) {
void Connect::register_device_values_room(std::shared_ptr<Connect::RoomCircuit> room) { void Connect::register_device_values_room(std::shared_ptr<Connect::RoomCircuit> room) {
auto tag = DeviceValueTAG::TAG_SRC1 + room->room(); auto tag = DeviceValueTAG::TAG_SRC1 + room->room();
register_device_value(tag, &room->temp_, DeviceValueType::INT16, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(roomTemp), DeviceValueUOM::DEGREES); register_device_value(tag, &room->temp_, DeviceValueType::INT16, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(roomTemp), DeviceValueUOM::DEGREES);
register_device_value(tag, &room->humidity_, DeviceValueType::INT8, FL_(airHumidity), DeviceValueUOM::PERCENT); register_device_value(tag, &room->humidity_, DeviceValueType::UINT8, FL_(airHumidity), DeviceValueUOM::PERCENT);
register_device_value(tag, &room->dewtemp_, DeviceValueType::INT16, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(dewTemperature), DeviceValueUOM::DEGREES); register_device_value(tag, &room->dewtemp_, DeviceValueType::INT16, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(dewTemperature), DeviceValueUOM::DEGREES);
register_device_value( register_device_value(
tag, &room->seltemp_, DeviceValueType::UINT8, DeviceValueNumOp::DV_NUMOP_DIV2, FL_(selRoomTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_seltemp), 5, 30); tag, &room->seltemp_, DeviceValueType::UINT8, DeviceValueNumOp::DV_NUMOP_DIV2, FL_(selRoomTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_seltemp), 5, 30);
register_device_value(tag, &room->mode_, DeviceValueType::ENUM, FL_(enum_mode2), FL_(mode), DeviceValueUOM::NONE, MAKE_CF_CB(set_mode)); register_device_value(tag, &room->mode_, DeviceValueType::ENUM, FL_(enum_mode2), FL_(mode), DeviceValueUOM::NONE, MAKE_CF_CB(set_mode));
// register_device_value(tag, &room->coolmode_, DeviceValueType::BOOL, FL_(coolingOn), DeviceValueUOM::NONE, MAKE_CF_CB(set_coolmode));
register_device_value(tag, &room->name_, DeviceValueType::STRING, FL_(name), DeviceValueUOM::NONE, MAKE_CF_CB(set_name)); register_device_value(tag, &room->name_, DeviceValueType::STRING, FL_(name), DeviceValueUOM::NONE, MAKE_CF_CB(set_name));
register_device_value(tag, &room->childlock_, DeviceValueType::BOOL, FL_(childlock), DeviceValueUOM::NONE, MAKE_CF_CB(set_childlock)); register_device_value(tag, &room->childlock_, DeviceValueType::BOOL, FL_(childlock), DeviceValueUOM::NONE, MAKE_CF_CB(set_childlock));
register_device_value(tag, &room->icon_, DeviceValueType::ENUM, FL_(enum_icons), FL_(icon), DeviceValueUOM::NONE, MAKE_CF_CB(set_icon)); register_device_value(tag, &room->icon_, DeviceValueType::ENUM, FL_(enum_icons), FL_(icon), DeviceValueUOM::NONE, MAKE_CF_CB(set_icon));
@@ -133,16 +135,8 @@ void Connect::process_roomThermostat(std::shared_ptr<const Telegram> telegram) {
if (!Mqtt::ha_enabled() || (Helpers::hasValue(rc->mode_) && Helpers::hasValue(rc->icon_))) { if (!Mqtt::ha_enabled() || (Helpers::hasValue(rc->mode_) && Helpers::hasValue(rc->icon_))) {
has_update(telegram, rc->seltemp_, 3); has_update(telegram, rc->seltemp_, 3);
} }
// calculate dew temperature // calculate dew temperature
if (rc->humidity_ >= 0 && rc->humidity_ <= 100) { has_update(rc->dewtemp_, Helpers::calc_dew(rc->temp_, rc->humidity_));
const float k2 = 17.62;
const float k3 = 243.12;
const float t = (float)rc->temp_ / 10;
const float h = (float)rc->humidity_ / 100;
int16_t dt = (10 * k3 * (((k2 * t) / (k3 + t)) + log(h)) / (((k2 * k3) / (k3 + t)) - log(h)));
has_update(rc->dewtemp_, dt);
}
} }
// gateway(0x48) W gateway(0x50), ?(0x0B42), data: 01 // icon in offset 0 // gateway(0x48) W gateway(0x50), ?(0x0B42), data: 01 // icon in offset 0
@@ -168,11 +162,33 @@ void Connect::process_roomThermostatSettings(std::shared_ptr<const Telegram> tel
if (rc == nullptr) { if (rc == nullptr) {
return; return;
} }
has_enumupdate(telegram, rc->mode_, 0, {3, 1, 0}); // modes off, manual auto // has_enumupdate(telegram, rc->mode_, 0, {3, 1, 0}); // modes off, manual auto
// has_update(telegram, rc->mode_, 0); // modes: auto, heat, cool, off // has_update(telegram, rc->mode_, 0); // modes: auto, heat, cool, off
// has_update(telegram, rc->tempautotemp_, 1); // FF means off // has_update(telegram, rc->tempautotemp_, 1); // FF means off
// has_update(telegram, rc->manualtemp_, 3); // has_update(telegram, rc->manualtemp_, 3);
// has_update(telegram, rc->coolmode_, 4); // 01-cooling, 00-heating
// has_update(telegram, rc->coolautotemp_, 5);
// has_update(telegram, rc->cooltemp_, 6);
has_update(telegram, rc->childlock_, 7); has_update(telegram, rc->childlock_, 7);
uint8_t mh = 0xFF;
uint8_t mc = 0xFF;
telegram->read_value(mh, 0);
telegram->read_value(mc, 4);
if (mc == 3) {
rc->coolmode_ = 1;
rc->mode_ = 0;
} else if (mh == 3) {
rc->coolmode_ = 0;
rc->mode_ = 0;
} else if (mc == 1) {
rc->coolmode_ = 1;
rc->mode_ = 1;
} else if (mh == 1) {
rc->coolmode_ = 0;
rc->mode_ = 1;
} else {
rc->mode_ = 2;
}
} }
// unknown telegrams, needs fetch // unknown telegrams, needs fetch
@@ -203,6 +219,53 @@ void Connect::process_roomSchedule(std::shared_ptr<const Telegram> telegram) {
toggle_fetch(telegram->type_id, false); // fetch only once if all is initialized toggle_fetch(telegram->type_id, false); // fetch only once if all is initialized
} }
void Connect::process_roomConfig(std::shared_ptr<const Telegram> telegram) {
if (telegram->offset == 0 && telegram->message_length > 3) {
uint16_t t = 0;
telegram->read_value(t, 1);
if (t >= 0x0AB5 && t <= 0x0AB5 + 15) {
auto rc = room_circuit(t - 0x0AB5);
if (rc == nullptr) {
return;
}
uint8_t bits = telegram->message_data[3];
if (rc->mode_ == 2) {
if (bits & 0x02) {
rc->coolmode_ = 0;
} else if (bits & 0x20) {
rc->coolmode_ = 1;
}
}
/*
switch (bits & 0x6A) {
case 0: // cooling off
rc->mode_ = 0;
rc->coolmode_ = 1;
break;
case 0x40: // manual cooling or heating off
rc->mode_ = 1;
rc->coolmode_ = 1;
break;
case 0x20: // auto cooling
rc->mode_ = 2;
rc->coolmode_ = 1;
break;
case 0x48: // manual heating
case 0x08: // manual heating
rc->mode_ = 1;
rc->coolmode_ = 0;
break;
case 0x42: // auto heating
case 0x02: // auto heating
rc->mode_ = 2;
rc->coolmode_ = 0;
break;
}
*/
}
}
}
// Settings: // Settings:
bool Connect::set_mode(const char * value, const int8_t id) { bool Connect::set_mode(const char * value, const int8_t id) {
@@ -216,7 +279,7 @@ bool Connect::set_mode(const char * value, const int8_t id) {
return false; return false;
} }
} }
write_command(0xBB5 + rc->room(), 0, v); // no validate, mode change is broadcasted write_command(0xBB5 + rc->room(), rc->coolmode_ == 1 ? 4 : 0, v); // no validate, mode change is broadcasted
return true; return true;
} }
@@ -226,10 +289,13 @@ bool Connect::set_seltemp(const char * value, const int8_t id) {
return false; return false;
} }
float v; float v;
if (Helpers::value2float(value, v)) { if (Helpers::value2temperature(value, v)) {
// depends on mode, auto (2 for enum_mode2, 0 for enum_mode8) set in offset 1 // depends on mode, auto (2 for enum_mode2, 0 for enum_mode8) set in offset 1
write_command(0xBB5 + rc->room(), rc->mode_ == 2 ? 1 : 3, v == -1 ? 0xFF : uint8_t(v * 2)); if (rc->coolmode_ == 1) {
// write_command(0xBB5 + rc->room(), rc->mode_ == 0 ? 1 : 3, v == -1 ? 0xFF : uint8_t(v * 2)); write_command(0xBB5 + rc->room(), rc->mode_ == 2 ? 5 : 6, v == -1 ? 0xFF : uint8_t(v * 2));
} else {
write_command(0xBB5 + rc->room(), rc->mode_ == 2 ? 1 : 3, v == -1 ? 0xFF : uint8_t(v * 2));
}
return true; return true;
} }
return false; return false;
@@ -257,6 +323,19 @@ bool Connect::set_name(const char * value, const int8_t id) {
return true; return true;
} }
bool Connect::set_coolmode(const char * value, const int8_t id) {
auto rc = room_circuit(id - DeviceValueTAG::TAG_SRC1);
if (rc == nullptr) {
return false;
}
bool b;
if (Helpers::value2bool(value, b)) {
write_command(0xBB5 + rc->room(), 4, b ? 1 : 0, 0xBB5 + rc->room());
return true;
}
return false;
}
bool Connect::set_childlock(const char * value, const int8_t id) { bool Connect::set_childlock(const char * value, const int8_t id) {
auto rc = room_circuit(id - DeviceValueTAG::TAG_SRC1); auto rc = room_circuit(id - DeviceValueTAG::TAG_SRC1);
if (rc == nullptr) { if (rc == nullptr) {
+6 -1
View File
@@ -34,15 +34,18 @@ class Connect : public EMSdevice {
} }
~RoomCircuit() = default; ~RoomCircuit() = default;
int16_t temp_; int16_t temp_;
int8_t humidity_; uint8_t humidity_;
uint8_t seltemp_; uint8_t seltemp_;
uint8_t mode_; uint8_t mode_;
char name_[51]; char name_[51];
int16_t dewtemp_; int16_t dewtemp_;
uint8_t childlock_; uint8_t childlock_;
uint8_t icon_; uint8_t icon_;
uint8_t coolmode_;
// uint8_t tempautotemp_; // uint8_t tempautotemp_;
// uint8_t manualtemp_; // uint8_t manualtemp_;
// uint8_t coolautotemp_;
// uint8_t cooltemp_;
uint8_t room() { uint8_t room() {
return room_; return room_;
@@ -62,9 +65,11 @@ class Connect : public EMSdevice {
void process_roomThermostatParam(std::shared_ptr<const Telegram> telegram); void process_roomThermostatParam(std::shared_ptr<const Telegram> telegram);
void process_roomThermostatData(std::shared_ptr<const Telegram> telegram); void process_roomThermostatData(std::shared_ptr<const Telegram> telegram);
void process_roomSchedule(std::shared_ptr<const Telegram> telegram); void process_roomSchedule(std::shared_ptr<const Telegram> telegram);
void process_roomConfig(std::shared_ptr<const Telegram> telegram);
bool set_mode(const char * value, const int8_t id); bool set_mode(const char * value, const int8_t id);
bool set_seltemp(const char * value, const int8_t id); bool set_seltemp(const char * value, const int8_t id);
bool set_name(const char * value, const int8_t id); bool set_name(const char * value, const int8_t id);
bool set_coolmode(const char * value, const int8_t id);
bool set_childlock(const char * value, const int8_t id); bool set_childlock(const char * value, const int8_t id);
bool set_icon(const char * value, const int8_t id); bool set_icon(const char * value, const int8_t id);
+95 -37
View File
@@ -810,16 +810,16 @@ void Thermostat::process_RemoteTemp(std::shared_ptr<const Telegram> telegram) {
// e.g. "38 10 FF 00 03 7B 08 24 00 4B" // e.g. "38 10 FF 00 03 7B 08 24 00 4B"
void Thermostat::process_RemoteHumidity(std::shared_ptr<const Telegram> telegram) { void Thermostat::process_RemoteHumidity(std::shared_ptr<const Telegram> telegram) {
// has_update(telegram, dewtemperature_, 0); // this is int8 // has_update(telegram, dewtemperature_, 0); // this is int8
has_update(telegram, humidity_, 1);
has_update(telegram, dewtemperature_, 2); // this is int16
// some thermostats use short telegram with int8 dewpoint, https://github.com/emsesp/EMS-ESP32/issues/1491 // some thermostats use short telegram with int8 dewpoint, https://github.com/emsesp/EMS-ESP32/issues/1491
if (telegram->offset == 0 && telegram->message_length < 4) { // but it's not a dewpoint in offset 0, removed, see https://github.com/emsesp/EMS-ESP32/issues/3135
int8_t dew = dewtemperature_ / 10; has_update(telegram, humidity_, 1);
telegram->read_value(dew, 0); // has_update(telegram, dewtemperature_, 2); // this is int16
if (dew != EMS_VALUE_INT8_NOTSET && dewtemperature_ != dew * 10) { int16_t dew = EMS_VALUE_INT16_NOTSET;
dewtemperature_ = dew * 10; if (telegram->read_value(dew, 2)) {
has_update(dewtemperature_); has_update(dewtemperature_, dew);
} } else if (telegram->offset == 0 && telegram->message_length < 4) {
dew = Helpers::calc_dew(tempsensor1_, humidity_);
has_update(dewtemperature_, dew);
} }
} }
@@ -1466,14 +1466,21 @@ void Thermostat::process_HPMode(std::shared_ptr<const Telegram> telegram) {
} }
// 0x467 ff HP settings // 0x467 ff HP settings
// thermostat(0x10) -W-> Me(0x0B), ?(0x0467), data: 0A 15 00 01 04 (from #1187)
// thermostat(0x10) W me(0x0B), HPSet(0x0467), data: 14 13 02 01 02 (from #3144)
void Thermostat::process_HPSet(std::shared_ptr<const Telegram> telegram) { void Thermostat::process_HPSet(std::shared_ptr<const Telegram> telegram) {
auto hc = heating_circuit(telegram); auto hc = heating_circuit(telegram);
if (hc == nullptr) { if (hc == nullptr) {
return; return;
} }
has_update(telegram, hc->dewoffset, 4); // 7-35°C if (hc->control == 3 || hc->control == 6) { // control with humidity
has_update(telegram, hc->roomtempdiff, 3); // 1-10K has_update(telegram, hc->hpminflowtemp, 0); // 7-35°C
has_update(telegram, hc->hpminflowtemp, 0); // 2-10K } else {
has_update(telegram, hc->hpminflowtemp, 1); // 7-35°C
}
// has_update(telegram, hc->roomtempoffset, 2); // 1-10K // not implemented, mentioned in #3144
has_update(telegram, hc->roomtempdiff, 3); // 1-10K
has_update(telegram, hc->dewoffset, 4); // 2-10K
} }
// type 0x41 - data from the RC30 thermostat(0x10) - 14 bytes long // type 0x41 - data from the RC30 thermostat(0x10) - 14 bytes long
@@ -1731,9 +1738,18 @@ void Thermostat::process_RCTime(std::shared_ptr<const Telegram> telegram) {
} }
// check clock // check clock
time_t now = time(nullptr); time_t now = time(nullptr);
tm * tm_ = localtime(&now); bool sync = false;
bool tset_ = tm_->tm_year > 110; // year 2010 and up, time is valid // change to thermostat timezone if different
EMSESP::esp32React.getNTPSettingsService()->read([&](const NTPSettings & settings) {
sync = settings.thermostat_sync != 0;
if (settings.thermostat_sync == 2) {
setenv("TZ", settings.tzFormatT.c_str(), 1);
tzset();
}
});
tm * tm_ = localtime(&now);
bool tset_ = tm_->tm_year > 110; // year 2010 and up, time is valid
tm_->tm_year = (telegram->message_data[0] & 0x7F) + 100; // IVT tm_->tm_year = (telegram->message_data[0] & 0x7F) + 100; // IVT
tm_->tm_mon = telegram->message_data[1] - 1; tm_->tm_mon = telegram->message_data[1] - 1;
tm_->tm_mday = telegram->message_data[3]; tm_->tm_mday = telegram->message_data[3];
@@ -1749,20 +1765,27 @@ void Thermostat::process_RCTime(std::shared_ptr<const Telegram> telegram) {
strftime(newdatetime, sizeof(dateTime_), "%d.%m.%Y %H:%M", tm_); strftime(newdatetime, sizeof(dateTime_), "%d.%m.%Y %H:%M", tm_);
has_update(dateTime_, newdatetime, sizeof(dateTime_)); has_update(dateTime_, newdatetime, sizeof(dateTime_));
bool ivtclock = (telegram->message_data[0] & 0x80) == 0x80; // dont sync ivt-clock, #439 bool ivtclock = (telegram->message_data[0] & 0x80) == 0x80; // dont sync ivt-clock, #439
bool junkersclock = model() == EMSdevice::EMS_DEVICE_FLAG_JUNKERS; tm_->tm_isdst = -1; // determine dst
time_t ttime = mktime(tm_); // thermostat time time_t ttime = mktime(tm_); // make thermostat time
// change back emsesp timezone
EMSESP::esp32React.getNTPSettingsService()->read([&](const NTPSettings & settings) {
if (settings.thermostat_sync == 2) {
setenv("TZ", settings.tzFormat.c_str(), 1);
tzset();
}
});
// correct thermostat clock if we have valid ntp time, and could write the command // correct thermostat clock if we have valid ntp time, and could write the command
if (!ivtclock && !junkersclock && tset_ && EMSESP::system_.ntp_connected() && !EMSESP::system_.readonly_mode() && has_command(&dateTime_)) { if (sync && !ivtclock && tset_ && EMSESP::system_.ntp_connected() && !EMSESP::system_.readonly_mode() && has_command(&dateTime_)) {
double difference = difftime(now, ttime); int difference = difftime(now, ttime);
if (difference > 15 || difference < -15) { if (difference > 15 || difference < -15) {
if (setTimeRetry < 3) { if (setTimeRetry < 3) {
if (!use_dst) { if (!use_dst) {
set_datetime("ntp", 0); // set from NTP without dst set_datetime("ntp", 0); // set from NTP without dst
LOG_INFO("thermostat time correction from ntp, ignoring dst"); LOG_INFO("thermostat time correction %d seconds from ntp, ignoring dst", difference);
} else { } else {
set_datetime("ntp", -1); // set from NTP set_datetime("ntp", -1); // set from NTP
LOG_INFO("thermostat time correction from ntp"); LOG_INFO("thermostat time correction %d seconds from ntp", difference);
} }
setTimeRetry++; setTimeRetry++;
} }
@@ -1772,10 +1795,6 @@ void Thermostat::process_RCTime(std::shared_ptr<const Telegram> telegram) {
} }
#ifndef EMSESP_STANDALONE #ifndef EMSESP_STANDALONE
if (!tset_ && tm_->tm_year > 110) { // emsesp clock not set, but thermostat clock if (!tset_ && tm_->tm_year > 110) { // emsesp clock not set, but thermostat clock
if (ivtclock) {
tm_->tm_isdst = -1; // determine dst
ttime = mktime(tm_); // thermostat time
}
struct timeval newnow = {.tv_sec = ttime, .tv_usec = 0}; struct timeval newnow = {.tv_sec = ttime, .tv_usec = 0};
#if CONFIG_IDF_TARGET_ESP32C3 #if CONFIG_IDF_TARGET_ESP32C3
// unknown how to set time on C3 // unknown how to set time on C3
@@ -1926,7 +1945,11 @@ bool Thermostat::set_hpminflowtemp(const char * value, const int8_t id) {
} }
int v; int v;
if (Helpers::value2temperature(value, v)) { if (Helpers::value2temperature(value, v)) {
write_command(hp_typeids[hc->hc()], 0, v, hp_typeids[hc->hc()]); if (hc->control == 3 || hc->control == 6) { // remotes with humidity
write_command(hp_typeids[hc->hc()], 0, v, hp_typeids[hc->hc()]);
} else {
write_command(hp_typeids[hc->hc()], 1, v, hp_typeids[hc->hc()]);
}
return true; return true;
} }
return false; return false;
@@ -3055,6 +3078,15 @@ bool Thermostat::set_datetime(const char * value, const int8_t id) {
if (dt == "ntp") { if (dt == "ntp") {
time_t now = time(nullptr); time_t now = time(nullptr);
tm * tm_ = localtime(&now); tm * tm_ = localtime(&now);
EMSESP::esp32React.getNTPSettingsService()->read([&](const NTPSettings & settings) {
if (settings.thermostat_sync == 2) {
setenv("TZ", settings.tzFormatT.c_str(), 1);
tzset();
tm_ = localtime(&now);
setenv("TZ", settings.tzFormat.c_str(), 1);
tzset();
}
});
if (tm_->tm_year < 110) { // no valid time if (tm_->tm_year < 110) { // no valid time
return false; return false;
} }
@@ -3070,11 +3102,7 @@ bool Thermostat::set_datetime(const char * value, const int8_t id) {
data[5] = tm_->tm_sec; data[5] = tm_->tm_sec;
data[6] = (tm_->tm_wday + 6) % 7; // Bosch counts from Mo, time from Su data[6] = (tm_->tm_wday + 6) % 7; // Bosch counts from Mo, time from Su
data[7] = (id == 0) ? 2 : tm_->tm_isdst + 2; // set DST and flag for ext. clock data[7] = (id == 0) ? 2 : tm_->tm_isdst + 2; // set DST and flag for ext. clock
if (model() == EMSdevice::EMS_DEVICE_FLAG_JUNKERS) { } else if (dt.length() == 23 || dt.length() == 21) {
data[6]++; // Junkers use 1-7;
data[7] = 0;
}
} else if (dt.length() == 23) {
data[0] = (dt[7] - '0') * 100 + (dt[8] - '0') * 10 + (dt[9] - '0'); // year data[0] = (dt[7] - '0') * 100 + (dt[8] - '0') * 10 + (dt[9] - '0'); // year
data[1] = (dt[3] - '0') * 10 + (dt[4] - '0'); // month data[1] = (dt[3] - '0') * 10 + (dt[4] - '0'); // month
data[2] = (dt[11] - '0') * 10 + (dt[12] - '0'); // hour data[2] = (dt[11] - '0') * 10 + (dt[12] - '0'); // hour
@@ -3082,10 +3110,35 @@ bool Thermostat::set_datetime(const char * value, const int8_t id) {
data[4] = (dt[14] - '0') * 10 + (dt[15] - '0'); // min data[4] = (dt[14] - '0') * 10 + (dt[15] - '0'); // min
data[5] = (dt[17] - '0') * 10 + (dt[18] - '0'); // sec data[5] = (dt[17] - '0') * 10 + (dt[18] - '0'); // sec
data[6] = (dt[20] - '0'); // day of week, Mo:0 data[6] = (dt[20] - '0'); // day of week, Mo:0
data[7] = (dt[22] - '0') + 2; // DST and flag data[7] = dt.length() == 21 ? 2 : (dt[22] - '0') + 2; // DST and flag
if (model() == EMSdevice::EMS_DEVICE_FLAG_JUNKERS) { } else if (dt.length() == 19 || dt.length() == 16) {
data[7] = 0; time_t now = time(nullptr);
} tm * tm_ = localtime(&now);
data[0] = (dt[7] - '0') * 100 + (dt[8] - '0') * 10 + (dt[9] - '0'); // year
tm_->tm_year = 100 + data[0];
data[1] = (dt[3] - '0') * 10 + (dt[4] - '0'); // month
tm_->tm_mon = data[1] - 1;
tm_->tm_hour = data[2] = (dt[11] - '0') * 10 + (dt[12] - '0'); // hour
tm_->tm_mday = data[3] = (dt[0] - '0') * 10 + (dt[1] - '0'); // day
tm_->tm_min = data[4] = (dt[14] - '0') * 10 + (dt[15] - '0'); // min
tm_->tm_sec = data[5] = dt.length() == 16 ? 0 : (dt[17] - '0') * 10 + (dt[18] - '0'); // sec
tm_->tm_isdst = -1;
// use thermostat time zone to determine day of week and daylight saving
EMSESP::esp32React.getNTPSettingsService()->read([&](const NTPSettings & settings) {
if (settings.thermostat_sync == 2) {
setenv("TZ", settings.tzFormatT.c_str(), 1);
tzset();
auto t = mktime(tm_);
tm_ = localtime(&t);
setenv("TZ", settings.tzFormat.c_str(), 1);
tzset();
} else {
auto t = mktime(tm_);
tm_ = localtime(&t);
}
});
data[6] = (tm_->tm_wday + 6) % 7; // Bosch counts from Mo, time from Su
data[7] = tm_->tm_isdst + 2; // set DST and flag for ext. clock
} else { } else {
LOG_WARNING("Set date: invalid data, wrong length"); LOG_WARNING("Set date: invalid data, wrong length");
return false; return false;
@@ -3095,6 +3148,11 @@ bool Thermostat::set_datetime(const char * value, const int8_t id) {
return false; return false;
} }
if (model() == EMSdevice::EMS_DEVICE_FLAG_JUNKERS) {
data[6]++; // Junkers use 1-7 for day of the week
data[7] = 0; // no dst setting
}
// LOG_INFO("Setting date and time: %02d.%02d.2%03d-%02d:%02d:%02d-%d-%d", data[3], data[1], data[0], data[2], data[4], data[5], data[6], data[7]); // LOG_INFO("Setting date and time: %02d.%02d.2%03d-%02d:%02d:%02d-%d-%d", data[3], data[1], data[0], data[2], data[4], data[5], data[6], data[7]);
write_command(EMS_TYPE_time, 0, data, 8, EMS_TYPE_time); write_command(EMS_TYPE_time, 0, data, 8, EMS_TYPE_time);
@@ -4189,7 +4247,7 @@ bool Thermostat::set_temperature(const float temperature, const uint8_t mode, co
if (model == EMSdevice::EMS_DEVICE_FLAG_CR120 && mode_ == HeatingCircuit::Mode::MANUAL) { if (model == EMSdevice::EMS_DEVICE_FLAG_CR120 && mode_ == HeatingCircuit::Mode::MANUAL) {
offset = 22; // manual offset CR120 offset = 22; // manual offset CR120
} else if (mode_ == HeatingCircuit::Mode::MANUAL) { } else if (mode_ == HeatingCircuit::Mode::MANUAL) {
offset = 10; // manual offset offset = hc->hpoperatingstate == 2 ? 17 : 10; // cooling or manual offset
} else { } else {
offset = 8; // auto offset offset = 8; // auto offset
// special case to reactivate auto temperature, see #737, #746 // special case to reactivate auto temperature, see #737, #746
+1 -1
View File
@@ -304,7 +304,7 @@ class Thermostat : public EMSdevice {
uint8_t instantstart; uint8_t instantstart;
uint8_t coolstart; uint8_t coolstart;
uint8_t coolondelay; uint8_t coolondelay;
uint8_t cooloffdelay; uint8_t cooloffdelay;
// HybridHP // HybridHP
uint8_t hybridStrategy_; // co2 = 1, cost = 2, temperature = 3, mix = 4 uint8_t hybridStrategy_; // co2 = 1, cost = 2, temperature = 3, mix = 4
+40 -39
View File
@@ -28,9 +28,41 @@ Water::Water(uint8_t device_type, uint8_t device_id, uint8_t product_id, const c
: EMSdevice(device_type, device_id, product_id, version, name, flags, brand) { : EMSdevice(device_type, device_id, product_id, version, name, flags, brand) {
dhw_ = device_id - EMSdevice::EMS_DEVICE_ID_DHW1; dhw_ = device_id - EMSdevice::EMS_DEVICE_ID_DHW1;
int8_t tag = DeviceValueTAG::TAG_DHW1 + dhw_; int8_t tag = DeviceValueTAG::TAG_DHW1 + dhw_;
if (flags == EMSdevice::EMS_DEVICE_FLAG_SM100) { // device_id 0x2A, DHW3 if (flags == EMSdevice::EMS_DEVICE_FLAG_IPM) {
dhw_ = 0;
// telegram handlers tag = DeviceValueTAG::TAG_DHW1;
register_telegram_type(0x34, "UBAMonitorWW", false, MAKE_PF_CB(process_IPMMonitorWW));
register_telegram_type(0x1E, "HydrTemp", false, MAKE_PF_CB(process_IPMHydrTemp));
register_telegram_type(0x33, "UBAParameterWW", true, MAKE_PF_CB(process_IPMParameterWW), 12);
// register_telegram_type(0x10D, "wwNTCStatus", false, MAKE_PF_CB(process_wwNTCStatus));
// device values...
register_device_value(tag, &wwSelTemp_, DeviceValueType::UINT8, FL_(wwSelTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_wwSelTemp));
register_device_value(tag, &wwTemp_, DeviceValueType::UINT16, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(wwTemp), DeviceValueUOM::DEGREES);
register_device_value(tag, &wwTemp2_, DeviceValueType::UINT16, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(wwCurTemp2), DeviceValueUOM::DEGREES);
register_device_value(tag, &HydrTemp_, DeviceValueType::UINT16, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(hydrTemp), DeviceValueUOM::DEGREES);
register_device_value(tag, &wwPump_, DeviceValueType::BOOL, FL_(wwPump), DeviceValueUOM::NONE);
register_device_value(tag, &wwFlowTempOffset_, DeviceValueType::UINT8, FL_(wwFlowTempOffset), DeviceValueUOM::DEGREES_R, MAKE_CF_CB(set_wwFlowTempOffset));
register_device_value(tag, &wwHystOn_, DeviceValueType::INT8, FL_(wwHystOn), DeviceValueUOM::DEGREES_R, MAKE_CF_CB(set_wwHystOn));
register_device_value(tag, &wwHystOff_, DeviceValueType::INT8, FL_(wwHystOff), DeviceValueUOM::DEGREES_R, MAKE_CF_CB(set_wwHystOff));
register_device_value(tag, &wwDisinfectionTemp_, DeviceValueType::UINT8, FL_(wwDisinfectionTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_wwDisinfectionTemp));
register_device_value(tag, &wwCirc_, DeviceValueType::BOOL, FL_(wwCirc), DeviceValueUOM::NONE, MAKE_CF_CB(set_wwCirc));
register_device_value(tag, &wwCircMode_, DeviceValueType::ENUM, FL_(enum_wwCircMode), FL_(wwCircMode), DeviceValueUOM::NONE, MAKE_CF_CB(set_wwCircMode));
} else if (tag == DeviceValueTAG::TAG_DHW1 || tag == DeviceValueTAG::TAG_DHW2) { // MM100 or SM100
register_telegram_type(0x331 + dhw_, "MMPLUSStatusMessage_WWC", false, MAKE_PF_CB(process_MMPLUSStatusMessage_WWC));
register_telegram_type(0x313 + dhw_, "MMPLUSConfigMessage_WWC", true, MAKE_PF_CB(process_MMPLUSConfigMessage_WWC), 11);
// register_telegram_type(0x33B + type_offset, "MMPLUSSetMessage_WWC", true, MAKE_PF_CB(process_MMPLUSSetMessage_WWC));
// device values...
register_device_value(tag, &wwTemp_, DeviceValueType::UINT16, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(wwTemp), DeviceValueUOM::DEGREES);
register_device_value(tag, &wwStatus_, DeviceValueType::INT8, FL_(wwTempStatus), DeviceValueUOM::NONE);
register_device_value(tag, &wwPump_, DeviceValueType::BOOL, FL_(wwPump), DeviceValueUOM::NONE);
register_device_value(tag, &wwMaxTemp_, DeviceValueType::UINT8, FL_(wwMaxTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_wwMaxTemp));
register_device_value(tag, &wwDiffTemp_, DeviceValueType::INT8, FL_(wwDiffTemp), DeviceValueUOM::DEGREES_R, MAKE_CF_CB(set_wwDiffTemp));
register_device_value(tag, &wwDisinfectionTemp_, DeviceValueType::UINT8, FL_(wwDisinfectionTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_wwDisinfectionTemp));
register_device_value(tag, &wwRedTemp_, DeviceValueType::UINT8, FL_(wwRedTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_wwRedTemp));
register_device_value(tag, &wwRequiredTemp_, DeviceValueType::UINT8, FL_(wwRequiredTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_wwRequiredTemp));
register_device_value(tag, &wwCirc_, DeviceValueType::BOOL, FL_(wwCirc), DeviceValueUOM::NONE, MAKE_CF_CB(set_wwCirc));
register_device_value(tag, &wwCircMode_, DeviceValueType::ENUM, FL_(enum_wwCircMode), FL_(wwCircMode), DeviceValueUOM::NONE, MAKE_CF_CB(set_wwCircMode));
} else { // device_id 0x2A, DHW3, SM100
register_telegram_type(0x07D6 + dhw_ - 2, "SM100wwTemperature", false, MAKE_PF_CB(process_SM100wwTemperature)); register_telegram_type(0x07D6 + dhw_ - 2, "SM100wwTemperature", false, MAKE_PF_CB(process_SM100wwTemperature));
register_telegram_type(0x07E0 + dhw_ - 2, "SM100wwStatus2", true, MAKE_PF_CB(process_SM100wwStatus2), 10); register_telegram_type(0x07E0 + dhw_ - 2, "SM100wwStatus2", true, MAKE_PF_CB(process_SM100wwStatus2), 10);
register_telegram_type(0x07A6, "SM100wwParam", true, MAKE_PF_CB(process_SM100wwParam), 20); // same telegram for all circuits register_telegram_type(0x07A6, "SM100wwParam", true, MAKE_PF_CB(process_SM100wwParam), 20); // same telegram for all circuits
@@ -67,41 +99,6 @@ Water::Water(uint8_t device_type, uint8_t device_id, uint8_t product_id, const c
register_device_value(tag, &wwRetValve_, DeviceValueType::UINT8, FL_(valveReturn), DeviceValueUOM::PERCENT, MAKE_CF_CB(set_wwRetValve)); register_device_value(tag, &wwRetValve_, DeviceValueType::UINT8, FL_(valveReturn), DeviceValueUOM::PERCENT, MAKE_CF_CB(set_wwRetValve));
register_device_value(tag, &wwDeltaTRet_, DeviceValueType::UINT8, FL_(deltaTRet), DeviceValueUOM::K, MAKE_CF_CB(set_wwDeltaTRet)); register_device_value(tag, &wwDeltaTRet_, DeviceValueType::UINT8, FL_(deltaTRet), DeviceValueUOM::K, MAKE_CF_CB(set_wwDeltaTRet));
register_device_value(tag, &errorDisp_, DeviceValueType::ENUM, FL_(enum_errorDisp), FL_(errorDisp), DeviceValueUOM::NONE, MAKE_CF_CB(set_errorDisp)); register_device_value(tag, &errorDisp_, DeviceValueType::ENUM, FL_(enum_errorDisp), FL_(errorDisp), DeviceValueUOM::NONE, MAKE_CF_CB(set_errorDisp));
} else if (flags == EMSdevice::EMS_DEVICE_FLAG_MMPLUS) { // dhw1 and dhw 2
register_telegram_type(0x331 + dhw_, "MMPLUSStatusMessage_WWC", false, MAKE_PF_CB(process_MMPLUSStatusMessage_WWC));
register_telegram_type(0x313 + dhw_, "MMPLUSConfigMessage_WWC", true, MAKE_PF_CB(process_MMPLUSConfigMessage_WWC), 11);
// register_telegram_type(0x33B + type_offset, "MMPLUSSetMessage_WWC", true, MAKE_PF_CB(process_MMPLUSSetMessage_WWC));
// device values...
register_device_value(tag, &wwTemp_, DeviceValueType::UINT16, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(wwTemp), DeviceValueUOM::DEGREES);
register_device_value(tag, &wwStatus_, DeviceValueType::INT8, FL_(wwTempStatus), DeviceValueUOM::NONE);
register_device_value(tag, &wwPump_, DeviceValueType::BOOL, FL_(wwPump), DeviceValueUOM::NONE);
register_device_value(tag, &wwMaxTemp_, DeviceValueType::UINT8, FL_(wwMaxTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_wwMaxTemp));
register_device_value(tag, &wwDiffTemp_, DeviceValueType::INT8, FL_(wwDiffTemp), DeviceValueUOM::DEGREES_R, MAKE_CF_CB(set_wwDiffTemp));
register_device_value(tag, &wwDisinfectionTemp_, DeviceValueType::UINT8, FL_(wwDisinfectionTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_wwDisinfectionTemp));
register_device_value(tag, &wwRedTemp_, DeviceValueType::UINT8, FL_(wwRedTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_wwRedTemp));
register_device_value(tag, &wwRequiredTemp_, DeviceValueType::UINT8, FL_(wwRequiredTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_wwRequiredTemp));
register_device_value(tag, &wwCirc_, DeviceValueType::BOOL, FL_(wwCirc), DeviceValueUOM::NONE, MAKE_CF_CB(set_wwCirc));
register_device_value(tag, &wwCircMode_, DeviceValueType::ENUM, FL_(enum_wwCircMode), FL_(wwCircMode), DeviceValueUOM::NONE, MAKE_CF_CB(set_wwCircMode));
} else if (flags == EMSdevice::EMS_DEVICE_FLAG_IPM) {
dhw_ = 0;
tag = DeviceValueTAG::TAG_DHW1;
register_telegram_type(0x34, "UBAMonitorWW", false, MAKE_PF_CB(process_IPMMonitorWW));
register_telegram_type(0x1E, "HydrTemp", false, MAKE_PF_CB(process_IPMHydrTemp));
register_telegram_type(0x33, "UBAParameterWW", true, MAKE_PF_CB(process_IPMParameterWW), 12);
// register_telegram_type(0x10D, "wwNTCStatus", false, MAKE_PF_CB(process_wwNTCStatus));
// device values...
register_device_value(tag, &wwSelTemp_, DeviceValueType::UINT8, FL_(wwSelTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_wwSelTemp));
register_device_value(tag, &wwTemp_, DeviceValueType::UINT16, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(wwTemp), DeviceValueUOM::DEGREES);
register_device_value(tag, &wwTemp2_, DeviceValueType::UINT16, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(wwCurTemp2), DeviceValueUOM::DEGREES);
register_device_value(tag, &HydrTemp_, DeviceValueType::UINT16, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(hydrTemp), DeviceValueUOM::DEGREES);
register_device_value(tag, &wwPump_, DeviceValueType::BOOL, FL_(wwPump), DeviceValueUOM::NONE);
register_device_value(tag, &wwFlowTempOffset_, DeviceValueType::UINT8, FL_(wwFlowTempOffset), DeviceValueUOM::DEGREES_R, MAKE_CF_CB(set_wwFlowTempOffset));
register_device_value(tag, &wwHystOn_, DeviceValueType::INT8, FL_(wwHystOn), DeviceValueUOM::DEGREES_R, MAKE_CF_CB(set_wwHystOn));
register_device_value(tag, &wwHystOff_, DeviceValueType::INT8, FL_(wwHystOff), DeviceValueUOM::DEGREES_R, MAKE_CF_CB(set_wwHystOff));
register_device_value(tag, &wwDisinfectionTemp_, DeviceValueType::UINT8, FL_(wwDisinfectionTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_wwDisinfectionTemp));
register_device_value(tag, &wwCirc_, DeviceValueType::BOOL, FL_(wwCirc), DeviceValueUOM::NONE, MAKE_CF_CB(set_wwCirc));
register_device_value(tag, &wwCircMode_, DeviceValueType::ENUM, FL_(enum_wwCircMode), FL_(wwCircMode), DeviceValueUOM::NONE, MAKE_CF_CB(set_wwCircMode));
} }
} }
@@ -361,7 +358,11 @@ bool Water::set_wwCircTc(const char * value, const int8_t id) {
if (!Helpers::value2bool(value, b)) { if (!Helpers::value2bool(value, b)) {
return false; return false;
} }
write_command(0x33B + dhw_, 4, b ? 0x01 : 0x00, 0x33B + dhw_); if (flags() == EMSdevice::EMS_DEVICE_FLAG_MMPLUS) {
write_command(0x33B + dhw_, 4, b ? 0x01 : 0x00, 0x33B + dhw_);
} else { // SM100
write_command(0x7A5, 4, b ? 0xFF : 0x00, 0x7A5);
}
return true; return true;
} }
+1 -1
View File
@@ -1 +1 @@
#define EMSESP_APP_VERSION "3.8.3-dev.5" #define EMSESP_APP_VERSION "3.8.3"
+6
View File
@@ -35,6 +35,11 @@ WebAPIService::WebAPIService(AsyncWebServer * server, SecurityManager * security
// POST|GET api/{device} // POST|GET api/{device}
// POST|GET api/{device}/{entity} // POST|GET api/{device}/{entity}
void WebAPIService::webAPIService(AsyncWebServerRequest * request, JsonVariant json) { void WebAPIService::webAPIService(AsyncWebServerRequest * request, JsonVariant json) {
static uint64_t lastcall = 0;
if (uuid::get_uptime_ms() - lastcall < 50 && request->method() == HTTP_GET) {
request->send(429); //too many requests
return;
}
JsonDocument input_doc; // has no body JSON so create dummy as empty input object JsonDocument input_doc; // has no body JSON so create dummy as empty input object
JsonObject input; JsonObject input;
// if no body then treat it as a secure GET // if no body then treat it as a secure GET
@@ -49,6 +54,7 @@ void WebAPIService::webAPIService(AsyncWebServerRequest * request, JsonVariant j
input["data"] = json.as<std::string>(); input["data"] = json.as<std::string>();
} }
parse(request, input); parse(request, input);
lastcall = uuid::get_uptime_ms();
} }
// for POSTS accepting plain text data // for POSTS accepting plain text data
+3 -1
View File
@@ -77,7 +77,9 @@ StateUpdateResult WebCustomEntity::update(JsonObject root, WebCustomEntity & web
if (entityItem.ram == 2) { // NVS if (entityItem.ram == 2) { // NVS
char key[sizeof(entityItem.name) + 2]; char key[sizeof(entityItem.name) + 2];
snprintf(key, sizeof(key), "c:%s", entityItem.name); snprintf(key, sizeof(key), "c:%s", entityItem.name);
EMSESP::nvs_.remove(key); if (EMSESP::nvs_.isKey(key)) {
EMSESP::nvs_.remove(key);
}
} }
if (entityItem.ram) { // save name/value pairs for change checking if (entityItem.ram) { // save name/value pairs for change checking
doc[entityItem.name] = entityItem.value; doc[entityItem.name] = entityItem.value;
+22 -11
View File
@@ -76,7 +76,9 @@ StateUpdateResult WebScheduler::update(JsonObject root, WebScheduler & webSchedu
for (ScheduleItem & scheduleItem : webScheduler.scheduleItems) { for (ScheduleItem & scheduleItem : webScheduler.scheduleItems) {
char key[sizeof(scheduleItem.name) + 2]; char key[sizeof(scheduleItem.name) + 2];
snprintf(key, sizeof(key), "s:%s", scheduleItem.name); snprintf(key, sizeof(key), "s:%s", scheduleItem.name);
EMSESP::nvs_.remove(key); if (EMSESP::nvs_.isKey(key)) {
EMSESP::nvs_.remove(key);
}
} }
webScheduler.scheduleItems.clear(); webScheduler.scheduleItems.clear();
EMSESP::webSchedulerService.ha_reset(); EMSESP::webSchedulerService.ha_reset();
@@ -345,7 +347,11 @@ uint8_t WebSchedulerService::count_entities(bool cmd_only) {
// execute scheduled command // execute scheduled command
bool WebSchedulerService::command(const char * name, const std::string & command, const std::string & data) { bool WebSchedulerService::command(const char * name, const std::string & command, const std::string & data) {
std::string cmd = Helpers::toLower(command); std::string cmd = Helpers::toLower(command);
if (cmd == "system/message") {
EMSESP::logger().info("Message: %s", data.c_str()); // send to log
Mqtt::queue_publish(F_(message), data); // send to MQTT if enabled
return true;
}
// check http commands. e.g. // check http commands. e.g.
// tasmota(get): http://<tasmotaIP>/cm?cmnd=power%20ON // tasmota(get): http://<tasmotaIP>/cm?cmnd=power%20ON
// shelly(get): http://<shellyIP>/relais/0?turn=on // shelly(get): http://<shellyIP>/relais/0?turn=on
@@ -353,7 +359,11 @@ bool WebSchedulerService::command(const char * name, const std::string & command
JsonDocument doc; JsonDocument doc;
if (deserializeJson(doc, cmd) == DeserializationError::Ok) { if (deserializeJson(doc, cmd) == DeserializationError::Ok) {
HTTPClient * http = new HTTPClient; HTTPClient * http = new HTTPClient;
std::string url = doc["url"] | ""; #ifndef EMSESP_STANDALONE
http->setConnectTimeout(10000);
http->setTimeout(10000);
#endif
std::string url = doc["url"] | "";
// for a GET with parameters replace commands with values // for a GET with parameters replace commands with values
// don't search the complete url, it may contain a devicename in path // don't search the complete url, it may contain a devicename in path
auto q = url.find_first_of('?'); auto q = url.find_first_of('?');
@@ -401,6 +411,7 @@ bool WebSchedulerService::command(const char * name, const std::string & command
return true; return true;
} }
// we can add other json tests here // we can add other json tests here
delete http;
} }
doc.clear(); doc.clear();
@@ -479,15 +490,14 @@ void WebSchedulerService::loop() {
static uint32_t last_uptime_min = 0; static uint32_t last_uptime_min = 0;
static uint32_t last_uptime_sec = 0; static uint32_t last_uptime_sec = 0;
if (!raw_value.empty()) { // process a value from system/message command
computed_value = compute(raw_value);
raw_value.clear();
}
// get list of scheduler events and exit if it's empty // get list of scheduler events and exit if it's empty
if (scheduleItems_->empty()) { if (scheduleItems_->empty()) {
return; return;
} }
// do not execute any command in the first 60 secondes
if (uuid::get_uptime_sec() < 60) {
return;
}
// check if we have onChange events // check if we have onChange events
while (!cmd_changed_.empty()) { while (!cmd_changed_.empty()) {
@@ -531,7 +541,8 @@ void WebSchedulerService::loop() {
// retry startup commands not yet executed // retry startup commands not yet executed
if (scheduleItem.active && scheduleItem.flags == SCHEDULEFLAG_SCHEDULE_TIMER && scheduleItem.elapsed_min == 0 if (scheduleItem.active && scheduleItem.flags == SCHEDULEFLAG_SCHEDULE_TIMER && scheduleItem.elapsed_min == 0
&& scheduleItem.retry_cnt < MAX_STARTUP_RETRIES) { && scheduleItem.retry_cnt < MAX_STARTUP_RETRIES) {
scheduleItem.retry_cnt = command(scheduleItem.name, scheduleItem.cmd.c_str(), scheduleItem.value.c_str()) ? 0xFF : scheduleItem.retry_cnt + 1; scheduleItem.retry_cnt =
command(scheduleItem.name, scheduleItem.cmd.c_str(), compute(scheduleItem.value.c_str())) ? 0xFF : scheduleItem.retry_cnt + 1;
} }
// scheduled timer commands // scheduled timer commands
if (scheduleItem.active && scheduleItem.flags == SCHEDULEFLAG_SCHEDULE_TIMER && scheduleItem.elapsed_min > 0 if (scheduleItem.active && scheduleItem.flags == SCHEDULEFLAG_SCHEDULE_TIMER && scheduleItem.elapsed_min > 0
@@ -542,10 +553,10 @@ void WebSchedulerService::loop() {
last_uptime_min = uptime_min; last_uptime_min = uptime_min;
} }
// check calender, sync to RTC, only execute if year is valid // check calender, sync to RTC, only execute if year is valid and uptime more than a minute
time_t now = time(nullptr); time_t now = time(nullptr);
tm * tm = localtime(&now); tm * tm = localtime(&now);
if (tm->tm_min != last_tm_min && tm->tm_year > 120) { if (tm->tm_min != last_tm_min && tm->tm_year > 120 && uuid::get_uptime_sec() > 60) {
// find the real dow and minute from RTC // find the real dow and minute from RTC
uint8_t real_dow = 1 << tm->tm_wday; // 1 is Sunday uint8_t real_dow = 1 << tm->tm_wday; // 1 is Sunday
uint16_t real_min = tm->tm_hour * 60 + tm->tm_min; uint16_t real_min = tm->tm_hour * 60 + tm->tm_min;
-3
View File
@@ -90,9 +90,6 @@ class WebSchedulerService : public StatefulService<WebScheduler> {
std::string get_metrics_prometheus(); std::string get_metrics_prometheus();
std::string raw_value;
std::string computed_value;
#if defined(EMSESP_TEST) #if defined(EMSESP_TEST)
void load_test_data(); void load_test_data();
#endif #endif
+3
View File
@@ -267,6 +267,9 @@ StateUpdateResult WebSettings::update(JsonObject root, WebSettings & settings) {
} }
settings.locale = root["locale"] | EMSESP_DEFAULT_LOCALE; settings.locale = root["locale"] | EMSESP_DEFAULT_LOCALE;
if (settings.locale == "cz") { // convert from older settings file
settings.locale = "cs";
}
EMSESP::system_.locale(settings.locale); EMSESP::system_.locale(settings.locale);
if (Mqtt::ha_enabled() && original_settings.locale != settings.locale) { if (Mqtt::ha_enabled() && original_settings.locale != settings.locale) {
add_flags(ChangeFlags::MQTT); add_flags(ChangeFlags::MQTT);
+5
View File
@@ -177,6 +177,10 @@ void WebStatusService::systemStatus(AsyncWebServerRequest * request) {
// we're ready to do the actual restart ASAP // we're ready to do the actual restart ASAP
EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_RESTART_REQUESTED); EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_RESTART_REQUESTED);
} }
if (EMSESP::system_.systemStatus() == SYSTEM_STATUS::SYSTEM_STATUS_ERROR_UPLOAD) {
// error is reported, back to normal state
EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_NORMAL);
}
#endif #endif
@@ -241,6 +245,7 @@ void WebStatusService::action(AsyncWebServerRequest * request, JsonVariant json)
if (!ok) { if (!ok) {
EMSESP::logger().err("Action '%s' failed", action.c_str()); EMSESP::logger().err("Action '%s' failed", action.c_str());
request->send(400); // bad request request->send(400); // bad request
delete response;
return; return;
} }
+1 -1
View File
@@ -588,4 +588,4 @@ void run_tests() {
RUN_TEST(test_57); RUN_TEST(test_57);
} }
// ---------- END - CUT HERE ---------- // ---------- END - CUT HERE ----------