102 Commits

Author SHA1 Message Date
Proddy
017a7de639 Merge pull request #3068 from MichaelDvP/dev
tx-mode: auto, default bus-id 0x49
2026-05-09 08:11:59 +02:00
MichaelDvP
7b61429a02 fix testdata to device-id 0x49, txMode 5 2026-05-09 08:01:29 +02:00
MichaelDvP
fed15f0f96 tx-mode: auto, default bus-id 0x49 2026-05-07 21:08:56 +02:00
Proddy
ad5d13168b Merge pull request #3066 from MichaelDvP/dev
fix modbus start #3064
2026-05-07 18:05:48 +02:00
MichaelDvP
ae5beccb9d dev.20, fixes #3064, handling of optional gpios 2026-05-07 12:02:33 +02:00
MichaelDvP
764c660b14 Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into dev 2026-05-06 09:23:27 +02:00
MichaelDvP
ca0f32b087 update pkg 2026-05-06 09:06:15 +02:00
Proddy
8ad91de54a Merge pull request #3061 from MichaelDvP/dev 2026-05-06 09:05:10 +02:00
MichaelDvP
58da157272 chore: update generated files for v3.8.2-dev.20 2026-05-05 16:04:29 +00:00
MichaelDvP
b5014bf9ac dev.20, check and set 0x470 to summer2_typeids[0] ony if received. #2686 2026-05-05 17:51:52 +02:00
MichaelDvP
14351436a7 fixes #3055, revert commit daffdcf 2026-05-05 10:33:56 +02:00
Proddy
469d412951 Merge pull request #3045 from MichaelDvP/dev
fix legegram length, #2969
2026-04-24 17:14:58 +02:00
MichaelDvP
6edbac86e2 fix legegram length, #2969 2026-04-24 14:46:53 +02:00
proddy
db2be70d66 chore: update generated files for v3.8.2-dev.18 2026-04-22 14:22:25 +00:00
Proddy
c36f231990 Merge pull request #3042 from proddy/dev
minor updates
2026-04-22 16:10:20 +02:00
proddy
26102121e1 async-validator fixes 2026-04-22 16:07:56 +02:00
proddy
8e64c6303e package update 2026-04-22 15:43:58 +02:00
proddy
74c76eb90b remove YIELD 2026-04-22 15:43:29 +02:00
proddy
daffdcf58e https://github.com/emsesp/EMS-ESP32/issues/2686 2026-04-22 15:43:20 +02:00
Proddy
4bc4fa903f Merge pull request #3040 from MichaelDvP/dev
version checks prelease
2026-04-22 15:11:02 +02:00
MichaelDvP
29380f0303 version checks prelease 2026-04-22 14:59:46 +02:00
Proddy
6b2370b79d Merge pull request #3035 from mattreim/dev
Update German translation
2026-04-22 08:34:14 +02:00
Proddy
dbc636c9bf Merge pull request #3036 from MichaelDvP/dev
small fixes
2026-04-22 08:33:38 +02:00
MichaelDvP
0c0660c04b Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into dev 2026-04-22 07:41:29 +02:00
mattreim
c9fd076394 Update German translation 2026-04-22 01:05:05 +02:00
MichaelDvP
35550553be check fetch length for custom entities, dev17 2026-04-21 19:51:48 +02:00
MichaelDvP
06ff219385 version check order 2026-04-21 18:58:42 +02:00
MichaelDvP
e705a5629f fetch length of holiday to 18 2026-04-21 18:58:13 +02:00
Proddy
cb3c9653ce Merge pull request #3032 from proddy/dev
rename build_webUI for Python
2026-04-20 15:38:52 +02:00
proddy
0b5a83f6ae package update (vite fix) 2026-04-20 15:37:50 +02:00
MichaelDvP
a079169005 backup nvs1 if exist 2026-04-20 13:18:50 +02:00
proddy
845c51d5f9 rename build_webUI for Python 2026-04-19 21:23:59 +02:00
Proddy
d6d3a034ad Merge pull request #3030 from proddy/dev
build_webUI -> build-webUI
2026-04-19 19:14:31 +02:00
proddy
ece08d96ee build_webUI -> build-webUI 2026-04-19 19:09:45 +02:00
MichaelDvP
854f4d559a Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into dev 2026-04-19 15:40:15 +02:00
Proddy
f186f2a8f2 Merge pull request #3028 from proddy/dev
heap memory optimizations
2026-04-19 15:15:45 +02:00
proddy
6b68cb7c61 store UTC epoch time and convert to localtime when render (fixes bug as TZ not set) 2026-04-19 15:12:22 +02:00
proddy
a1e0288e09 close dialog after downloading 2026-04-19 15:11:41 +02:00
proddy
e6c173bdf9 don't show system backup as it's the same page! 2026-04-19 15:11:29 +02:00
MichaelDvP
d9b6de0652 Merge branch 'dev' of https://github.com/proddy/EMS-ESP32 into dev 2026-04-19 13:41:01 +02:00
MichaelDvP
c54da18822 remove pr#3021 2026-04-19 13:40:51 +02:00
proddy
555801dc5c remove lazy loading, optimize chunking 2026-04-19 10:05:47 +02:00
proddy
6f81945da6 typo 2026-04-18 18:43:03 +02:00
proddy
865c309475 remove c++17 2026-04-18 18:39:45 +02:00
proddy
77b8b21aea use C++ 20 (espressif32@6.13.0 still uses GCC 8 so only 2a supported) 2026-04-18 18:34:47 +02:00
proddy
2f5edffec6 update changelog 2026-04-18 18:29:30 +02:00
proddy
71de64502e include cstdint for uint8_t on new GCC 2026-04-18 17:52:05 +02:00
proddy
6994d3559a package update 2026-04-18 17:43:31 +02:00
proddy
a7d484d218 3.8.2-dev.16 2026-04-18 17:43:23 +02:00
proddy
a810c41acd exclude js 2026-04-18 17:43:14 +02:00
proddy
2fbfdf94ab minor optimizations, use EMSESP_Version, only call esp_image_verify() and store the entry for partitions that actually have a value 2026-04-18 17:40:21 +02:00
proddy
2d7c8f0863 remove semver 2026-04-18 17:36:08 +02:00
proddy
c3b734ab47 add back LTO, remove semver 2026-04-18 17:35:57 +02:00
proddy
644abf105d replace semver with home grown simplier alternative 2026-04-18 17:35:11 +02:00
proddy
5a8a451774 improve chunking 2026-04-18 17:34:48 +02:00
proddy
dae139aa01 single static-content handler serving all assets 2026-04-18 17:33:13 +02:00
proddy
b13fcd8939 single static-content handler serving all assets 2026-04-18 17:32:54 +02:00
Proddy
26b42b4eea Merge pull request #3027 from proddy/dev
update github actions
2026-04-18 10:22:39 +02:00
proddy
c9005e8aa9 upgrade github actions 2026-04-18 10:02:25 +02:00
proddy
6658b11adf use c++20 2026-04-17 18:01:51 +02:00
proddy
e542f5809f remove bogus file 2026-04-17 18:01:44 +02:00
proddy
ce1dd6233d update 2026-04-17 18:01:37 +02:00
proddy
fe488443da package update 2026-04-17 18:01:26 +02:00
Proddy
b264a39780 Merge pull request #3026 from MichaelDvP/dev 2026-04-17 16:41:59 +02:00
MichaelDvP
d2302eaa85 dev15, rollback mbedlt change for memory saving 2026-04-17 15:59:13 +02:00
Proddy
994706c9f2 Merge pull request #3023 from MichaelDvP/dev
fetch telegrams with length, dev14
2026-04-16 12:22:06 +02:00
MichaelDvP
8a72ab42cb dev 14, changelog 2026-04-16 08:18:17 +02:00
MichaelDvP
c4db8e3914 set length for more fetch telegrams 2026-04-16 08:10:43 +02:00
MichaelDvP
8d0225e595 Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into dev 2026-04-16 08:09:58 +02:00
Proddy
966049d0c9 Merge pull request #3006 from proddy/dev
sync with core3 features
2026-04-15 21:29:24 +02:00
proddy
907a65a701 show link to backup page 2026-04-15 21:19:09 +02:00
proddy
f97b8e14e7 package update 2026-04-15 21:18:51 +02:00
proddy
e65f634b21 3.8.2 2026-04-15 21:18:38 +02:00
proddy
fc71ed2b9d 3.8.2 2026-04-15 21:18:27 +02:00
proddy
5a8195d430 auto-formatting 2026-04-15 08:12:22 +02:00
proddy
24a7a607f3 add test data 2026-04-15 08:08:49 +02:00
proddy
061f9ffc52 update prettier 2026-04-15 08:08:45 +02:00
proddy
9e17936bfc fix lint 2026-04-14 21:13:25 +02:00
Proddy
18bb2c4f39 Merge branch 'emsesp:dev' into dev 2026-04-14 21:09:31 +02:00
proddy
7c3782a43f upload warnings 2026-04-14 21:08:45 +02:00
proddy
3ac807bdd5 text change 2026-04-14 21:08:04 +02:00
proddy
1111458863 upgrade message warnings 2026-04-14 09:31:50 +02:00
proddy
99c5e2230c fix link 2026-04-14 09:31:35 +02:00
proddy
3317aa845a package update 2026-04-14 09:31:19 +02:00
proddy
97cd657336 fix links 2026-04-14 09:31:05 +02:00
MichaelDvP
3338f919bd Merge branch 'dev' of https://github.com/proddy/EMS-ESP32 into dev 2026-04-14 08:09:50 +02:00
proddy
7dd13bcab7 mui upgrade 2026-04-13 23:30:36 +02:00
MichaelDvP
f226cb359f Merge branch 'dev' of https://github.com/MichaelDvP/EMS-ESP32 into dev 2026-04-13 21:04:23 +02:00
MichaelDvP
abbba0aa42 telegram length for fetched telegrams 2026-04-13 20:57:20 +02:00
MichaelDvP
39b5a52b01 chore: update generated files for v3.8.2-dev.13 2026-04-13 12:12:42 +00:00
MichaelDvP
b6c3fc5bee read fragmented telegram 0x484, #3017 2026-04-13 14:00:12 +02:00
MichaelDvP
909bea00df update espressif32 6.13.0 2026-04-13 13:59:12 +02:00
MichaelDvP
9522945e06 uart buffer size 2026-04-13 13:58:30 +02:00
MichaelDvP
d6a9f2a731 prepare for translations #3015, update pkg 2026-04-13 13:58:10 +02:00
proddy
0f30c81554 fix compile on linux/osx 2026-04-12 20:32:36 +02:00
MichaelDvP
e514ba4bb5 Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into dev 2026-04-12 09:46:43 +02:00
Proddy
38e63e3eaa Merge pull request #3016 from misa1515/patch-30
Update index.ts
2026-04-11 22:44:28 +02:00
misa1515
0058324edd Update index.ts 2026-04-10 21:10:36 +02:00
MichaelDvP
ac143d607a http-client to heap 2026-04-10 14:20:17 +02:00
MichaelDvP
e9e3759db3 add solar ts3 2026-04-10 14:19:39 +02:00
proddy
fb09e10f19 sync with core3 features 2026-03-30 23:26:04 +02:00
Proddy
09473f17a0 Merge pull request #2994 from MichaelDvP/dev
dev.12, add dhw4, 5,.. circuits, #2991
2026-03-21 15:46:21 +01:00
163 changed files with 3931 additions and 3350 deletions

View File

@@ -28,7 +28,7 @@ jobs:
node-version: 24 node-version: 24
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v5 uses: actions/checkout@v6
- name: Enable Corepack - name: Enable Corepack
run: corepack enable pnpm run: corepack enable pnpm
@@ -47,7 +47,7 @@ jobs:
- name: Build webUI - name: Build webUI
run: | run: |
platformio run -e build_webUI platformio run -e build-webUI
- name: Build modbus - name: Build modbus
run: | run: |
@@ -62,13 +62,13 @@ jobs:
platformio run platformio run
- name: Commit the generated files - name: Commit the generated files
uses: stefanzweifel/git-auto-commit-action@v5 uses: stefanzweifel/git-auto-commit-action@v7
with: with:
commit_message: "chore: update generated files for v${{steps.build_info.outputs.VERSION}}" commit_message: "chore: update generated files for v${{steps.build_info.outputs.VERSION}}"
- name: Create GitHub Release - name: Create GitHub Release
id: 'automatic_releases' id: 'automatic_releases'
uses: emsesp/action-automatic-releases@v1.0.0 uses: emsesp/action-automatic-releases@v1.0.1
with: with:
repo_token: '${{ secrets.GITHUB_TOKEN }}' repo_token: '${{ secrets.GITHUB_TOKEN }}'
title: Development Build v${{steps.build_info.outputs.VERSION}} title: Development Build v${{steps.build_info.outputs.VERSION}}

View File

@@ -11,7 +11,7 @@ jobs:
contents: read contents: read
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v5 uses: actions/checkout@v6
- name: GitHub Releases To Discord - name: GitHub Releases To Discord
uses: SethCohen/github-releases-to-discord@v1.13.1 uses: SethCohen/github-releases-to-discord@v1.13.1

View File

@@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v5 uses: actions/checkout@v6
- name: Install python 3.13 - name: Install python 3.13
uses: actions/setup-python@v6 uses: actions/setup-python@v6

View File

@@ -19,7 +19,7 @@ jobs:
BUILD_WRAPPER_OUT_DIR: bw-output BUILD_WRAPPER_OUT_DIR: bw-output
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v5 uses: actions/checkout@v6
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Install Build Wrapper - name: Install Build Wrapper

View File

@@ -26,7 +26,7 @@ jobs:
node-version: 24 node-version: 24
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v5 uses: actions/checkout@v6
- name: Enable Corepack - name: Enable Corepack
run: corepack enable pnpm run: corepack enable pnpm
@@ -39,7 +39,7 @@ jobs:
- name: Build webUI - name: Build webUI
run: | run: |
platformio run -e build_webUI platformio run -e build-webUI
- name: Build modbus - name: Build modbus
run: | run: |
@@ -54,7 +54,7 @@ jobs:
platformio run platformio run
- name: Create GitHub Release - name: Create GitHub Release
uses: emsesp/action-automatic-releases@v1.0.0 uses: emsesp/action-automatic-releases@v1.0.1
with: with:
repo_token: '${{ secrets.GITHUB_TOKEN }}' repo_token: '${{ secrets.GITHUB_TOKEN }}'
prerelease: false prerelease: false

View File

@@ -28,7 +28,7 @@ jobs:
node-version: 24 node-version: 24
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v5 uses: actions/checkout@v6
- name: Enable Corepack - name: Enable Corepack
run: corepack enable pnpm run: corepack enable pnpm
@@ -47,7 +47,7 @@ jobs:
- name: Build webUI - name: Build webUI
run: | run: |
platformio run -e build_webUI platformio run -e build-webUI
- name: Build modbus - name: Build modbus
run: | run: |
@@ -63,7 +63,7 @@ jobs:
- name: Create GitHub Release - name: Create GitHub Release
id: 'automatic_releases' id: 'automatic_releases'
uses: emsesp/action-automatic-releases@v1.0.0 uses: emsesp/action-automatic-releases@v1.0.1
with: with:
repo_token: '${{ secrets.GITHUB_TOKEN }}' repo_token: '${{ secrets.GITHUB_TOKEN }}'
title: Test Build v${{steps.build_info.outputs.VERSION}} title: Test Build v${{steps.build_info.outputs.VERSION}}

View File

@@ -12,12 +12,16 @@ For more details go to [emsesp.org](https://emsesp.org/).
- prometheus metrics for temperature/analog/scheduler/custom [#2962](https://github.com/emsesp/EMS-ESP32/issues/2962) - prometheus metrics for temperature/analog/scheduler/custom [#2962](https://github.com/emsesp/EMS-ESP32/issues/2962)
- boiler pumpkick [#2965](https://github.com/emsesp/EMS-ESP32/discussions/2965) - boiler pumpkick [#2965](https://github.com/emsesp/EMS-ESP32/discussions/2965)
- heatpump reset [#2933](https://github.com/emsesp/EMS-ESP32/issues/2933) - heatpump reset [#2933](https://github.com/emsesp/EMS-ESP32/issues/2933)
- e-mail notification using ReadyMail Client - 2.nd freshwater module (dhw4, dhw5) [#2991](https://github.com/emsesp/EMS-ESP32/issues/2991)
- 2.nd freshwater module (dhw4) [#2991](https://github.com/emsesp/EMS-ESP32/issues/2991) - full system backup and restore
- auto-logic to set ht3/ems+ tx-mode
## Fixed ## Fixed
- SRC climate creation [#2936](https://github.com/emsesp/EMS-ESP32/issues/2936) and [#2960](https://github.com/emsesp/EMS-ESP32/issues/2960) - SRC climate creation [#2936](https://github.com/emsesp/EMS-ESP32/issues/2936) and [#2960](https://github.com/emsesp/EMS-ESP32/issues/2960)
- missing translations [#3015](https://github.com/emsesp/EMS-ESP32/issues/3015)
- custom entities check fetch length
- modbus initialization [#3064](https://github.com/emsesp/EMS-ESP32/issues/3064)
## Changed ## Changed
@@ -29,5 +33,8 @@ For more details go to [emsesp.org](https://emsesp.org/).
- support `minflowtemp` and `baseflowtemp` [#2969](https://github.com/emsesp/EMS-ESP32/discussions/2969) - support `minflowtemp` and `baseflowtemp` [#2969](https://github.com/emsesp/EMS-ESP32/discussions/2969)
- update version if it is 00.00 in first read [#2981](https://github.com/emsesp/EMS-ESP32/issues/2981) - update version if it is 00.00 in first read [#2981](https://github.com/emsesp/EMS-ESP32/issues/2981)
- device class for % values [#2980](https://github.com/emsesp/EMS-ESP32/issues/2980) - device class for % values [#2980](https://github.com/emsesp/EMS-ESP32/issues/2980)
- use tasmota core 2026.03.30 - fetch telegrams: set length to fetch [#3017](https://github.com/emsesp/EMS-ESP32/issues/3017)
- secure mqtt uses ESP_SSLClient - move http client from stack to heap
- heap optimizations [#3021](https://github.com/emsesp/EMS-ESP32/discussions/3021)
- check and read 0x470 as summer2_typeids[0] only if received [#2686](https://github.com/emsesp/EMS-ESP32/issues/2686), [#3055](https://github.com/emsesp/EMS-ESP32/issues/3055)
- default bus-id: gateway1(0x49), tx-mode: auto

View File

@@ -47,18 +47,18 @@ MAKEFLAGS += -j$(JOBS) -l$(shell echo $$(($(JOBS) * 2)))
#---------------------------------------------------------------------- #----------------------------------------------------------------------
TARGET := emsesp TARGET := emsesp
BUILD := build BUILD := build
SOURCES := src/core src/devices src/web src/test lib_standalone lib/semver lib/espMqttClient/src lib/espMqttClient/src/* lib/ArduinoJson/src lib/uuid-common/src lib/uuid-console/src lib/uuid-log/src lib/PButton SOURCES := src/core src/devices src/web src/test lib_standalone lib/espMqttClient/src lib/espMqttClient/src/* lib/ArduinoJson/src lib/uuid-common/src lib/uuid-console/src lib/uuid-log/src lib/PButton
INCLUDES := src/core src/devices src/web src/test lib_standalone lib/* lib/semver lib/espMqttClient/src lib/espMqttClient/src/Transport lib/ArduinoJson/src lib/uuid-common/src lib/uuid-console/src lib/uuid-log/src lib/uuid-telnet/src lib/uuid-syslog/src INCLUDES := src/core src/devices src/web src/test lib_standalone lib/* lib/espMqttClient/src lib/espMqttClient/src/Transport lib/ArduinoJson/src lib/uuid-common/src lib/uuid-console/src lib/uuid-log/src lib/uuid-telnet/src lib/uuid-syslog/src
LIBRARIES := LIBRARIES :=
CPPCHECK = cppcheck CPPCHECK = cppcheck
CHECKFLAGS = -q --force --std=gnu++17 CHECKFLAGS = -q --force --std=gnu++20
#---------------------------------------------------------------------- #----------------------------------------------------------------------
# Languages Standard # Languages Standard
#---------------------------------------------------------------------- #----------------------------------------------------------------------
C_STANDARD := -std=c17 C_STANDARD := -std=c20
CXX_STANDARD := -std=gnu++17 CXX_STANDARD := -std=gnu++20
#---------------------------------------------------------------------- #----------------------------------------------------------------------
# Defined Symbols # Defined Symbols

View File

@@ -5,7 +5,7 @@
}, },
"core": "esp32", "core": "esp32",
"extra_flags": [ "extra_flags": [
"-DNO_TLS_SUPPORT", "-DTASMOTA_SDK",
"-DARDUINO_LOLIN_C3_MINI", "-DARDUINO_LOLIN_C3_MINI",
"-DARDUINO_USB_MODE=1", "-DARDUINO_USB_MODE=1",
"-DARDUINO_USB_CDC_ON_BOOT=1" "-DARDUINO_USB_CDC_ON_BOOT=1"

View File

@@ -6,7 +6,7 @@
"core": "esp32", "core": "esp32",
"extra_flags": [ "extra_flags": [
"-DBOARD_HAS_PSRAM", "-DBOARD_HAS_PSRAM",
"-DNO_TLS_SUPPORT", "-DTASMOTA_SDK",
"-DARDUINO_USB_CDC_ON_BOOT=1", "-DARDUINO_USB_CDC_ON_BOOT=1",
"-DARDUINO_USB_MODE=0" "-DARDUINO_USB_MODE=0"
], ],

View File

@@ -21,7 +21,7 @@
"arduino", "arduino",
"espidf" "espidf"
], ],
"name": "Tasmota ESP32-S3 32M Flash OPI PSRAM, 4608KB Code/OTA, 2MB FS", "name": "Espressif ESP32-S3 32M Flash OPI PSRAM, 4608KB Code/OTA, 2MB FS",
"upload": { "upload": {
"flash_size": "32MB", "flash_size": "32MB",
"maximum_ram_size": 327680, "maximum_ram_size": 327680,

View File

@@ -1,7 +1,7 @@
{ {
"build": { "build": {
"core": "esp32", "core": "esp32",
"extra_flags": "-DNO_TLS_SUPPORT", "extra_flags": "-DTASMOTA_SDK",
"f_cpu": "240000000L", "f_cpu": "240000000L",
"f_flash": "40000000L", "f_flash": "40000000L",
"flash_mode": "dio", "flash_mode": "dio",
@@ -19,7 +19,7 @@
"arduino", "arduino",
"espidf" "espidf"
], ],
"name": "Tasmota ESP32 16M Flash, 4608KB Code/OTA, 2MB FS", "name": "Espressif ESP32 16M Flash, 4608KB Code/OTA, 2MB FS",
"upload": { "upload": {
"flash_size": "16MB", "flash_size": "16MB",
"maximum_ram_size": 327680, "maximum_ram_size": 327680,

View File

@@ -19,7 +19,7 @@
"arduino", "arduino",
"espidf" "espidf"
], ],
"name": "Tasmota ESP32 16M Flash DIO PSRAM, 4608KB Code/OTA, 2MB FS", "name": "Espressif ESP32 16M Flash DIO PSRAM, 4608KB Code/OTA, 2MB FS",
"upload": { "upload": {
"flash_size": "16MB", "flash_size": "16MB",
"maximum_ram_size": 327680, "maximum_ram_size": 327680,

View File

@@ -1,7 +1,7 @@
{ {
"build": { "build": {
"core": "esp32", "core": "esp32",
"extra_flags": "-DNO_TLS_SUPPORT", "extra_flags": "-DTASMOTA_SDK",
"f_cpu": "240000000L", "f_cpu": "240000000L",
"f_flash": "40000000L", "f_flash": "40000000L",
"flash_mode": "dio", "flash_mode": "dio",

View File

@@ -2,7 +2,6 @@
"build": { "build": {
"core": "esp32", "core": "esp32",
"extra_flags": [ "extra_flags": [
"-DNO_TLS_SUPPORT",
"-DARDUINO_XIAO_ESP32C6", "-DARDUINO_XIAO_ESP32C6",
"-DARDUINO_USB_MODE=1", "-DARDUINO_USB_MODE=1",
"-DARDUINO_USB_CDC_ON_BOOT=1" "-DARDUINO_USB_CDC_ON_BOOT=1"

View File

@@ -39,6 +39,6 @@
"lib/esp32-psram/**", "lib/esp32-psram/**",
"test/test_api/test_api.h", "test/test_api/test_api.h",
"lib_standalone/**", "lib_standalone/**",
"lib/mbedtls_ssl/**" "**/*.js"
] ]
} }

View File

@@ -1,10 +1,22 @@
{
"type": "systembackup",
"version": "3.8.2",
"date": "2026-03-29T13:28:15",
"systembackup": [
{ {
"type": "settings", "type": "settings",
"Network": { "Network": {
"ssid": "my_wifi_ssid", "ssid": "",
"bssid": "", "bssid": "",
"password": "my_wifi_password", "password": "",
"hostname": "ems-esp" "hostname": "ems-esp",
"static_ip_config": false,
"bandwidth20": false,
"nosleep": true,
"enableMDNS": true,
"enableCORS": false,
"CORSOrigin": "*",
"tx_power": 0
}, },
"AP": { "AP": {
"provision_mode": 2, "provision_mode": 2,
@@ -21,12 +33,14 @@
"enableTLS": false, "enableTLS": false,
"rootCA": "", "rootCA": "",
"enabled": false, "enabled": false,
"host": "127.0.0.1", "host": "",
"port": 1883, "port": 1883,
"base": "ems-esp", "base": "ems-esp",
"username": "username", "username": "",
"password": "password", "password": "",
"client_id": "ems-esp", "client_id": "esp32-b8ffc9ec",
"keep_alive": 60,
"clean_session": false,
"entity_format": 1, "entity_format": 1,
"publish_time_boiler": 10, "publish_time_boiler": 10,
"publish_time_thermostat": 10, "publish_time_thermostat": 10,
@@ -42,6 +56,7 @@
"nested_format": 1, "nested_format": 1,
"discovery_prefix": "homeassistant", "discovery_prefix": "homeassistant",
"discovery_type": 0, "discovery_type": 0,
"ha_number_mode": 0,
"publish_single": false, "publish_single": false,
"publish_single2cmd": false, "publish_single2cmd": false,
"send_response": false "send_response": false
@@ -68,18 +83,146 @@
] ]
}, },
"Settings": { "Settings": {
"board_profile": "S3", "version": "3.8.2",
"board_profile": "E32V2_2",
"platform": "ESP32",
"locale": "en", "locale": "en",
"tx_mode": 1, "tx_mode": 1,
"ems_bus_id": 11, "ems_bus_id": 11,
"syslog_enabled": false,
"syslog_level": 3,
"trace_raw": false,
"syslog_mark_interval": 0,
"syslog_host": "",
"syslog_port": 514,
"boiler_heatingoff": false, "boiler_heatingoff": false,
"hide_led": true, "remote_timeout": 24,
"remote_timeout_en": false,
"shower_timer": false,
"shower_alert": false,
"shower_alert_coldshot": 10,
"shower_alert_trigger": 7,
"shower_min_duration": 180,
"rx_gpio": 4,
"tx_gpio": 5,
"dallas_gpio": 14,
"dallas_parasite": false,
"led_gpio": 32,
"hide_led": false,
"led_type": 1,
"low_clock": false,
"telnet_enabled": true, "telnet_enabled": true,
"notoken_api": false, "notoken_api": false,
"readonly_mode": false,
"analog_enabled": true, "analog_enabled": true,
"pbutton_gpio": 34,
"solar_maxflow": 30,
"fahrenheit": false, "fahrenheit": false,
"bool_format": 1, "bool_format": 1,
"bool_dashboard": 1, "bool_dashboard": 1,
"enum_format": 1 "enum_format": 1,
"weblog_level": 6,
"weblog_buffer": 50,
"weblog_compact": true,
"phy_type": 1,
"eth_power": 15,
"eth_phy_addr": 0,
"eth_clock_mode": 1,
"modbus_enabled": false,
"modbus_port": 502,
"modbus_max_clients": 10,
"modbus_timeout": 300,
"developer_mode": true,
"email_enabled": false,
"email_ssl": false,
"email_starttls": true,
"email_server": "smtp.example.net",
"email_port": 587,
"email_login": "",
"email_pass": "",
"email_sender": "ems-esp@example.net",
"email_recp": "",
"email_subject": "ems-esp notification"
}
},
{
"type": "schedule",
"Schedule": {
"schedule": []
}
},
{
"type": "customizations",
"Customizations": {
"ts": [
{
"id": "28_1767_7B13_2502",
"name": "gateway_temperature",
"offset": 0,
"is_system": true
}
],
"as": [
{
"gpio": 39,
"name": "core_voltage",
"offset": 0,
"factor": 0.003771,
"uom": 23,
"type": 3,
"is_system": true
},
{
"gpio": 36,
"name": "supply_voltage",
"offset": 0,
"factor": 0.017,
"uom": 23,
"type": 3,
"is_system": true
},
{
"gpio": 2,
"name": "led",
"offset": 0,
"factor": 1,
"uom": 0,
"type": 6,
"is_system": true
}
],
"masked_entities": []
}
},
{
"type": "entities",
"Entities": {
"entities": []
}
},
{
"type": "modules",
"Modules": {
"modules": []
}
},
{
"type": "customSupport",
"Support": {
"html": [
"This product is installed and managed by:",
"",
"<b>Bosch Installer Example</b>",
"",
"Nefit Road 12",
"1234 AB Amsterdam",
"Phone: +31 123 456 789",
"email: support@boschinstaller.nl",
"",
"For help and questions please <a target='_blank' href='https://emsesp.org'>contact</a> your installer."
],
"img_url": "https://emsesp.org/media/images/designer.png"
} }
} }
]
}

View File

@@ -7654,7 +7654,7 @@ uint8
| cylmaxtemp | maximum cylinder temperature | uint8 (&gt;=0&lt;=0) | C | true | DEVICE_DATA | 5 | 1 | 1 | | cylmaxtemp | maximum cylinder temperature | uint8 (&gt;=0&lt;=0) | C | true | DEVICE_DATA | 5 | 1 | 1 |
| collectorshutdown | collector shutdown | boolean | | false | DEVICE_DATA | 6 | 1 | 1 | | collectorshutdown | collector shutdown | boolean | | false | DEVICE_DATA | 6 | 1 | 1 |
| cylheated | cyl heated | boolean | | false | DEVICE_DATA | 7 | 1 | 1 | | cylheated | cyl heated | boolean | | false | DEVICE_DATA | 7 | 1 | 1 |
| cylmiddletemp | cylinder middle temperature (TS3) | int16 | C | false | DEVICE_DATA | 17 | 1 | 1/10 | | cylmiddletemp | cylinder middle temperature (TS14) | int16 | C | false | DEVICE_DATA | 17 | 1 | 1/10 |
| retheatassist | return temperature heat assistance (TS4) | int16 | C | false | DEVICE_DATA | 18 | 1 | 1/10 | | retheatassist | return temperature heat assistance (TS4) | int16 | C | false | DEVICE_DATA | 18 | 1 | 1/10 |
| heatassistvalve | heat assistance valve (M1) | boolean | | false | DEVICE_DATA | 19 | 1 | 1 | | heatassistvalve | heat assistance valve (M1) | boolean | | false | DEVICE_DATA | 19 | 1 | 1 |
| energylasthour | energy last hour | uint24 | Wh | false | DEVICE_DATA | 13 | 2 | 1/10 | | energylasthour | energy last hour | uint24 | Wh | false | DEVICE_DATA | 13 | 2 | 1/10 |
@@ -7671,7 +7671,7 @@ uint8
| cylmaxtemp | maximum cylinder temperature | uint8 (&gt;=0&lt;=0) | C | true | DEVICE_DATA | 5 | 1 | 1 | | cylmaxtemp | maximum cylinder temperature | uint8 (&gt;=0&lt;=0) | C | true | DEVICE_DATA | 5 | 1 | 1 |
| collectorshutdown | collector shutdown | boolean | | false | DEVICE_DATA | 6 | 1 | 1 | | collectorshutdown | collector shutdown | boolean | | false | DEVICE_DATA | 6 | 1 | 1 |
| cylheated | cyl heated | boolean | | false | DEVICE_DATA | 7 | 1 | 1 | | cylheated | cyl heated | boolean | | false | DEVICE_DATA | 7 | 1 | 1 |
| cylmiddletemp | cylinder middle temperature (TS3) | int16 | C | false | DEVICE_DATA | 17 | 1 | 1/10 | | cylmiddletemp | cylinder middle temperature (TS14) | int16 | C | false | DEVICE_DATA | 17 | 1 | 1/10 |
| retheatassist | return temperature heat assistance (TS4) | int16 | C | false | DEVICE_DATA | 18 | 1 | 1/10 | | retheatassist | return temperature heat assistance (TS4) | int16 | C | false | DEVICE_DATA | 18 | 1 | 1/10 |
| heatassistvalve | heat assistance valve (M1) | boolean | | false | DEVICE_DATA | 19 | 1 | 1 | | heatassistvalve | heat assistance valve (M1) | boolean | | false | DEVICE_DATA | 19 | 1 | 1 |
| energylasthour | energy last hour | uint24 | Wh | false | DEVICE_DATA | 13 | 2 | 1/10 | | energylasthour | energy last hour | uint24 | Wh | false | DEVICE_DATA | 13 | 2 | 1/10 |
@@ -7696,67 +7696,68 @@ uint8
uint8 uint8
| turnoffdiff | pump turn off difference | uint8 (&gt;=0&lt;=0) | C | true | DEVICE_DATA | 11 | 1 | 1/10 | | turnoffdiff | pump turn off difference | uint8 (&gt;=0&lt;=0) | C | true | DEVICE_DATA | 11 | 1 | 1/10 |
| collector2temp | collector 2 temperature (TS7) | int16 | C | false | DEVICE_DATA | 20 | 1 | 1/10 | | collector2temp | collector 2 temperature (TS7) | int16 | C | false | DEVICE_DATA | 20 | 1 | 1/10 |
| cylmiddletemp | cylinder middle temperature (TS3) | int16 | C | false | DEVICE_DATA | 17 | 1 | 1/10 | | cylmiddletemp | cylinder middle temperature (TS14) | int16 | C | false | DEVICE_DATA | 17 | 1 | 1/10 |
| ts3 | cylinder middle temperature (TS3) | int16 | C | false | DEVICE_DATA | 21 | 1 | 1/10 |
| retheatassist | return temperature heat assistance (TS4) | int16 | C | false | DEVICE_DATA | 18 | 1 | 1/10 | | retheatassist | return temperature heat assistance (TS4) | int16 | C | false | DEVICE_DATA | 18 | 1 | 1/10 |
| ts8 | (TS8) | int16 | C | false | DEVICE_DATA | 21 | 1 | 1/10 | | ts8 | (TS8) | int16 | C | false | DEVICE_DATA | 22 | 1 | 1/10 |
| ts16 | (TS16) | int16 | C | false | DEVICE_DATA | 22 | 1 | 1/10 | | ts16 | (TS16) | int16 | C | false | DEVICE_DATA | 23 | 1 | 1/10 |
| heatassistvalve | heat assistance valve (M1) | boolean | | false | DEVICE_DATA | 19 | 1 | 1 | | heatassistvalve | heat assistance valve (M1) | boolean | | false | DEVICE_DATA | 19 | 1 | 1 |
| heatassistpower | heat assistance valve power (M1) | uint8 | % | false | DEVICE_DATA | 23 | 1 | 1 | | heatassistpower | heat assistance valve power (M1) | uint8 | % | false | DEVICE_DATA | 24 | 1 | 1 |
| solarpump2 | pump 2 (PS4) | boolean | | false | DEVICE_DATA | 24 | 1 | 1 | | solarpump2 | pump 2 (PS4) | boolean | | false | DEVICE_DATA | 25 | 1 | 1 |
| solarpump2mod | pump 2 modulation (PS4) | uint8 | % | false | DEVICE_DATA | 25 | 1 | 1 | | solarpump2mod | pump 2 modulation (PS4) | uint8 | % | false | DEVICE_DATA | 26 | 1 | 1 |
| cyl2bottomtemp | second cylinder bottom temperature (TS5) | int16 | C | false | DEVICE_DATA | 26 | 1 | 1/10 | | cyl2bottomtemp | second cylinder bottom temperature (TS5) | int16 | C | false | DEVICE_DATA | 27 | 1 | 1/10 |
| cyl3bottomtemp | third cylinder bottom temperature (TS11) | int16 | C | false | DEVICE_DATA | 27 | 1 | 1/10 | | cyl3bottomtemp | third cylinder bottom temperature (TS11) | int16 | C | false | DEVICE_DATA | 28 | 1 | 1/10 |
| cyltoptemp | cylinder top temperature (TS10) | int16 | C | false | DEVICE_DATA | 28 | 1 | 1/10 | | cyltoptemp | cylinder top temperature (TS10) | int16 | C | false | DEVICE_DATA | 29 | 1 | 1/10 |
| heatexchangertemp | heat exchanger temperature (TS6) | int16 | C | false | DEVICE_DATA | 29 | 1 | 1/10 | | heatexchangertemp | heat exchanger temperature (TS6) | int16 | C | false | DEVICE_DATA | 30 | 1 | 1/10 |
| cylpumpmod | cylinder pump modulation (PS5) | uint8 | % | false | DEVICE_DATA | 30 | 1 | 1 | | cylpumpmod | cylinder pump modulation (PS5) | uint8 | % | false | DEVICE_DATA | 31 | 1 | 1 |
| valvestatus | valve status | boolean | | false | DEVICE_DATA | 31 | 1 | 1 | | valvestatus | valve status | boolean | | false | DEVICE_DATA | 32 | 1 | 1 |
| vs1status | valve status VS1 | boolean | | false | DEVICE_DATA | 32 | 1 | 1 | | vs1status | valve status VS1 | boolean | | false | DEVICE_DATA | 33 | 1 | 1 |
| vs3status | valve status VS3 | boolean | | false | DEVICE_DATA | 33 | 1 | 1 | | vs3status | valve status VS3 | boolean | | false | DEVICE_DATA | 34 | 1 | 1 |
| transferpump | transfer pump | boolean | | false | DEVICE_DATA | 34 | 1 | 1 | | transferpump | transfer pump | boolean | | false | DEVICE_DATA | 35 | 1 | 1 |
| transferpumpmod | transfer pump modulation | uint8 | % | false | DEVICE_DATA | 35 | 1 | 1 | | transferpumpmod | transfer pump modulation | uint8 | % | false | DEVICE_DATA | 36 | 1 | 1 |
uint8 uint8
| collectormaxtemp | maximum collector temperature | uint8 (&gt;=0&lt;=0) | C | true | DEVICE_DATA | 36 | 1 | 1 | | collectormaxtemp | maximum collector temperature | uint8 (&gt;=0&lt;=0) | C | true | DEVICE_DATA | 37 | 1 | 1 |
uint8 uint8
| collectormintemp | minimum collector temperature | uint8 (&gt;=0&lt;=0) | C | true | DEVICE_DATA | 37 | 1 | 1 | | collectormintemp | minimum collector temperature | uint8 (&gt;=0&lt;=0) | C | true | DEVICE_DATA | 38 | 1 | 1 |
| energylasthour | energy last hour | uint24 | Wh | false | DEVICE_DATA | 13 | 2 | 1/10 | | energylasthour | energy last hour | uint24 | Wh | false | DEVICE_DATA | 13 | 2 | 1/10 |
| energytoday | total energy today | uint24 | Wh | false | DEVICE_DATA | 38 | 2 | 1 | | energytoday | total energy today | uint24 | Wh | false | DEVICE_DATA | 39 | 2 | 1 |
| energytotal | total energy | uint24 | kWh | false | DEVICE_DATA | 40 | 2 | 1/10 | | energytotal | total energy | uint24 | kWh | false | DEVICE_DATA | 41 | 2 | 1/10 |
| pump2worktime | pump 2 working time | time | minutes | false | DEVICE_DATA | 42 | 2 | 1 | | pump2worktime | pump 2 working time | time | minutes | false | DEVICE_DATA | 43 | 2 | 1 |
| m1worktime | differential control working time | time | minutes | false | DEVICE_DATA | 44 | 2 | 1 | | m1worktime | differential control working time | time | minutes | false | DEVICE_DATA | 45 | 2 | 1 |
| heattransfersystem | heattransfer system | boolean | | true | DEVICE_DATA | 46 | 1 | 1 | | heattransfersystem | heattransfer system | boolean | | true | DEVICE_DATA | 47 | 1 | 1 |
| externalcyl | external cylinder | boolean | | true | DEVICE_DATA | 47 | 1 | 1 | | externalcyl | external cylinder | boolean | | true | DEVICE_DATA | 48 | 1 | 1 |
| thermaldisinfect | thermal disinfection | boolean | | true | DEVICE_DATA | 48 | 1 | 1 | | thermaldisinfect | thermal disinfection | boolean | | true | DEVICE_DATA | 49 | 1 | 1 |
| heatmetering | heatmetering | boolean | | true | DEVICE_DATA | 49 | 1 | 1 | | heatmetering | heatmetering | boolean | | true | DEVICE_DATA | 50 | 1 | 1 |
| activated | activated | boolean | | true | DEVICE_DATA | 50 | 1 | 1 | | activated | activated | boolean | | true | DEVICE_DATA | 51 | 1 | 1 |
| solarpumpmode | solar pump mode | enum | | true | DEVICE_DATA | 51 | 1 | 1 | | solarpumpmode | solar pump mode | enum | | true | DEVICE_DATA | 52 | 1 | 1 |
| solarpumpkick | solar pump kick | boolean | | true | DEVICE_DATA | 52 | 1 | 1 | | solarpumpkick | solar pump kick | boolean | | true | DEVICE_DATA | 53 | 1 | 1 |
| plainwatermode | plain water mode | boolean | | true | DEVICE_DATA | 53 | 1 | 1 | | plainwatermode | plain water mode | boolean | | true | DEVICE_DATA | 54 | 1 | 1 |
| doublematchflow | doublematchflow | boolean | | true | DEVICE_DATA | 54 | 1 | 1 | | doublematchflow | doublematchflow | boolean | | true | DEVICE_DATA | 55 | 1 | 1 |
uint8 uint8
| pump2minmod | minimum pump 2 modulation | uint8 (&gt;=0&lt;=0) | % | true | DEVICE_DATA | 55 | 1 | 1 | | pump2minmod | minimum pump 2 modulation | uint8 (&gt;=0&lt;=0) | % | true | DEVICE_DATA | 56 | 1 | 1 |
uint8 uint8
| turnondiff2 | pump 2 turn on difference | uint8 (&gt;=0&lt;=0) | C | true | DEVICE_DATA | 56 | 1 | 1/10 | | turnondiff2 | pump 2 turn on difference | uint8 (&gt;=0&lt;=0) | C | true | DEVICE_DATA | 57 | 1 | 1/10 |
uint8 uint8
| turnoffdiff2 | pump 2 turn off difference | uint8 (&gt;=0&lt;=0) | C | true | DEVICE_DATA | 57 | 1 | 1/10 | | turnoffdiff2 | pump 2 turn off difference | uint8 (&gt;=0&lt;=0) | C | true | DEVICE_DATA | 58 | 1 | 1/10 |
| pump2kick | pump kick 2 | boolean | | true | DEVICE_DATA | 58 | 1 | 1 | | pump2kick | pump kick 2 | boolean | | true | DEVICE_DATA | 59 | 1 | 1 |
uint8 uint8
| climatezone | climate zone | uint8 (&gt;=0&lt;=0) | | true | DEVICE_DATA | 59 | 1 | 1 | | climatezone | climate zone | uint8 (&gt;=0&lt;=0) | | true | DEVICE_DATA | 60 | 1 | 1 |
uint16 uint16
| collector1area | collector 1 area | uint16 (&gt;=0&lt;=0) | m² | true | DEVICE_DATA | 60 | 1 | 1/10 | | collector1area | collector 1 area | uint16 (&gt;=0&lt;=0) | m² | true | DEVICE_DATA | 61 | 1 | 1/10 |
| collector1type | collector 1 type | enum | | true | DEVICE_DATA | 61 | 1 | 1 | | collector1type | collector 1 type | enum | | true | DEVICE_DATA | 62 | 1 | 1 |
uint16 uint16
| collector2area | collector 2 area | uint16 (&gt;=0&lt;=0) | m² | true | DEVICE_DATA | 62 | 1 | 1/10 | | collector2area | collector 2 area | uint16 (&gt;=0&lt;=0) | m² | true | DEVICE_DATA | 63 | 1 | 1/10 |
| collector2type | collector 2 type | enum | | true | DEVICE_DATA | 63 | 1 | 1 | | collector2type | collector 2 type | enum | | true | DEVICE_DATA | 64 | 1 | 1 |
| cylpriority | cylinder priority | enum | | true | DEVICE_DATA | 64 | 1 | 1 | | cylpriority | cylinder priority | enum | | true | DEVICE_DATA | 65 | 1 | 1 |
| heatcntflowtemp | heat counter flow temperature | uint16 | C | false | DEVICE_DATA | 65 | 1 | 1/10 | | heatcntflowtemp | heat counter flow temperature | uint16 | C | false | DEVICE_DATA | 66 | 1 | 1/10 |
| heatcntrettemp | heat counter return temperature | uint16 | C | false | DEVICE_DATA | 66 | 1 | 1/10 | | heatcntrettemp | heat counter return temperature | uint16 | C | false | DEVICE_DATA | 67 | 1 | 1/10 |
| heatcnt | heat counter impulses | uint8 | | false | DEVICE_DATA | 67 | 1 | 1 | | heatcnt | heat counter impulses | uint8 | | false | DEVICE_DATA | 68 | 1 | 1 |
| swapflowtemp | swap flow temperature (TS14) | uint16 | C | false | DEVICE_DATA | 68 | 1 | 1/10 | | swapflowtemp | swap flow temperature (TS14) | uint16 | C | false | DEVICE_DATA | 69 | 1 | 1/10 |
| swaprettemp | swap return temperature (TS15) | uint16 | C | false | DEVICE_DATA | 69 | 1 | 1/10 | | swaprettemp | swap return temperature (TS15) | uint16 | C | false | DEVICE_DATA | 70 | 1 | 1/10 |
int8 int8
| heatassiston | heat assistance on | int8 (&gt;=0&lt;=0) | K | true | DEVICE_DATA | 70 | 1 | 1/10 | | heatassiston | heat assistance on | int8 (&gt;=0&lt;=0) | K | true | DEVICE_DATA | 71 | 1 | 1/10 |
int8 int8
| heatassistoff | heat assistance off | int8 (&gt;=0&lt;=0) | K | true | DEVICE_DATA | 71 | 1 | 1/10 | | heatassistoff | heat assistance off | int8 (&gt;=0&lt;=0) | K | true | DEVICE_DATA | 72 | 1 | 1/10 |
### SM100, MS100 ### SM100, MS100
@@ -7778,67 +7779,68 @@ uint8
uint8 uint8
| turnoffdiff | pump turn off difference | uint8 (&gt;=0&lt;=0) | C | true | DEVICE_DATA | 11 | 1 | 1/10 | | turnoffdiff | pump turn off difference | uint8 (&gt;=0&lt;=0) | C | true | DEVICE_DATA | 11 | 1 | 1/10 |
| collector2temp | collector 2 temperature (TS7) | int16 | C | false | DEVICE_DATA | 20 | 1 | 1/10 | | collector2temp | collector 2 temperature (TS7) | int16 | C | false | DEVICE_DATA | 20 | 1 | 1/10 |
| cylmiddletemp | cylinder middle temperature (TS3) | int16 | C | false | DEVICE_DATA | 17 | 1 | 1/10 | | cylmiddletemp | cylinder middle temperature (TS14) | int16 | C | false | DEVICE_DATA | 17 | 1 | 1/10 |
| ts3 | cylinder middle temperature (TS3) | int16 | C | false | DEVICE_DATA | 21 | 1 | 1/10 |
| retheatassist | return temperature heat assistance (TS4) | int16 | C | false | DEVICE_DATA | 18 | 1 | 1/10 | | retheatassist | return temperature heat assistance (TS4) | int16 | C | false | DEVICE_DATA | 18 | 1 | 1/10 |
| ts8 | (TS8) | int16 | C | false | DEVICE_DATA | 21 | 1 | 1/10 | | ts8 | (TS8) | int16 | C | false | DEVICE_DATA | 22 | 1 | 1/10 |
| ts16 | (TS16) | int16 | C | false | DEVICE_DATA | 22 | 1 | 1/10 | | ts16 | (TS16) | int16 | C | false | DEVICE_DATA | 23 | 1 | 1/10 |
| heatassistvalve | heat assistance valve (M1) | boolean | | false | DEVICE_DATA | 19 | 1 | 1 | | heatassistvalve | heat assistance valve (M1) | boolean | | false | DEVICE_DATA | 19 | 1 | 1 |
| heatassistpower | heat assistance valve power (M1) | uint8 | % | false | DEVICE_DATA | 23 | 1 | 1 | | heatassistpower | heat assistance valve power (M1) | uint8 | % | false | DEVICE_DATA | 24 | 1 | 1 |
| solarpump2 | pump 2 (PS4) | boolean | | false | DEVICE_DATA | 24 | 1 | 1 | | solarpump2 | pump 2 (PS4) | boolean | | false | DEVICE_DATA | 25 | 1 | 1 |
| solarpump2mod | pump 2 modulation (PS4) | uint8 | % | false | DEVICE_DATA | 25 | 1 | 1 | | solarpump2mod | pump 2 modulation (PS4) | uint8 | % | false | DEVICE_DATA | 26 | 1 | 1 |
| cyl2bottomtemp | second cylinder bottom temperature (TS5) | int16 | C | false | DEVICE_DATA | 26 | 1 | 1/10 | | cyl2bottomtemp | second cylinder bottom temperature (TS5) | int16 | C | false | DEVICE_DATA | 27 | 1 | 1/10 |
| cyl3bottomtemp | third cylinder bottom temperature (TS11) | int16 | C | false | DEVICE_DATA | 27 | 1 | 1/10 | | cyl3bottomtemp | third cylinder bottom temperature (TS11) | int16 | C | false | DEVICE_DATA | 28 | 1 | 1/10 |
| cyltoptemp | cylinder top temperature (TS10) | int16 | C | false | DEVICE_DATA | 28 | 1 | 1/10 | | cyltoptemp | cylinder top temperature (TS10) | int16 | C | false | DEVICE_DATA | 29 | 1 | 1/10 |
| heatexchangertemp | heat exchanger temperature (TS6) | int16 | C | false | DEVICE_DATA | 29 | 1 | 1/10 | | heatexchangertemp | heat exchanger temperature (TS6) | int16 | C | false | DEVICE_DATA | 30 | 1 | 1/10 |
| cylpumpmod | cylinder pump modulation (PS5) | uint8 | % | false | DEVICE_DATA | 30 | 1 | 1 | | cylpumpmod | cylinder pump modulation (PS5) | uint8 | % | false | DEVICE_DATA | 31 | 1 | 1 |
| valvestatus | valve status | boolean | | false | DEVICE_DATA | 31 | 1 | 1 | | valvestatus | valve status | boolean | | false | DEVICE_DATA | 32 | 1 | 1 |
| vs1status | valve status VS1 | boolean | | false | DEVICE_DATA | 32 | 1 | 1 | | vs1status | valve status VS1 | boolean | | false | DEVICE_DATA | 33 | 1 | 1 |
| vs3status | valve status VS3 | boolean | | false | DEVICE_DATA | 33 | 1 | 1 | | vs3status | valve status VS3 | boolean | | false | DEVICE_DATA | 34 | 1 | 1 |
| transferpump | transfer pump | boolean | | false | DEVICE_DATA | 34 | 1 | 1 | | transferpump | transfer pump | boolean | | false | DEVICE_DATA | 35 | 1 | 1 |
| transferpumpmod | transfer pump modulation | uint8 | % | false | DEVICE_DATA | 35 | 1 | 1 | | transferpumpmod | transfer pump modulation | uint8 | % | false | DEVICE_DATA | 36 | 1 | 1 |
uint8 uint8
| collectormaxtemp | maximum collector temperature | uint8 (&gt;=0&lt;=0) | C | true | DEVICE_DATA | 36 | 1 | 1 | | collectormaxtemp | maximum collector temperature | uint8 (&gt;=0&lt;=0) | C | true | DEVICE_DATA | 37 | 1 | 1 |
uint8 uint8
| collectormintemp | minimum collector temperature | uint8 (&gt;=0&lt;=0) | C | true | DEVICE_DATA | 37 | 1 | 1 | | collectormintemp | minimum collector temperature | uint8 (&gt;=0&lt;=0) | C | true | DEVICE_DATA | 38 | 1 | 1 |
| energylasthour | energy last hour | uint24 | Wh | false | DEVICE_DATA | 13 | 2 | 1/10 | | energylasthour | energy last hour | uint24 | Wh | false | DEVICE_DATA | 13 | 2 | 1/10 |
| energytoday | total energy today | uint24 | Wh | false | DEVICE_DATA | 38 | 2 | 1 | | energytoday | total energy today | uint24 | Wh | false | DEVICE_DATA | 39 | 2 | 1 |
| energytotal | total energy | uint24 | kWh | false | DEVICE_DATA | 40 | 2 | 1/10 | | energytotal | total energy | uint24 | kWh | false | DEVICE_DATA | 41 | 2 | 1/10 |
| pump2worktime | pump 2 working time | time | minutes | false | DEVICE_DATA | 42 | 2 | 1 | | pump2worktime | pump 2 working time | time | minutes | false | DEVICE_DATA | 43 | 2 | 1 |
| m1worktime | differential control working time | time | minutes | false | DEVICE_DATA | 44 | 2 | 1 | | m1worktime | differential control working time | time | minutes | false | DEVICE_DATA | 45 | 2 | 1 |
| heattransfersystem | heattransfer system | boolean | | true | DEVICE_DATA | 46 | 1 | 1 | | heattransfersystem | heattransfer system | boolean | | true | DEVICE_DATA | 47 | 1 | 1 |
| externalcyl | external cylinder | boolean | | true | DEVICE_DATA | 47 | 1 | 1 | | externalcyl | external cylinder | boolean | | true | DEVICE_DATA | 48 | 1 | 1 |
| thermaldisinfect | thermal disinfection | boolean | | true | DEVICE_DATA | 48 | 1 | 1 | | thermaldisinfect | thermal disinfection | boolean | | true | DEVICE_DATA | 49 | 1 | 1 |
| heatmetering | heatmetering | boolean | | true | DEVICE_DATA | 49 | 1 | 1 | | heatmetering | heatmetering | boolean | | true | DEVICE_DATA | 50 | 1 | 1 |
| activated | activated | boolean | | true | DEVICE_DATA | 50 | 1 | 1 | | activated | activated | boolean | | true | DEVICE_DATA | 51 | 1 | 1 |
| solarpumpmode | solar pump mode | enum | | true | DEVICE_DATA | 51 | 1 | 1 | | solarpumpmode | solar pump mode | enum | | true | DEVICE_DATA | 52 | 1 | 1 |
| solarpumpkick | solar pump kick | boolean | | true | DEVICE_DATA | 52 | 1 | 1 | | solarpumpkick | solar pump kick | boolean | | true | DEVICE_DATA | 53 | 1 | 1 |
| plainwatermode | plain water mode | boolean | | true | DEVICE_DATA | 53 | 1 | 1 | | plainwatermode | plain water mode | boolean | | true | DEVICE_DATA | 54 | 1 | 1 |
| doublematchflow | doublematchflow | boolean | | true | DEVICE_DATA | 54 | 1 | 1 | | doublematchflow | doublematchflow | boolean | | true | DEVICE_DATA | 55 | 1 | 1 |
uint8 uint8
| pump2minmod | minimum pump 2 modulation | uint8 (&gt;=0&lt;=0) | % | true | DEVICE_DATA | 55 | 1 | 1 | | pump2minmod | minimum pump 2 modulation | uint8 (&gt;=0&lt;=0) | % | true | DEVICE_DATA | 56 | 1 | 1 |
uint8 uint8
| turnondiff2 | pump 2 turn on difference | uint8 (&gt;=0&lt;=0) | C | true | DEVICE_DATA | 56 | 1 | 1/10 | | turnondiff2 | pump 2 turn on difference | uint8 (&gt;=0&lt;=0) | C | true | DEVICE_DATA | 57 | 1 | 1/10 |
uint8 uint8
| turnoffdiff2 | pump 2 turn off difference | uint8 (&gt;=0&lt;=0) | C | true | DEVICE_DATA | 57 | 1 | 1/10 | | turnoffdiff2 | pump 2 turn off difference | uint8 (&gt;=0&lt;=0) | C | true | DEVICE_DATA | 58 | 1 | 1/10 |
| pump2kick | pump kick 2 | boolean | | true | DEVICE_DATA | 58 | 1 | 1 | | pump2kick | pump kick 2 | boolean | | true | DEVICE_DATA | 59 | 1 | 1 |
uint8 uint8
| climatezone | climate zone | uint8 (&gt;=0&lt;=0) | | true | DEVICE_DATA | 59 | 1 | 1 | | climatezone | climate zone | uint8 (&gt;=0&lt;=0) | | true | DEVICE_DATA | 60 | 1 | 1 |
uint16 uint16
| collector1area | collector 1 area | uint16 (&gt;=0&lt;=0) | m² | true | DEVICE_DATA | 60 | 1 | 1/10 | | collector1area | collector 1 area | uint16 (&gt;=0&lt;=0) | m² | true | DEVICE_DATA | 61 | 1 | 1/10 |
| collector1type | collector 1 type | enum | | true | DEVICE_DATA | 61 | 1 | 1 | | collector1type | collector 1 type | enum | | true | DEVICE_DATA | 62 | 1 | 1 |
uint16 uint16
| collector2area | collector 2 area | uint16 (&gt;=0&lt;=0) | m² | true | DEVICE_DATA | 62 | 1 | 1/10 | | collector2area | collector 2 area | uint16 (&gt;=0&lt;=0) | m² | true | DEVICE_DATA | 63 | 1 | 1/10 |
| collector2type | collector 2 type | enum | | true | DEVICE_DATA | 63 | 1 | 1 | | collector2type | collector 2 type | enum | | true | DEVICE_DATA | 64 | 1 | 1 |
| cylpriority | cylinder priority | enum | | true | DEVICE_DATA | 64 | 1 | 1 | | cylpriority | cylinder priority | enum | | true | DEVICE_DATA | 65 | 1 | 1 |
| heatcntflowtemp | heat counter flow temperature | uint16 | C | false | DEVICE_DATA | 65 | 1 | 1/10 | | heatcntflowtemp | heat counter flow temperature | uint16 | C | false | DEVICE_DATA | 66 | 1 | 1/10 |
| heatcntrettemp | heat counter return temperature | uint16 | C | false | DEVICE_DATA | 66 | 1 | 1/10 | | heatcntrettemp | heat counter return temperature | uint16 | C | false | DEVICE_DATA | 67 | 1 | 1/10 |
| heatcnt | heat counter impulses | uint8 | | false | DEVICE_DATA | 67 | 1 | 1 | | heatcnt | heat counter impulses | uint8 | | false | DEVICE_DATA | 68 | 1 | 1 |
| swapflowtemp | swap flow temperature (TS14) | uint16 | C | false | DEVICE_DATA | 68 | 1 | 1/10 | | swapflowtemp | swap flow temperature (TS14) | uint16 | C | false | DEVICE_DATA | 69 | 1 | 1/10 |
| swaprettemp | swap return temperature (TS15) | uint16 | C | false | DEVICE_DATA | 69 | 1 | 1/10 | | swaprettemp | swap return temperature (TS15) | uint16 | C | false | DEVICE_DATA | 70 | 1 | 1/10 |
int8 int8
| heatassiston | heat assistance on | int8 (&gt;=0&lt;=0) | K | true | DEVICE_DATA | 70 | 1 | 1/10 | | heatassiston | heat assistance on | int8 (&gt;=0&lt;=0) | K | true | DEVICE_DATA | 71 | 1 | 1/10 |
int8 int8
| heatassistoff | heat assistance off | int8 (&gt;=0&lt;=0) | K | true | DEVICE_DATA | 71 | 1 | 1/10 | | heatassistoff | heat assistance off | int8 (&gt;=0&lt;=0) | K | true | DEVICE_DATA | 72 | 1 | 1/10 |
### SM200, MS200 ### SM200, MS200
@@ -7860,67 +7862,68 @@ uint8
uint8 uint8
| turnoffdiff | pump turn off difference | uint8 (&gt;=0&lt;=0) | C | true | DEVICE_DATA | 11 | 1 | 1/10 | | turnoffdiff | pump turn off difference | uint8 (&gt;=0&lt;=0) | C | true | DEVICE_DATA | 11 | 1 | 1/10 |
| collector2temp | collector 2 temperature (TS7) | int16 | C | false | DEVICE_DATA | 20 | 1 | 1/10 | | collector2temp | collector 2 temperature (TS7) | int16 | C | false | DEVICE_DATA | 20 | 1 | 1/10 |
| cylmiddletemp | cylinder middle temperature (TS3) | int16 | C | false | DEVICE_DATA | 17 | 1 | 1/10 | | cylmiddletemp | cylinder middle temperature (TS14) | int16 | C | false | DEVICE_DATA | 17 | 1 | 1/10 |
| ts3 | cylinder middle temperature (TS3) | int16 | C | false | DEVICE_DATA | 21 | 1 | 1/10 |
| retheatassist | return temperature heat assistance (TS4) | int16 | C | false | DEVICE_DATA | 18 | 1 | 1/10 | | retheatassist | return temperature heat assistance (TS4) | int16 | C | false | DEVICE_DATA | 18 | 1 | 1/10 |
| ts8 | (TS8) | int16 | C | false | DEVICE_DATA | 21 | 1 | 1/10 | | ts8 | (TS8) | int16 | C | false | DEVICE_DATA | 22 | 1 | 1/10 |
| ts16 | (TS16) | int16 | C | false | DEVICE_DATA | 22 | 1 | 1/10 | | ts16 | (TS16) | int16 | C | false | DEVICE_DATA | 23 | 1 | 1/10 |
| heatassistvalve | heat assistance valve (M1) | boolean | | false | DEVICE_DATA | 19 | 1 | 1 | | heatassistvalve | heat assistance valve (M1) | boolean | | false | DEVICE_DATA | 19 | 1 | 1 |
| heatassistpower | heat assistance valve power (M1) | uint8 | % | false | DEVICE_DATA | 23 | 1 | 1 | | heatassistpower | heat assistance valve power (M1) | uint8 | % | false | DEVICE_DATA | 24 | 1 | 1 |
| solarpump2 | pump 2 (PS4) | boolean | | false | DEVICE_DATA | 24 | 1 | 1 | | solarpump2 | pump 2 (PS4) | boolean | | false | DEVICE_DATA | 25 | 1 | 1 |
| solarpump2mod | pump 2 modulation (PS4) | uint8 | % | false | DEVICE_DATA | 25 | 1 | 1 | | solarpump2mod | pump 2 modulation (PS4) | uint8 | % | false | DEVICE_DATA | 26 | 1 | 1 |
| cyl2bottomtemp | second cylinder bottom temperature (TS5) | int16 | C | false | DEVICE_DATA | 26 | 1 | 1/10 | | cyl2bottomtemp | second cylinder bottom temperature (TS5) | int16 | C | false | DEVICE_DATA | 27 | 1 | 1/10 |
| cyl3bottomtemp | third cylinder bottom temperature (TS11) | int16 | C | false | DEVICE_DATA | 27 | 1 | 1/10 | | cyl3bottomtemp | third cylinder bottom temperature (TS11) | int16 | C | false | DEVICE_DATA | 28 | 1 | 1/10 |
| cyltoptemp | cylinder top temperature (TS10) | int16 | C | false | DEVICE_DATA | 28 | 1 | 1/10 | | cyltoptemp | cylinder top temperature (TS10) | int16 | C | false | DEVICE_DATA | 29 | 1 | 1/10 |
| heatexchangertemp | heat exchanger temperature (TS6) | int16 | C | false | DEVICE_DATA | 29 | 1 | 1/10 | | heatexchangertemp | heat exchanger temperature (TS6) | int16 | C | false | DEVICE_DATA | 30 | 1 | 1/10 |
| cylpumpmod | cylinder pump modulation (PS5) | uint8 | % | false | DEVICE_DATA | 30 | 1 | 1 | | cylpumpmod | cylinder pump modulation (PS5) | uint8 | % | false | DEVICE_DATA | 31 | 1 | 1 |
| valvestatus | valve status | boolean | | false | DEVICE_DATA | 31 | 1 | 1 | | valvestatus | valve status | boolean | | false | DEVICE_DATA | 32 | 1 | 1 |
| vs1status | valve status VS1 | boolean | | false | DEVICE_DATA | 32 | 1 | 1 | | vs1status | valve status VS1 | boolean | | false | DEVICE_DATA | 33 | 1 | 1 |
| vs3status | valve status VS3 | boolean | | false | DEVICE_DATA | 33 | 1 | 1 | | vs3status | valve status VS3 | boolean | | false | DEVICE_DATA | 34 | 1 | 1 |
| transferpump | transfer pump | boolean | | false | DEVICE_DATA | 34 | 1 | 1 | | transferpump | transfer pump | boolean | | false | DEVICE_DATA | 35 | 1 | 1 |
| transferpumpmod | transfer pump modulation | uint8 | % | false | DEVICE_DATA | 35 | 1 | 1 | | transferpumpmod | transfer pump modulation | uint8 | % | false | DEVICE_DATA | 36 | 1 | 1 |
uint8 uint8
| collectormaxtemp | maximum collector temperature | uint8 (&gt;=0&lt;=0) | C | true | DEVICE_DATA | 36 | 1 | 1 | | collectormaxtemp | maximum collector temperature | uint8 (&gt;=0&lt;=0) | C | true | DEVICE_DATA | 37 | 1 | 1 |
uint8 uint8
| collectormintemp | minimum collector temperature | uint8 (&gt;=0&lt;=0) | C | true | DEVICE_DATA | 37 | 1 | 1 | | collectormintemp | minimum collector temperature | uint8 (&gt;=0&lt;=0) | C | true | DEVICE_DATA | 38 | 1 | 1 |
| energylasthour | energy last hour | uint24 | Wh | false | DEVICE_DATA | 13 | 2 | 1/10 | | energylasthour | energy last hour | uint24 | Wh | false | DEVICE_DATA | 13 | 2 | 1/10 |
| energytoday | total energy today | uint24 | Wh | false | DEVICE_DATA | 38 | 2 | 1 | | energytoday | total energy today | uint24 | Wh | false | DEVICE_DATA | 39 | 2 | 1 |
| energytotal | total energy | uint24 | kWh | false | DEVICE_DATA | 40 | 2 | 1/10 | | energytotal | total energy | uint24 | kWh | false | DEVICE_DATA | 41 | 2 | 1/10 |
| pump2worktime | pump 2 working time | time | minutes | false | DEVICE_DATA | 42 | 2 | 1 | | pump2worktime | pump 2 working time | time | minutes | false | DEVICE_DATA | 43 | 2 | 1 |
| m1worktime | differential control working time | time | minutes | false | DEVICE_DATA | 44 | 2 | 1 | | m1worktime | differential control working time | time | minutes | false | DEVICE_DATA | 45 | 2 | 1 |
| heattransfersystem | heattransfer system | boolean | | true | DEVICE_DATA | 46 | 1 | 1 | | heattransfersystem | heattransfer system | boolean | | true | DEVICE_DATA | 47 | 1 | 1 |
| externalcyl | external cylinder | boolean | | true | DEVICE_DATA | 47 | 1 | 1 | | externalcyl | external cylinder | boolean | | true | DEVICE_DATA | 48 | 1 | 1 |
| thermaldisinfect | thermal disinfection | boolean | | true | DEVICE_DATA | 48 | 1 | 1 | | thermaldisinfect | thermal disinfection | boolean | | true | DEVICE_DATA | 49 | 1 | 1 |
| heatmetering | heatmetering | boolean | | true | DEVICE_DATA | 49 | 1 | 1 | | heatmetering | heatmetering | boolean | | true | DEVICE_DATA | 50 | 1 | 1 |
| activated | activated | boolean | | true | DEVICE_DATA | 50 | 1 | 1 | | activated | activated | boolean | | true | DEVICE_DATA | 51 | 1 | 1 |
| solarpumpmode | solar pump mode | enum | | true | DEVICE_DATA | 51 | 1 | 1 | | solarpumpmode | solar pump mode | enum | | true | DEVICE_DATA | 52 | 1 | 1 |
| solarpumpkick | solar pump kick | boolean | | true | DEVICE_DATA | 52 | 1 | 1 | | solarpumpkick | solar pump kick | boolean | | true | DEVICE_DATA | 53 | 1 | 1 |
| plainwatermode | plain water mode | boolean | | true | DEVICE_DATA | 53 | 1 | 1 | | plainwatermode | plain water mode | boolean | | true | DEVICE_DATA | 54 | 1 | 1 |
| doublematchflow | doublematchflow | boolean | | true | DEVICE_DATA | 54 | 1 | 1 | | doublematchflow | doublematchflow | boolean | | true | DEVICE_DATA | 55 | 1 | 1 |
uint8 uint8
| pump2minmod | minimum pump 2 modulation | uint8 (&gt;=0&lt;=0) | % | true | DEVICE_DATA | 55 | 1 | 1 | | pump2minmod | minimum pump 2 modulation | uint8 (&gt;=0&lt;=0) | % | true | DEVICE_DATA | 56 | 1 | 1 |
uint8 uint8
| turnondiff2 | pump 2 turn on difference | uint8 (&gt;=0&lt;=0) | C | true | DEVICE_DATA | 56 | 1 | 1/10 | | turnondiff2 | pump 2 turn on difference | uint8 (&gt;=0&lt;=0) | C | true | DEVICE_DATA | 57 | 1 | 1/10 |
uint8 uint8
| turnoffdiff2 | pump 2 turn off difference | uint8 (&gt;=0&lt;=0) | C | true | DEVICE_DATA | 57 | 1 | 1/10 | | turnoffdiff2 | pump 2 turn off difference | uint8 (&gt;=0&lt;=0) | C | true | DEVICE_DATA | 58 | 1 | 1/10 |
| pump2kick | pump kick 2 | boolean | | true | DEVICE_DATA | 58 | 1 | 1 | | pump2kick | pump kick 2 | boolean | | true | DEVICE_DATA | 59 | 1 | 1 |
uint8 uint8
| climatezone | climate zone | uint8 (&gt;=0&lt;=0) | | true | DEVICE_DATA | 59 | 1 | 1 | | climatezone | climate zone | uint8 (&gt;=0&lt;=0) | | true | DEVICE_DATA | 60 | 1 | 1 |
uint16 uint16
| collector1area | collector 1 area | uint16 (&gt;=0&lt;=0) | m² | true | DEVICE_DATA | 60 | 1 | 1/10 | | collector1area | collector 1 area | uint16 (&gt;=0&lt;=0) | m² | true | DEVICE_DATA | 61 | 1 | 1/10 |
| collector1type | collector 1 type | enum | | true | DEVICE_DATA | 61 | 1 | 1 | | collector1type | collector 1 type | enum | | true | DEVICE_DATA | 62 | 1 | 1 |
uint16 uint16
| collector2area | collector 2 area | uint16 (&gt;=0&lt;=0) | m² | true | DEVICE_DATA | 62 | 1 | 1/10 | | collector2area | collector 2 area | uint16 (&gt;=0&lt;=0) | m² | true | DEVICE_DATA | 63 | 1 | 1/10 |
| collector2type | collector 2 type | enum | | true | DEVICE_DATA | 63 | 1 | 1 | | collector2type | collector 2 type | enum | | true | DEVICE_DATA | 64 | 1 | 1 |
| cylpriority | cylinder priority | enum | | true | DEVICE_DATA | 64 | 1 | 1 | | cylpriority | cylinder priority | enum | | true | DEVICE_DATA | 65 | 1 | 1 |
| heatcntflowtemp | heat counter flow temperature | uint16 | C | false | DEVICE_DATA | 65 | 1 | 1/10 | | heatcntflowtemp | heat counter flow temperature | uint16 | C | false | DEVICE_DATA | 66 | 1 | 1/10 |
| heatcntrettemp | heat counter return temperature | uint16 | C | false | DEVICE_DATA | 66 | 1 | 1/10 | | heatcntrettemp | heat counter return temperature | uint16 | C | false | DEVICE_DATA | 67 | 1 | 1/10 |
| heatcnt | heat counter impulses | uint8 | | false | DEVICE_DATA | 67 | 1 | 1 | | heatcnt | heat counter impulses | uint8 | | false | DEVICE_DATA | 68 | 1 | 1 |
| swapflowtemp | swap flow temperature (TS14) | uint16 | C | false | DEVICE_DATA | 68 | 1 | 1/10 | | swapflowtemp | swap flow temperature (TS14) | uint16 | C | false | DEVICE_DATA | 69 | 1 | 1/10 |
| swaprettemp | swap return temperature (TS15) | uint16 | C | false | DEVICE_DATA | 69 | 1 | 1/10 | | swaprettemp | swap return temperature (TS15) | uint16 | C | false | DEVICE_DATA | 70 | 1 | 1/10 |
int8 int8
| heatassiston | heat assistance on | int8 (&gt;=0&lt;=0) | K | true | DEVICE_DATA | 70 | 1 | 1/10 | | heatassiston | heat assistance on | int8 (&gt;=0&lt;=0) | K | true | DEVICE_DATA | 71 | 1 | 1/10 |
int8 int8
| heatassistoff | heat assistance off | int8 (&gt;=0&lt;=0) | K | true | DEVICE_DATA | 71 | 1 | 1/10 | | heatassistoff | heat assistance off | int8 (&gt;=0&lt;=0) | K | true | DEVICE_DATA | 72 | 1 | 1/10 |
## Devices of type \_heatpump ## Devices of type \_heatpump

View File

@@ -5495,7 +5495,7 @@ device name,device type,product id,shortname,fullname,type [options...] \| (min/
"ISM1",solar,101,cylmaxtemp,maximum cylinder temperature,uint8 (>=0<=0),C,true,number.solar_maximum_cylinder_temperature,number.solar_cylmaxtemp,8,0,1,5,1 "ISM1",solar,101,cylmaxtemp,maximum cylinder temperature,uint8 (>=0<=0),C,true,number.solar_maximum_cylinder_temperature,number.solar_cylmaxtemp,8,0,1,5,1
"ISM1",solar,101,collectorshutdown,collector shutdown,boolean, ,false,binary_sensor.solar_collector_shutdown,binary_sensor.solar_collectorshutdown,8,0,1,6,1 "ISM1",solar,101,collectorshutdown,collector shutdown,boolean, ,false,binary_sensor.solar_collector_shutdown,binary_sensor.solar_collectorshutdown,8,0,1,6,1
"ISM1",solar,101,cylheated,cyl heated,boolean, ,false,binary_sensor.solar_cyl_heated,binary_sensor.solar_cylheated,8,0,1,7,1 "ISM1",solar,101,cylheated,cyl heated,boolean, ,false,binary_sensor.solar_cyl_heated,binary_sensor.solar_cylheated,8,0,1,7,1
"ISM1",solar,101,cylmiddletemp,cylinder middle temperature (TS3),int16,C,false,sensor.solar_cylinder_middle_temperature_(TS3),sensor.solar_cylmiddletemp,8,0,1/10,17,1 "ISM1",solar,101,cylmiddletemp,cylinder middle temperature (TS14),int16,C,false,sensor.solar_cylinder_middle_temperature_(TS14),sensor.solar_cylmiddletemp,8,0,1/10,17,1
"ISM1",solar,101,retheatassist,return temperature heat assistance (TS4),int16,C,false,sensor.solar_return_temperature_heat_assistance_(TS4),sensor.solar_retheatassist,8,0,1/10,18,1 "ISM1",solar,101,retheatassist,return temperature heat assistance (TS4),int16,C,false,sensor.solar_return_temperature_heat_assistance_(TS4),sensor.solar_retheatassist,8,0,1/10,18,1
"ISM1",solar,101,heatassistvalve,heat assistance valve (M1),boolean, ,false,binary_sensor.solar_heat_assistance_valve_(M1),binary_sensor.solar_heatassistvalve,8,0,1,19,1 "ISM1",solar,101,heatassistvalve,heat assistance valve (M1),boolean, ,false,binary_sensor.solar_heat_assistance_valve_(M1),binary_sensor.solar_heatassistvalve,8,0,1,19,1
"ISM1",solar,101,energylasthour,energy last hour,uint24,Wh,false,sensor.solar_energy_last_hour,sensor.solar_energylasthour,8,0,1/10,13,2 "ISM1",solar,101,energylasthour,energy last hour,uint24,Wh,false,sensor.solar_energy_last_hour,sensor.solar_energylasthour,8,0,1/10,13,2
@@ -5506,7 +5506,7 @@ device name,device type,product id,shortname,fullname,type [options...] \| (min/
"ISM2",solar,103,cylmaxtemp,maximum cylinder temperature,uint8 (>=0<=0),C,true,number.solar_maximum_cylinder_temperature,number.solar_cylmaxtemp,8,0,1,5,1 "ISM2",solar,103,cylmaxtemp,maximum cylinder temperature,uint8 (>=0<=0),C,true,number.solar_maximum_cylinder_temperature,number.solar_cylmaxtemp,8,0,1,5,1
"ISM2",solar,103,collectorshutdown,collector shutdown,boolean, ,false,binary_sensor.solar_collector_shutdown,binary_sensor.solar_collectorshutdown,8,0,1,6,1 "ISM2",solar,103,collectorshutdown,collector shutdown,boolean, ,false,binary_sensor.solar_collector_shutdown,binary_sensor.solar_collectorshutdown,8,0,1,6,1
"ISM2",solar,103,cylheated,cyl heated,boolean, ,false,binary_sensor.solar_cyl_heated,binary_sensor.solar_cylheated,8,0,1,7,1 "ISM2",solar,103,cylheated,cyl heated,boolean, ,false,binary_sensor.solar_cyl_heated,binary_sensor.solar_cylheated,8,0,1,7,1
"ISM2",solar,103,cylmiddletemp,cylinder middle temperature (TS3),int16,C,false,sensor.solar_cylinder_middle_temperature_(TS3),sensor.solar_cylmiddletemp,8,0,1/10,17,1 "ISM2",solar,103,cylmiddletemp,cylinder middle temperature (TS14),int16,C,false,sensor.solar_cylinder_middle_temperature_(TS14),sensor.solar_cylmiddletemp,8,0,1/10,17,1
"ISM2",solar,103,retheatassist,return temperature heat assistance (TS4),int16,C,false,sensor.solar_return_temperature_heat_assistance_(TS4),sensor.solar_retheatassist,8,0,1/10,18,1 "ISM2",solar,103,retheatassist,return temperature heat assistance (TS4),int16,C,false,sensor.solar_return_temperature_heat_assistance_(TS4),sensor.solar_retheatassist,8,0,1/10,18,1
"ISM2",solar,103,heatassistvalve,heat assistance valve (M1),boolean, ,false,binary_sensor.solar_heat_assistance_valve_(M1),binary_sensor.solar_heatassistvalve,8,0,1,19,1 "ISM2",solar,103,heatassistvalve,heat assistance valve (M1),boolean, ,false,binary_sensor.solar_heat_assistance_valve_(M1),binary_sensor.solar_heatassistvalve,8,0,1,19,1
"ISM2",solar,103,energylasthour,energy last hour,uint24,Wh,false,sensor.solar_energy_last_hour,sensor.solar_energylasthour,8,0,1/10,13,2 "ISM2",solar,103,energylasthour,energy last hour,uint24,Wh,false,sensor.solar_energy_last_hour,sensor.solar_energylasthour,8,0,1/10,13,2
@@ -5522,57 +5522,58 @@ device name,device type,product id,shortname,fullname,type [options...] \| (min/
"SM50",solar,162,turnondiff,pump turn on difference,uint8 (>=0<=0),C,true,number.solar_pump_turn_on_difference,number.solar_turnondiff,8,0,1/10,10,1 "SM50",solar,162,turnondiff,pump turn on difference,uint8 (>=0<=0),C,true,number.solar_pump_turn_on_difference,number.solar_turnondiff,8,0,1/10,10,1
"SM50",solar,162,turnoffdiff,pump turn off difference,uint8 (>=0<=0),C,true,number.solar_pump_turn_off_difference,number.solar_turnoffdiff,8,0,1/10,11,1 "SM50",solar,162,turnoffdiff,pump turn off difference,uint8 (>=0<=0),C,true,number.solar_pump_turn_off_difference,number.solar_turnoffdiff,8,0,1/10,11,1
"SM50",solar,162,collector2temp,collector 2 temperature (TS7),int16,C,false,sensor.solar_collector_2_temperature_(TS7),sensor.solar_collector2temp,8,0,1/10,20,1 "SM50",solar,162,collector2temp,collector 2 temperature (TS7),int16,C,false,sensor.solar_collector_2_temperature_(TS7),sensor.solar_collector2temp,8,0,1/10,20,1
"SM50",solar,162,cylmiddletemp,cylinder middle temperature (TS3),int16,C,false,sensor.solar_cylinder_middle_temperature_(TS3),sensor.solar_cylmiddletemp,8,0,1/10,17,1 "SM50",solar,162,cylmiddletemp,cylinder middle temperature (TS14),int16,C,false,sensor.solar_cylinder_middle_temperature_(TS14),sensor.solar_cylmiddletemp,8,0,1/10,17,1
"SM50",solar,162,ts3,cylinder middle temperature (TS3),int16,C,false,sensor.solar_cylinder_middle_temperature_(TS3),sensor.solar_ts3,8,0,1/10,21,1
"SM50",solar,162,retheatassist,return temperature heat assistance (TS4),int16,C,false,sensor.solar_return_temperature_heat_assistance_(TS4),sensor.solar_retheatassist,8,0,1/10,18,1 "SM50",solar,162,retheatassist,return temperature heat assistance (TS4),int16,C,false,sensor.solar_return_temperature_heat_assistance_(TS4),sensor.solar_retheatassist,8,0,1/10,18,1
"SM50",solar,162,ts8,(TS8),int16,C,false,sensor.solar_(TS8),sensor.solar_ts8,8,0,1/10,21,1 "SM50",solar,162,ts8,(TS8),int16,C,false,sensor.solar_(TS8),sensor.solar_ts8,8,0,1/10,22,1
"SM50",solar,162,ts16,(TS16),int16,C,false,sensor.solar_(TS16),sensor.solar_ts16,8,0,1/10,22,1 "SM50",solar,162,ts16,(TS16),int16,C,false,sensor.solar_(TS16),sensor.solar_ts16,8,0,1/10,23,1
"SM50",solar,162,heatassistvalve,heat assistance valve (M1),boolean, ,false,binary_sensor.solar_heat_assistance_valve_(M1),binary_sensor.solar_heatassistvalve,8,0,1,19,1 "SM50",solar,162,heatassistvalve,heat assistance valve (M1),boolean, ,false,binary_sensor.solar_heat_assistance_valve_(M1),binary_sensor.solar_heatassistvalve,8,0,1,19,1
"SM50",solar,162,heatassistpower,heat assistance valve power (M1),uint8,%,false,sensor.solar_heat_assistance_valve_power_(M1),sensor.solar_heatassistpower,8,0,1,23,1 "SM50",solar,162,heatassistpower,heat assistance valve power (M1),uint8,%,false,sensor.solar_heat_assistance_valve_power_(M1),sensor.solar_heatassistpower,8,0,1,24,1
"SM50",solar,162,solarpump2,pump 2 (PS4),boolean, ,false,binary_sensor.solar_pump_2_(PS4),binary_sensor.solar_solarpump2,8,0,1,24,1 "SM50",solar,162,solarpump2,pump 2 (PS4),boolean, ,false,binary_sensor.solar_pump_2_(PS4),binary_sensor.solar_solarpump2,8,0,1,25,1
"SM50",solar,162,solarpump2mod,pump 2 modulation (PS4),uint8,%,false,sensor.solar_pump_2_modulation_(PS4),sensor.solar_solarpump2mod,8,0,1,25,1 "SM50",solar,162,solarpump2mod,pump 2 modulation (PS4),uint8,%,false,sensor.solar_pump_2_modulation_(PS4),sensor.solar_solarpump2mod,8,0,1,26,1
"SM50",solar,162,cyl2bottomtemp,second cylinder bottom temperature (TS5),int16,C,false,sensor.solar_second_cylinder_bottom_temperature_(TS5),sensor.solar_cyl2bottomtemp,8,0,1/10,26,1 "SM50",solar,162,cyl2bottomtemp,second cylinder bottom temperature (TS5),int16,C,false,sensor.solar_second_cylinder_bottom_temperature_(TS5),sensor.solar_cyl2bottomtemp,8,0,1/10,27,1
"SM50",solar,162,cyl3bottomtemp,third cylinder bottom temperature (TS11),int16,C,false,sensor.solar_third_cylinder_bottom_temperature_(TS11),sensor.solar_cyl3bottomtemp,8,0,1/10,27,1 "SM50",solar,162,cyl3bottomtemp,third cylinder bottom temperature (TS11),int16,C,false,sensor.solar_third_cylinder_bottom_temperature_(TS11),sensor.solar_cyl3bottomtemp,8,0,1/10,28,1
"SM50",solar,162,cyltoptemp,cylinder top temperature (TS10),int16,C,false,sensor.solar_cylinder_top_temperature_(TS10),sensor.solar_cyltoptemp,8,0,1/10,28,1 "SM50",solar,162,cyltoptemp,cylinder top temperature (TS10),int16,C,false,sensor.solar_cylinder_top_temperature_(TS10),sensor.solar_cyltoptemp,8,0,1/10,29,1
"SM50",solar,162,heatexchangertemp,heat exchanger temperature (TS6),int16,C,false,sensor.solar_heat_exchanger_temperature_(TS6),sensor.solar_heatexchangertemp,8,0,1/10,29,1 "SM50",solar,162,heatexchangertemp,heat exchanger temperature (TS6),int16,C,false,sensor.solar_heat_exchanger_temperature_(TS6),sensor.solar_heatexchangertemp,8,0,1/10,30,1
"SM50",solar,162,cylpumpmod,cylinder pump modulation (PS5),uint8,%,false,sensor.solar_cylinder_pump_modulation_(PS5),sensor.solar_cylpumpmod,8,0,1,30,1 "SM50",solar,162,cylpumpmod,cylinder pump modulation (PS5),uint8,%,false,sensor.solar_cylinder_pump_modulation_(PS5),sensor.solar_cylpumpmod,8,0,1,31,1
"SM50",solar,162,valvestatus,valve status,boolean, ,false,binary_sensor.solar_valve_status,binary_sensor.solar_valvestatus,8,0,1,31,1 "SM50",solar,162,valvestatus,valve status,boolean, ,false,binary_sensor.solar_valve_status,binary_sensor.solar_valvestatus,8,0,1,32,1
"SM50",solar,162,vs1status,valve status VS1,boolean, ,false,binary_sensor.solar_valve_status_VS1,binary_sensor.solar_vs1status,8,0,1,32,1 "SM50",solar,162,vs1status,valve status VS1,boolean, ,false,binary_sensor.solar_valve_status_VS1,binary_sensor.solar_vs1status,8,0,1,33,1
"SM50",solar,162,vs3status,valve status VS3,boolean, ,false,binary_sensor.solar_valve_status_VS3,binary_sensor.solar_vs3status,8,0,1,33,1 "SM50",solar,162,vs3status,valve status VS3,boolean, ,false,binary_sensor.solar_valve_status_VS3,binary_sensor.solar_vs3status,8,0,1,34,1
"SM50",solar,162,transferpump,transfer pump,boolean, ,false,binary_sensor.solar_transfer_pump,binary_sensor.solar_transferpump,8,0,1,34,1 "SM50",solar,162,transferpump,transfer pump,boolean, ,false,binary_sensor.solar_transfer_pump,binary_sensor.solar_transferpump,8,0,1,35,1
"SM50",solar,162,transferpumpmod,transfer pump modulation,uint8,%,false,sensor.solar_transfer_pump_modulation,sensor.solar_transferpumpmod,8,0,1,35,1 "SM50",solar,162,transferpumpmod,transfer pump modulation,uint8,%,false,sensor.solar_transfer_pump_modulation,sensor.solar_transferpumpmod,8,0,1,36,1
"SM50",solar,162,collectormaxtemp,maximum collector temperature,uint8 (>=0<=0),C,true,number.solar_maximum_collector_temperature,number.solar_collectormaxtemp,8,0,1,36,1 "SM50",solar,162,collectormaxtemp,maximum collector temperature,uint8 (>=0<=0),C,true,number.solar_maximum_collector_temperature,number.solar_collectormaxtemp,8,0,1,37,1
"SM50",solar,162,collectormintemp,minimum collector temperature,uint8 (>=0<=0),C,true,number.solar_minimum_collector_temperature,number.solar_collectormintemp,8,0,1,37,1 "SM50",solar,162,collectormintemp,minimum collector temperature,uint8 (>=0<=0),C,true,number.solar_minimum_collector_temperature,number.solar_collectormintemp,8,0,1,38,1
"SM50",solar,162,energylasthour,energy last hour,uint24,Wh,false,sensor.solar_energy_last_hour,sensor.solar_energylasthour,8,0,1/10,13,2 "SM50",solar,162,energylasthour,energy last hour,uint24,Wh,false,sensor.solar_energy_last_hour,sensor.solar_energylasthour,8,0,1/10,13,2
"SM50",solar,162,energytoday,total energy today,uint24,Wh,false,sensor.solar_total_energy_today,sensor.solar_energytoday,8,0,1,38,2 "SM50",solar,162,energytoday,total energy today,uint24,Wh,false,sensor.solar_total_energy_today,sensor.solar_energytoday,8,0,1,39,2
"SM50",solar,162,energytotal,total energy,uint24,kWh,false,sensor.solar_total_energy,sensor.solar_energytotal,8,0,1/10,40,2 "SM50",solar,162,energytotal,total energy,uint24,kWh,false,sensor.solar_total_energy,sensor.solar_energytotal,8,0,1/10,41,2
"SM50",solar,162,pump2worktime,pump 2 working time,time,minutes,false,sensor.solar_pump_2_working_time,sensor.solar_pump2worktime,8,0,1,42,2 "SM50",solar,162,pump2worktime,pump 2 working time,time,minutes,false,sensor.solar_pump_2_working_time,sensor.solar_pump2worktime,8,0,1,43,2
"SM50",solar,162,m1worktime,differential control working time,time,minutes,false,sensor.solar_differential_control_working_time,sensor.solar_m1worktime,8,0,1,44,2 "SM50",solar,162,m1worktime,differential control working time,time,minutes,false,sensor.solar_differential_control_working_time,sensor.solar_m1worktime,8,0,1,45,2
"SM50",solar,162,heattransfersystem,heattransfer system,boolean (>=0<=0), ,true,switch.solar_heattransfer_system,switch.solar_heattransfersystem,8,0,1,46,1 "SM50",solar,162,heattransfersystem,heattransfer system,boolean (>=0<=0), ,true,switch.solar_heattransfer_system,switch.solar_heattransfersystem,8,0,1,47,1
"SM50",solar,162,externalcyl,external cylinder,boolean (>=0<=0), ,true,switch.solar_external_cylinder,switch.solar_externalcyl,8,0,1,47,1 "SM50",solar,162,externalcyl,external cylinder,boolean (>=0<=0), ,true,switch.solar_external_cylinder,switch.solar_externalcyl,8,0,1,48,1
"SM50",solar,162,thermaldisinfect,thermal disinfection,boolean (>=0<=0), ,true,switch.solar_thermal_disinfection,switch.solar_thermaldisinfect,8,0,1,48,1 "SM50",solar,162,thermaldisinfect,thermal disinfection,boolean (>=0<=0), ,true,switch.solar_thermal_disinfection,switch.solar_thermaldisinfect,8,0,1,49,1
"SM50",solar,162,heatmetering,heatmetering,boolean (>=0<=0), ,true,switch.solar_heatmetering,switch.solar_heatmetering,8,0,1,49,1 "SM50",solar,162,heatmetering,heatmetering,boolean (>=0<=0), ,true,switch.solar_heatmetering,switch.solar_heatmetering,8,0,1,50,1
"SM50",solar,162,activated,activated,boolean (>=0<=0), ,true,switch.solar_activated,switch.solar_activated,8,0,1,50,1 "SM50",solar,162,activated,activated,boolean (>=0<=0), ,true,switch.solar_activated,switch.solar_activated,8,0,1,51,1
"SM50",solar,162,solarpumpmode,solar pump mode,enum [constant\|pwm\|analog] (>=0<=0), ,true,select.solar_solar_pump_mode,select.solar_solarpumpmode,8,0,1,51,1 "SM50",solar,162,solarpumpmode,solar pump mode,enum [constant\|pwm\|analog] (>=0<=0), ,true,select.solar_solar_pump_mode,select.solar_solarpumpmode,8,0,1,52,1
"SM50",solar,162,solarpumpkick,solar pump kick,boolean (>=0<=0), ,true,switch.solar_solar_pump_kick,switch.solar_solarpumpkick,8,0,1,52,1 "SM50",solar,162,solarpumpkick,solar pump kick,boolean (>=0<=0), ,true,switch.solar_solar_pump_kick,switch.solar_solarpumpkick,8,0,1,53,1
"SM50",solar,162,plainwatermode,plain water mode,boolean (>=0<=0), ,true,switch.solar_plain_water_mode,switch.solar_plainwatermode,8,0,1,53,1 "SM50",solar,162,plainwatermode,plain water mode,boolean (>=0<=0), ,true,switch.solar_plain_water_mode,switch.solar_plainwatermode,8,0,1,54,1
"SM50",solar,162,doublematchflow,doublematchflow,boolean (>=0<=0), ,true,switch.solar_doublematchflow,switch.solar_doublematchflow,8,0,1,54,1 "SM50",solar,162,doublematchflow,doublematchflow,boolean (>=0<=0), ,true,switch.solar_doublematchflow,switch.solar_doublematchflow,8,0,1,55,1
"SM50",solar,162,pump2minmod,minimum pump 2 modulation,uint8 (>=0<=0),%,true,number.solar_minimum_pump_2_modulation,number.solar_pump2minmod,8,0,1,55,1 "SM50",solar,162,pump2minmod,minimum pump 2 modulation,uint8 (>=0<=0),%,true,number.solar_minimum_pump_2_modulation,number.solar_pump2minmod,8,0,1,56,1
"SM50",solar,162,turnondiff2,pump 2 turn on difference,uint8 (>=0<=0),C,true,number.solar_pump_2_turn_on_difference,number.solar_turnondiff2,8,0,1/10,56,1 "SM50",solar,162,turnondiff2,pump 2 turn on difference,uint8 (>=0<=0),C,true,number.solar_pump_2_turn_on_difference,number.solar_turnondiff2,8,0,1/10,57,1
"SM50",solar,162,turnoffdiff2,pump 2 turn off difference,uint8 (>=0<=0),C,true,number.solar_pump_2_turn_off_difference,number.solar_turnoffdiff2,8,0,1/10,57,1 "SM50",solar,162,turnoffdiff2,pump 2 turn off difference,uint8 (>=0<=0),C,true,number.solar_pump_2_turn_off_difference,number.solar_turnoffdiff2,8,0,1/10,58,1
"SM50",solar,162,pump2kick,pump kick 2,boolean (>=0<=0), ,true,switch.solar_pump_kick_2,switch.solar_pump2kick,8,0,1,58,1 "SM50",solar,162,pump2kick,pump kick 2,boolean (>=0<=0), ,true,switch.solar_pump_kick_2,switch.solar_pump2kick,8,0,1,59,1
"SM50",solar,162,climatezone,climate zone,uint8 (>=0<=0), ,true,number.solar_climate_zone,number.solar_climatezone,8,0,1,59,1 "SM50",solar,162,climatezone,climate zone,uint8 (>=0<=0), ,true,number.solar_climate_zone,number.solar_climatezone,8,0,1,60,1
"SM50",solar,162,collector1area,collector 1 area,uint16 (>=0<=0),,true,number.solar_collector_1_area,number.solar_collector1area,8,0,1/10,60,1 "SM50",solar,162,collector1area,collector 1 area,uint16 (>=0<=0),,true,number.solar_collector_1_area,number.solar_collector1area,8,0,1/10,61,1
"SM50",solar,162,collector1type,collector 1 type,enum [flat\|vacuum] (>=0<=0), ,true,select.solar_collector_1_type,select.solar_collector1type,8,0,1,61,1 "SM50",solar,162,collector1type,collector 1 type,enum [flat\|vacuum] (>=0<=0), ,true,select.solar_collector_1_type,select.solar_collector1type,8,0,1,62,1
"SM50",solar,162,collector2area,collector 2 area,uint16 (>=0<=0),,true,number.solar_collector_2_area,number.solar_collector2area,8,0,1/10,62,1 "SM50",solar,162,collector2area,collector 2 area,uint16 (>=0<=0),,true,number.solar_collector_2_area,number.solar_collector2area,8,0,1/10,63,1
"SM50",solar,162,collector2type,collector 2 type,enum [flat\|vacuum] (>=0<=0), ,true,select.solar_collector_2_type,select.solar_collector2type,8,0,1,63,1 "SM50",solar,162,collector2type,collector 2 type,enum [flat\|vacuum] (>=0<=0), ,true,select.solar_collector_2_type,select.solar_collector2type,8,0,1,64,1
"SM50",solar,162,cylpriority,cylinder priority,enum [cyl 1\|cyl 2] (>=0<=0), ,true,select.solar_cylinder_priority,select.solar_cylpriority,8,0,1,64,1 "SM50",solar,162,cylpriority,cylinder priority,enum [cyl 1\|cyl 2] (>=0<=0), ,true,select.solar_cylinder_priority,select.solar_cylpriority,8,0,1,65,1
"SM50",solar,162,heatcntflowtemp,heat counter flow temperature,uint16,C,false,sensor.solar_heat_counter_flow_temperature,sensor.solar_heatcntflowtemp,8,0,1/10,65,1 "SM50",solar,162,heatcntflowtemp,heat counter flow temperature,uint16,C,false,sensor.solar_heat_counter_flow_temperature,sensor.solar_heatcntflowtemp,8,0,1/10,66,1
"SM50",solar,162,heatcntrettemp,heat counter return temperature,uint16,C,false,sensor.solar_heat_counter_return_temperature,sensor.solar_heatcntrettemp,8,0,1/10,66,1 "SM50",solar,162,heatcntrettemp,heat counter return temperature,uint16,C,false,sensor.solar_heat_counter_return_temperature,sensor.solar_heatcntrettemp,8,0,1/10,67,1
"SM50",solar,162,heatcnt,heat counter impulses,uint8, ,false,sensor.solar_heat_counter_impulses,sensor.solar_heatcnt,8,0,1,67,1 "SM50",solar,162,heatcnt,heat counter impulses,uint8, ,false,sensor.solar_heat_counter_impulses,sensor.solar_heatcnt,8,0,1,68,1
"SM50",solar,162,swapflowtemp,swap flow temperature (TS14),uint16,C,false,sensor.solar_swap_flow_temperature_(TS14),sensor.solar_swapflowtemp,8,0,1/10,68,1 "SM50",solar,162,swapflowtemp,swap flow temperature (TS14),uint16,C,false,sensor.solar_swap_flow_temperature_(TS14),sensor.solar_swapflowtemp,8,0,1/10,69,1
"SM50",solar,162,swaprettemp,swap return temperature (TS15),uint16,C,false,sensor.solar_swap_return_temperature_(TS15),sensor.solar_swaprettemp,8,0,1/10,69,1 "SM50",solar,162,swaprettemp,swap return temperature (TS15),uint16,C,false,sensor.solar_swap_return_temperature_(TS15),sensor.solar_swaprettemp,8,0,1/10,70,1
"SM50",solar,162,heatassiston,heat assistance on,int8 (>=0<=0),K,true,number.solar_heat_assistance_on,number.solar_heatassiston,8,0,1/10,70,1 "SM50",solar,162,heatassiston,heat assistance on,int8 (>=0<=0),K,true,number.solar_heat_assistance_on,number.solar_heatassiston,8,0,1/10,71,1
"SM50",solar,162,heatassistoff,heat assistance off,int8 (>=0<=0),K,true,number.solar_heat_assistance_off,number.solar_heatassistoff,8,0,1/10,71,1 "SM50",solar,162,heatassistoff,heat assistance off,int8 (>=0<=0),K,true,number.solar_heat_assistance_off,number.solar_heatassistoff,8,0,1/10,72,1
"SM100, MS100",solar,163,collectortemp,collector temperature (TS1),int16,C,false,sensor.solar_collector_temperature_(TS1),sensor.solar_collectortemp,8,0,1/10,0,1 "SM100, MS100",solar,163,collectortemp,collector temperature (TS1),int16,C,false,sensor.solar_collector_temperature_(TS1),sensor.solar_collectortemp,8,0,1/10,0,1
"SM100, MS100",solar,163,cylbottomtemp,cylinder bottom temperature (TS2),int16,C,false,sensor.solar_cylinder_bottom_temperature_(TS2),sensor.solar_cylbottomtemp,8,0,1/10,1,1 "SM100, MS100",solar,163,cylbottomtemp,cylinder bottom temperature (TS2),int16,C,false,sensor.solar_cylinder_bottom_temperature_(TS2),sensor.solar_cylbottomtemp,8,0,1/10,1,1
"SM100, MS100",solar,163,solarpump,pump (PS1),boolean, ,false,binary_sensor.solar_pump_(PS1),binary_sensor.solar_solarpump,8,0,1,2,1 "SM100, MS100",solar,163,solarpump,pump (PS1),boolean, ,false,binary_sensor.solar_pump_(PS1),binary_sensor.solar_solarpump,8,0,1,2,1
@@ -5585,57 +5586,58 @@ device name,device type,product id,shortname,fullname,type [options...] \| (min/
"SM100, MS100",solar,163,turnondiff,pump turn on difference,uint8 (>=0<=0),C,true,number.solar_pump_turn_on_difference,number.solar_turnondiff,8,0,1/10,10,1 "SM100, MS100",solar,163,turnondiff,pump turn on difference,uint8 (>=0<=0),C,true,number.solar_pump_turn_on_difference,number.solar_turnondiff,8,0,1/10,10,1
"SM100, MS100",solar,163,turnoffdiff,pump turn off difference,uint8 (>=0<=0),C,true,number.solar_pump_turn_off_difference,number.solar_turnoffdiff,8,0,1/10,11,1 "SM100, MS100",solar,163,turnoffdiff,pump turn off difference,uint8 (>=0<=0),C,true,number.solar_pump_turn_off_difference,number.solar_turnoffdiff,8,0,1/10,11,1
"SM100, MS100",solar,163,collector2temp,collector 2 temperature (TS7),int16,C,false,sensor.solar_collector_2_temperature_(TS7),sensor.solar_collector2temp,8,0,1/10,20,1 "SM100, MS100",solar,163,collector2temp,collector 2 temperature (TS7),int16,C,false,sensor.solar_collector_2_temperature_(TS7),sensor.solar_collector2temp,8,0,1/10,20,1
"SM100, MS100",solar,163,cylmiddletemp,cylinder middle temperature (TS3),int16,C,false,sensor.solar_cylinder_middle_temperature_(TS3),sensor.solar_cylmiddletemp,8,0,1/10,17,1 "SM100, MS100",solar,163,cylmiddletemp,cylinder middle temperature (TS14),int16,C,false,sensor.solar_cylinder_middle_temperature_(TS14),sensor.solar_cylmiddletemp,8,0,1/10,17,1
"SM100, MS100",solar,163,ts3,cylinder middle temperature (TS3),int16,C,false,sensor.solar_cylinder_middle_temperature_(TS3),sensor.solar_ts3,8,0,1/10,21,1
"SM100, MS100",solar,163,retheatassist,return temperature heat assistance (TS4),int16,C,false,sensor.solar_return_temperature_heat_assistance_(TS4),sensor.solar_retheatassist,8,0,1/10,18,1 "SM100, MS100",solar,163,retheatassist,return temperature heat assistance (TS4),int16,C,false,sensor.solar_return_temperature_heat_assistance_(TS4),sensor.solar_retheatassist,8,0,1/10,18,1
"SM100, MS100",solar,163,ts8,(TS8),int16,C,false,sensor.solar_(TS8),sensor.solar_ts8,8,0,1/10,21,1 "SM100, MS100",solar,163,ts8,(TS8),int16,C,false,sensor.solar_(TS8),sensor.solar_ts8,8,0,1/10,22,1
"SM100, MS100",solar,163,ts16,(TS16),int16,C,false,sensor.solar_(TS16),sensor.solar_ts16,8,0,1/10,22,1 "SM100, MS100",solar,163,ts16,(TS16),int16,C,false,sensor.solar_(TS16),sensor.solar_ts16,8,0,1/10,23,1
"SM100, MS100",solar,163,heatassistvalve,heat assistance valve (M1),boolean, ,false,binary_sensor.solar_heat_assistance_valve_(M1),binary_sensor.solar_heatassistvalve,8,0,1,19,1 "SM100, MS100",solar,163,heatassistvalve,heat assistance valve (M1),boolean, ,false,binary_sensor.solar_heat_assistance_valve_(M1),binary_sensor.solar_heatassistvalve,8,0,1,19,1
"SM100, MS100",solar,163,heatassistpower,heat assistance valve power (M1),uint8,%,false,sensor.solar_heat_assistance_valve_power_(M1),sensor.solar_heatassistpower,8,0,1,23,1 "SM100, MS100",solar,163,heatassistpower,heat assistance valve power (M1),uint8,%,false,sensor.solar_heat_assistance_valve_power_(M1),sensor.solar_heatassistpower,8,0,1,24,1
"SM100, MS100",solar,163,solarpump2,pump 2 (PS4),boolean, ,false,binary_sensor.solar_pump_2_(PS4),binary_sensor.solar_solarpump2,8,0,1,24,1 "SM100, MS100",solar,163,solarpump2,pump 2 (PS4),boolean, ,false,binary_sensor.solar_pump_2_(PS4),binary_sensor.solar_solarpump2,8,0,1,25,1
"SM100, MS100",solar,163,solarpump2mod,pump 2 modulation (PS4),uint8,%,false,sensor.solar_pump_2_modulation_(PS4),sensor.solar_solarpump2mod,8,0,1,25,1 "SM100, MS100",solar,163,solarpump2mod,pump 2 modulation (PS4),uint8,%,false,sensor.solar_pump_2_modulation_(PS4),sensor.solar_solarpump2mod,8,0,1,26,1
"SM100, MS100",solar,163,cyl2bottomtemp,second cylinder bottom temperature (TS5),int16,C,false,sensor.solar_second_cylinder_bottom_temperature_(TS5),sensor.solar_cyl2bottomtemp,8,0,1/10,26,1 "SM100, MS100",solar,163,cyl2bottomtemp,second cylinder bottom temperature (TS5),int16,C,false,sensor.solar_second_cylinder_bottom_temperature_(TS5),sensor.solar_cyl2bottomtemp,8,0,1/10,27,1
"SM100, MS100",solar,163,cyl3bottomtemp,third cylinder bottom temperature (TS11),int16,C,false,sensor.solar_third_cylinder_bottom_temperature_(TS11),sensor.solar_cyl3bottomtemp,8,0,1/10,27,1 "SM100, MS100",solar,163,cyl3bottomtemp,third cylinder bottom temperature (TS11),int16,C,false,sensor.solar_third_cylinder_bottom_temperature_(TS11),sensor.solar_cyl3bottomtemp,8,0,1/10,28,1
"SM100, MS100",solar,163,cyltoptemp,cylinder top temperature (TS10),int16,C,false,sensor.solar_cylinder_top_temperature_(TS10),sensor.solar_cyltoptemp,8,0,1/10,28,1 "SM100, MS100",solar,163,cyltoptemp,cylinder top temperature (TS10),int16,C,false,sensor.solar_cylinder_top_temperature_(TS10),sensor.solar_cyltoptemp,8,0,1/10,29,1
"SM100, MS100",solar,163,heatexchangertemp,heat exchanger temperature (TS6),int16,C,false,sensor.solar_heat_exchanger_temperature_(TS6),sensor.solar_heatexchangertemp,8,0,1/10,29,1 "SM100, MS100",solar,163,heatexchangertemp,heat exchanger temperature (TS6),int16,C,false,sensor.solar_heat_exchanger_temperature_(TS6),sensor.solar_heatexchangertemp,8,0,1/10,30,1
"SM100, MS100",solar,163,cylpumpmod,cylinder pump modulation (PS5),uint8,%,false,sensor.solar_cylinder_pump_modulation_(PS5),sensor.solar_cylpumpmod,8,0,1,30,1 "SM100, MS100",solar,163,cylpumpmod,cylinder pump modulation (PS5),uint8,%,false,sensor.solar_cylinder_pump_modulation_(PS5),sensor.solar_cylpumpmod,8,0,1,31,1
"SM100, MS100",solar,163,valvestatus,valve status,boolean, ,false,binary_sensor.solar_valve_status,binary_sensor.solar_valvestatus,8,0,1,31,1 "SM100, MS100",solar,163,valvestatus,valve status,boolean, ,false,binary_sensor.solar_valve_status,binary_sensor.solar_valvestatus,8,0,1,32,1
"SM100, MS100",solar,163,vs1status,valve status VS1,boolean, ,false,binary_sensor.solar_valve_status_VS1,binary_sensor.solar_vs1status,8,0,1,32,1 "SM100, MS100",solar,163,vs1status,valve status VS1,boolean, ,false,binary_sensor.solar_valve_status_VS1,binary_sensor.solar_vs1status,8,0,1,33,1
"SM100, MS100",solar,163,vs3status,valve status VS3,boolean, ,false,binary_sensor.solar_valve_status_VS3,binary_sensor.solar_vs3status,8,0,1,33,1 "SM100, MS100",solar,163,vs3status,valve status VS3,boolean, ,false,binary_sensor.solar_valve_status_VS3,binary_sensor.solar_vs3status,8,0,1,34,1
"SM100, MS100",solar,163,transferpump,transfer pump,boolean, ,false,binary_sensor.solar_transfer_pump,binary_sensor.solar_transferpump,8,0,1,34,1 "SM100, MS100",solar,163,transferpump,transfer pump,boolean, ,false,binary_sensor.solar_transfer_pump,binary_sensor.solar_transferpump,8,0,1,35,1
"SM100, MS100",solar,163,transferpumpmod,transfer pump modulation,uint8,%,false,sensor.solar_transfer_pump_modulation,sensor.solar_transferpumpmod,8,0,1,35,1 "SM100, MS100",solar,163,transferpumpmod,transfer pump modulation,uint8,%,false,sensor.solar_transfer_pump_modulation,sensor.solar_transferpumpmod,8,0,1,36,1
"SM100, MS100",solar,163,collectormaxtemp,maximum collector temperature,uint8 (>=0<=0),C,true,number.solar_maximum_collector_temperature,number.solar_collectormaxtemp,8,0,1,36,1 "SM100, MS100",solar,163,collectormaxtemp,maximum collector temperature,uint8 (>=0<=0),C,true,number.solar_maximum_collector_temperature,number.solar_collectormaxtemp,8,0,1,37,1
"SM100, MS100",solar,163,collectormintemp,minimum collector temperature,uint8 (>=0<=0),C,true,number.solar_minimum_collector_temperature,number.solar_collectormintemp,8,0,1,37,1 "SM100, MS100",solar,163,collectormintemp,minimum collector temperature,uint8 (>=0<=0),C,true,number.solar_minimum_collector_temperature,number.solar_collectormintemp,8,0,1,38,1
"SM100, MS100",solar,163,energylasthour,energy last hour,uint24,Wh,false,sensor.solar_energy_last_hour,sensor.solar_energylasthour,8,0,1/10,13,2 "SM100, MS100",solar,163,energylasthour,energy last hour,uint24,Wh,false,sensor.solar_energy_last_hour,sensor.solar_energylasthour,8,0,1/10,13,2
"SM100, MS100",solar,163,energytoday,total energy today,uint24,Wh,false,sensor.solar_total_energy_today,sensor.solar_energytoday,8,0,1,38,2 "SM100, MS100",solar,163,energytoday,total energy today,uint24,Wh,false,sensor.solar_total_energy_today,sensor.solar_energytoday,8,0,1,39,2
"SM100, MS100",solar,163,energytotal,total energy,uint24,kWh,false,sensor.solar_total_energy,sensor.solar_energytotal,8,0,1/10,40,2 "SM100, MS100",solar,163,energytotal,total energy,uint24,kWh,false,sensor.solar_total_energy,sensor.solar_energytotal,8,0,1/10,41,2
"SM100, MS100",solar,163,pump2worktime,pump 2 working time,time,minutes,false,sensor.solar_pump_2_working_time,sensor.solar_pump2worktime,8,0,1,42,2 "SM100, MS100",solar,163,pump2worktime,pump 2 working time,time,minutes,false,sensor.solar_pump_2_working_time,sensor.solar_pump2worktime,8,0,1,43,2
"SM100, MS100",solar,163,m1worktime,differential control working time,time,minutes,false,sensor.solar_differential_control_working_time,sensor.solar_m1worktime,8,0,1,44,2 "SM100, MS100",solar,163,m1worktime,differential control working time,time,minutes,false,sensor.solar_differential_control_working_time,sensor.solar_m1worktime,8,0,1,45,2
"SM100, MS100",solar,163,heattransfersystem,heattransfer system,boolean (>=0<=0), ,true,switch.solar_heattransfer_system,switch.solar_heattransfersystem,8,0,1,46,1 "SM100, MS100",solar,163,heattransfersystem,heattransfer system,boolean (>=0<=0), ,true,switch.solar_heattransfer_system,switch.solar_heattransfersystem,8,0,1,47,1
"SM100, MS100",solar,163,externalcyl,external cylinder,boolean (>=0<=0), ,true,switch.solar_external_cylinder,switch.solar_externalcyl,8,0,1,47,1 "SM100, MS100",solar,163,externalcyl,external cylinder,boolean (>=0<=0), ,true,switch.solar_external_cylinder,switch.solar_externalcyl,8,0,1,48,1
"SM100, MS100",solar,163,thermaldisinfect,thermal disinfection,boolean (>=0<=0), ,true,switch.solar_thermal_disinfection,switch.solar_thermaldisinfect,8,0,1,48,1 "SM100, MS100",solar,163,thermaldisinfect,thermal disinfection,boolean (>=0<=0), ,true,switch.solar_thermal_disinfection,switch.solar_thermaldisinfect,8,0,1,49,1
"SM100, MS100",solar,163,heatmetering,heatmetering,boolean (>=0<=0), ,true,switch.solar_heatmetering,switch.solar_heatmetering,8,0,1,49,1 "SM100, MS100",solar,163,heatmetering,heatmetering,boolean (>=0<=0), ,true,switch.solar_heatmetering,switch.solar_heatmetering,8,0,1,50,1
"SM100, MS100",solar,163,activated,activated,boolean (>=0<=0), ,true,switch.solar_activated,switch.solar_activated,8,0,1,50,1 "SM100, MS100",solar,163,activated,activated,boolean (>=0<=0), ,true,switch.solar_activated,switch.solar_activated,8,0,1,51,1
"SM100, MS100",solar,163,solarpumpmode,solar pump mode,enum [constant\|pwm\|analog] (>=0<=0), ,true,select.solar_solar_pump_mode,select.solar_solarpumpmode,8,0,1,51,1 "SM100, MS100",solar,163,solarpumpmode,solar pump mode,enum [constant\|pwm\|analog] (>=0<=0), ,true,select.solar_solar_pump_mode,select.solar_solarpumpmode,8,0,1,52,1
"SM100, MS100",solar,163,solarpumpkick,solar pump kick,boolean (>=0<=0), ,true,switch.solar_solar_pump_kick,switch.solar_solarpumpkick,8,0,1,52,1 "SM100, MS100",solar,163,solarpumpkick,solar pump kick,boolean (>=0<=0), ,true,switch.solar_solar_pump_kick,switch.solar_solarpumpkick,8,0,1,53,1
"SM100, MS100",solar,163,plainwatermode,plain water mode,boolean (>=0<=0), ,true,switch.solar_plain_water_mode,switch.solar_plainwatermode,8,0,1,53,1 "SM100, MS100",solar,163,plainwatermode,plain water mode,boolean (>=0<=0), ,true,switch.solar_plain_water_mode,switch.solar_plainwatermode,8,0,1,54,1
"SM100, MS100",solar,163,doublematchflow,doublematchflow,boolean (>=0<=0), ,true,switch.solar_doublematchflow,switch.solar_doublematchflow,8,0,1,54,1 "SM100, MS100",solar,163,doublematchflow,doublematchflow,boolean (>=0<=0), ,true,switch.solar_doublematchflow,switch.solar_doublematchflow,8,0,1,55,1
"SM100, MS100",solar,163,pump2minmod,minimum pump 2 modulation,uint8 (>=0<=0),%,true,number.solar_minimum_pump_2_modulation,number.solar_pump2minmod,8,0,1,55,1 "SM100, MS100",solar,163,pump2minmod,minimum pump 2 modulation,uint8 (>=0<=0),%,true,number.solar_minimum_pump_2_modulation,number.solar_pump2minmod,8,0,1,56,1
"SM100, MS100",solar,163,turnondiff2,pump 2 turn on difference,uint8 (>=0<=0),C,true,number.solar_pump_2_turn_on_difference,number.solar_turnondiff2,8,0,1/10,56,1 "SM100, MS100",solar,163,turnondiff2,pump 2 turn on difference,uint8 (>=0<=0),C,true,number.solar_pump_2_turn_on_difference,number.solar_turnondiff2,8,0,1/10,57,1
"SM100, MS100",solar,163,turnoffdiff2,pump 2 turn off difference,uint8 (>=0<=0),C,true,number.solar_pump_2_turn_off_difference,number.solar_turnoffdiff2,8,0,1/10,57,1 "SM100, MS100",solar,163,turnoffdiff2,pump 2 turn off difference,uint8 (>=0<=0),C,true,number.solar_pump_2_turn_off_difference,number.solar_turnoffdiff2,8,0,1/10,58,1
"SM100, MS100",solar,163,pump2kick,pump kick 2,boolean (>=0<=0), ,true,switch.solar_pump_kick_2,switch.solar_pump2kick,8,0,1,58,1 "SM100, MS100",solar,163,pump2kick,pump kick 2,boolean (>=0<=0), ,true,switch.solar_pump_kick_2,switch.solar_pump2kick,8,0,1,59,1
"SM100, MS100",solar,163,climatezone,climate zone,uint8 (>=0<=0), ,true,number.solar_climate_zone,number.solar_climatezone,8,0,1,59,1 "SM100, MS100",solar,163,climatezone,climate zone,uint8 (>=0<=0), ,true,number.solar_climate_zone,number.solar_climatezone,8,0,1,60,1
"SM100, MS100",solar,163,collector1area,collector 1 area,uint16 (>=0<=0),,true,number.solar_collector_1_area,number.solar_collector1area,8,0,1/10,60,1 "SM100, MS100",solar,163,collector1area,collector 1 area,uint16 (>=0<=0),,true,number.solar_collector_1_area,number.solar_collector1area,8,0,1/10,61,1
"SM100, MS100",solar,163,collector1type,collector 1 type,enum [flat\|vacuum] (>=0<=0), ,true,select.solar_collector_1_type,select.solar_collector1type,8,0,1,61,1 "SM100, MS100",solar,163,collector1type,collector 1 type,enum [flat\|vacuum] (>=0<=0), ,true,select.solar_collector_1_type,select.solar_collector1type,8,0,1,62,1
"SM100, MS100",solar,163,collector2area,collector 2 area,uint16 (>=0<=0),,true,number.solar_collector_2_area,number.solar_collector2area,8,0,1/10,62,1 "SM100, MS100",solar,163,collector2area,collector 2 area,uint16 (>=0<=0),,true,number.solar_collector_2_area,number.solar_collector2area,8,0,1/10,63,1
"SM100, MS100",solar,163,collector2type,collector 2 type,enum [flat\|vacuum] (>=0<=0), ,true,select.solar_collector_2_type,select.solar_collector2type,8,0,1,63,1 "SM100, MS100",solar,163,collector2type,collector 2 type,enum [flat\|vacuum] (>=0<=0), ,true,select.solar_collector_2_type,select.solar_collector2type,8,0,1,64,1
"SM100, MS100",solar,163,cylpriority,cylinder priority,enum [cyl 1\|cyl 2] (>=0<=0), ,true,select.solar_cylinder_priority,select.solar_cylpriority,8,0,1,64,1 "SM100, MS100",solar,163,cylpriority,cylinder priority,enum [cyl 1\|cyl 2] (>=0<=0), ,true,select.solar_cylinder_priority,select.solar_cylpriority,8,0,1,65,1
"SM100, MS100",solar,163,heatcntflowtemp,heat counter flow temperature,uint16,C,false,sensor.solar_heat_counter_flow_temperature,sensor.solar_heatcntflowtemp,8,0,1/10,65,1 "SM100, MS100",solar,163,heatcntflowtemp,heat counter flow temperature,uint16,C,false,sensor.solar_heat_counter_flow_temperature,sensor.solar_heatcntflowtemp,8,0,1/10,66,1
"SM100, MS100",solar,163,heatcntrettemp,heat counter return temperature,uint16,C,false,sensor.solar_heat_counter_return_temperature,sensor.solar_heatcntrettemp,8,0,1/10,66,1 "SM100, MS100",solar,163,heatcntrettemp,heat counter return temperature,uint16,C,false,sensor.solar_heat_counter_return_temperature,sensor.solar_heatcntrettemp,8,0,1/10,67,1
"SM100, MS100",solar,163,heatcnt,heat counter impulses,uint8, ,false,sensor.solar_heat_counter_impulses,sensor.solar_heatcnt,8,0,1,67,1 "SM100, MS100",solar,163,heatcnt,heat counter impulses,uint8, ,false,sensor.solar_heat_counter_impulses,sensor.solar_heatcnt,8,0,1,68,1
"SM100, MS100",solar,163,swapflowtemp,swap flow temperature (TS14),uint16,C,false,sensor.solar_swap_flow_temperature_(TS14),sensor.solar_swapflowtemp,8,0,1/10,68,1 "SM100, MS100",solar,163,swapflowtemp,swap flow temperature (TS14),uint16,C,false,sensor.solar_swap_flow_temperature_(TS14),sensor.solar_swapflowtemp,8,0,1/10,69,1
"SM100, MS100",solar,163,swaprettemp,swap return temperature (TS15),uint16,C,false,sensor.solar_swap_return_temperature_(TS15),sensor.solar_swaprettemp,8,0,1/10,69,1 "SM100, MS100",solar,163,swaprettemp,swap return temperature (TS15),uint16,C,false,sensor.solar_swap_return_temperature_(TS15),sensor.solar_swaprettemp,8,0,1/10,70,1
"SM100, MS100",solar,163,heatassiston,heat assistance on,int8 (>=0<=0),K,true,number.solar_heat_assistance_on,number.solar_heatassiston,8,0,1/10,70,1 "SM100, MS100",solar,163,heatassiston,heat assistance on,int8 (>=0<=0),K,true,number.solar_heat_assistance_on,number.solar_heatassiston,8,0,1/10,71,1
"SM100, MS100",solar,163,heatassistoff,heat assistance off,int8 (>=0<=0),K,true,number.solar_heat_assistance_off,number.solar_heatassistoff,8,0,1/10,71,1 "SM100, MS100",solar,163,heatassistoff,heat assistance off,int8 (>=0<=0),K,true,number.solar_heat_assistance_off,number.solar_heatassistoff,8,0,1/10,72,1
"SM200, MS200",solar,164,collectortemp,collector temperature (TS1),int16,C,false,sensor.solar_collector_temperature_(TS1),sensor.solar_collectortemp,8,0,1/10,0,1 "SM200, MS200",solar,164,collectortemp,collector temperature (TS1),int16,C,false,sensor.solar_collector_temperature_(TS1),sensor.solar_collectortemp,8,0,1/10,0,1
"SM200, MS200",solar,164,cylbottomtemp,cylinder bottom temperature (TS2),int16,C,false,sensor.solar_cylinder_bottom_temperature_(TS2),sensor.solar_cylbottomtemp,8,0,1/10,1,1 "SM200, MS200",solar,164,cylbottomtemp,cylinder bottom temperature (TS2),int16,C,false,sensor.solar_cylinder_bottom_temperature_(TS2),sensor.solar_cylbottomtemp,8,0,1/10,1,1
"SM200, MS200",solar,164,solarpump,pump (PS1),boolean, ,false,binary_sensor.solar_pump_(PS1),binary_sensor.solar_solarpump,8,0,1,2,1 "SM200, MS200",solar,164,solarpump,pump (PS1),boolean, ,false,binary_sensor.solar_pump_(PS1),binary_sensor.solar_solarpump,8,0,1,2,1
@@ -5648,57 +5650,58 @@ device name,device type,product id,shortname,fullname,type [options...] \| (min/
"SM200, MS200",solar,164,turnondiff,pump turn on difference,uint8 (>=0<=0),C,true,number.solar_pump_turn_on_difference,number.solar_turnondiff,8,0,1/10,10,1 "SM200, MS200",solar,164,turnondiff,pump turn on difference,uint8 (>=0<=0),C,true,number.solar_pump_turn_on_difference,number.solar_turnondiff,8,0,1/10,10,1
"SM200, MS200",solar,164,turnoffdiff,pump turn off difference,uint8 (>=0<=0),C,true,number.solar_pump_turn_off_difference,number.solar_turnoffdiff,8,0,1/10,11,1 "SM200, MS200",solar,164,turnoffdiff,pump turn off difference,uint8 (>=0<=0),C,true,number.solar_pump_turn_off_difference,number.solar_turnoffdiff,8,0,1/10,11,1
"SM200, MS200",solar,164,collector2temp,collector 2 temperature (TS7),int16,C,false,sensor.solar_collector_2_temperature_(TS7),sensor.solar_collector2temp,8,0,1/10,20,1 "SM200, MS200",solar,164,collector2temp,collector 2 temperature (TS7),int16,C,false,sensor.solar_collector_2_temperature_(TS7),sensor.solar_collector2temp,8,0,1/10,20,1
"SM200, MS200",solar,164,cylmiddletemp,cylinder middle temperature (TS3),int16,C,false,sensor.solar_cylinder_middle_temperature_(TS3),sensor.solar_cylmiddletemp,8,0,1/10,17,1 "SM200, MS200",solar,164,cylmiddletemp,cylinder middle temperature (TS14),int16,C,false,sensor.solar_cylinder_middle_temperature_(TS14),sensor.solar_cylmiddletemp,8,0,1/10,17,1
"SM200, MS200",solar,164,ts3,cylinder middle temperature (TS3),int16,C,false,sensor.solar_cylinder_middle_temperature_(TS3),sensor.solar_ts3,8,0,1/10,21,1
"SM200, MS200",solar,164,retheatassist,return temperature heat assistance (TS4),int16,C,false,sensor.solar_return_temperature_heat_assistance_(TS4),sensor.solar_retheatassist,8,0,1/10,18,1 "SM200, MS200",solar,164,retheatassist,return temperature heat assistance (TS4),int16,C,false,sensor.solar_return_temperature_heat_assistance_(TS4),sensor.solar_retheatassist,8,0,1/10,18,1
"SM200, MS200",solar,164,ts8,(TS8),int16,C,false,sensor.solar_(TS8),sensor.solar_ts8,8,0,1/10,21,1 "SM200, MS200",solar,164,ts8,(TS8),int16,C,false,sensor.solar_(TS8),sensor.solar_ts8,8,0,1/10,22,1
"SM200, MS200",solar,164,ts16,(TS16),int16,C,false,sensor.solar_(TS16),sensor.solar_ts16,8,0,1/10,22,1 "SM200, MS200",solar,164,ts16,(TS16),int16,C,false,sensor.solar_(TS16),sensor.solar_ts16,8,0,1/10,23,1
"SM200, MS200",solar,164,heatassistvalve,heat assistance valve (M1),boolean, ,false,binary_sensor.solar_heat_assistance_valve_(M1),binary_sensor.solar_heatassistvalve,8,0,1,19,1 "SM200, MS200",solar,164,heatassistvalve,heat assistance valve (M1),boolean, ,false,binary_sensor.solar_heat_assistance_valve_(M1),binary_sensor.solar_heatassistvalve,8,0,1,19,1
"SM200, MS200",solar,164,heatassistpower,heat assistance valve power (M1),uint8,%,false,sensor.solar_heat_assistance_valve_power_(M1),sensor.solar_heatassistpower,8,0,1,23,1 "SM200, MS200",solar,164,heatassistpower,heat assistance valve power (M1),uint8,%,false,sensor.solar_heat_assistance_valve_power_(M1),sensor.solar_heatassistpower,8,0,1,24,1
"SM200, MS200",solar,164,solarpump2,pump 2 (PS4),boolean, ,false,binary_sensor.solar_pump_2_(PS4),binary_sensor.solar_solarpump2,8,0,1,24,1 "SM200, MS200",solar,164,solarpump2,pump 2 (PS4),boolean, ,false,binary_sensor.solar_pump_2_(PS4),binary_sensor.solar_solarpump2,8,0,1,25,1
"SM200, MS200",solar,164,solarpump2mod,pump 2 modulation (PS4),uint8,%,false,sensor.solar_pump_2_modulation_(PS4),sensor.solar_solarpump2mod,8,0,1,25,1 "SM200, MS200",solar,164,solarpump2mod,pump 2 modulation (PS4),uint8,%,false,sensor.solar_pump_2_modulation_(PS4),sensor.solar_solarpump2mod,8,0,1,26,1
"SM200, MS200",solar,164,cyl2bottomtemp,second cylinder bottom temperature (TS5),int16,C,false,sensor.solar_second_cylinder_bottom_temperature_(TS5),sensor.solar_cyl2bottomtemp,8,0,1/10,26,1 "SM200, MS200",solar,164,cyl2bottomtemp,second cylinder bottom temperature (TS5),int16,C,false,sensor.solar_second_cylinder_bottom_temperature_(TS5),sensor.solar_cyl2bottomtemp,8,0,1/10,27,1
"SM200, MS200",solar,164,cyl3bottomtemp,third cylinder bottom temperature (TS11),int16,C,false,sensor.solar_third_cylinder_bottom_temperature_(TS11),sensor.solar_cyl3bottomtemp,8,0,1/10,27,1 "SM200, MS200",solar,164,cyl3bottomtemp,third cylinder bottom temperature (TS11),int16,C,false,sensor.solar_third_cylinder_bottom_temperature_(TS11),sensor.solar_cyl3bottomtemp,8,0,1/10,28,1
"SM200, MS200",solar,164,cyltoptemp,cylinder top temperature (TS10),int16,C,false,sensor.solar_cylinder_top_temperature_(TS10),sensor.solar_cyltoptemp,8,0,1/10,28,1 "SM200, MS200",solar,164,cyltoptemp,cylinder top temperature (TS10),int16,C,false,sensor.solar_cylinder_top_temperature_(TS10),sensor.solar_cyltoptemp,8,0,1/10,29,1
"SM200, MS200",solar,164,heatexchangertemp,heat exchanger temperature (TS6),int16,C,false,sensor.solar_heat_exchanger_temperature_(TS6),sensor.solar_heatexchangertemp,8,0,1/10,29,1 "SM200, MS200",solar,164,heatexchangertemp,heat exchanger temperature (TS6),int16,C,false,sensor.solar_heat_exchanger_temperature_(TS6),sensor.solar_heatexchangertemp,8,0,1/10,30,1
"SM200, MS200",solar,164,cylpumpmod,cylinder pump modulation (PS5),uint8,%,false,sensor.solar_cylinder_pump_modulation_(PS5),sensor.solar_cylpumpmod,8,0,1,30,1 "SM200, MS200",solar,164,cylpumpmod,cylinder pump modulation (PS5),uint8,%,false,sensor.solar_cylinder_pump_modulation_(PS5),sensor.solar_cylpumpmod,8,0,1,31,1
"SM200, MS200",solar,164,valvestatus,valve status,boolean, ,false,binary_sensor.solar_valve_status,binary_sensor.solar_valvestatus,8,0,1,31,1 "SM200, MS200",solar,164,valvestatus,valve status,boolean, ,false,binary_sensor.solar_valve_status,binary_sensor.solar_valvestatus,8,0,1,32,1
"SM200, MS200",solar,164,vs1status,valve status VS1,boolean, ,false,binary_sensor.solar_valve_status_VS1,binary_sensor.solar_vs1status,8,0,1,32,1 "SM200, MS200",solar,164,vs1status,valve status VS1,boolean, ,false,binary_sensor.solar_valve_status_VS1,binary_sensor.solar_vs1status,8,0,1,33,1
"SM200, MS200",solar,164,vs3status,valve status VS3,boolean, ,false,binary_sensor.solar_valve_status_VS3,binary_sensor.solar_vs3status,8,0,1,33,1 "SM200, MS200",solar,164,vs3status,valve status VS3,boolean, ,false,binary_sensor.solar_valve_status_VS3,binary_sensor.solar_vs3status,8,0,1,34,1
"SM200, MS200",solar,164,transferpump,transfer pump,boolean, ,false,binary_sensor.solar_transfer_pump,binary_sensor.solar_transferpump,8,0,1,34,1 "SM200, MS200",solar,164,transferpump,transfer pump,boolean, ,false,binary_sensor.solar_transfer_pump,binary_sensor.solar_transferpump,8,0,1,35,1
"SM200, MS200",solar,164,transferpumpmod,transfer pump modulation,uint8,%,false,sensor.solar_transfer_pump_modulation,sensor.solar_transferpumpmod,8,0,1,35,1 "SM200, MS200",solar,164,transferpumpmod,transfer pump modulation,uint8,%,false,sensor.solar_transfer_pump_modulation,sensor.solar_transferpumpmod,8,0,1,36,1
"SM200, MS200",solar,164,collectormaxtemp,maximum collector temperature,uint8 (>=0<=0),C,true,number.solar_maximum_collector_temperature,number.solar_collectormaxtemp,8,0,1,36,1 "SM200, MS200",solar,164,collectormaxtemp,maximum collector temperature,uint8 (>=0<=0),C,true,number.solar_maximum_collector_temperature,number.solar_collectormaxtemp,8,0,1,37,1
"SM200, MS200",solar,164,collectormintemp,minimum collector temperature,uint8 (>=0<=0),C,true,number.solar_minimum_collector_temperature,number.solar_collectormintemp,8,0,1,37,1 "SM200, MS200",solar,164,collectormintemp,minimum collector temperature,uint8 (>=0<=0),C,true,number.solar_minimum_collector_temperature,number.solar_collectormintemp,8,0,1,38,1
"SM200, MS200",solar,164,energylasthour,energy last hour,uint24,Wh,false,sensor.solar_energy_last_hour,sensor.solar_energylasthour,8,0,1/10,13,2 "SM200, MS200",solar,164,energylasthour,energy last hour,uint24,Wh,false,sensor.solar_energy_last_hour,sensor.solar_energylasthour,8,0,1/10,13,2
"SM200, MS200",solar,164,energytoday,total energy today,uint24,Wh,false,sensor.solar_total_energy_today,sensor.solar_energytoday,8,0,1,38,2 "SM200, MS200",solar,164,energytoday,total energy today,uint24,Wh,false,sensor.solar_total_energy_today,sensor.solar_energytoday,8,0,1,39,2
"SM200, MS200",solar,164,energytotal,total energy,uint24,kWh,false,sensor.solar_total_energy,sensor.solar_energytotal,8,0,1/10,40,2 "SM200, MS200",solar,164,energytotal,total energy,uint24,kWh,false,sensor.solar_total_energy,sensor.solar_energytotal,8,0,1/10,41,2
"SM200, MS200",solar,164,pump2worktime,pump 2 working time,time,minutes,false,sensor.solar_pump_2_working_time,sensor.solar_pump2worktime,8,0,1,42,2 "SM200, MS200",solar,164,pump2worktime,pump 2 working time,time,minutes,false,sensor.solar_pump_2_working_time,sensor.solar_pump2worktime,8,0,1,43,2
"SM200, MS200",solar,164,m1worktime,differential control working time,time,minutes,false,sensor.solar_differential_control_working_time,sensor.solar_m1worktime,8,0,1,44,2 "SM200, MS200",solar,164,m1worktime,differential control working time,time,minutes,false,sensor.solar_differential_control_working_time,sensor.solar_m1worktime,8,0,1,45,2
"SM200, MS200",solar,164,heattransfersystem,heattransfer system,boolean (>=0<=0), ,true,switch.solar_heattransfer_system,switch.solar_heattransfersystem,8,0,1,46,1 "SM200, MS200",solar,164,heattransfersystem,heattransfer system,boolean (>=0<=0), ,true,switch.solar_heattransfer_system,switch.solar_heattransfersystem,8,0,1,47,1
"SM200, MS200",solar,164,externalcyl,external cylinder,boolean (>=0<=0), ,true,switch.solar_external_cylinder,switch.solar_externalcyl,8,0,1,47,1 "SM200, MS200",solar,164,externalcyl,external cylinder,boolean (>=0<=0), ,true,switch.solar_external_cylinder,switch.solar_externalcyl,8,0,1,48,1
"SM200, MS200",solar,164,thermaldisinfect,thermal disinfection,boolean (>=0<=0), ,true,switch.solar_thermal_disinfection,switch.solar_thermaldisinfect,8,0,1,48,1 "SM200, MS200",solar,164,thermaldisinfect,thermal disinfection,boolean (>=0<=0), ,true,switch.solar_thermal_disinfection,switch.solar_thermaldisinfect,8,0,1,49,1
"SM200, MS200",solar,164,heatmetering,heatmetering,boolean (>=0<=0), ,true,switch.solar_heatmetering,switch.solar_heatmetering,8,0,1,49,1 "SM200, MS200",solar,164,heatmetering,heatmetering,boolean (>=0<=0), ,true,switch.solar_heatmetering,switch.solar_heatmetering,8,0,1,50,1
"SM200, MS200",solar,164,activated,activated,boolean (>=0<=0), ,true,switch.solar_activated,switch.solar_activated,8,0,1,50,1 "SM200, MS200",solar,164,activated,activated,boolean (>=0<=0), ,true,switch.solar_activated,switch.solar_activated,8,0,1,51,1
"SM200, MS200",solar,164,solarpumpmode,solar pump mode,enum [constant\|pwm\|analog] (>=0<=0), ,true,select.solar_solar_pump_mode,select.solar_solarpumpmode,8,0,1,51,1 "SM200, MS200",solar,164,solarpumpmode,solar pump mode,enum [constant\|pwm\|analog] (>=0<=0), ,true,select.solar_solar_pump_mode,select.solar_solarpumpmode,8,0,1,52,1
"SM200, MS200",solar,164,solarpumpkick,solar pump kick,boolean (>=0<=0), ,true,switch.solar_solar_pump_kick,switch.solar_solarpumpkick,8,0,1,52,1 "SM200, MS200",solar,164,solarpumpkick,solar pump kick,boolean (>=0<=0), ,true,switch.solar_solar_pump_kick,switch.solar_solarpumpkick,8,0,1,53,1
"SM200, MS200",solar,164,plainwatermode,plain water mode,boolean (>=0<=0), ,true,switch.solar_plain_water_mode,switch.solar_plainwatermode,8,0,1,53,1 "SM200, MS200",solar,164,plainwatermode,plain water mode,boolean (>=0<=0), ,true,switch.solar_plain_water_mode,switch.solar_plainwatermode,8,0,1,54,1
"SM200, MS200",solar,164,doublematchflow,doublematchflow,boolean (>=0<=0), ,true,switch.solar_doublematchflow,switch.solar_doublematchflow,8,0,1,54,1 "SM200, MS200",solar,164,doublematchflow,doublematchflow,boolean (>=0<=0), ,true,switch.solar_doublematchflow,switch.solar_doublematchflow,8,0,1,55,1
"SM200, MS200",solar,164,pump2minmod,minimum pump 2 modulation,uint8 (>=0<=0),%,true,number.solar_minimum_pump_2_modulation,number.solar_pump2minmod,8,0,1,55,1 "SM200, MS200",solar,164,pump2minmod,minimum pump 2 modulation,uint8 (>=0<=0),%,true,number.solar_minimum_pump_2_modulation,number.solar_pump2minmod,8,0,1,56,1
"SM200, MS200",solar,164,turnondiff2,pump 2 turn on difference,uint8 (>=0<=0),C,true,number.solar_pump_2_turn_on_difference,number.solar_turnondiff2,8,0,1/10,56,1 "SM200, MS200",solar,164,turnondiff2,pump 2 turn on difference,uint8 (>=0<=0),C,true,number.solar_pump_2_turn_on_difference,number.solar_turnondiff2,8,0,1/10,57,1
"SM200, MS200",solar,164,turnoffdiff2,pump 2 turn off difference,uint8 (>=0<=0),C,true,number.solar_pump_2_turn_off_difference,number.solar_turnoffdiff2,8,0,1/10,57,1 "SM200, MS200",solar,164,turnoffdiff2,pump 2 turn off difference,uint8 (>=0<=0),C,true,number.solar_pump_2_turn_off_difference,number.solar_turnoffdiff2,8,0,1/10,58,1
"SM200, MS200",solar,164,pump2kick,pump kick 2,boolean (>=0<=0), ,true,switch.solar_pump_kick_2,switch.solar_pump2kick,8,0,1,58,1 "SM200, MS200",solar,164,pump2kick,pump kick 2,boolean (>=0<=0), ,true,switch.solar_pump_kick_2,switch.solar_pump2kick,8,0,1,59,1
"SM200, MS200",solar,164,climatezone,climate zone,uint8 (>=0<=0), ,true,number.solar_climate_zone,number.solar_climatezone,8,0,1,59,1 "SM200, MS200",solar,164,climatezone,climate zone,uint8 (>=0<=0), ,true,number.solar_climate_zone,number.solar_climatezone,8,0,1,60,1
"SM200, MS200",solar,164,collector1area,collector 1 area,uint16 (>=0<=0),,true,number.solar_collector_1_area,number.solar_collector1area,8,0,1/10,60,1 "SM200, MS200",solar,164,collector1area,collector 1 area,uint16 (>=0<=0),,true,number.solar_collector_1_area,number.solar_collector1area,8,0,1/10,61,1
"SM200, MS200",solar,164,collector1type,collector 1 type,enum [flat\|vacuum] (>=0<=0), ,true,select.solar_collector_1_type,select.solar_collector1type,8,0,1,61,1 "SM200, MS200",solar,164,collector1type,collector 1 type,enum [flat\|vacuum] (>=0<=0), ,true,select.solar_collector_1_type,select.solar_collector1type,8,0,1,62,1
"SM200, MS200",solar,164,collector2area,collector 2 area,uint16 (>=0<=0),,true,number.solar_collector_2_area,number.solar_collector2area,8,0,1/10,62,1 "SM200, MS200",solar,164,collector2area,collector 2 area,uint16 (>=0<=0),,true,number.solar_collector_2_area,number.solar_collector2area,8,0,1/10,63,1
"SM200, MS200",solar,164,collector2type,collector 2 type,enum [flat\|vacuum] (>=0<=0), ,true,select.solar_collector_2_type,select.solar_collector2type,8,0,1,63,1 "SM200, MS200",solar,164,collector2type,collector 2 type,enum [flat\|vacuum] (>=0<=0), ,true,select.solar_collector_2_type,select.solar_collector2type,8,0,1,64,1
"SM200, MS200",solar,164,cylpriority,cylinder priority,enum [cyl 1\|cyl 2] (>=0<=0), ,true,select.solar_cylinder_priority,select.solar_cylpriority,8,0,1,64,1 "SM200, MS200",solar,164,cylpriority,cylinder priority,enum [cyl 1\|cyl 2] (>=0<=0), ,true,select.solar_cylinder_priority,select.solar_cylpriority,8,0,1,65,1
"SM200, MS200",solar,164,heatcntflowtemp,heat counter flow temperature,uint16,C,false,sensor.solar_heat_counter_flow_temperature,sensor.solar_heatcntflowtemp,8,0,1/10,65,1 "SM200, MS200",solar,164,heatcntflowtemp,heat counter flow temperature,uint16,C,false,sensor.solar_heat_counter_flow_temperature,sensor.solar_heatcntflowtemp,8,0,1/10,66,1
"SM200, MS200",solar,164,heatcntrettemp,heat counter return temperature,uint16,C,false,sensor.solar_heat_counter_return_temperature,sensor.solar_heatcntrettemp,8,0,1/10,66,1 "SM200, MS200",solar,164,heatcntrettemp,heat counter return temperature,uint16,C,false,sensor.solar_heat_counter_return_temperature,sensor.solar_heatcntrettemp,8,0,1/10,67,1
"SM200, MS200",solar,164,heatcnt,heat counter impulses,uint8, ,false,sensor.solar_heat_counter_impulses,sensor.solar_heatcnt,8,0,1,67,1 "SM200, MS200",solar,164,heatcnt,heat counter impulses,uint8, ,false,sensor.solar_heat_counter_impulses,sensor.solar_heatcnt,8,0,1,68,1
"SM200, MS200",solar,164,swapflowtemp,swap flow temperature (TS14),uint16,C,false,sensor.solar_swap_flow_temperature_(TS14),sensor.solar_swapflowtemp,8,0,1/10,68,1 "SM200, MS200",solar,164,swapflowtemp,swap flow temperature (TS14),uint16,C,false,sensor.solar_swap_flow_temperature_(TS14),sensor.solar_swapflowtemp,8,0,1/10,69,1
"SM200, MS200",solar,164,swaprettemp,swap return temperature (TS15),uint16,C,false,sensor.solar_swap_return_temperature_(TS15),sensor.solar_swaprettemp,8,0,1/10,69,1 "SM200, MS200",solar,164,swaprettemp,swap return temperature (TS15),uint16,C,false,sensor.solar_swap_return_temperature_(TS15),sensor.solar_swaprettemp,8,0,1/10,70,1
"SM200, MS200",solar,164,heatassiston,heat assistance on,int8 (>=0<=0),K,true,number.solar_heat_assistance_on,number.solar_heatassiston,8,0,1/10,70,1 "SM200, MS200",solar,164,heatassiston,heat assistance on,int8 (>=0<=0),K,true,number.solar_heat_assistance_on,number.solar_heatassiston,8,0,1/10,71,1
"SM200, MS200",solar,164,heatassistoff,heat assistance off,int8 (>=0<=0),K,true,number.solar_heat_assistance_off,number.solar_heatassistoff,8,0,1/10,71,1 "SM200, MS200",solar,164,heatassistoff,heat assistance off,int8 (>=0<=0),K,true,number.solar_heat_assistance_off,number.solar_heatassistoff,8,0,1/10,72,1
"HP Module",heatpump,252,airhumidity,relative air humidity,uint8,%,false,sensor.heatpump_relative_air_humidity,sensor.heatpump_airhumidity,9,0,1,0,1 "HP Module",heatpump,252,airhumidity,relative air humidity,uint8,%,false,sensor.heatpump_relative_air_humidity,sensor.heatpump_airhumidity,9,0,1,0,1
"HP Module",heatpump,252,dewtemperature,dew point temperature,uint8,C,false,sensor.heatpump_dew_point_temperature,sensor.heatpump_dewtemperature,9,0,1,1,1 "HP Module",heatpump,252,dewtemperature,dew point temperature,uint8,C,false,sensor.heatpump_dew_point_temperature,sensor.heatpump_dewtemperature,9,0,1,1,1
"HP Module",heatpump,252,curflowtemp,current flow temperature,int16,C,false,sensor.heatpump_current_flow_temperature,sensor.heatpump_curflowtemp,9,0,1/10,2,1 "HP Module",heatpump,252,curflowtemp,current flow temperature,int16,C,false,sensor.heatpump_current_flow_temperature,sensor.heatpump_curflowtemp,9,0,1/10,2,1
Can't render this file because it is too large.

View File

@@ -112,10 +112,10 @@ telegram_type_id,name,is_fetched
0x02A0,RC300Curves, 0x02A0,RC300Curves,
0x02A1,RC300Curves, 0x02A1,RC300Curves,
0x02A2,RC300Curves, 0x02A2,RC300Curves,
0x02A5,RC300Monitor,fetched 0x02A5,RC300Monitor,
0x02A6,CRFMonitor, 0x02A6,RC300Monitor,
0x02A7,RC300Monitor, 0x02A7,RC300Monitor,
0x02A8,CRFMonitor, 0x02A8,RC300Monitor,
0x02A9,RC300Monitor, 0x02A9,RC300Monitor,
0x02AA,RC300Monitor, 0x02AA,RC300Monitor,
0x02AB,RC300Monitor, 0x02AB,RC300Monitor,
@@ -171,6 +171,7 @@ telegram_type_id,name,is_fetched
0x0468,HPSet, 0x0468,HPSet,
0x0469,HPSet, 0x0469,HPSet,
0x046A,HPSet, 0x046A,HPSet,
0x0470,RC300Summer2,fetched
0x0471,RC300Summer2, 0x0471,RC300Summer2,
0x0472,RC300Summer2, 0x0472,RC300Summer2,
0x0473,RC300Summer2, 0x0473,RC300Summer2,
1 telegram_type_id name is_fetched
112 0x02A0 RC300Curves
113 0x02A1 RC300Curves
114 0x02A2 RC300Curves
115 0x02A5 RC300Monitor fetched
116 0x02A6 CRFMonitor RC300Monitor
117 0x02A7 RC300Monitor
118 0x02A8 CRFMonitor RC300Monitor
119 0x02A9 RC300Monitor
120 0x02AA RC300Monitor
121 0x02AB RC300Monitor
171 0x0468 HPSet
172 0x0469 HPSet
173 0x046A HPSet
174 0x0470 RC300Summer2 fetched
175 0x0471 RC300Summer2
176 0x0472 RC300Summer2
177 0x0473 RC300Summer2

View File

@@ -1,6 +1,6 @@
{ {
"name": "EMS-ESP", "name": "EMS-ESP",
"version": "3.8.0", "version": "3.8.2",
"description": "EMS-ESP WebUI", "description": "EMS-ESP WebUI",
"homepage": "https://emsesp.org", "homepage": "https://emsesp.org",
"author": "proddy, emsesp.org", "author": "proddy, emsesp.org",
@@ -17,7 +17,7 @@
"preview-standalone": "typesafe-i18n --no-watch && vite build && concurrently -c \"auto\" \"pnpm:mock-rest\" \"vite preview\"", "preview-standalone": "typesafe-i18n --no-watch && vite build && concurrently -c \"auto\" \"pnpm:mock-rest\" \"vite preview\"",
"standalone": "concurrently -c \"auto\" \"typesafe-i18n\" \"pnpm:mock-rest\" \"vite dev\"", "standalone": "concurrently -c \"auto\" \"typesafe-i18n\" \"pnpm:mock-rest\" \"vite dev\"",
"typesafe-i18n": "typesafe-i18n --no-watch", "typesafe-i18n": "typesafe-i18n --no-watch",
"build_webUI": "typesafe-i18n --no-watch && vite build && node progmem-generator.js", "build-webUI": "typesafe-i18n --no-watch && vite build && node progmem-generator.js",
"format": "prettier -l -w '**/*.{ts,tsx,js,css,json,md}'", "format": "prettier -l -w '**/*.{ts,tsx,js,css,json,md}'",
"lint": "eslint . --fix", "lint": "eslint . --fix",
"standalone-devcontainer": "concurrently -c \"auto\" \"typesafe-i18n\" \"pnpm:mock-rest\" \"vite --host\"" "standalone-devcontainer": "concurrently -c \"auto\" \"typesafe-i18n\" \"pnpm:mock-rest\" \"vite --host\""
@@ -26,25 +26,25 @@
"@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": "^7.3.9", "@mui/icons-material": "^9.0.1",
"@mui/material": "^7.3.9", "@mui/material": "^9.0.1",
"@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",
"async-validator": "^4.2.5", "async-validator": "^4.2.5",
"etag": "^1.8.1", "etag": "^1.8.1",
"formidable": "^3.5.4", "formidable": "^3.5.4",
"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.0", "preact": "^10.29.1",
"react": "^19.2.4", "react": "^19.2.6",
"react-dom": "^19.2.4", "react-dom": "^19.2.6",
"react-icons": "^5.6.0", "react-icons": "^5.6.0",
"react-router": "^7.13.1", "react-router": "^7.15.0",
"react-toastify": "^11.0.5", "react-toastify": "^11.1.0",
"typesafe-i18n": "^5.27.1", "typesafe-i18n": "^5.27.1",
"typescript": "^5.9.3" "typescript": "^6.0.3"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.29.0", "@babel/core": "^7.29.0",
@@ -52,19 +52,20 @@
"@preact/compat": "^18.3.2", "@preact/compat": "^18.3.2",
"@preact/preset-vite": "^2.10.5", "@preact/preset-vite": "^2.10.5",
"@trivago/prettier-plugin-sort-imports": "^6.0.2", "@trivago/prettier-plugin-sort-imports": "^6.0.2",
"@types/node": "^25.5.0", "@types/node": "^25.6.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.1", "axe-core": "^4.11.4",
"concurrently": "^9.2.1", "concurrently": "^9.2.1",
"eslint": "^10.1.0", "eslint": "^10.3.0",
"eslint-config-prettier": "^10.1.8", "eslint-config-prettier": "^10.1.8",
"prettier": "^3.8.1", "prettier": "^3.8.3",
"rollup-plugin-visualizer": "^7.0.1", "rollup-plugin-visualizer": "^7.0.1",
"terser": "^5.46.1", "terser": "^5.47.0",
"typescript-eslint": "^8.57.1", "typescript-eslint": "^8.59.2",
"vite": "^8.0.1", "vite": "^8.0.11",
"vite-plugin-imagemin": "^0.6.1" "vite-plugin-imagemin": "^0.6.1",
"vite-tsconfig-paths": "^6.1.1"
}, },
"packageManager": "pnpm@10.32.1+sha512.a706938f0e89ac1456b6563eab4edf1d1faf3368d1191fc5c59790e96dc918e4456ab2e67d613de1043d2e8c81f87303e6b40d4ffeca9df15ef1ad567348f2be" "packageManager": "pnpm@10.33.4"
} }

1154
interface/pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -24,20 +24,26 @@ let bundleStats = {
other: { count: 0, uncompressed: 0, compressed: 0 } other: { count: 0, uncompressed: 0, compressed: 0 }
}; };
const generateWWWClass = // AsyncWebHandler that performs the lookup.
() => `typedef std::function<void(const char * uri, const String & contentType, const uint8_t * content, size_t len, const String & hash)> RouteRegistrationHandler; const generateWWWClass = () => `// Bundle Statistics:
// Bundle Statistics:
// - Total compressed size: ${(totalSize / 1000).toFixed(1)} KB // - Total compressed size: ${(totalSize / 1000).toFixed(1)} KB
// - Total uncompressed size: ${(Object.values(bundleStats).reduce((sum, stat) => sum + stat.uncompressed, 0) / 1000).toFixed(1)} KB // - Total uncompressed size: ${(Object.values(bundleStats).reduce((sum, stat) => sum + stat.uncompressed, 0) / 1000).toFixed(1)} KB
// - Compression ratio: ${(((Object.values(bundleStats).reduce((sum, stat) => sum + stat.uncompressed, 0) - totalSize) / Object.values(bundleStats).reduce((sum, stat) => sum + stat.uncompressed, 0)) * 100).toFixed(1)}% // - Compression ratio: ${(((Object.values(bundleStats).reduce((sum, stat) => sum + stat.uncompressed, 0) - totalSize) / Object.values(bundleStats).reduce((sum, stat) => sum + stat.uncompressed, 0)) * 100).toFixed(1)}%
// - Generated on: ${new Date().toISOString()} // - Generated on: ${new Date().toISOString()}
class WWWData { struct WWWAsset {
${INDENT}public: ${INDENT}const char * uri;
${INDENT.repeat(2)}static void registerRoutes(RouteRegistrationHandler handler) { ${INDENT}const char * contentType;
${fileInfo.map((f) => `${INDENT.repeat(3)}handler("${f.uri}", "${f.mimeType}", ${f.variable}, ${f.size}, ${f.hash});`).join('\n')} ${INDENT}const uint8_t * content;
${INDENT.repeat(2)}} ${INDENT}size_t len;
${INDENT}const char * etag; // already includes enclosing double quotes
}; };
static const WWWAsset WWW_ASSETS[] = {
${fileInfo.map((f) => `${INDENT}{"${f.uri}", "${f.mimeType}", ${f.variable}, ${f.size}, "\\"${f.rawHash}\\""},`).join('\n')}
};
static constexpr size_t WWW_ASSETS_COUNT = sizeof(WWW_ASSETS) / sizeof(WWW_ASSETS[0]);
`; `;
const getFilesSync = (dir, files = []) => { const getFilesSync = (dir, files = []) => {
@@ -72,6 +78,7 @@ const writeFile = (relativeFilePath, buffer) => {
const zipBuffer = zlib.gzipSync(buffer, { level: 9 }); const zipBuffer = zlib.gzipSync(buffer, { level: 9 });
// const hash = crypto.createHash('sha256').update(zipBuffer).digest('hex'); // const hash = crypto.createHash('sha256').update(zipBuffer).digest('hex');
const hash = etag(zipBuffer); // use smaller md5 instead of sha256 const hash = etag(zipBuffer); // use smaller md5 instead of sha256
const rawHash = hash.replace(/^"|"$/g, '');
zipBuffer.forEach((b) => { zipBuffer.forEach((b) => {
if (!(size % bytesPerLine)) { if (!(size % bytesPerLine)) {
@@ -94,7 +101,8 @@ const writeFile = (relativeFilePath, buffer) => {
mimeType, mimeType,
variable, variable,
size, size,
hash hash,
rawHash
}); });
totalSize += size; totalSize += size;

View File

@@ -1,19 +1,13 @@
import { type FC, Suspense, lazy, memo, useContext, useEffect, useRef } from 'react'; import { type FC, memo, useContext, useEffect, useRef } from 'react';
import { Navigate, Route, Routes } from 'react-router'; import { Navigate, Route, Routes } from 'react-router';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import { import AuthenticatedRouting from 'AuthenticatedRouting';
LoadingSpinner, import SignIn from 'SignIn';
RequireAuthenticated, import { RequireAuthenticated, RequireUnauthenticated } from 'components';
RequireUnauthenticated
} from 'components';
import { Authentication, AuthenticationContext } from 'contexts/authentication'; import { Authentication, AuthenticationContext } from 'contexts/authentication';
import { useI18nContext } from 'i18n/i18n-react'; import { useI18nContext } from 'i18n/i18n-react';
// Lazy load route components for better code splitting
const SignIn = lazy(() => import('SignIn'));
const AuthenticatedRouting = lazy(() => import('AuthenticatedRouting'));
interface SecurityRedirectProps { interface SecurityRedirectProps {
readonly message: string; readonly message: string;
readonly signOut?: boolean; readonly signOut?: boolean;
@@ -45,7 +39,6 @@ const AppRouting: FC = memo(() => {
return ( return (
<Authentication> <Authentication>
<Suspense fallback={<LoadingSpinner />}>
<Routes> <Routes>
<Route <Route
path="/unauthorized" path="/unauthorized"
@@ -72,7 +65,6 @@ const AppRouting: FC = memo(() => {
} }
/> />
</Routes> </Routes>
</Suspense>
</Authentication> </Authentication>
); );
}); });

View File

@@ -1,44 +1,39 @@
import { Suspense, lazy, memo, useContext } from 'react'; import { memo, useContext } from 'react';
import { Navigate, Route, Routes } from 'react-router'; import { Navigate, Route, Routes } from 'react-router';
import { Layout, LoadingSpinner } from 'components'; import CustomEntities from 'app/main/CustomEntities';
import Customizations from 'app/main/Customizations';
import Dashboard from 'app/main/Dashboard';
import Devices from 'app/main/Devices';
import Help from 'app/main/Help';
import Modules from 'app/main/Modules';
import Scheduler from 'app/main/Scheduler';
import Sensors from 'app/main/Sensors';
import UserProfile from 'app/main/UserProfile';
import APSettings from 'app/settings/APSettings';
import ApplicationSettings from 'app/settings/ApplicationSettings';
import DownloadUpload from 'app/settings/DownloadUpload';
import MqttSettings from 'app/settings/MqttSettings';
import NTPSettings from 'app/settings/NTPSettings';
import Settings from 'app/settings/Settings';
import Network from 'app/settings/network/Network';
import Security from 'app/settings/security/Security';
import APStatus from 'app/status/APStatus';
import Activity from 'app/status/Activity';
import HardwareStatus from 'app/status/HardwareStatus';
import MqttStatus from 'app/status/MqttStatus';
import NTPStatus from 'app/status/NTPStatus';
import NetworkStatus from 'app/status/NetworkStatus';
import Status from 'app/status/Status';
import SystemLog from 'app/status/SystemLog';
import Version from 'app/status/Version';
import { Layout } from 'components';
import { AuthenticatedContext } from 'contexts/authentication'; import { AuthenticatedContext } from 'contexts/authentication';
// Lazy load all route components for better code splitting
const Dashboard = lazy(() => import('app/main/Dashboard'));
const Devices = lazy(() => import('app/main/Devices'));
const Sensors = lazy(() => import('app/main/Sensors'));
const Help = lazy(() => import('app/main/Help'));
const Customizations = lazy(() => import('app/main/Customizations'));
const Scheduler = lazy(() => import('app/main/Scheduler'));
const CustomEntities = lazy(() => import('app/main/CustomEntities'));
const Modules = lazy(() => import('app/main/Modules'));
const UserProfile = lazy(() => import('app/main/UserProfile'));
const Status = lazy(() => import('app/status/Status'));
const HardwareStatus = lazy(() => import('app/status/HardwareStatus'));
const Activity = lazy(() => import('app/status/Activity'));
const SystemLog = lazy(() => import('app/status/SystemLog'));
const MqttStatus = lazy(() => import('app/status/MqttStatus'));
const NTPStatus = lazy(() => import('app/status/NTPStatus'));
const APStatus = lazy(() => import('app/status/APStatus'));
const NetworkStatus = lazy(() => import('app/status/NetworkStatus'));
const Version = lazy(() => import('app/status/Version'));
const Settings = lazy(() => import('app/settings/Settings'));
const ApplicationSettings = lazy(() => import('app/settings/ApplicationSettings'));
const MqttSettings = lazy(() => import('app/settings/MqttSettings'));
const NTPSettings = lazy(() => import('app/settings/NTPSettings'));
const APSettings = lazy(() => import('app/settings/APSettings'));
const DownloadUpload = lazy(() => import('app/settings/DownloadUpload'));
const Network = lazy(() => import('app/settings/network/Network'));
const Security = lazy(() => import('app/settings/security/Security'));
const AuthenticatedRouting = memo(() => { const AuthenticatedRouting = memo(() => {
const { me } = useContext(AuthenticatedContext); const { me } = useContext(AuthenticatedContext);
return ( return (
<Layout> <Layout>
<Suspense fallback={<LoadingSpinner />}>
<Routes> <Routes>
<Route path="/dashboard/*" element={<Dashboard />} /> <Route path="/dashboard/*" element={<Dashboard />} />
<Route path="/devices/*" element={<Devices />} /> <Route path="/devices/*" element={<Devices />} />
@@ -59,10 +54,7 @@ const AuthenticatedRouting = memo(() => {
{me.admin && ( {me.admin && (
<> <>
<Route path="/settings" element={<Settings />} /> <Route path="/settings" element={<Settings />} />
<Route <Route path="/settings/application" element={<ApplicationSettings />} />
path="/settings/application"
element={<ApplicationSettings />}
/>
<Route path="/settings/mqtt" element={<MqttSettings />} /> <Route path="/settings/mqtt" element={<MqttSettings />} />
<Route path="/settings/ntp" element={<NTPSettings />} /> <Route path="/settings/ntp" element={<NTPSettings />} />
<Route path="/settings/ap" element={<APSettings />} /> <Route path="/settings/ap" element={<APSettings />} />
@@ -80,7 +72,6 @@ const AuthenticatedRouting = memo(() => {
<Route path="/*" element={<Navigate to="/" />} /> <Route path="/*" element={<Navigate to="/" />} />
</Routes> </Routes>
</Suspense>
</Layout> </Layout>
); );
}); });

View File

@@ -3,6 +3,7 @@ import { toast } from 'react-toastify';
import ForwardIcon from '@mui/icons-material/Forward'; import ForwardIcon from '@mui/icons-material/Forward';
import { Box, Button, Paper, Typography } from '@mui/material'; import { Box, Button, Paper, Typography } from '@mui/material';
import type { Theme } from '@mui/material/styles';
import * as AuthenticationApi from 'components/routing/authentication'; import * as AuthenticationApi from 'components/routing/authentication';
import { useRequest } from 'alova/client'; import { useRequest } from 'alova/client';
@@ -17,7 +18,7 @@ import { PROJECT_NAME } from 'env';
import { useI18nContext } from 'i18n/i18n-react'; import { useI18nContext } from 'i18n/i18n-react';
import type { SignInRequest } from 'types'; import type { SignInRequest } from 'types';
import { onEnterCallback, updateValue } from 'utils'; import { onEnterCallback, updateValue } from 'utils';
import { SIGN_IN_REQUEST_VALIDATOR, validate } from 'validators'; import { SIGN_IN_REQUEST_VALIDATOR, ValidationError, validate } from 'validators';
const SignIn = memo(() => { const SignIn = memo(() => {
const authenticationContext = useContext(AuthenticationContext); const authenticationContext = useContext(AuthenticationContext);
@@ -36,7 +37,7 @@ const SignIn = memo(() => {
{ {
immediate: false immediate: false
} }
).onSuccess((response) => { ).onSuccess((response: { data: { access_token: string } }) => {
if (response.data) { if (response.data) {
authenticationContext.signIn(response.data.access_token); authenticationContext.signIn(response.data.access_token);
} }
@@ -73,12 +74,11 @@ const SignIn = memo(() => {
await validate(SIGN_IN_REQUEST_VALIDATOR, signInRequest); await validate(SIGN_IN_REQUEST_VALIDATOR, signInRequest);
await signIn(); await signIn();
} catch (error) { } catch (error) {
setFieldErrors(error as ValidateFieldsError); setFieldErrors((error as ValidationError).fieldErrors);
setProcessing(false); setProcessing(false);
} }
}, [signInRequest, signIn, LL]); }, [signInRequest, signIn, LL]);
// Memoize callback to prevent recreation on every render
const submitOnEnter = useMemo(() => onEnterCallback(signIn), [signIn]); const submitOnEnter = useMemo(() => onEnterCallback(signIn), [signIn]);
// get rid of scrollbar // get rid of scrollbar
@@ -92,13 +92,15 @@ const SignIn = memo(() => {
return ( return (
<Box <Box
display="flex" sx={(theme: Theme) => ({
height="100vh" display: 'flex',
margin="auto" height: '100vh',
padding={2} margin: 'auto',
justifyContent="center" padding: 2,
flexDirection="column" justifyContent: 'center',
maxWidth={(theme) => theme.breakpoints.values.sm} flexDirection: 'column',
maxWidth: theme.breakpoints.values.sm
})}
> >
<Paper <Paper
sx={(theme) => ({ sx={(theme) => ({
@@ -111,16 +113,18 @@ const SignIn = memo(() => {
width: '100%' width: '100%'
})} })}
> >
<Typography mb={1} variant="h4"> <Typography sx={{ mb: 1 }} variant="h4">
{PROJECT_NAME} {PROJECT_NAME}
</Typography> </Typography>
<LanguageSelector /> <LanguageSelector />
<Box <Box
mt={1} sx={{
display="flex" mt: 1,
flexDirection="column" display: 'flex',
gap={1} flexDirection: 'column',
alignItems="center" gap: 1,
alignItems: 'center'
}}
> >
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors || {}} fieldErrors={fieldErrors || {}}

View File

@@ -343,9 +343,9 @@ const CustomEntities = () => {
return ( return (
<SectionContent> <SectionContent>
{blocker ? <BlockNavigation blocker={blocker} /> : null} {blocker ? <BlockNavigation blocker={blocker} /> : null}
<Box mb={2} color="warning.main"> <Typography sx={{ mb: 2 }} color="warning" variant="body1">
<Typography variant="body1">{LL.ENTITIES_HELP_1()}.</Typography> {LL.ENTITIES_HELP_1()}.
</Box> </Typography>
{renderEntity()} {renderEntity()}
@@ -361,8 +361,8 @@ const CustomEntities = () => {
/> />
)} )}
<Box mt={2} display="flex" flexWrap="wrap"> <Box sx={{ mt: 2, display: 'flex', flexWrap: 'wrap' }}>
<Box flexGrow={1}> <Box sx={{ flexGrow: 1 }}>
{numChanges > 0 && ( {numChanges > 0 && (
<ButtonRow> <ButtonRow>
<Button <Button
@@ -384,7 +384,7 @@ const CustomEntities = () => {
</ButtonRow> </ButtonRow>
)} )}
</Box> </Box>
<Box flexWrap="nowrap" whiteSpace="nowrap"> <Box sx={{ flexWrap: 'nowrap', whiteSpace: 'nowrap' }}>
<Button <Button
startIcon={<AddIcon />} startIcon={<AddIcon />}
variant="outlined" variant="outlined"

View File

@@ -7,7 +7,7 @@ import DoneIcon from '@mui/icons-material/Done';
import EditOffOutlinedIcon from '@mui/icons-material/EditOffOutlined'; import EditOffOutlinedIcon from '@mui/icons-material/EditOffOutlined';
import EditOutlinedIcon from '@mui/icons-material/EditOutlined'; import EditOutlinedIcon from '@mui/icons-material/EditOutlined';
import InsertCommentOutlinedIcon from '@mui/icons-material/InsertCommentOutlined'; import InsertCommentOutlinedIcon from '@mui/icons-material/InsertCommentOutlined';
import RemoveIcon from '@mui/icons-material/RemoveCircleOutline'; import RemoveIcon from '@mui/icons-material/RemoveCircleOutlined';
import { import {
Box, Box,
Button, Button,
@@ -28,7 +28,7 @@ import type { ValidateFieldsError } from 'async-validator';
import { BlockFormControlLabel, ValidatedTextField } from 'components'; import { BlockFormControlLabel, ValidatedTextField } from 'components';
import { useI18nContext } from 'i18n/i18n-react'; import { useI18nContext } from 'i18n/i18n-react';
import { numberValue, updateValue } from 'utils'; import { numberValue, updateValue } from 'utils';
import { validate } from 'validators'; import { ValidationError, validate } from 'validators';
import { DeviceValueType, DeviceValueTypeNames, DeviceValueUOM_s } from './types'; import { DeviceValueType, DeviceValueTypeNames, DeviceValueUOM_s } from './types';
import type { EntityItem } from './types'; import type { EntityItem } from './types';
@@ -136,7 +136,7 @@ const CustomEntitiesDialog = ({
} }
onSave(processedItem); onSave(processedItem);
} catch (error) { } catch (error) {
setFieldErrors(error as ValidateFieldsError); setFieldErrors((error as ValidationError).fieldErrors);
} }
}, [validator, editItem, onSave]); }, [validator, editItem, onSave]);
@@ -178,7 +178,7 @@ const CustomEntitiesDialog = ({
onChange={updateFormValue} onChange={updateFormValue}
/> />
</Grid> </Grid>
<Grid mt={3}> <Grid sx={{ mt: 3 }}>
<BlockFormControlLabel <BlockFormControlLabel
control={ control={
<Checkbox <Checkbox
@@ -215,7 +215,7 @@ const CustomEntitiesDialog = ({
name="value" name="value"
label={LL.DEFAULT(0) + ' ' + LL.VALUE(0)} label={LL.DEFAULT(0) + ' ' + LL.VALUE(0)}
type="string" type="string"
value={editItem.value as string} value={editItem.value}
variant="outlined" variant="outlined"
onChange={updateFormValue} onChange={updateFormValue}
fullWidth fullWidth
@@ -238,7 +238,7 @@ const CustomEntitiesDialog = ({
)} )}
{editItem.ram === 0 && ( {editItem.ram === 0 && (
<> <>
<Grid mt={3}> <Grid sx={{ mt: 3 }}>
<BlockFormControlLabel <BlockFormControlLabel
control={ control={
<Checkbox <Checkbox
@@ -260,7 +260,7 @@ const CustomEntitiesDialog = ({
margin="normal" margin="normal"
sx={{ width: '11ch' }} sx={{ width: '11ch' }}
type="string" type="string"
value={editItem.device_id as string} value={editItem.device_id}
onChange={updateFormValue} onChange={updateFormValue}
slotProps={{ slotProps={{
input: { input: {
@@ -280,7 +280,7 @@ const CustomEntitiesDialog = ({
margin="normal" margin="normal"
sx={{ width: '11ch' }} sx={{ width: '11ch' }}
type="string" type="string"
value={editItem.type_id as string} value={editItem.type_id}
onChange={updateFormValue} onChange={updateFormValue}
slotProps={{ slotProps={{
input: { input: {
@@ -381,7 +381,7 @@ const CustomEntitiesDialog = ({
fieldErrors={fieldErrors || {}} fieldErrors={fieldErrors || {}}
name="factor" name="factor"
label={LL.BITMASK()} label={LL.BITMASK()}
value={editItem.factor as string} value={editItem.factor}
sx={{ width: '11ch' }} sx={{ width: '11ch' }}
variant="outlined" variant="outlined"
onChange={updateFormValue} onChange={updateFormValue}
@@ -404,7 +404,7 @@ const CustomEntitiesDialog = ({
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
{!creating && ( {!creating && (
<Box flexGrow={1}> <Box sx={{ flexGrow: 1 }}>
<Button <Button
startIcon={<RemoveIcon />} startIcon={<RemoveIcon />}
variant="outlined" variant="outlined"

View File

@@ -470,10 +470,10 @@ const Customizations = () => {
const renderDeviceList = () => ( const renderDeviceList = () => (
<> <>
<Box mb={1} color="warning.main"> <Typography sx={{ mb: 1 }} color="warning" variant="body1">
<Typography variant="body1">{LL.CUSTOMIZATIONS_HELP_1()}.</Typography> {LL.CUSTOMIZATIONS_HELP_1()}.
</Box> </Typography>
<Box display="flex" flexWrap="wrap" alignItems="center" gap={2}> <Box sx={{ display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 2 }}>
{rename ? ( {rename ? (
<> <>
<TextField <TextField
@@ -570,27 +570,22 @@ const Customizations = () => {
const renderDeviceData = () => { const renderDeviceData = () => {
return ( return (
<> <>
<Box color="warning.main"> <Typography sx={{ mt: 1, mb: 1 }} color="warning" variant="body2">
<Typography variant="body2" mt={1} mb={1}>
<OptionIcon type="favorite" isSet={true} />={LL.CUSTOMIZATIONS_HELP_2()} <OptionIcon type="favorite" isSet={true} />={LL.CUSTOMIZATIONS_HELP_2()}
&nbsp;&nbsp; &nbsp;&nbsp;
<OptionIcon type="readonly" isSet={true} />={LL.CUSTOMIZATIONS_HELP_3()} <OptionIcon type="readonly" isSet={true} />={LL.CUSTOMIZATIONS_HELP_3()}
&nbsp;&nbsp; &nbsp;&nbsp;
<OptionIcon type="api_mqtt_exclude" isSet={true} />= <OptionIcon type="api_mqtt_exclude" isSet={true} />=
{LL.CUSTOMIZATIONS_HELP_4()}&nbsp;&nbsp; {LL.CUSTOMIZATIONS_HELP_4()}&nbsp;&nbsp;
<OptionIcon type="web_exclude" isSet={true} />= <OptionIcon type="web_exclude" isSet={true} />={LL.CUSTOMIZATIONS_HELP_5()}
{LL.CUSTOMIZATIONS_HELP_5()}&nbsp;&nbsp; &nbsp;&nbsp;
<OptionIcon type="deleted" isSet={true} />={LL.CUSTOMIZATIONS_HELP_6()} <OptionIcon type="deleted" isSet={true} />={LL.CUSTOMIZATIONS_HELP_6()}
</Typography> </Typography>
</Box>
<Grid <Grid
container container
mb={1}
mt={0}
spacing={2} spacing={2}
direction="row" direction="row"
justifyContent="flex-start" sx={{ mb: 1, mt: 0, justifyContent: 'flex-start', alignItems: 'center' }}
alignItems="center"
> >
<Grid> <Grid>
<TextField <TextField
@@ -779,8 +774,8 @@ const Customizations = () => {
</Button> </Button>
</MessageBox> </MessageBox>
) : ( ) : (
<Box display="flex" flexWrap="wrap"> <Box sx={{ display: 'flex', flexWrap: 'wrap' }}>
<Box flexGrow={1}> <Box sx={{ flexGrow: 1 }}>
{numChanges !== 0 && ( {numChanges !== 0 && (
<ButtonRow> <ButtonRow>
<Button <Button

View File

@@ -37,7 +37,7 @@ interface LabelValueProps {
const LabelValue = memo(({ label, value }: LabelValueProps) => ( const LabelValue = memo(({ label, value }: LabelValueProps) => (
<Grid container direction="row"> <Grid container direction="row">
<Typography variant="body2" color="warning.main"> <Typography variant="body2" color="warning">
{label}:&nbsp; {label}:&nbsp;
</Typography> </Typography>
<Typography variant="body2">{value}</Typography> <Typography variant="body2">{value}</Typography>
@@ -131,7 +131,7 @@ const CustomizationsDialog = ({
/> />
<LabelValue label={LL.WRITEABLE()} value={writeableIcon} /> <LabelValue label={LL.WRITEABLE()} value={writeableIcon} />
<Box mt={1} mb={2}> <Box sx={{ mt: 1, mb: 2 }}>
<EntityMaskToggle onUpdate={updateDeviceEntity} de={editItem} /> <EntityMaskToggle onUpdate={updateDeviceEntity} de={editItem} />
</Box> </Box>
@@ -172,7 +172,7 @@ const CustomizationsDialog = ({
</Grid> </Grid>
{error && ( {error && (
<Typography variant="body2" color="error" mt={2}> <Typography sx={{ mt: 2 }} variant="body2" color="error">
Error: Check min and max values Error: Check min and max values
</Typography> </Typography>
)} )}

View File

@@ -6,7 +6,7 @@ import { toast } from 'react-toastify';
import ChevronRightIcon from '@mui/icons-material/ChevronRight'; import ChevronRightIcon from '@mui/icons-material/ChevronRight';
import EditIcon from '@mui/icons-material/Edit'; import EditIcon from '@mui/icons-material/Edit';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import HelpOutlineIcon from '@mui/icons-material/HelpOutline'; import HelpOutlineIcon from '@mui/icons-material/HelpOutlined';
import UnfoldLessIcon from '@mui/icons-material/UnfoldLess'; import UnfoldLessIcon from '@mui/icons-material/UnfoldLess';
import UnfoldMoreIcon from '@mui/icons-material/UnfoldMore'; import UnfoldMoreIcon from '@mui/icons-material/UnfoldMore';
import { import {
@@ -262,22 +262,8 @@ const Dashboard = memo(() => {
return ( return (
<> <>
{!data.connected && (
<MessageBox level="error" message={LL.EMS_BUS_WARNING() + '.'}>
&nbsp;(
<Link
target="_blank"
to="https://docs.emsesp.org/Troubleshooting#ems-bus-is-not-connecting"
style={{ color: 'white' }}
>
{LL.ONLINE_HELP()}
</Link>
)
</MessageBox>
)}
{data.connected && data.nodes.length > 0 && !hasFavEntities && ( {data.connected && data.nodes.length > 0 && !hasFavEntities && (
<MessageBox mb={2} level="warning"> <MessageBox sx={{ mb: 2 }} level="warning">
<Typography> <Typography>
{LL.NO_DATA_1()}&nbsp; {LL.NO_DATA_1()}&nbsp;
<Link to="/customizations" style={{ color: 'white' }}> <Link to="/customizations" style={{ color: 'white' }}>
@@ -294,10 +280,12 @@ const Dashboard = memo(() => {
)} )}
<Box <Box
display="flex" sx={{
justifyContent="flex-end" display: 'flex',
flexWrap="nowrap" justifyContent: 'flex-end',
whiteSpace="nowrap" flexWrap: 'nowrap',
whiteSpace: 'nowrap'
}}
> >
<ToggleButtonGroup <ToggleButtonGroup
size="small" size="small"
@@ -320,7 +308,7 @@ const Dashboard = memo(() => {
</Box> </Box>
{data.nodes.length > 0 ? ( {data.nodes.length > 0 ? (
<Box mt={1} justifyContent="center" flexDirection="column"> <Box sx={{ mt: 1, justifyContent: 'center', flexDirection: 'column' }}>
<IconContext.Provider <IconContext.Provider
value={{ value={{
color: 'lightblue', color: 'lightblue',
@@ -387,13 +375,8 @@ const Dashboard = memo(() => {
</IconContext.Provider> </IconContext.Provider>
</Box> </Box>
) : ( ) : (
<Box <Box sx={{ display: 'flex' }}>
display="flex" <Typography sx={{ mt: 1 }} color="warning" variant="body1">
// justifyContent="flex-end"
// flexWrap="nowrap"
// whiteSpace="nowrap"
>
<Typography mt={1} color="warning.main" variant="body1">
no data no data
</Typography> </Typography>
<Tooltip title={LL.DASHBOARD_1()}> <Tooltip title={LL.DASHBOARD_1()}>

View File

@@ -1,4 +1,5 @@
import { memo } from 'react'; import { memo } from 'react';
import type { IconType } from 'react-icons';
import { AiOutlineAlert, AiOutlineControl, AiOutlineGateway } from 'react-icons/ai'; import { AiOutlineAlert, AiOutlineControl, AiOutlineGateway } from 'react-icons/ai';
import { CgSmartHomeBoiler } from 'react-icons/cg'; import { CgSmartHomeBoiler } from 'react-icons/cg';
import { FaSolarPanel } from 'react-icons/fa'; import { FaSolarPanel } from 'react-icons/fa';
@@ -15,14 +16,9 @@ import { PiFan, PiGauge } from 'react-icons/pi';
import { TiFlowSwitch, TiThermometer } from 'react-icons/ti'; import { TiFlowSwitch, TiThermometer } from 'react-icons/ti';
import { VscVmConnect } from 'react-icons/vsc'; import { VscVmConnect } from 'react-icons/vsc';
import type { SvgIconProps } from '@mui/material';
import { DeviceType } from './types'; import { DeviceType } from './types';
const deviceIconLookup: Record< const deviceIconLookup: Record<DeviceType, IconType | null> = {
DeviceType,
React.ComponentType<SvgIconProps> | null
> = {
[DeviceType.TEMPERATURESENSOR]: TiThermometer, [DeviceType.TEMPERATURESENSOR]: TiThermometer,
[DeviceType.ANALOGSENSOR]: PiGauge, [DeviceType.ANALOGSENSOR]: PiGauge,
[DeviceType.BOILER]: CgSmartHomeBoiler, [DeviceType.BOILER]: CgSmartHomeBoiler,

View File

@@ -546,7 +546,7 @@ const Devices = memo(() => {
) )
</MessageBox> </MessageBox>
) : ( ) : (
<Box justifyContent="center" flexDirection="column"> <Box sx={{ justifyContent: 'center', flexDirection: 'column' }}>
<IconContext.Provider <IconContext.Provider
value={{ value={{
color: 'lightblue', color: 'lightblue',
@@ -670,12 +670,12 @@ const Devices = memo(() => {
}} }}
> >
<Box sx={{ p: 1 }}> <Box sx={{ p: 1 }}>
<Grid container justifyContent="space-between"> <Grid container sx={{ justifyContent: 'space-between' }}>
<Typography noWrap variant="subtitle1" color="warning.main"> <Typography noWrap variant="subtitle1" color="warning">
{deviceInfo.n}&nbsp;( {deviceInfo.n}&nbsp;(
{deviceInfo.tn}) {deviceInfo.tn})
</Typography> </Typography>
<Grid justifyContent="flex-end"> <Grid sx={{ justifyContent: 'flex-end' }}>
<ButtonTooltip title={LL.CLOSE()}> <ButtonTooltip title={LL.CLOSE()}>
<IconButton onClick={resetDeviceSelect} aria-label={LL.CLOSE()}> <IconButton onClick={resetDeviceSelect} aria-label={LL.CLOSE()}>
<HighlightOffIcon color="primary" sx={{ fontSize: 18 }} /> <HighlightOffIcon color="primary" sx={{ fontSize: 18 }} />

View File

@@ -24,7 +24,7 @@ import type { ValidateFieldsError } from 'async-validator';
import { ValidatedTextField } from 'components'; import { ValidatedTextField } from 'components';
import { useI18nContext } from 'i18n/i18n-react'; import { useI18nContext } from 'i18n/i18n-react';
import { numberValue, updateValue } from 'utils'; import { numberValue, updateValue } from 'utils';
import { validate } from 'validators'; import { ValidationError, validate } from 'validators';
import { DeviceValueUOM, DeviceValueUOM_s } from './types'; import { DeviceValueUOM, DeviceValueUOM_s } from './types';
import type { DeviceValue } from './types'; import type { DeviceValue } from './types';
@@ -67,7 +67,7 @@ const DevicesDialog = ({
await validate(validator, editItem); await validate(validator, editItem);
onSave(editItem); onSave(editItem);
} catch (error) { } catch (error) {
setFieldErrors(error as ValidateFieldsError); setFieldErrors((error as ValidationError).fieldErrors);
} }
}, [validator, editItem, onSave]); }, [validator, editItem, onSave]);
@@ -128,9 +128,9 @@ const DevicesDialog = ({
<Dialog sx={dialogStyle} open={open} onClose={onClose}> <Dialog sx={dialogStyle} open={open} onClose={onClose}>
<DialogTitle>{dialogTitle}</DialogTitle> <DialogTitle>{dialogTitle}</DialogTitle>
<DialogContent dividers> <DialogContent dividers>
<Box color="warning.main" mb={2}> <Typography sx={{ mb: 2 }} color="warning" variant="body2">
<Typography variant="body2">{editItem.id.slice(2)}</Typography> {editItem.id.slice(2)}
</Box> </Typography>
<Grid container> <Grid container>
<Grid size={12}> <Grid size={12}>
{editItem.l ? ( {editItem.l ? (

View File

@@ -11,6 +11,7 @@ import {
Box, Box,
Button, Button,
Divider, Divider,
Grid,
Link, Link,
List, List,
ListItem, ListItem,
@@ -42,7 +43,7 @@ interface CustomSupport {
html: string | null; html: string | null;
} }
const DEFAULT_IMAGE_URL = 'https://emsesp.org/_media/images/installer.jpeg'; const DEFAULT_IMAGE_URL = 'https://emsesp.org/media/images/installer.jpeg';
const SUPPORT_BOX_STYLES: SxProps<Theme> = { const SUPPORT_BOX_STYLES: SxProps<Theme> = {
borderRadius: 3, borderRadius: 3,
@@ -71,7 +72,6 @@ const HelpComponent = () => {
}); });
const [imgError, setImgError] = useState<boolean>(false); const [imgError, setImgError] = useState<boolean>(false);
// Memoize the request method to prevent re-creation on every render
const getCustomSupportMethod = useMemo( const getCustomSupportMethod = useMemo(
() => callAction({ action: 'getCustomSupport' }), () => callAction({ action: 'getCustomSupport' }),
[] []
@@ -146,11 +146,9 @@ const HelpComponent = () => {
<SectionContent> <SectionContent>
{customSupport.html && ( {customSupport.html && (
<Stack <Stack
padding={1}
mb={2}
direction="row" direction="row"
divider={<Divider orientation="vertical" flexItem />} divider={<Divider orientation="vertical" flexItem />}
sx={SUPPORT_BOX_STYLES} sx={{ padding: 1, mb: 2, ...SUPPORT_BOX_STYLES }}
> >
<Typography variant="subtitle1"> <Typography variant="subtitle1">
<div dangerouslySetInnerHTML={{ __html: customSupport.html }} /> <div dangerouslySetInnerHTML={{ __html: customSupport.html }} />
@@ -185,9 +183,9 @@ const HelpComponent = () => {
</List> </List>
)} )}
<Box p={2} color="warning.main"> <Grid container spacing={2} sx={{ mt: 2, alignItems: 'center' }}>
<Typography mb={1} variant="body1"> <Typography sx={{ mb: 1 }} color="warning" variant="body1">
{LL.HELP_INFORMATION_4()}. {LL.HELP_INFORMATION_4()}:
</Typography> </Typography>
<Button <Button
startIcon={<DownloadIcon />} startIcon={<DownloadIcon />}
@@ -197,11 +195,11 @@ const HelpComponent = () => {
> >
{LL.SUPPORT_INFORMATION(0)} {LL.SUPPORT_INFORMATION(0)}
</Button> </Button>
</Box> </Grid>
<Divider sx={{ mt: 4 }} /> <Divider sx={{ mt: 4 }} />
<Typography color="white" variant="subtitle1" align="center" mt={1}> <Typography color="white" variant="subtitle1" align="center" sx={{ mt: 1 }}>
&copy;&nbsp; &copy;&nbsp;
<Link <Link
target="_blank" target="_blank"

View File

@@ -186,9 +186,9 @@ const Modules = () => {
return ( return (
<> <>
<Box mb={2} color="warning.main"> <Typography sx={{ mb: 2 }} color="warning" variant="body1">
<Typography variant="body1">{LL.MODULES_DESCRIPTION()}.</Typography> {LL.MODULES_DESCRIPTION()}.
</Box> </Typography>
<Table <Table
data={{ nodes: modules }} data={{ nodes: modules }}
theme={modules_theme} theme={modules_theme}
@@ -236,8 +236,8 @@ const Modules = () => {
)} )}
</Table> </Table>
<Box mt={1} display="flex" flexWrap="wrap"> <Box sx={{ mt: 1, display: 'flex', flexWrap: 'wrap' }}>
<Box flexGrow={1}> <Box sx={{ flexGrow: 1 }}>
{numChanges !== 0 && ( {numChanges !== 0 && (
<ButtonRow> <ButtonRow>
<Button <Button

View File

@@ -79,7 +79,7 @@ const ModulesDialog = ({
label="Enabled" label="Enabled"
/> />
</Grid> </Grid>
<Box mt={2} mb={1}> <Box sx={{ mt: 2, mb: 1 }}>
<TextField <TextField
name="license" name="license"
label="License Key" label="License Key"

View File

@@ -2,12 +2,12 @@ import { memo } from 'react';
import CommentsDisabledOutlinedIcon from '@mui/icons-material/CommentsDisabledOutlined'; import CommentsDisabledOutlinedIcon from '@mui/icons-material/CommentsDisabledOutlined';
import DeleteForeverIcon from '@mui/icons-material/DeleteForever'; import DeleteForeverIcon from '@mui/icons-material/DeleteForever';
import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline'; import DeleteOutlineIcon from '@mui/icons-material/DeleteOutlined';
import EditOffOutlinedIcon from '@mui/icons-material/EditOffOutlined'; import EditOffOutlinedIcon from '@mui/icons-material/EditOffOutlined';
import EditOutlinedIcon from '@mui/icons-material/EditOutlined'; import EditOutlinedIcon from '@mui/icons-material/EditOutlined';
import InsertCommentOutlinedIcon from '@mui/icons-material/InsertCommentOutlined'; import InsertCommentOutlinedIcon from '@mui/icons-material/InsertCommentOutlined';
import StarIcon from '@mui/icons-material/Star'; import StarIcon from '@mui/icons-material/Star';
import StarOutlineIcon from '@mui/icons-material/StarOutline'; import StarOutlineIcon from '@mui/icons-material/StarOutlined';
import VisibilityOffOutlinedIcon from '@mui/icons-material/VisibilityOffOutlined'; import VisibilityOffOutlinedIcon from '@mui/icons-material/VisibilityOffOutlined';
import VisibilityOutlinedIcon from '@mui/icons-material/VisibilityOutlined'; import VisibilityOutlinedIcon from '@mui/icons-material/VisibilityOutlined';
import type { SvgIconProps } from '@mui/material'; import type { SvgIconProps } from '@mui/material';

View File

@@ -358,9 +358,9 @@ const Scheduler = () => {
return ( return (
<SectionContent> <SectionContent>
{blocker ? <BlockNavigation blocker={blocker} /> : null} {blocker ? <BlockNavigation blocker={blocker} /> : null}
<Box mb={2} color="warning.main"> <Typography sx={{ mb: 2 }} color="warning" variant="body1">
<Typography variant="body1">{LL.SCHEDULER_HELP_1()}.</Typography> {LL.SCHEDULER_HELP_1()}.
</Box> </Typography>
{renderSchedule()} {renderSchedule()}
{selectedScheduleItem && ( {selectedScheduleItem && (
@@ -375,8 +375,8 @@ const Scheduler = () => {
/> />
)} )}
<Box display="flex" flexWrap="wrap"> <Box sx={{ display: 'flex', flexWrap: 'wrap' }}>
<Box flexGrow={1}> <Box sx={{ flexGrow: 1 }}>
{numChanges !== 0 && ( {numChanges !== 0 && (
<ButtonRow> <ButtonRow>
<Button <Button
@@ -398,7 +398,7 @@ const Scheduler = () => {
</ButtonRow> </ButtonRow>
)} )}
</Box> </Box>
<Box flexWrap="nowrap" whiteSpace="nowrap"> <Box sx={{ flexWrap: 'nowrap', whiteSpace: 'nowrap' }}>
<ButtonRow> <ButtonRow>
<Button <Button
startIcon={<AddIcon />} startIcon={<AddIcon />}

View File

@@ -4,7 +4,7 @@ import AddIcon from '@mui/icons-material/Add';
import CancelIcon from '@mui/icons-material/Cancel'; import CancelIcon from '@mui/icons-material/Cancel';
import DoneIcon from '@mui/icons-material/Done'; import DoneIcon from '@mui/icons-material/Done';
import PlayArrowIcon from '@mui/icons-material/PlayArrow'; import PlayArrowIcon from '@mui/icons-material/PlayArrow';
import RemoveIcon from '@mui/icons-material/RemoveCircleOutline'; import RemoveIcon from '@mui/icons-material/RemoveCircleOutlined';
import { import {
Box, Box,
Button, Button,
@@ -26,7 +26,7 @@ import type { ValidateFieldsError } from 'async-validator';
import { BlockFormControlLabel, ValidatedTextField } from 'components'; import { BlockFormControlLabel, ValidatedTextField } from 'components';
import { useI18nContext } from 'i18n/i18n-react'; import { useI18nContext } from 'i18n/i18n-react';
import { updateValue } from 'utils'; import { updateValue } from 'utils';
import { validate } from 'validators'; import { ValidationError, validate } from 'validators';
import { ScheduleFlag } from './types'; import { ScheduleFlag } from './types';
import type { ScheduleItem } from './types'; import type { ScheduleItem } from './types';
@@ -120,7 +120,7 @@ const SchedulerDialog = ({
await validate(validator, itemToSave); await validate(validator, itemToSave);
onSave(itemToSave); onSave(itemToSave);
} catch (error) { } catch (error) {
setFieldErrors(error as ValidateFieldsError); setFieldErrors((error as ValidationError).fieldErrors);
} }
}, },
[validator, onSave] [validator, onSave]
@@ -338,11 +338,13 @@ const SchedulerDialog = ({
onChange={updateFormValue} onChange={updateFormValue}
/> />
{isTimerSchedule && ( {isTimerSchedule && (
<Box color="warning.main" ml={2} mt={4}> <Typography
<Typography variant="body2"> sx={{ ml: 2, mt: 4 }}
color="warning"
variant="body2"
>
{LL.SCHEDULER_HELP_2()} {LL.SCHEDULER_HELP_2()}
</Typography> </Typography>
</Box>
)} )}
</> </>
) : ( ) : (
@@ -391,7 +393,7 @@ const SchedulerDialog = ({
<DialogActions> <DialogActions>
{!creating && ( {!creating && (
<Box flexGrow={1}> <Box sx={{ flexGrow: 1 }}>
<Button <Button
startIcon={<RemoveIcon />} startIcon={<RemoveIcon />}
variant="outlined" variant="outlined"

View File

@@ -348,7 +348,7 @@ const Sensors = () => {
const addAnalogSensor = useCallback(() => { const addAnalogSensor = useCallback(() => {
if (firstAvailableGPIO.current === undefined) { if (firstAvailableGPIO.current === undefined) {
toast.error('No available GPIO found'); toast.error(LL.NO_GPIO());
return; return;
} }
setCreating(true); setCreating(true);
@@ -591,7 +591,14 @@ const Sensors = () => {
/> />
)} )}
{sensorData?.analog_enabled === true && me.admin && ( {sensorData?.analog_enabled === true && me.admin && (
<Box mt={2} display="flex" flexWrap="wrap" justifyContent="flex-end"> <Box
sx={{
mt: 2,
display: 'flex',
flexWrap: 'wrap',
justifyContent: 'flex-end'
}}
>
<Button <Button
variant="outlined" variant="outlined"
color="primary" color="primary"

View File

@@ -2,7 +2,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react';
import CancelIcon from '@mui/icons-material/Cancel'; import CancelIcon from '@mui/icons-material/Cancel';
import DoneIcon from '@mui/icons-material/Done'; import DoneIcon from '@mui/icons-material/Done';
import RemoveIcon from '@mui/icons-material/RemoveCircleOutline'; import RemoveIcon from '@mui/icons-material/RemoveCircleOutlined';
import WarningIcon from '@mui/icons-material/Warning'; import WarningIcon from '@mui/icons-material/Warning';
import { import {
Box, Box,
@@ -23,7 +23,7 @@ import type { ValidateFieldsError } from 'async-validator';
import { ValidatedTextField } from 'components'; import { ValidatedTextField } from 'components';
import { useI18nContext } from 'i18n/i18n-react'; import { useI18nContext } from 'i18n/i18n-react';
import { numberValue, updateValue } from 'utils'; import { numberValue, updateValue } from 'utils';
import { validate } from 'validators'; import { ValidationError, validate } from 'validators';
import { AnalogType, AnalogTypeNames, DeviceValueUOM_s } from './types'; import { AnalogType, AnalogTypeNames, DeviceValueUOM_s } from './types';
import type { AnalogSensor } from './types'; import type { AnalogSensor } from './types';
@@ -172,7 +172,7 @@ const SensorsAnalogDialog = ({
await validate(validator, editItem); await validate(validator, editItem);
onSave(editItem); onSave(editItem);
} catch (error) { } catch (error) {
setFieldErrors(error as ValidateFieldsError); setFieldErrors((error as ValidationError).fieldErrors);
} }
}, [validator, editItem, onSave]); }, [validator, editItem, onSave]);
@@ -479,7 +479,7 @@ const SensorsAnalogDialog = ({
)} )}
</Grid> </Grid>
{fieldErrors && Object.keys(fieldErrors).length > 0 && ( {fieldErrors && Object.keys(fieldErrors).length > 0 && (
<Box mt={1}> <Box sx={{ mt: 1 }}>
{Object.values(fieldErrors).map((errArr, idx) => {Object.values(fieldErrors).map((errArr, idx) =>
Array.isArray(errArr) Array.isArray(errArr)
? errArr.map((err, j) => ( ? errArr.map((err, j) => (
@@ -487,7 +487,7 @@ const SensorsAnalogDialog = ({
key={`${idx}-${j}`} key={`${idx}-${j}`}
color="error" color="error"
variant="caption" variant="caption"
display="block" sx={{ display: 'block' }}
> >
{err.message} {err.message}
</Typography> </Typography>
@@ -498,7 +498,7 @@ const SensorsAnalogDialog = ({
)} )}
{editItem.s && ( {editItem.s && (
<Grid> <Grid>
<Typography mt={1} color="warning.main" variant="body2"> <Typography sx={{ mt: 1 }} color="warning" variant="body2">
<WarningIcon <WarningIcon
fontSize="small" fontSize="small"
sx={{ mr: 1, verticalAlign: 'middle' }} sx={{ mr: 1, verticalAlign: 'middle' }}
@@ -511,7 +511,7 @@ const SensorsAnalogDialog = ({
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
{!creating && ( {!creating && (
<Box flexGrow={1} sx={{ '& button': { mt: 0 } }}> <Box sx={{ flexGrow: 1, '& button': { mt: 0 } }}>
<Button <Button
startIcon={<RemoveIcon />} startIcon={<RemoveIcon />}
disabled={editItem.s} disabled={editItem.s}

View File

@@ -4,7 +4,6 @@ import CancelIcon from '@mui/icons-material/Cancel';
import DoneIcon from '@mui/icons-material/Done'; import DoneIcon from '@mui/icons-material/Done';
import WarningIcon from '@mui/icons-material/Warning'; import WarningIcon from '@mui/icons-material/Warning';
import { import {
Box,
Button, Button,
Dialog, Dialog,
DialogActions, DialogActions,
@@ -22,7 +21,7 @@ import type { ValidateFieldsError } from 'async-validator';
import { ValidatedTextField } from 'components'; import { ValidatedTextField } from 'components';
import { useI18nContext } from 'i18n/i18n-react'; import { useI18nContext } from 'i18n/i18n-react';
import { numberValue, updateValue } from 'utils'; import { numberValue, updateValue } from 'utils';
import { validate } from 'validators'; import { ValidationError, validate } from 'validators';
import type { TemperatureSensor } from './types'; import type { TemperatureSensor } from './types';
@@ -85,7 +84,7 @@ const SensorsTemperatureDialog = ({
await validate(validator, editItem); await validate(validator, editItem);
onSave(editItem); onSave(editItem);
} catch (error) { } catch (error) {
setFieldErrors(error as ValidateFieldsError); setFieldErrors((error as ValidationError).fieldErrors);
} }
}, [validator, editItem, onSave]); }, [validator, editItem, onSave]);
@@ -111,11 +110,9 @@ const SensorsTemperatureDialog = ({
<Dialog sx={dialogStyle} open={open} onClose={handleClose}> <Dialog sx={dialogStyle} open={open} onClose={handleClose}>
<DialogTitle>{dialogTitle}</DialogTitle> <DialogTitle>{dialogTitle}</DialogTitle>
<DialogContent dividers> <DialogContent dividers>
<Box color="warning.main" mb={2}> <Typography sx={{ mb: 2 }} color="warning" variant="body2">
<Typography variant="body2">
{LL.ID_OF(LL.SENSOR(0))}: {editItem.id} {LL.ID_OF(LL.SENSOR(0))}: {editItem.id}
</Typography> </Typography>
</Box>
<Grid container spacing={2}> <Grid container spacing={2}>
<Grid> <Grid>
<ValidatedTextField <ValidatedTextField
@@ -142,7 +139,7 @@ const SensorsTemperatureDialog = ({
</Grid> </Grid>
{editItem.s && ( {editItem.s && (
<Grid> <Grid>
<Typography mt={1} color="warning.main" variant="body2"> <Typography sx={{ mt: 1 }} color="warning" variant="body2">
<WarningIcon <WarningIcon
fontSize="small" fontSize="small"
sx={{ mr: 1, verticalAlign: 'middle' }} sx={{ mr: 1, verticalAlign: 'middle' }}

View File

@@ -41,8 +41,12 @@ const UserProfileComponent = () => {
/> />
</ListItem> </ListItem>
</List> </List>
<Box mt={2} mb={2} display="flex" alignItems="center"> <Box sx={{ mt: 2, mb: 2, display: 'flex', alignItems: 'center' }}>
<Typography mr={2} variant="body1" align="center"> <Typography
sx={{ mr: 2, textAlign: 'center' }}
color="warning"
variant="body1"
>
{LL.LANGUAGE()}: {LL.LANGUAGE()}:
</Typography> </Typography>
<LanguageSelector /> <LanguageSelector />

View File

@@ -43,16 +43,6 @@ export interface Settings {
modbus_port: number; modbus_port: number;
modbus_max_clients: number; modbus_max_clients: number;
modbus_timeout: number; modbus_timeout: number;
email_enabled: boolean;
email_ssl?: boolean;
email_starttls?: boolean;
email_server: string;
email_port: number;
email_login: string;
email_pass: string;
email_sender: string;
email_recp: string;
email_subject: string;
developer_mode: boolean; developer_mode: boolean;
} }

View File

@@ -21,7 +21,7 @@ import { useI18nContext } from 'i18n/i18n-react';
import type { APSettingsType } from 'types'; import type { APSettingsType } from 'types';
import { APProvisionMode } from 'types'; import { APProvisionMode } from 'types';
import { numberValue, updateValueDirty, useRest } from 'utils'; import { numberValue, updateValueDirty, useRest } from 'utils';
import { createAPSettingsValidator, validate } from 'validators'; import { ValidationError, createAPSettingsValidator, validate } from 'validators';
export const isAPEnabled = ({ provision_mode }: APSettingsType) => export const isAPEnabled = ({ provision_mode }: APSettingsType) =>
provision_mode === APProvisionMode.AP_MODE_ALWAYS || provision_mode === APProvisionMode.AP_MODE_ALWAYS ||
@@ -86,7 +86,7 @@ const APSettings = () => {
await validate(createAPSettingsValidator(data), data); await validate(createAPSettingsValidator(data), data);
await saveData(); await saveData();
} catch (error) { } catch (error) {
setFieldErrors(error as ValidateFieldsError); setFieldErrors((error as ValidationError).fieldErrors);
} }
}, [data, saveData]); }, [data, saveData]);

View File

@@ -28,13 +28,12 @@ import {
FormLoader, FormLoader,
MessageBox, MessageBox,
SectionContent, SectionContent,
ValidatedPasswordField,
ValidatedTextField, ValidatedTextField,
useLayoutTitle useLayoutTitle
} from 'components'; } from 'components';
import { useI18nContext } from 'i18n/i18n-react'; import { useI18nContext } from 'i18n/i18n-react';
import { numberValue, updateValueDirty, useRest } from 'utils'; import { numberValue, updateValueDirty, useRest } from 'utils';
import { validate } from 'validators'; import { ValidationError, validate } from 'validators';
import { API, getBoardProfile, readSettings, writeSettings } from '../../api/app'; import { API, getBoardProfile, readSettings, writeSettings } from '../../api/app';
import { BOARD_PROFILES } from '../main/types'; import { BOARD_PROFILES } from '../main/types';
@@ -154,7 +153,7 @@ const ApplicationSettings = () => {
setFieldErrors(undefined); setFieldErrors(undefined);
await validate(createSettingsValidator(data), data); await validate(createSettingsValidator(data), data);
} catch (error) { } catch (error) {
setFieldErrors(error as ValidateFieldsError); setFieldErrors((error as ValidationError).fieldErrors);
} finally { } finally {
await saveData(); await saveData();
} }
@@ -352,156 +351,6 @@ const ApplicationSettings = () => {
</Grid> </Grid>
</Grid> </Grid>
)} )}
<Typography color="secondary">eMail</Typography>
<BlockFormControlLabel
control={
<Checkbox
checked={data.email_enabled}
onChange={updateFormValue}
name="email_enabled"
disabled={!hardwareData.psram}
/>
}
label={
<Typography color={!hardwareData.psram ? 'grey' : 'default'}>
Enable eMail notification
{!hardwareData.psram && (
<Typography variant="caption">
&nbsp; &#40;{LL.IS_REQUIRED('PSRAM')}&#41;
</Typography>
)}
</Typography>
}
/>
{data.email_enabled && (
<>
<Grid
container
spacing={2}
direction="row"
justifyContent="flex-start"
alignItems="flex-start"
>
<Grid>
<ValidatedTextField
fieldErrors={fieldErrors || {}}
name="email_server"
label="SMTP Server"
variant="outlined"
value={data.email_server}
onChange={updateFormValue}
margin="normal"
/>
</Grid>
<Grid>
<ValidatedTextField
fieldErrors={fieldErrors || {}}
sx={{ width: '12ch' }}
name="email_port"
variant="outlined"
label="Port"
value={numberValue(data.email_port)}
type="number"
onChange={updateFormValue}
margin="normal"
/>
</Grid>
<Grid size={4} mt={!data.email_ssl && !data.email_starttls ? 0 : 3}>
{!data.email_starttls && (
<BlockFormControlLabel
sx={{ width: '12ch' }}
control={
<Checkbox
checked={data.email_ssl}
onChange={updateFormValue}
name="email_ssl"
disabled={
data.email_starttls || data.email_ssl === undefined
}
/>
}
label="SSL/TLS"
/>
)}
{!data.email_ssl && (
<BlockFormControlLabel
sx={{ width: '12ch' }}
control={
<Checkbox
checked={data.email_starttls}
onChange={updateFormValue}
name="email_starttls"
disabled={
data.email_ssl || data.email_starttls === undefined
}
/>
}
label="STARTTLS"
/>
)}
</Grid>
</Grid>
<Grid container spacing={2} rowSpacing={0}>
<Grid>
<ValidatedTextField
fieldErrors={fieldErrors || {}}
name="email_login"
label="Login"
variant="outlined"
value={data.email_login}
onChange={updateFormValue}
margin="normal"
/>
</Grid>
<Grid>
<ValidatedPasswordField
fieldErrors={fieldErrors || {}}
name="email_pass"
label="Password"
variant="outlined"
value={data.email_pass}
onChange={updateFormValue}
margin="normal"
/>
</Grid>
</Grid>
<Grid container spacing={2} rowSpacing={0}>
<Grid>
<ValidatedTextField
fieldErrors={fieldErrors || {}}
name="email_sender"
label="From"
variant="outlined"
value={data.email_sender}
onChange={updateFormValue}
margin="normal"
/>
</Grid>
<Grid>
<ValidatedTextField
fieldErrors={fieldErrors || {}}
name="email_recp"
label="To"
variant="outlined"
value={data.email_recp}
onChange={updateFormValue}
margin="normal"
/>
</Grid>
<Grid>
<ValidatedTextField
fieldErrors={fieldErrors || {}}
name="email_subject"
label="Subject"
variant="outlined"
value={data.email_subject}
onChange={updateFormValue}
margin="normal"
/>
</Grid>
</Grid>
</>
)}
<Typography sx={{ pb: 1, pt: 2 }} variant="h6" color="primary"> <Typography sx={{ pb: 1, pt: 2 }} variant="h6" color="primary">
{LL.SENSORS()} {LL.SENSORS()}
</Typography> </Typography>
@@ -828,6 +677,7 @@ const ApplicationSettings = () => {
<MenuItem value={2}>EMS+</MenuItem> <MenuItem value={2}>EMS+</MenuItem>
<MenuItem value={3}>HT3</MenuItem> <MenuItem value={3}>HT3</MenuItem>
<MenuItem value={4}>{LL.HARDWARE()}</MenuItem> <MenuItem value={4}>{LL.HARDWARE()}</MenuItem>
<MenuItem value={5}>Auto</MenuItem>
</TextField> </TextField>
</Grid> </Grid>
<Grid> <Grid>
@@ -922,7 +772,7 @@ const ApplicationSettings = () => {
label={LL.REMOTE_TIMEOUT_EN()} label={LL.REMOTE_TIMEOUT_EN()}
/> />
{data.remote_timeout_en && ( {data.remote_timeout_en && (
<Box mt={2}> <Box sx={{ mt: 2 }}>
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors || {}} fieldErrors={fieldErrors || {}}
name="remote_timeout" name="remote_timeout"

View File

@@ -1,12 +1,23 @@
import { useCallback, useMemo, useState } from 'react'; import { useCallback, useMemo, useState } from 'react';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import CancelIcon from '@mui/icons-material/Cancel';
import DownloadIcon from '@mui/icons-material/GetApp'; import DownloadIcon from '@mui/icons-material/GetApp';
import { Box, Button, Grid, Typography } from '@mui/material'; import WarningIcon from '@mui/icons-material/Warning';
import {
Button,
Dialog,
DialogActions,
DialogContent,
DialogTitle,
Grid,
Typography
} from '@mui/material';
import * as SystemApi from 'api/system'; import * as SystemApi from 'api/system';
import { API, callAction } from 'api/app'; import { API, callAction } from 'api/app';
import { dialogStyle } from '@/CustomTheme';
import { useRequest } from 'alova/client'; import { useRequest } from 'alova/client';
import type { APIcall } from 'app/main/types'; import type { APIcall } from 'app/main/types';
import SystemMonitor from 'app/status/SystemMonitor'; import SystemMonitor from 'app/status/SystemMonitor';
@@ -19,15 +30,11 @@ import {
import { useI18nContext } from 'i18n/i18n-react'; import { useI18nContext } from 'i18n/i18n-react';
import { saveFile } from 'utils'; import { saveFile } from 'utils';
interface DownloadButton {
type: string;
label: string | number;
isGridButton: boolean;
}
const DownloadUpload = () => { const DownloadUpload = () => {
const { LL } = useI18nContext(); const { LL } = useI18nContext();
const [confirmBackup, setConfirmBackup] = useState<boolean>(false);
const [restarting, setRestarting] = useState<boolean>(false); const [restarting, setRestarting] = useState<boolean>(false);
const { send: sendExportData } = useRequest( const { send: sendExportData } = useRequest(
@@ -62,45 +69,50 @@ const DownloadUpload = () => {
useLayoutTitle(LL.DOWNLOAD_UPLOAD()); useLayoutTitle(LL.DOWNLOAD_UPLOAD());
const downloadButtons: DownloadButton[] = useMemo( const handleCloseBackupDialog = useCallback(() => {
() => [ setConfirmBackup(false);
{ }, []);
type: 'settings',
label: LL.SETTINGS_OF(LL.APPLICATION()), const renderBackupDialog = useMemo(
isGridButton: true () => (
}, <Dialog
{ sx={dialogStyle}
type: 'customizations', open={confirmBackup}
label: LL.CUSTOMIZATIONS(), onClose={handleCloseBackupDialog}
isGridButton: true >
}, <DialogTitle>{LL.DOWNLOAD_SYSTEM_BACKUP()}</DialogTitle>
{ <DialogContent dividers>
type: 'entities', <WarningIcon color="warning" sx={{ fontSize: 18 }} />
label: LL.CUSTOM_ENTITIES(0), &nbsp;
isGridButton: true {LL.WARNING_SYSTEM_BACKUP()}
}, </DialogContent>
{ <DialogActions>
type: 'schedule', <Button
label: LL.SCHEDULE(0), startIcon={<CancelIcon />}
isGridButton: true variant="outlined"
}, onClick={handleCloseBackupDialog}
{ color="secondary"
type: 'systembackup', >
label: LL.DOWNLOAD_SYSTEM_BACKUP(), {LL.CANCEL()}
isGridButton: true </Button>
}, <Button
{ startIcon={<DownloadIcon />}
type: 'allvalues', variant="outlined"
label: LL.ALLVALUES(), onClick={() => handleDownload('systembackup')()}
isGridButton: false color="primary"
} >
], {LL.DOWNLOAD(0)}
[LL] </Button>
</DialogActions>
</Dialog>
),
[confirmBackup, handleCloseBackupDialog, LL]
); );
const handleDownload = useCallback( const handleDownload = useCallback(
(type: string) => () => { (type: string) => () => {
void sendExportData(type); void sendExportData(type);
setConfirmBackup(false);
}, },
[sendExportData] [sendExportData]
); );
@@ -117,58 +129,57 @@ const DownloadUpload = () => {
); );
} }
const gridButtons = downloadButtons.filter((btn) => btn.isGridButton);
const standaloneButton = downloadButtons.find((btn) => !btn.isGridButton);
return ( return (
<SectionContent> <SectionContent>
{renderBackupDialog}
<Typography sx={{ pb: 2 }} variant="h6" color="primary"> <Typography sx={{ pb: 2 }} variant="h6" color="primary">
{LL.DOWNLOAD(0)} {LL.DOWNLOAD(0)}
</Typography> </Typography>
<Typography mb={1} variant="body1" color="warning"> <Grid
{LL.DOWNLOAD_SETTINGS_TEXT()}. container
spacing={2}
sx={{
alignItems: 'center'
}}
>
<Typography variant="body1" color="warning">
{LL.DOWNLOAD_SETTINGS_TEXT()}:
</Typography> </Typography>
<Grid container spacing={2}>
{gridButtons.map((button) => (
<Grid key={button.type}>
<Button <Button
startIcon={<DownloadIcon />} startIcon={<DownloadIcon />}
variant="outlined" variant="outlined"
color="primary" color="primary"
onClick={handleDownload(button.type)} onClick={() => setConfirmBackup(true)}
> >
{button.label} {LL.DOWNLOAD_SYSTEM_BACKUP()}
</Button> </Button>
</Grid> </Grid>
))}
</Grid>
<Typography mt={2} mb={1} variant="body1" color="warning"> <Grid container spacing={2} sx={{ mt: 2, alignItems: 'center' }}>
{LL.DOWNLOAD_SETTINGS_TEXT2()}. <Typography variant="body1" color="warning">
{LL.DOWNLOAD_SETTINGS_TEXT2()}:
</Typography> </Typography>
{standaloneButton && (
<Button <Button
startIcon={<DownloadIcon />} startIcon={<DownloadIcon />}
variant="outlined" variant="outlined"
color="primary" color="primary"
onClick={handleDownload(standaloneButton.type)} onClick={handleDownload('allvalues')}
> >
{standaloneButton.label} {LL.ALLVALUES()}
</Button> </Button>
)} </Grid>
<Typography sx={{ pt: 2, pb: 2 }} variant="h6" color="primary"> <Typography sx={{ pt: 2, pb: 2 }} variant="h6" color="primary">
{LL.UPLOAD()} {LL.UPLOAD()}
</Typography> </Typography>
<Box color="warning.main" sx={{ pb: 2 }}> <Typography sx={{ pb: 2 }} color="warning" variant="body1">
<Typography variant="body1">{LL.UPLOAD_TEXT()}.</Typography> {LL.UPLOAD_TEXT()}:
</Box> </Typography>
<SingleUpload text={LL.UPLOAD_DRAG()} doRestart={doRestart} /> <SingleUpload doRestart={doRestart} />
</SectionContent> </SectionContent>
); );
}; };

View File

@@ -31,7 +31,7 @@ import {
import { useI18nContext } from 'i18n/i18n-react'; import { useI18nContext } from 'i18n/i18n-react';
import type { MqttSettingsType } from 'types'; import type { MqttSettingsType } from 'types';
import { numberValue, updateValueDirty, useRest } from 'utils'; import { numberValue, updateValueDirty, useRest } from 'utils';
import { createMqttSettingsValidator, validate } from 'validators'; import { ValidationError, createMqttSettingsValidator, validate } from 'validators';
import { callAction } from '../../api/app'; import { callAction } from '../../api/app';
@@ -94,7 +94,7 @@ const MqttSettings = () => {
await validate(createMqttSettingsValidator(data), data); await validate(createMqttSettingsValidator(data), data);
await saveData(); await saveData();
} catch (error) { } catch (error) {
setFieldErrors(error as ValidateFieldsError); setFieldErrors((error as ValidationError).fieldErrors);
} }
}, [data, saveData]); }, [data, saveData]);
@@ -129,7 +129,7 @@ const MqttSettings = () => {
<SectionContent> <SectionContent>
{blocker ? <BlockNavigation blocker={blocker} /> : null} {blocker ? <BlockNavigation blocker={blocker} /> : null}
<> <>
<Box display="flex" gap={2} mb={1}> <Box sx={{ display: 'flex', gap: 2, mb: 1 }}>
<BlockFormControlLabel <BlockFormControlLabel
control={ control={
<Checkbox <Checkbox

View File

@@ -36,7 +36,7 @@ import {
import { useI18nContext } from 'i18n/i18n-react'; import { useI18nContext } from 'i18n/i18n-react';
import type { NTPSettingsType, Time } from 'types'; import type { NTPSettingsType, Time } from 'types';
import { formatLocalDateTime, updateValueDirty, useRest } from 'utils'; import { formatLocalDateTime, updateValueDirty, useRest } from 'utils';
import { 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, useTimeZoneSelectItems } from './TZ';
@@ -133,7 +133,7 @@ const NTPSettings = () => {
await validate(NTP_SETTINGS_VALIDATOR, data); await validate(NTP_SETTINGS_VALIDATOR, data);
await saveData(); await saveData();
} catch (error) { } catch (error) {
setFieldErrors(error as ValidateFieldsError); setFieldErrors((error as ValidationError).fieldErrors);
} }
}, [data, saveData]); }, [data, saveData]);
@@ -193,9 +193,9 @@ const NTPSettings = () => {
{timeZoneItems} {timeZoneItems}
</ValidatedTextField> </ValidatedTextField>
<Box display="flex" flexWrap="wrap"> <Box sx={{ display: 'flex', flexWrap: 'wrap' }}>
{!data.enabled && !dirtyFlags.length && ( {!data.enabled && !dirtyFlags.length && (
<Box flexWrap="nowrap" whiteSpace="nowrap"> <Box sx={{ flexWrap: 'nowrap', whiteSpace: 'nowrap' }}>
<ButtonRow> <ButtonRow>
<Button <Button
onClick={openSetTime} onClick={openSetTime}
@@ -259,9 +259,9 @@ const NTPSettings = () => {
<Dialog sx={dialogStyle} open={settingTime} onClose={handleCloseSetTime}> <Dialog sx={dialogStyle} open={settingTime} onClose={handleCloseSetTime}>
<DialogTitle>{LL.SET_TIME(1)}</DialogTitle> <DialogTitle>{LL.SET_TIME(1)}</DialogTitle>
<DialogContent dividers> <DialogContent dividers>
<Box color="warning.main" p={0} pl={0} pr={0} mt={0} mb={2}> <Typography color="warning" variant="body2">
<Typography variant="body2">{LL.SET_TIME_TEXT()}</Typography> {LL.SET_TIME_TEXT()}
</Box> </Typography>
<TextField <TextField
label={LL.LOCAL_TIME(0)} label={LL.LOCAL_TIME(0)}
type="datetime-local" type="datetime-local"

View File

@@ -156,11 +156,13 @@ const Settings = () => {
<Divider /> <Divider />
<Box <Box
mt={2} sx={{
display="flex" mt: 2,
justifyContent="flex-end" display: 'flex',
flexWrap="nowrap" justifyContent: 'flex-end',
whiteSpace="nowrap" flexWrap: 'nowrap',
whiteSpace: 'nowrap'
}}
> >
<Button <Button
startIcon={<SettingsBackupRestoreIcon />} startIcon={<SettingsBackupRestoreIcon />}

View File

@@ -40,7 +40,7 @@ import {
import { useI18nContext } from 'i18n/i18n-react'; import { useI18nContext } from 'i18n/i18n-react';
import type { NetworkSettingsType } from 'types'; import type { NetworkSettingsType } from 'types';
import { updateValueDirty, useRest } from 'utils'; import { updateValueDirty, useRest } from 'utils';
import { validate } from 'validators'; import { ValidationError, validate } from 'validators';
import { createNetworkSettingsValidator } from 'validators/network'; import { createNetworkSettingsValidator } from 'validators/network';
import SystemMonitor from '../../status/SystemMonitor'; import SystemMonitor from '../../status/SystemMonitor';
@@ -116,7 +116,7 @@ const NetworkSettings = () => {
await validate(createNetworkSettingsValidator(data), data); await validate(createNetworkSettingsValidator(data), data);
await saveData(); await saveData();
} catch (error) { } catch (error) {
setFieldErrors(error as ValidateFieldsError); setFieldErrors((error as ValidationError).fieldErrors);
} }
deselectNetwork(); deselectNetwork();
}, [data, saveData, deselectNetwork]); }, [data, saveData, deselectNetwork]);

View File

@@ -54,19 +54,27 @@ const GenerateToken = ({ username, onClose }: GenerateTokenProps) => {
<DialogContent dividers> <DialogContent dividers>
{token ? ( {token ? (
<> <>
<MessageBox message={LL.ACCESS_TOKEN_TEXT()} level="info" my={2} /> <MessageBox
<Box mt={2} mb={2}> message={LL.ACCESS_TOKEN_TEXT()}
level="info"
sx={{ mt: 2, mb: 2 }}
/>
<Box sx={{ mt: 2, mb: 2 }}>
<TextField <TextField
label="Token" label="Token"
multiline multiline
value={token.token} value={token.token}
fullWidth fullWidth
contentEditable={false} slotProps={{
input: {
readOnly: true
}
}}
/> />
</Box> </Box>
</> </>
) : ( ) : (
<Box m={4} textAlign="center"> <Box sx={{ m: 4, textAlign: 'center' }}>
<LinearProgress /> <LinearProgress />
<Typography variant="h6">{LL.GENERATING_TOKEN()}&hellip;</Typography> <Typography variant="h6">{LL.GENERATING_TOKEN()}&hellip;</Typography>
</Box> </Box>

View File

@@ -240,12 +240,16 @@ const ManageUsers = () => {
</Table> </Table>
{noAdminConfigured() && ( {noAdminConfigured() && (
<MessageBox level="warning" message={LL.USER_WARNING()} my={2} /> <MessageBox
level="warning"
message={LL.USER_WARNING()}
sx={{ mt: 2, mb: 2 }}
/>
)} )}
<Box display="flex" flexWrap="wrap"> <Box sx={{ display: 'flex', flexWrap: 'wrap' }}>
{changed !== 0 && ( {changed !== 0 && (
<Box flexGrow={1} sx={{ '& button': { mt: 2 } }}> <Box sx={{ flexGrow: 1, '& button': { mt: 2 } }}>
<ButtonRow> <ButtonRow>
<Button <Button
startIcon={<CancelIcon />} startIcon={<CancelIcon />}
@@ -270,7 +274,7 @@ const ManageUsers = () => {
</ButtonRow> </ButtonRow>
</Box> </Box>
)} )}
<Box flexWrap="nowrap" whiteSpace="nowrap"> <Box sx={{ flexWrap: 'nowrap', whiteSpace: 'nowrap' }}>
<ButtonRow> <ButtonRow>
<Button <Button
startIcon={<PersonAddIcon />} startIcon={<PersonAddIcon />}

View File

@@ -19,7 +19,7 @@ import { AuthenticatedContext } from 'contexts/authentication';
import { useI18nContext } from 'i18n/i18n-react'; import { useI18nContext } from 'i18n/i18n-react';
import type { SecuritySettingsType } from 'types'; import type { SecuritySettingsType } from 'types';
import { updateValueDirty, useRest } from 'utils'; import { updateValueDirty, useRest } from 'utils';
import { SECURITY_SETTINGS_VALIDATOR, validate } from 'validators'; import { SECURITY_SETTINGS_VALIDATOR, ValidationError, validate } from 'validators';
const SecuritySettings = () => { const SecuritySettings = () => {
const { LL } = useI18nContext(); const { LL } = useI18nContext();
@@ -58,7 +58,7 @@ const SecuritySettings = () => {
await saveData(); await saveData();
await authenticatedContext.refresh(); await authenticatedContext.refresh();
} catch (error) { } catch (error) {
setFieldErrors(error as ValidateFieldsError); setFieldErrors((error as ValidationError).fieldErrors);
} }
}, [data, saveData, authenticatedContext]); }, [data, saveData, authenticatedContext]);

View File

@@ -24,7 +24,7 @@ import {
import { useI18nContext } from 'i18n/i18n-react'; import { useI18nContext } from 'i18n/i18n-react';
import type { UserType } from 'types'; import type { UserType } from 'types';
import { updateValue } from 'utils'; import { updateValue } from 'utils';
import { validate } from 'validators'; import { ValidationError, validate } from 'validators';
interface UserFormProps { interface UserFormProps {
creating: boolean; creating: boolean;
@@ -69,7 +69,7 @@ const User: FC<UserFormProps> = ({
await validate(validator, user); await validate(validator, user);
onDoneEditing(); onDoneEditing();
} catch (error) { } catch (error) {
setFieldErrors(error as ValidateFieldsError); setFieldErrors((error as ValidationError).fieldErrors);
} }
} }
}, [user, validator, onDoneEditing]); }, [user, validator, onDoneEditing]);

View File

@@ -266,7 +266,7 @@ const SystemLog = () => {
return ( return (
<> <>
<Grid container spacing={2} alignItems="center"> <Grid container spacing={2} sx={{ alignItems: 'center' }}>
<Grid> <Grid>
<TextField <TextField
name="level" name="level"

View File

@@ -118,17 +118,15 @@ const SystemMonitor = () => {
p: 3 p: 3
}} }}
> >
<Box display="flex" alignItems="center" flexDirection="column"> <Box sx={{ display: 'flex', alignItems: 'center', flexDirection: 'column' }}>
<img <img
src="/app/icon.png" src="/app/icon.png"
alt="EMS-ESP" alt="EMS-ESP"
style={{ width: '40px', height: '40px', marginBottom: '16px' }} style={{ width: '40px', height: '40px', marginBottom: '16px' }}
/> />
<Typography <Typography
color="secondary" sx={{ color: 'secondary', fontWeight: 400, textAlign: 'center' }}
variant="h6" variant="h6"
fontWeight={400}
textAlign="center"
> >
{statusMessage} {statusMessage}
</Typography> </Typography>
@@ -148,11 +146,14 @@ const SystemMonitor = () => {
</MessageBox> </MessageBox>
) : ( ) : (
<> <>
<Typography mt={2} variant="h6" fontWeight={400} textAlign="center"> <Typography
sx={{ mt: 2, fontWeight: 400, textAlign: 'center' }}
variant="h6"
>
{LL.PLEASE_WAIT()}&hellip; {LL.PLEASE_WAIT()}&hellip;
</Typography> </Typography>
{isUploading && ( {isUploading && (
<Box width="100%" pl={2} pr={2} py={2}> <Box sx={{ width: '100%', pl: 2, pr: 2, py: 2 }}>
<LinearProgressWithLabel value={progressValue} /> <LinearProgressWithLabel value={progressValue} />
</Box> </Box>
)} )}

View File

@@ -7,6 +7,7 @@ import {
useRef, useRef,
useState useState
} from 'react'; } from 'react';
import { Link } from 'react-router';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import CancelIcon from '@mui/icons-material/Cancel'; import CancelIcon from '@mui/icons-material/Cancel';
@@ -24,7 +25,6 @@ import {
DialogTitle, DialogTitle,
Grid, Grid,
IconButton, IconButton,
Link,
Table, Table,
TableBody, TableBody,
TableCell, TableCell,
@@ -274,6 +274,8 @@ const InstallDialog = memo(
fetchDevVersion, fetchDevVersion,
latestVersion, latestVersion,
latestDevVersion, latestDevVersion,
upgradeImportantMessageType,
downloadOnly,
platform, platform,
LL, LL,
onClose, onClose,
@@ -283,6 +285,8 @@ const InstallDialog = memo(
fetchDevVersion: boolean; fetchDevVersion: boolean;
latestVersion?: VersionInfo; latestVersion?: VersionInfo;
latestDevVersion?: VersionInfo; latestDevVersion?: VersionInfo;
upgradeImportantMessageType: number;
downloadOnly: boolean;
platform: string; platform: string;
LL: TranslationFunctions; LL: TranslationFunctions;
onClose: () => void; onClose: () => void;
@@ -305,12 +309,33 @@ const InstallDialog = memo(
{`${LL.INSTALL()} ${fetchDevVersion ? LL.DEVELOPMENT() : LL.STABLE()} Firmware`} {`${LL.INSTALL()} ${fetchDevVersion ? LL.DEVELOPMENT() : LL.STABLE()} Firmware`}
</DialogTitle> </DialogTitle>
<DialogContent dividers> <DialogContent dividers>
<Typography mb={2}> <Typography sx={{ mb: 2 }}>
{LL.INSTALL_VERSION( {LL.INSTALL_VERSION(
LL.INSTALL(), downloadOnly ? LL.DOWNLOAD(1) : LL.INSTALL(),
fetchDevVersion ? latestDevVersion?.name : latestVersion?.name fetchDevVersion ? latestDevVersion?.name : latestVersion?.name
)} )}
</Typography> </Typography>
{upgradeImportantMessageType === 2 && LL.UPGRADE_IMPORTANT_MESSAGES_2()}
{upgradeImportantMessageType === 1 && (
<>
{LL.UPGRADE_IMPORTANT_MESSAGES_1()}
<Typography sx={{ mt: 2 }}>
<Link to="/settings/downloadUpload" style={{ color: 'lightblue' }}>
{LL.DOWNLOAD_SYSTEM_BACKUP()}
</Link>
</Typography>
</>
)}
<Typography sx={{ mt: 2 }}>
<Link
to="https://docs.emsesp.org/FAQ#upgrading-the-firmware"
target="_blank"
rel="noreferrer"
style={{ color: 'lightblue' }}
>
{LL.ONLINE_HELP()}
</Link>
</Typography>
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<Button <Button
@@ -327,10 +352,16 @@ const InstallDialog = memo(
onClick={onClose} onClick={onClose}
color="primary" color="primary"
> >
<Link underline="none" target="_blank" href={binURL} color="primary"> <Link
to={binURL}
target="_blank"
rel="noreferrer"
style={{ color: 'lightblue', textDecoration: 'none' }}
>
{LL.DOWNLOAD(0)} {LL.DOWNLOAD(0)}
</Link> </Link>
</Button> </Button>
{!downloadOnly && (
<Button <Button
startIcon={<WarningIcon color="warning" />} startIcon={<WarningIcon color="warning" />}
variant="outlined" variant="outlined"
@@ -339,6 +370,7 @@ const InstallDialog = memo(
> >
{LL.INSTALL()} {LL.INSTALL()}
</Button> </Button>
)}
</DialogActions> </DialogActions>
</Dialog> </Dialog>
); );
@@ -367,7 +399,9 @@ const InstallPartitionDialog = memo(
{LL.INSTALL()} {LL.STORED_VERSIONS()} {LL.INSTALL()} {LL.STORED_VERSIONS()}
</DialogTitle> </DialogTitle>
<DialogContent dividers> <DialogContent dividers>
<Typography mb={2}>{LL.INSTALL_VERSION(LL.INSTALL(), version)}</Typography> <Typography sx={{ mb: 2 }}>
{LL.INSTALL_VERSION(LL.INSTALL(), version)}
</Typography>
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<Button <Button
@@ -419,6 +453,7 @@ const Version = () => {
const [stableUpgradeAvailable, setStableUpgradeAvailable] = const [stableUpgradeAvailable, setStableUpgradeAvailable] =
useState<boolean>(false); useState<boolean>(false);
const [internetLive, setInternetLive] = useState<boolean>(false); const [internetLive, setInternetLive] = useState<boolean>(false);
const [downloadOnly, setDownloadOnly] = useState<boolean>(false);
const [showVersionInfo, setShowVersionInfo] = useState<number>(0); // 1 = stable, 2 = dev, 3 = partition const [showVersionInfo, setShowVersionInfo] = useState<number>(0); // 1 = stable, 2 = dev, 3 = partition
const [firmwareSize, setFirmwareSize] = useState<number>(0); const [firmwareSize, setFirmwareSize] = useState<number>(0);
@@ -444,6 +479,9 @@ const Version = () => {
error error
} = useRequest(SystemApi.readSystemStatus).onSuccess((event) => { } = useRequest(SystemApi.readSystemStatus).onSuccess((event) => {
const systemData = event.data as VersionData; const systemData = event.data as VersionData;
if (systemData.arduino_version.startsWith('Tasmota')) {
setDownloadOnly(true);
}
setUsingDevVersion(systemData.emsesp_version.includes('dev')); setUsingDevVersion(systemData.emsesp_version.includes('dev'));
}); });
@@ -459,6 +497,26 @@ const Version = () => {
immediate: false immediate: false
}); });
const [upgradeImportantMessageType, setUpgradeImportantMessageType] =
useState<number>(0);
const { send: checkUpgradeImportantMessages } = useRequest(
(version: string) =>
callAction({ action: 'upgradeImportantMessages', param: version }),
{
immediate: false
}
)
.onSuccess((event) => {
const upgradeImportantMessageType_n = (
event.data as { upgradeImportantMessageType: number }
).upgradeImportantMessageType;
setUpgradeImportantMessageType(upgradeImportantMessageType_n);
})
.onError((error) => {
toast.error(String(error.error?.message || 'An error occurred'));
});
// Memoized values // Memoized values
const platform = useMemo(() => (data ? getPlatform(data) : ''), [data]); const platform = useMemo(() => (data ? getPlatform(data) : ''), [data]);
@@ -524,10 +582,16 @@ const Version = () => {
[] []
); );
const showFirmwareDialog = useCallback((useDevVersion: boolean) => { const showFirmwareDialog = useCallback(
(useDevVersion: boolean) => {
setFetchDevVersion(useDevVersion); setFetchDevVersion(useDevVersion);
void checkUpgradeImportantMessages(
useDevVersion ? latestDevVersion?.name : latestVersion?.name
);
setOpenInstallDialog(true); setOpenInstallDialog(true);
}, []); },
[latestDevVersion, latestVersion, fetchDevVersion]
);
const closeInstallDialog = useCallback(() => { const closeInstallDialog = useCallback(() => {
setOpenInstallDialog(false); setOpenInstallDialog(false);
@@ -629,8 +693,8 @@ const Version = () => {
return ( return (
<> <>
<Box p={2} border="1px solid grey" borderRadius={2}> <Box sx={{ p: 2, border: '1px solid #565656', borderRadius: 2 }}>
<Typography mb={1} variant="h6" color="primary"> <Typography sx={{ mb: 1 }} variant="h6" color="primary">
{LL.THIS_VERSION()} {LL.THIS_VERSION()}
</Typography> </Typography>
@@ -695,7 +759,7 @@ const Version = () => {
{internetLive ? ( {internetLive ? (
<> <>
<Typography mt={4} mb={1} variant="h6" color="primary"> <Typography sx={{ mt: 4, mb: 1 }} variant="h6" color="primary">
{LL.AVAILABLE_VERSION()} {LL.AVAILABLE_VERSION()}
</Typography> </Typography>
@@ -717,7 +781,7 @@ const Version = () => {
</Grid> </Grid>
<Grid size={{ xs: 8, md: 10 }}> <Grid size={{ xs: 8, md: 10 }}>
{otherPartitions.map((partition) => ( {otherPartitions.map((partition) => (
<Typography key={partition.partition} mb={1}> <Typography key={partition.partition} sx={{ mb: 1 }}>
{partition.version} {partition.version}
<IconButton <IconButton
onClick={() => onClick={() =>
@@ -783,7 +847,7 @@ const Version = () => {
</Grid> </Grid>
</> </>
) : ( ) : (
<Typography mt={2} color="warning"> <Typography sx={{ mt: 2 }} color="warning">
<WarningIcon color="warning" sx={{ verticalAlign: 'middle', mr: 2 }} /> <WarningIcon color="warning" sx={{ verticalAlign: 'middle', mr: 2 }} />
{LL.INTERNET_CONNECTION_REQUIRED()} {LL.INTERNET_CONNECTION_REQUIRED()}
</Typography> </Typography>
@@ -807,6 +871,8 @@ const Version = () => {
fetchDevVersion={fetchDevVersion} fetchDevVersion={fetchDevVersion}
latestVersion={latestVersion} latestVersion={latestVersion}
latestDevVersion={latestDevVersion} latestDevVersion={latestDevVersion}
upgradeImportantMessageType={upgradeImportantMessageType}
downloadOnly={downloadOnly}
platform={platform} platform={platform}
LL={LL} LL={LL}
onClose={closeInstallDialog} onClose={closeInstallDialog}
@@ -823,7 +889,7 @@ const Version = () => {
<Typography sx={{ pt: 2, pb: 2 }} variant="h6" color="primary"> <Typography sx={{ pt: 2, pb: 2 }} variant="h6" color="primary">
{LL.UPLOAD()} {LL.UPLOAD()}
</Typography> </Typography>
<SingleUpload text={LL.UPLOAD_DROP_TEXT()} doRestart={doRestart} /> <SingleUpload doRestart={doRestart} />
</> </>
)} )}
</Box> </Box>
@@ -842,6 +908,7 @@ const Version = () => {
locale, locale,
openInstallDialog, openInstallDialog,
fetchDevVersion, fetchDevVersion,
downloadOnly,
me.admin, me.admin,
showButtons, showButtons,
handleVersionInfoClose, handleVersionInfoClose,

View File

@@ -53,12 +53,16 @@ const MessageBox: FC<PropsWithChildren<MessageBoxProps>> = ({
return ( return (
<Box <Box
p={2}
display="flex"
alignItems="center"
borderRadius={1}
sx={{ backgroundColor, color: 'white', ...sx }}
{...rest} {...rest}
sx={{
display: 'flex',
alignItems: 'center',
borderRadius: 1,
backgroundColor,
color: 'white',
p: 2,
...sx
}}
> >
<Icon /> <Icon />
{(message || children) && ( {(message || children) && (

View File

@@ -29,7 +29,7 @@ const LayoutDrawerComponent = ({ mobileOpen, onClose }: LayoutDrawerProps) => {
() => ( () => (
<> <>
<Toolbar disableGutters> <Toolbar disableGutters>
<Box display="flex" alignItems="center" px={2}> <Box sx={{ display: 'flex', alignItems: 'center', p: 2 }}>
<LayoutDrawerLogo src="/app/icon.png" alt={PROJECT_NAME} /> <LayoutDrawerLogo src="/app/icon.png" alt={PROJECT_NAME} />
<Typography variant="h6">{PROJECT_NAME}</Typography> <Typography variant="h6">{PROJECT_NAME}</Typography>
</Box> </Box>

View File

@@ -51,9 +51,7 @@ const LayoutMenuComponent = () => {
sx={{ my: 0 }} sx={{ my: 0 }}
slotProps={{ slotProps={{
primary: { primary: {
fontWeight: '600', sx: { fontWeight: 600, mb: '2px', color: 'lightblue' }
mb: '2px',
color: 'lightblue'
} }
}} }}
/> />

View File

@@ -32,8 +32,16 @@ const FormLoaderComponent = ({ errorMessage, onRetry }: FormLoaderProps) => {
); );
} }
return ( return (
<Box m={2} py={2} display="flex" alignItems="center" flexDirection="column"> <Box
<Box py={2}> sx={{
m: 2,
py: 2,
display: 'flex',
alignItems: 'center',
flexDirection: 'column'
}}
>
<Box sx={{ p: 2 }}>
<CircularProgress size={100} /> <CircularProgress size={100} />
</Box> </Box>
</Box> </Box>

View File

@@ -15,12 +15,14 @@ const circularProgressStyles: SxProps<Theme> = (theme: Theme) => ({
const LoadingSpinner = ({ height = '100%' }: LoadingSpinnerProps) => { const LoadingSpinner = ({ height = '100%' }: LoadingSpinnerProps) => {
return ( return (
<Box <Box
display="flex" sx={{
alignItems="center" display: 'flex',
justifyContent="center" alignItems: 'center',
flexDirection="column" justifyContent: 'center',
padding={2} flexDirection: 'column',
height={height} padding: 2,
height
}}
> >
<CircularProgress sx={circularProgressStyles} size={100} /> <CircularProgress sx={circularProgressStyles} size={100} />
</Box> </Box>

View File

@@ -1,4 +1,4 @@
// Code inspired by Prince Azubuike from https://medium.com/@dprincecoder/creating-a-drag-and-drop-file-upload-component-in-react-a-step-by-step-guide-4d93b6cc21e0 // drag/drop code inspired by Prince Azubuike from https://medium.com/@dprincecoder/creating-a-drag-and-drop-file-upload-component-in-react-a-step-by-step-guide-4d93b6cc21e0
import { import {
type ChangeEvent, type ChangeEvent,
type DragEvent, type DragEvent,
@@ -6,12 +6,28 @@ import {
useRef, useRef,
useState useState
} from 'react'; } from 'react';
import { Link } from 'react-router';
import { toast } from 'react-toastify';
import CancelIcon from '@mui/icons-material/Cancel'; import CancelIcon from '@mui/icons-material/Cancel';
import CloudUploadIcon from '@mui/icons-material/CloudUpload'; import CloudUploadIcon from '@mui/icons-material/CloudUpload';
import UploadIcon from '@mui/icons-material/Upload'; import UploadIcon from '@mui/icons-material/Upload';
import { Box, Button, Typography, styled } from '@mui/material'; import WarningIcon from '@mui/icons-material/Warning';
import {
Box,
Button,
Dialog,
DialogActions,
DialogContent,
DialogTitle,
Typography,
styled
} from '@mui/material';
import { callAction } from 'api/app';
import { dialogStyle } from '@/CustomTheme';
import { useRequest } from 'alova/client';
import { useI18nContext } from 'i18n/i18n-react'; import { useI18nContext } from 'i18n/i18n-react';
const DocumentUploader = styled(Box)<{ active?: boolean }>(({ theme, active }) => ({ const DocumentUploader = styled(Box)<{ active?: boolean }>(({ theme, active }) => ({
@@ -58,6 +74,29 @@ const DragNdrop = ({ text, onFileSelected }: DragNdropProps) => {
const [dragged, setDragged] = useState(false); const [dragged, setDragged] = useState(false);
const inputRef = useRef<HTMLInputElement | null>(null); const inputRef = useRef<HTMLInputElement | null>(null);
const { LL } = useI18nContext(); const { LL } = useI18nContext();
const [showUpgradeDialog, setShowUpgradeDialog] = useState(false);
const [upgradeImportantMessageType, setUpgradeImportantMessageType] =
useState<number>(0);
const { send: checkUpgradeImportantMessages } = useRequest(
(version: string) =>
callAction({ action: 'upgradeImportantMessages', param: version }),
{
immediate: false
}
)
.onSuccess((event) => {
const upgradeImportantMessageType_n = (
event.data as { upgradeImportantMessageType: number }
).upgradeImportantMessageType;
setUpgradeImportantMessageType(upgradeImportantMessageType_n);
if (upgradeImportantMessageType_n === 0) {
onFileSelected(file);
}
})
.onError((error) => {
toast.error(String(error.error?.message || 'An error occurred'));
});
const checkFileExtension = (file: File) => { const checkFileExtension = (file: File) => {
const validExtensions = ['.json', '.bin', '.md5']; const validExtensions = ['.json', '.bin', '.md5'];
@@ -97,9 +136,8 @@ const DragNdrop = ({ text, onFileSelected }: DragNdropProps) => {
const handleUploadClick = (event: MouseEvent<HTMLButtonElement>) => { const handleUploadClick = (event: MouseEvent<HTMLButtonElement>) => {
event.stopPropagation(); event.stopPropagation();
if (file) { void checkUpgradeImportantMessages(file?.name || '');
onFileSelected(file); setShowUpgradeDialog(true);
}
}; };
const handleBrowseClick = () => { const handleBrowseClick = () => {
@@ -158,6 +196,56 @@ const DragNdrop = ({ text, onFileSelected }: DragNdropProps) => {
{LL.UPLOAD()} {LL.UPLOAD()}
</Button> </Button>
</Box> </Box>
{showUpgradeDialog && upgradeImportantMessageType > 0 && (
<Dialog
sx={dialogStyle}
open={showUpgradeDialog}
onClose={() => setShowUpgradeDialog(false)}
>
<DialogTitle>
<WarningIcon
color="warning"
sx={{ fontSize: 18, verticalAlign: 'middle' }}
/>
&nbsp;&nbsp;
{LL.UPGRADE_IMPORTANT_MESSAGES()}
</DialogTitle>
<DialogContent dividers>
{upgradeImportantMessageType === 2 &&
LL.UPGRADE_IMPORTANT_MESSAGES_2()}
{upgradeImportantMessageType === 1 &&
LL.UPGRADE_IMPORTANT_MESSAGES_1()}
<Typography sx={{ mt: 2 }}>
<Link
to="https://docs.emsesp.org/FAQ#upgrading-the-firmware"
target="_blank"
rel="noreferrer"
style={{ color: 'lightblue' }}
>
{LL.ONLINE_HELP()}
</Link>
</Typography>
</DialogContent>
<DialogActions>
<Button
startIcon={<CancelIcon />}
variant="outlined"
onClick={() => setShowUpgradeDialog(false)}
color="secondary"
>
{LL.CANCEL()}
</Button>
<Button
startIcon={<UploadIcon />}
variant="outlined"
onClick={() => onFileSelected(file)}
color="primary"
>
{LL.UPLOAD()}
</Button>
</DialogActions>
</Dialog>
)}
</> </>
)} )}
</DocumentUploader> </DocumentUploader>

View File

@@ -13,11 +13,10 @@ import DragNdrop from './DragNdrop';
import { LinearProgressWithLabel } from './LinearProgressWithLabel'; import { LinearProgressWithLabel } from './LinearProgressWithLabel';
interface SingleUploadProps { interface SingleUploadProps {
text: string;
doRestart: () => void; doRestart: () => void;
} }
const SingleUpload = ({ text, doRestart }: SingleUploadProps) => { const SingleUpload = ({ doRestart }: SingleUploadProps) => {
const [md5, setMd5] = useState<string>(); const [md5, setMd5] = useState<string>();
const [file, setFile] = useState<File>(); const [file, setFile] = useState<File>();
const { LL } = useI18nContext(); const { LL } = useI18nContext();
@@ -58,7 +57,7 @@ const SingleUpload = ({ text, doRestart }: SingleUploadProps) => {
<> <>
{isUploading ? ( {isUploading ? (
<> <>
<Box width="100%" pl={2} pr={2}> <Box sx={{ width: '100%', pl: 2, pr: 2 }}>
<LinearProgressWithLabel <LinearProgressWithLabel
value={ value={
progress.total === 0 || progress.loaded === 0 progress.total === 0 || progress.loaded === 0
@@ -81,11 +80,11 @@ const SingleUpload = ({ text, doRestart }: SingleUploadProps) => {
</Button> </Button>
</> </>
) : ( ) : (
<DragNdrop text={text} onFileSelected={setFile} /> <DragNdrop text={LL.UPLOAD_DROP_TEXT()} onFileSelected={setFile} />
)} )}
{md5 && ( {md5 && (
<Box mt={2}> <Box sx={{ mt: 2 }}>
<Typography variant="body2">{'MD5: ' + md5}</Typography> <Typography variant="body2">{'MD5: ' + md5}</Typography>
</Box> </Box>
)} )}

View File

@@ -350,16 +350,22 @@ const cz: Translation = {
NO_DATA_1: 'Nebyly nalezeny žádné oblíbené entity. Použijte modul', NO_DATA_1: 'Nebyly nalezeny žádné oblíbené entity. Použijte modul',
NO_DATA_2: 'pro jejich výběr.', NO_DATA_2: 'pro jejich výběr.',
NO_DATA_3: 'Pro zobrazení všech dostupných entit navštivte stránku', NO_DATA_3: 'Pro zobrazení všech dostupných entit navštivte stránku',
NO_GPIO: 'Nebylo nalezeno žádné volné GPIO',
THIS_VERSION: 'Tato verze', THIS_VERSION: 'Tato verze',
PLATFORM: 'Platforma', PLATFORM: 'Platforma',
RELEASE_TYPE: 'Typ sestavení', RELEASE_TYPE: 'Typ sestavení',
INTERNET_CONNECTION_REQUIRED: 'Pro automatickou kontrolu a instalaci aktualizací je třeba internetové připojení', INTERNET_CONNECTION_REQUIRED: 'Pro automatickou kontrolu a instalaci aktualizací je třeba internetové připojení',
SWITCH_RELEASE_TYPE: 'Přepnout na {0} verzi', SWITCH_RELEASE_TYPE: 'Přepnout na {0} verzi',
FIRMWARE_VERSION_INFO: 'Informace o verzi firmwaru', FIRMWARE_VERSION_INFO: 'Informace o verzi firmwaru',
NO_DATA: 'Žádná data', NO_DATA: 'žádné údaje',
USER_PROFILE: 'Uživatelský profil', USER_PROFILE: 'Uživatelský profil',
STORED_VERSIONS: 'Uložené verze', STORED_VERSIONS: 'Uložené verze',
ONLINE_HELP: 'online nápověda' ONLINE_HELP: 'online nápověda',
UPGRADE_IMPORTANT_MESSAGES: 'Aktualizovat důležité zprávy',
UPGRADE_IMPORTANT_MESSAGES_1: 'Tato aktualizace vyžaduje obnovení továrního nastavení. Ujistěte se, že nejprve stáhnete systémovou zálohu před pokračováním a poté nahrajte tento soubor po instalaci nové verze.',
UPGRADE_IMPORTANT_MESSAGES_2: 'Aktualizujete se na novou hlavní verzi. Ujistěte se, že jste přečetli ChangeLog pro jakékoliv závažné změny.',
WARNING_SYSTEM_BACKUP: 'Toto vytvoří zálohu vašich celých systémových konfigurací a nastavení. Všechna hesla budou v zálohovém souboru čitelná. Buďte opatrní při sdílení! Opravdu chcete pokračovat?'
}; };
export default cz; export default cz;

View File

@@ -187,7 +187,7 @@ const de: Translation = {
COMPACT: 'Kompakte Darstellung', COMPACT: 'Kompakte Darstellung',
DOWNLOAD_SETTINGS_TEXT: 'Erstellen Sie eine Sicherung Ihrer Konfigurationen und Einstellungen', DOWNLOAD_SETTINGS_TEXT: 'Erstellen Sie eine Sicherung Ihrer Konfigurationen und Einstellungen',
DOWNLOAD_SETTINGS_TEXT2: 'Exportiere alle Werte', DOWNLOAD_SETTINGS_TEXT2: 'Exportiere alle Werte',
DOWNLOAD_SYSTEM_BACKUP: 'System Sicherung', DOWNLOAD_SYSTEM_BACKUP: 'Systemsicherung',
UPLOAD_TEXT: 'Laden Sie eine neue Firmware-Datei (.bin) oder eine Sicherungsdatei (.json) hoch', UPLOAD_TEXT: 'Laden Sie eine neue Firmware-Datei (.bin) oder eine Sicherungsdatei (.json) hoch',
UPLOAD_DROP_TEXT: 'Legen Sie eine Firmware-Datei (.bin) ab oder klicken Sie hier', UPLOAD_DROP_TEXT: 'Legen Sie eine Firmware-Datei (.bin) ab oder klicken Sie hier',
ERROR: 'Unerwarteter Fehler, bitte versuchen Sie es erneut.', ERROR: 'Unerwarteter Fehler, bitte versuchen Sie es erneut.',
@@ -350,6 +350,7 @@ const de: Translation = {
NO_DATA_1: 'Keine favorisierten EMS-Entitäten gefunden! Verwenden Sie das Modul', NO_DATA_1: 'Keine favorisierten EMS-Entitäten gefunden! Verwenden Sie das Modul',
NO_DATA_2: ', um sie zu markieren.', NO_DATA_2: ', um sie zu markieren.',
NO_DATA_3: 'Um alle verfügbaren Entitäten anzuzeigen, gehen Sie zu', NO_DATA_3: 'Um alle verfügbaren Entitäten anzuzeigen, gehen Sie zu',
NO_GPIO: 'Keine freien GPIO gefunden',
THIS_VERSION: 'Diese Version', THIS_VERSION: 'Diese Version',
PLATFORM: 'Plattform', PLATFORM: 'Plattform',
RELEASE_TYPE: 'Release Typ', RELEASE_TYPE: 'Release Typ',
@@ -359,7 +360,12 @@ const de: Translation = {
NO_DATA: 'Keine Daten', NO_DATA: 'Keine Daten',
USER_PROFILE: 'Benutzerprofil', USER_PROFILE: 'Benutzerprofil',
STORED_VERSIONS: 'Gespeicherte Versionen', STORED_VERSIONS: 'Gespeicherte Versionen',
ONLINE_HELP: 'Online-Hilfe' ONLINE_HELP: 'Online-Hilfe',
UPGRADE_IMPORTANT_MESSAGES: 'Wichtige Nachrichten aktualisieren',
UPGRADE_IMPORTANT_MESSAGES_1: 'Für diese Aktualisierung ist ein Werksreset erforderlich. Stellen Sie sicher, dass Sie zuerst eine Systemsicherung herunterladen, bevor Sie fortfahren, und laden Sie diese Datei dann nach der Installation der neuen Version hoch.',
UPGRADE_IMPORTANT_MESSAGES_2: 'Sie aktualisieren auf eine neue Hauptversion. Stellen Sie sicher, dass Sie den ChangeLog für alle wichtigen Änderungen gelesen haben.',
WARNING_SYSTEM_BACKUP: 'Dies wird eine Sicherung Ihrer vollständigen Systemkonfiguration und Einstellungen erstellen. Alle Passwörter werden in dieser Sicherungsdatei lesbar sein. Seien Sie vorsichtig beim Teilen! Möchten Sie fortfahren?'
}; };
export default de; export default de;

View File

@@ -350,6 +350,7 @@ const en: Translation = {
NO_DATA_1: 'No favorite EMS entities found yet. Use the', NO_DATA_1: 'No favorite EMS entities found yet. Use the',
NO_DATA_2: 'module to mark them.', NO_DATA_2: 'module to mark them.',
NO_DATA_3: 'To see all available entities go to', NO_DATA_3: 'To see all available entities go to',
NO_GPIO: 'No available GPIO found',
THIS_VERSION: 'This Version', THIS_VERSION: 'This Version',
PLATFORM: 'Platform', PLATFORM: 'Platform',
RELEASE_TYPE: 'Release Type', RELEASE_TYPE: 'Release Type',
@@ -359,7 +360,12 @@ const en: Translation = {
NO_DATA: 'No data', NO_DATA: 'No data',
USER_PROFILE: 'User Profile', USER_PROFILE: 'User Profile',
STORED_VERSIONS: 'Stored Versions', STORED_VERSIONS: 'Stored Versions',
ONLINE_HELP: 'online help' ONLINE_HELP: 'online help',
UPGRADE_IMPORTANT_MESSAGES: 'Upgrade Important Messages',
UPGRADE_IMPORTANT_MESSAGES_1: 'This upgrade requires a factory reset. Make sure you first download a System Backup before continuing, and then upload this file after the new version is installed.',
UPGRADE_IMPORTANT_MESSAGES_2: 'You are upgrading to a new major version. Make sure you have read the ChangeLog for any breaking changes.',
WARNING_SYSTEM_BACKUP: 'This will create a backup of your full system configuration and settings. All passwords will be readable in the backup file. Be careful with sharing! Do you want to continue?'
}; };
export default en; export default en;

View File

@@ -350,6 +350,7 @@ const fr: Translation = {
NO_DATA_1: 'Aucune entité EMS favorite trouvée. Utilisez le', NO_DATA_1: 'Aucune entité EMS favorite trouvée. Utilisez le',
NO_DATA_2: 'module pour les marquer.', NO_DATA_2: 'module pour les marquer.',
NO_DATA_3: 'Pour voir toutes les entités disponibles, aller à', NO_DATA_3: 'Pour voir toutes les entités disponibles, aller à',
NO_GPIO: "Aucun GPIO disponible n'a été détecté",
THIS_VERSION: 'Cette version', THIS_VERSION: 'Cette version',
PLATFORM: 'Plateforme', PLATFORM: 'Plateforme',
RELEASE_TYPE: 'Type de version', RELEASE_TYPE: 'Type de version',
@@ -359,7 +360,12 @@ const fr: Translation = {
NO_DATA: 'Aucune donnée', NO_DATA: 'Aucune donnée',
USER_PROFILE: 'Profil utilisateur', USER_PROFILE: 'Profil utilisateur',
STORED_VERSIONS: 'Versions stockées', STORED_VERSIONS: 'Versions stockées',
ONLINE_HELP: 'aide en ligne' ONLINE_HELP: 'aide en ligne',
UPGRADE_IMPORTANT_MESSAGES: 'Mettre à jour les messages importants',
UPGRADE_IMPORTANT_MESSAGES_1: 'Cette mise à jour nécessite une réinitialisation de fabrique. Assurez-vous de télécharger une sauvegarde système avant de continuer, et de la charger après l\'installation de la nouvelle version.',
UPGRADE_IMPORTANT_MESSAGES_2: 'Vous mettez à jour vers une nouvelle version majeure. Assurez-vous de lire le ChangeLog pour tout changement important.',
WARNING_SYSTEM_BACKUP: 'Cela créera une sauvegarde de votre configuration et paramètres complets. Tous les mots de passe seront lisibles dans le fichier de sauvegarde. Soyez prudent avec le partage ! Voulez-vous continuer ?'
}; };
export default fr; export default fr;

View File

@@ -350,6 +350,7 @@ const it: Translation = {
NO_DATA_1: 'Nessuna entità EMS preferita trovata. Usa il', NO_DATA_1: 'Nessuna entità EMS preferita trovata. Usa il',
NO_DATA_2: 'modulo per marcarle.', NO_DATA_2: 'modulo per marcarle.',
NO_DATA_3: 'Per vedere tutte le entità disponibili vai a', NO_DATA_3: 'Per vedere tutte le entità disponibili vai a',
NO_GPIO: 'Non è stato trovato alcun GPIO disponibile',
THIS_VERSION: 'Questa versione', THIS_VERSION: 'Questa versione',
PLATFORM: 'Piattaforma', PLATFORM: 'Piattaforma',
RELEASE_TYPE: 'Tipo di rilascio', RELEASE_TYPE: 'Tipo di rilascio',
@@ -359,7 +360,12 @@ const it: Translation = {
NO_DATA: 'Nessun dato', NO_DATA: 'Nessun dato',
USER_PROFILE: 'Profilo utente', USER_PROFILE: 'Profilo utente',
STORED_VERSIONS: 'Versioni memorizzate', STORED_VERSIONS: 'Versioni memorizzate',
ONLINE_HELP: 'aiuto online' ONLINE_HELP: 'aiuto online',
UPGRADE_IMPORTANT_MESSAGES: 'Aggiorna Messaggi Importanti',
UPGRADE_IMPORTANT_MESSAGES_1: 'Questa aggiornamento richiede un ripristino di fabbrica. Assicurati di prima scaricare un backup del sistema prima di continuare, e poi caricare questo file dopo l\'installazione della nuova versione.',
UPGRADE_IMPORTANT_MESSAGES_2: 'Stai aggiornando a una nuova versione principale. Assicurati di aver letto il ChangeLog per qualsiasi cambiamento importante.',
WARNING_SYSTEM_BACKUP: 'Questo creerà un backup delle tue configurazioni e impostazioni complete. Tutte le password saranno leggibili nel file di backup. Sei sicuro di voler continuare?'
}; };
export default it; export default it;

View File

@@ -350,6 +350,7 @@ const nl: Translation = {
NO_DATA_1: 'Er zijn nog geen favoriete EMS-entiteiten gevonden. Gebruik de', NO_DATA_1: 'Er zijn nog geen favoriete EMS-entiteiten gevonden. Gebruik de',
NO_DATA_2: 'module om ze te markeren.', NO_DATA_2: 'module om ze te markeren.',
NO_DATA_3: 'Om alle beschikbare entiteiten te zien, ga naar', NO_DATA_3: 'Om alle beschikbare entiteiten te zien, ga naar',
NO_GPIO: 'Er is geen beschikbare GPIO gevonden',
THIS_VERSION: 'Deze Versie', THIS_VERSION: 'Deze Versie',
PLATFORM: 'Platform', PLATFORM: 'Platform',
RELEASE_TYPE: 'Release Typ', RELEASE_TYPE: 'Release Typ',
@@ -359,7 +360,12 @@ const nl: Translation = {
NO_DATA: 'Geen data', NO_DATA: 'Geen data',
USER_PROFILE: 'Gebruikersprofiel', USER_PROFILE: 'Gebruikersprofiel',
STORED_VERSIONS: 'Opgeslagen versies', STORED_VERSIONS: 'Opgeslagen versies',
ONLINE_HELP: 'online help' ONLINE_HELP: 'online help',
UPGRADE_IMPORTANT_MESSAGES: 'Upgrade Belangrijke Berichten',
UPGRADE_IMPORTANT_MESSAGES_1: 'Deze upgrade vereist een fabrieksinstelling. Zorg ervoor dat u eerst een Systeem Backup download voordat u doorgaat, en upload deze file na de installatie van de nieuwe versie.',
UPGRADE_IMPORTANT_MESSAGES_2: 'U updatet naar een nieuwe grote versie. Zorg ervoor dat u de ChangeLog hebt gelezen voor alle brekende wijzigingen.',
WARNING_SYSTEM_BACKUP: 'Dit zal een back-up van uw volledige systeemconfiguratie en instellingen maken. Alle wachtwoorden zijn leesbaar in het back-upbestand. Wees voorzichtig bij delen! Wilt u doorgaan?'
}; };
export default nl; export default nl;

View File

@@ -350,6 +350,7 @@ const no: Translation = {
NO_DATA_1: 'Ingen favoritte EMS enheter funnet enda. Bruk', NO_DATA_1: 'Ingen favoritte EMS enheter funnet enda. Bruk',
NO_DATA_2: 'modul for å markere dem.', NO_DATA_2: 'modul for å markere dem.',
NO_DATA_3: 'For å se alle tilgjengelige enheter, gå til', NO_DATA_3: 'For å se alle tilgjengelige enheter, gå til',
NO_GPIO: 'Det ble ikke funnet noen tilgjengelige GPIO-porter',
THIS_VERSION: 'Denne versjonen', THIS_VERSION: 'Denne versjonen',
PLATFORM: 'Plattform', PLATFORM: 'Plattform',
RELEASE_TYPE: 'Utgivelses type', RELEASE_TYPE: 'Utgivelses type',
@@ -359,7 +360,12 @@ const no: Translation = {
NO_DATA: 'Ingen data', NO_DATA: 'Ingen data',
USER_PROFILE: 'Brukerprofil', USER_PROFILE: 'Brukerprofil',
STORED_VERSIONS: 'Lagret versjoner', STORED_VERSIONS: 'Lagret versjoner',
ONLINE_HELP: 'online hjelp' ONLINE_HELP: 'online hjelp',
UPGRADE_IMPORTANT_MESSAGES: 'Oppdater viktige meldinger',
UPGRADE_IMPORTANT_MESSAGES_1: 'Denne oppdateringen krever en fabriksinstilling. Sørg for at du først lastet ned en System Backup før du fortsetter, og last denne filen etter at den nye versjonen er installert.',
UPGRADE_IMPORTANT_MESSAGES_2: 'Du oppdaterer til en ny hovedversjon. Sørg for at du har lest ChangeLog for eventuelle bruddende endringer.',
WARNING_SYSTEM_BACKUP: 'Dette vil lage en sikkerhetskopi av din fullstendige systemkonfigurasjon og innstillinger. Alle passord vil være lesbare i sikkerhetskopien. Vær forsiktig med deling! Vil du fortsette?'
}; };
export default no; export default no;

View File

@@ -350,6 +350,7 @@ const pl: BaseTranslation = {
NO_DATA_1: 'Brak ulubionych encji EMS. Użyj', NO_DATA_1: 'Brak ulubionych encji EMS. Użyj',
NO_DATA_2: 'moduł do ich oznaczenia.', NO_DATA_2: 'moduł do ich oznaczenia.',
NO_DATA_3: 'Aby zobaczyć wszystkie dostępne encje przejdź do', NO_DATA_3: 'Aby zobaczyć wszystkie dostępne encje przejdź do',
NO_GPIO: 'Nie znaleziono dostępnych pinów GPIO',
THIS_VERSION: 'Ta wersja', THIS_VERSION: 'Ta wersja',
PLATFORM: 'Platforma', PLATFORM: 'Platforma',
RELEASE_TYPE: 'Typ wydania', RELEASE_TYPE: 'Typ wydania',
@@ -359,7 +360,12 @@ const pl: BaseTranslation = {
NO_DATA: 'Brak danych', NO_DATA: 'Brak danych',
USER_PROFILE: 'Profil użytkownika', USER_PROFILE: 'Profil użytkownika',
STORED_VERSIONS: 'Zapisane wersje', STORED_VERSIONS: 'Zapisane wersje',
ONLINE_HELP: 'pomoc online' ONLINE_HELP: 'pomoc online',
UPGRADE_IMPORTANT_MESSAGES: 'Aktualizuj ważne wiadomości',
UPGRADE_IMPORTANT_MESSAGES_1: 'Ta aktualizacja wymaga resetu fabrycznego. Upewnij się, że najpierw pobierzesz kopię zapasową systemu przed kontynuowaniem, a następnie przesuń tę plik po zainstalowaniu nowej wersji.',
UPGRADE_IMPORTANT_MESSAGES_2: 'Aktualizujesz się do nowej głównej wersji. Upewnij się, że przeczytałeś ChangeLog dla wszelkich istotnych zmian.',
WARNING_SYSTEM_BACKUP: 'To spowoduje utworzenie kopii zapasowej całej konfiguracji i ustawień systemu. Wszystkie hasła będą widoczne w pliku kopii zapasowej. Bądź ostrożny przy udostępnianiu! Chcesz kontynuować?'
}; };
export default pl; export default pl;

View File

@@ -4,7 +4,7 @@ const sk: Translation = {
LANGUAGE: 'Jazyk', LANGUAGE: 'Jazyk',
RETRY: 'Opakovať', RETRY: 'Opakovať',
LOADING: 'Načítanie', LOADING: 'Načítanie',
IS_REQUIRED: '{0} je požadovaných', IS_REQUIRED: '{0} je požadovaná',
SIGN_IN: 'Prihlásiť sa', SIGN_IN: 'Prihlásiť sa',
SIGN_OUT: 'Odhlásiť sa', SIGN_OUT: 'Odhlásiť sa',
USERNAME: 'Užívateľské meno', USERNAME: 'Užívateľské meno',
@@ -276,11 +276,11 @@ const sk: Translation = {
NETWORK_SUBNET: 'Maska podsiete', NETWORK_SUBNET: 'Maska podsiete',
NETWORK_DNS: 'DNS servery', NETWORK_DNS: 'DNS servery',
ADDRESS_OF: '{0} adresa', ADDRESS_OF: '{0} adresa',
ADMINISTRATOR: 'Administrator', ADMINISTRATOR: 'Administrátor',
GUEST: 'Hosť', GUEST: 'Hosť',
NEW: 'Nová', NEW: 'Novú',
NEW_NAME_OF: 'Nový názov {0}', NEW_NAME_OF: 'Nový názov {0}',
ENTITY: 'entita', ENTITY: 'entitu',
MIN: 'min', MIN: 'min',
MAX: 'max', MAX: 'max',
BLOCK_NAVIGATE_1: 'Máte neuložené zmeny', BLOCK_NAVIGATE_1: 'Máte neuložené zmeny',
@@ -350,6 +350,7 @@ const sk: Translation = {
NO_DATA_1: 'Nenašli sa žiadne obľúbené entity EMS. Použite', NO_DATA_1: 'Nenašli sa žiadne obľúbené entity EMS. Použite',
NO_DATA_2: 'modul na ich označenie.', NO_DATA_2: 'modul na ich označenie.',
NO_DATA_3: 'Ak chcete zobraziť všetky dostupné entity, prejdite na', NO_DATA_3: 'Ak chcete zobraziť všetky dostupné entity, prejdite na',
NO_GPIO: 'Nebol nájdený žiadny dostupný GPIO',
THIS_VERSION: 'Táto verzia', THIS_VERSION: 'Táto verzia',
PLATFORM: 'Platforma', PLATFORM: 'Platforma',
RELEASE_TYPE: 'Typ vydania', RELEASE_TYPE: 'Typ vydania',
@@ -359,7 +360,12 @@ const sk: Translation = {
NO_DATA: 'Žiadne dáta', NO_DATA: 'Žiadne dáta',
USER_PROFILE: 'Profil používateľa', USER_PROFILE: 'Profil používateľa',
STORED_VERSIONS: 'Uložené verzie', STORED_VERSIONS: 'Uložené verzie',
ONLINE_HELP: 'online pomoc' ONLINE_HELP: 'online pomoc',
UPGRADE_IMPORTANT_MESSAGES: 'Aktualizovať dôležité správy',
UPGRADE_IMPORTANT_MESSAGES_1: 'Táto aktualizácia vyžaduje reštart základných nastavení. Uistite sa, že najprv stiahnete systémovú zálohu pred pokračovaním, a potom nahrajte tento súbor po instalácii novej verzie.',
UPGRADE_IMPORTANT_MESSAGES_2: 'Aktualizujete sa na novú hlavnú verziu. Uistite sa, že ste prečítali ChangeLog pre akékoľvek dôležité zmeny.',
WARNING_SYSTEM_BACKUP: 'Toto vytvorí zálohu všetkých vašich celých systémových konfigurácií a nastavení. Všetky hesla budú čitateľné v zálohovom súbore. Buďte opatrní pri zdieľaní! Chcete pokračovať?'
}; };
export default sk; export default sk;

View File

@@ -350,6 +350,7 @@ const sv: Translation = {
NO_DATA_1: 'Inga favorit EMS enheter hittade än. Använd', NO_DATA_1: 'Inga favorit EMS enheter hittade än. Använd',
NO_DATA_2: 'modul för att markera dem.', NO_DATA_2: 'modul för att markera dem.',
NO_DATA_3: 'För att se alla tillgängliga enheter, gå till', NO_DATA_3: 'För att se alla tillgängliga enheter, gå till',
NO_GPIO: 'Inga tillgängliga GPIO-portar hittades',
THIS_VERSION: 'Denna version', THIS_VERSION: 'Denna version',
PLATFORM: 'Plattform', PLATFORM: 'Plattform',
RELEASE_TYPE: 'Utgivelsestyp', RELEASE_TYPE: 'Utgivelsestyp',
@@ -359,7 +360,12 @@ const sv: Translation = {
NO_DATA: 'Ingen data', NO_DATA: 'Ingen data',
USER_PROFILE: 'Användarprofil', USER_PROFILE: 'Användarprofil',
STORED_VERSIONS: 'Lagrad versioner', STORED_VERSIONS: 'Lagrad versioner',
ONLINE_HELP: 'online hjälp' ONLINE_HELP: 'online hjälp',
UPGRADE_IMPORTANT_MESSAGES: 'Uppdatera viktiga meddelanden',
UPGRADE_IMPORTANT_MESSAGES_1: 'Denna uppdatering kräver en fabriksåterställning. Se till att du först laddar ned en System Backup innan du fortsätter, och ladda upp denna fil efter att den nya versionen är installerad.',
UPGRADE_IMPORTANT_MESSAGES_2: 'Du uppdaterar till en ny huvudversion. Se till att du har läst ChangeLog för eventuella brkande ändringar.',
WARNING_SYSTEM_BACKUP: 'Detta kommer att skapa en säkerhetskopia av din fullständiga systemkonfiguration och inställningar. Alla lösenord kommer att vara läsbara i säkerhetskopien. Var försiktig med att dela! Vill du fortsätta?'
}; };
export default sv; export default sv;

View File

@@ -350,6 +350,7 @@ const tr: Translation = {
NO_DATA_1: 'Henüz bir favori EMS varlığı bulunamadı. Kullanın', NO_DATA_1: 'Henüz bir favori EMS varlığı bulunamadı. Kullanın',
NO_DATA_2: 'modülünü kullanın.', NO_DATA_2: 'modülünü kullanın.',
NO_DATA_3: 'Tüm kullanılabilir varlıkları görmek için git', NO_DATA_3: 'Tüm kullanılabilir varlıkları görmek için git',
NO_GPIO: 'Kullanılabilir GPIO bulunamadı',
THIS_VERSION: 'Bu Sürüm', THIS_VERSION: 'Bu Sürüm',
PLATFORM: 'Platforma', PLATFORM: 'Platforma',
RELEASE_TYPE: 'Sürüm Tipi', RELEASE_TYPE: 'Sürüm Tipi',
@@ -359,7 +360,12 @@ const tr: Translation = {
NO_DATA: 'Hiçbir veri yok', NO_DATA: 'Hiçbir veri yok',
USER_PROFILE: 'Kullanıcı Profili', USER_PROFILE: 'Kullanıcı Profili',
STORED_VERSIONS: 'Kaydedilmiş Sürümler', STORED_VERSIONS: 'Kaydedilmiş Sürümler',
ONLINE_HELP: 'online yardım' ONLINE_HELP: 'online yardım',
UPGRADE_IMPORTANT_MESSAGES: 'Önemli Mesajları Güncelle',
UPGRADE_IMPORTANT_MESSAGES_1: 'Bu güncelleme továrnı ayarlarını gerektirir. Yapılandırmanızı ve ayarlarınızı önce yedekleyin ve ardından yeni sürüm yüklendikten sonra yükleyin.',
UPGRADE_IMPORTANT_MESSAGES_2: 'Yeni bir büyük sürüme yükselteceksiniz. Değişiklikleri ChangeLogı okuduğunuzdan emin olun.',
WARNING_SYSTEM_BACKUP: 'Bu, sistem yapılandırmanızı ve ayarlarınızın bir yedeklemesi oluşturacaktır. Tüm şifreler yedekleme dosyasında okunabilir olacaktır. Paylaşırken dikkatli olun! Devam etmek istediğinize emin misiniz?'
}; };
export default tr; export default tr;

View File

@@ -1,6 +1,6 @@
import { useEffect, useRef } from 'react'; import { useEffect, useRef } from 'react';
const DEFAULT_DELAY = 3000; const DEFAULT_DELAY = 5000;
/** /**
* Custom hook for setting up an interval with proper cleanup * Custom hook for setting up an interval with proper cleanup

View File

@@ -1,6 +1,20 @@
import type { InternalRuleItem, ValidateOption } from 'async-validator'; import type {
InternalRuleItem,
ValidateFieldsError,
ValidateOption
} from 'async-validator';
import type Schema from 'async-validator'; import type Schema from 'async-validator';
export class ValidationError extends Error {
readonly fieldErrors: ValidateFieldsError;
constructor(fieldErrors: ValidateFieldsError) {
super('Validation failed');
this.name = 'ValidationError';
this.fieldErrors = fieldErrors;
}
}
export const validate = <T extends object>( export const validate = <T extends object>(
validator: Schema, validator: Schema,
source: Partial<T>, source: Partial<T>,
@@ -8,7 +22,7 @@ export const validate = <T extends object>(
): Promise<T> => ): Promise<T> =>
new Promise((resolve, reject) => { new Promise((resolve, reject) => {
void validator.validate(source, options ?? {}, (errors, fieldErrors) => { void validator.validate(source, options ?? {}, (errors, fieldErrors) => {
errors ? reject(fieldErrors as Error) : resolve(source as T); errors ? reject(new ValidationError(fieldErrors)) : resolve(source as T);
}); });
}); });

View File

@@ -15,7 +15,7 @@ const REPEAT_CHAR = '=';
const REPEAT_COUNT = 50; const REPEAT_COUNT = 50;
const DEFAULT_OUT_DIR = 'dist'; const DEFAULT_OUT_DIR = 'dist';
const ES_TARGET = 'es2020'; const ES_TARGET = 'es2020';
const CHUNK_SIZE_WARNING_LIMIT = 512; const CHUNK_SIZE_WARNING_LIMIT = 1024;
const ASSETS_INLINE_LIMIT = 4096; const ASSETS_INLINE_LIMIT = 4096;
// Common resolve aliases // Common resolve aliases
@@ -130,41 +130,10 @@ const createBasePlugins = (
return plugins; return plugins;
}; };
// Manual chunk splitting strategy const manualChunks = (id: string): string | undefined => {
const createManualChunks = (detailed = false) => { if (id.includes('node_modules')) return 'vendor';
return (id: string): string | undefined => {
if (id.includes('node_modules')) {
if (id.includes('preact')) return '@preact';
if (detailed) {
if (id.includes('react-router')) return '@react-router';
if (id.includes('@mui/material')) return '@mui-material';
if (id.includes('@mui/icons-material')) return '@mui-icons';
if (id.includes('alova')) return '@alova';
if (id.includes('typesafe-i18n')) return '@i18n';
if (id.includes('react-toastify')) return '@toastify';
if (id.includes('@table-library')) return '@table-library';
if (id.includes('uuid')) return '@uuid';
if (id.includes('axios') || id.includes('fetch')) return '@http';
if (id.includes('lodash') || id.includes('ramda')) return '@utils';
}
return 'vendor';
}
if (detailed) {
// Group circularly dependent modules together to avoid circular chunk warnings
// components, app, and utils are tightly coupled, so combine them
if (
id.includes('components/') ||
id.includes('app/') ||
id.includes('utils/')
) {
return 'app';
}
// Keep api separate as it's typically more independent
if (id.includes('api/')) return 'api';
}
return undefined; return undefined;
}; };
};
// Common build base configuration // Common build base configuration
const createBaseBuildConfig = () => ({ const createBaseBuildConfig = () => ({
@@ -290,7 +259,7 @@ export default defineConfig(
moduleSideEffects: false moduleSideEffects: false
}, },
output: { output: {
manualChunks: createManualChunks(false) manualChunks
} }
} }
} }
@@ -330,7 +299,7 @@ export default defineConfig(
chunkFileNames: 'assets/[name]-[hash].js', chunkFileNames: 'assets/[name]-[hash].js',
entryFileNames: 'assets/[name]-[hash].js', entryFileNames: 'assets/[name]-[hash].js',
assetFileNames: 'assets/[name]-[hash].[ext]', assetFileNames: 'assets/[name]-[hash].[ext]',
manualChunks: createManualChunks(true), manualChunks,
sourcemap: false sourcemap: false
} }
} }

View File

@@ -49,10 +49,6 @@ the LICENSE file.
#define EMC_CLIENTID_LENGTH 23 + 1 #define EMC_CLIENTID_LENGTH 23 + 1
#endif #endif
#ifdef EMSESP_MQTT_STACKSIZE
#define EMC_TASK_STACK_SIZE EMSESP_MQTT_STACKSIZE
#endif
#ifndef EMC_TASK_STACK_SIZE #ifndef EMC_TASK_STACK_SIZE
#define EMC_TASK_STACK_SIZE 5120 #define EMC_TASK_STACK_SIZE 5120
#endif #endif
@@ -77,3 +73,7 @@ the LICENSE file.
#define EMC_SIZE_POOL_ELEMENTS 128 #define EMC_SIZE_POOL_ELEMENTS 128
#endif #endif
#endif #endif
#ifndef TASMOTA_SDK
#define EMC_CLIENT_SECURE
#endif

View File

@@ -62,19 +62,14 @@ MqttClient::MqttClient(espMqttClientTypes::UseInternalTask useInternalTask, uint
_xSemaphore = xSemaphoreCreateMutex(); _xSemaphore = xSemaphoreCreateMutex();
EMC_SEMAPHORE_GIVE(); // release before first use EMC_SEMAPHORE_GIVE(); // release before first use
if (_useInternalTask == espMqttClientTypes::UseInternalTask::YES) { if (_useInternalTask == espMqttClientTypes::UseInternalTask::YES) {
if (core > 1) {
xTaskCreate((TaskFunction_t)_loop, "mqttclient", EMC_TASK_STACK_SIZE, this, priority, &_taskHandle);
} else {
xTaskCreatePinnedToCore((TaskFunction_t)_loop, "mqttclient", EMC_TASK_STACK_SIZE, this, priority, &_taskHandle, core); xTaskCreatePinnedToCore((TaskFunction_t)_loop, "mqttclient", EMC_TASK_STACK_SIZE, this, priority, &_taskHandle, core);
} }
}
#else #else
(void) useInternalTask; (void) useInternalTask;
(void) priority; (void) priority;
(void) core; (void) core;
#endif #endif
_clientId = _generatedClientId; _clientId = _generatedClientId;
_core = core;
} }
MqttClient::~MqttClient() { MqttClient::~MqttClient() {

View File

@@ -69,16 +69,6 @@ class MqttClient {
const char* getClientId() const; const char* getClientId() const;
size_t queueSize(); // No const because of mutex size_t queueSize(); // No const because of mutex
void loop(); void loop();
uint32_t stack() {
#ifndef EMSESP_STANDALONE
return uxTaskGetStackHighWaterMark(_taskHandle);
#else
return 0;
#endif
}
uint8_t core() {
return _core;
}
protected: protected:
explicit MqttClient(espMqttClientTypes::UseInternalTask useInternalTask, uint8_t priority = 1, uint8_t core = 1); explicit MqttClient(espMqttClientTypes::UseInternalTask useInternalTask, uint8_t priority = 1, uint8_t core = 1);
@@ -108,7 +98,6 @@ class MqttClient {
uint8_t _willQos; uint8_t _willQos;
bool _willRetain; bool _willRetain;
uint32_t _timeout; uint32_t _timeout;
uint8_t _core;
// state is protected to allow state changes by the transport system, defined in child classes // state is protected to allow state changes by the transport system, defined in child classes
// eg. to allow AsyncTCP // eg. to allow AsyncTCP

View File

@@ -6,31 +6,28 @@ For a copy, see <https://opensource.org/licenses/MIT> or
the LICENSE file. the LICENSE file.
*/ */
#ifndef NO_TLS_SUPPORT #if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
#include "ClientSecureSync.h" #include "ClientSecureSync.h"
#include <lwip/sockets.h> #include <lwip/sockets.h> // socket options
#include "../Config.h"
namespace espMqttClientInternals { namespace espMqttClientInternals {
ClientSecureSync::ClientSecureSync() ClientSecureSync::ClientSecureSync()
: client() { : client() {
client.setClient(&basic_client, true); // empty
client.setBufferSizes(EMC_RX_BUFFER_SIZE, EMC_TX_BUFFER_SIZE);
client.setSessionTimeout(120); // Set the timeout in seconds (>=120 seconds)
}
ClientSecureSync::~ClientSecureSync() {
stop();
} }
bool ClientSecureSync::connect(IPAddress ip, uint16_t port) { bool ClientSecureSync::connect(IPAddress ip, uint16_t port) {
bool ret = client.connect(ip, port); // implicit conversion of return code int --> bool bool ret = client.connect(ip, port); // implicit conversion of return code int --> bool
if (ret) { if (ret) {
#if defined(ARDUINO_ARCH_ESP8266)
client.setNoDelay(true);
#elif defined(ARDUINO_ARCH_ESP32)
// Set TCP option directly to bypass lack of working setNoDelay for WiFiClientSecure // Set TCP option directly to bypass lack of working setNoDelay for WiFiClientSecure
int val = true; int val = true;
basic_client.setSocketOption(IPPROTO_TCP, TCP_NODELAY, &val, sizeof(int)); client.setSocketOption(IPPROTO_TCP, TCP_NODELAY, &val, sizeof(int));
#endif
} }
return ret; return ret;
} }
@@ -38,9 +35,13 @@ bool ClientSecureSync::connect(IPAddress ip, uint16_t port) {
bool ClientSecureSync::connect(const char* host, uint16_t port) { bool ClientSecureSync::connect(const char* host, uint16_t port) {
bool ret = client.connect(host, port); // implicit conversion of return code int --> bool bool ret = client.connect(host, port); // implicit conversion of return code int --> bool
if (ret) { if (ret) {
#if defined(ARDUINO_ARCH_ESP8266)
client.setNoDelay(true);
#elif defined(ARDUINO_ARCH_ESP32)
// Set TCP option directly to bypass lack of working setNoDelay for WiFiClientSecure // Set TCP option directly to bypass lack of working setNoDelay for WiFiClientSecure
int val = true; int val = true;
basic_client.setSocketOption(IPPROTO_TCP, TCP_NODELAY, &val, sizeof(int)); client.setSocketOption(IPPROTO_TCP, TCP_NODELAY, &val, sizeof(int));
#endif
} }
return ret; return ret;
} }

View File

@@ -8,11 +8,15 @@ the LICENSE file.
#pragma once #pragma once
#ifndef NO_TLS_SUPPORT #if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
// #include "esp_tls.h" // Added for EMS-ESP
#include "../Config.h"
#if defined(EMC_CLIENT_SECURE)
#include <WiFiClientSecure.h> // includes IPAddress
#else
#include <WiFiClient.h> #include <WiFiClient.h>
#include <ESP_SSLClient.h> #endif
#include "Transport.h" #include "Transport.h"
namespace espMqttClientInternals { namespace espMqttClientInternals {
@@ -20,7 +24,6 @@ namespace espMqttClientInternals {
class ClientSecureSync : public Transport { class ClientSecureSync : public Transport {
public: public:
ClientSecureSync(); ClientSecureSync();
~ClientSecureSync();
bool connect(IPAddress ip, uint16_t port) override; bool connect(IPAddress ip, uint16_t port) override;
bool connect(const char * host, uint16_t port) override; bool connect(const char * host, uint16_t port) override;
size_t write(const uint8_t * buf, size_t size) override; size_t write(const uint8_t * buf, size_t size) override;
@@ -28,9 +31,12 @@ class ClientSecureSync : public Transport {
void stop() override; void stop() override;
bool connected() override; bool connected() override;
bool disconnected() override; bool disconnected() override;
// added for EMS-ESP
WiFiClient basic_client; #if defined(EMC_CLIENT_SECURE)
ESP_SSLClient client; WiFiClientSecure client;
#else
WiFiClient client;
#endif
}; };
} // namespace espMqttClientInternals } // namespace espMqttClientInternals

View File

@@ -8,6 +8,50 @@ the LICENSE file.
#include "espMqttClient.h" #include "espMqttClient.h"
#if defined(ARDUINO_ARCH_ESP8266)
espMqttClient::espMqttClient()
: MqttClientSetup(espMqttClientTypes::UseInternalTask::NO)
, _client() {
_transport = &_client;
}
espMqttClientSecure::espMqttClientSecure()
: MqttClientSetup(espMqttClientTypes::UseInternalTask::NO)
, _client() {
_transport = &_client;
}
espMqttClientSecure & espMqttClientSecure::setInsecure() {
_client.client.setInsecure();
return *this;
}
espMqttClientSecure & espMqttClientSecure::setFingerprint(const uint8_t fingerprint[20]) {
_client.client.setFingerprint(fingerprint);
return *this;
}
espMqttClientSecure & espMqttClientSecure::setTrustAnchors(const X509List * ta) {
_client.client.setTrustAnchors(ta);
return *this;
}
espMqttClientSecure & espMqttClientSecure::setClientRSACert(const X509List * cert, const PrivateKey * sk) {
_client.client.setClientRSACert(cert, sk);
return *this;
}
espMqttClientSecure & espMqttClientSecure::setClientECCert(const X509List * cert, const PrivateKey * sk, unsigned allowed_usages, unsigned cert_issuer_key_type) {
_client.client.setClientECCert(cert, sk, allowed_usages, cert_issuer_key_type);
return *this;
}
espMqttClientSecure & espMqttClientSecure::setCertStore(CertStoreBase * certStore) {
_client.client.setCertStore(certStore);
return *this;
}
#endif
#if defined(ARDUINO_ARCH_ESP32) #if defined(ARDUINO_ARCH_ESP32)
espMqttClient::espMqttClient(espMqttClientTypes::UseInternalTask useInternalTask) espMqttClient::espMqttClient(espMqttClientTypes::UseInternalTask useInternalTask)
: MqttClientSetup(useInternalTask) : MqttClientSetup(useInternalTask)
@@ -34,35 +78,36 @@ espMqttClientSecure::espMqttClientSecure(uint8_t priority, uint8_t core)
} }
espMqttClientSecure & espMqttClientSecure::setInsecure() { espMqttClientSecure & espMqttClientSecure::setInsecure() {
#ifndef NO_TLS_SUPPORT #if defined(EMC_CLIENT_SECURE)
_client.client.setInsecure(); _client.client.setInsecure();
#endif #endif
return *this; return *this;
} }
espMqttClientSecure & espMqttClientSecure::setCACert(const char * rootCA) { espMqttClientSecure & espMqttClientSecure::setCACert(const char * rootCA) {
#ifndef NO_TLS_SUPPORT #if defined(EMC_CLIENT_SECURE)
_client.client.setCACert(rootCA); _client.client.setCACert(rootCA);
#endif #endif
return *this; return *this;
} }
espMqttClientSecure & espMqttClientSecure::setCertificate(const char * clientCa) { espMqttClientSecure & espMqttClientSecure::setCertificate(const char * clientCa) {
#ifndef NO_TLS_SUPPORT #if defined(EMC_CLIENT_SECURE)
_client.client.setCertificate(clientCa); _client.client.setCertificate(clientCa);
#endif #endif
return *this; return *this;
} }
espMqttClientSecure & espMqttClientSecure::setPrivateKey(const char * privateKey) { espMqttClientSecure & espMqttClientSecure::setPrivateKey(const char * privateKey) {
#ifndef NO_TLS_SUPPORT #if defined(EMC_CLIENT_SECURE)
_client.client.setPrivateKey(privateKey); _client.client.setPrivateKey(privateKey);
#endif #endif
return *this; return *this;
} }
espMqttClientSecure & espMqttClientSecure::setPreSharedKey(const char * pskIdent, const char * psKey) { espMqttClientSecure & espMqttClientSecure::setPreSharedKey(const char * pskIdent, const char * psKey) {
#ifndef NO_TLS_SUPPORT #if defined(EMC_CLIENT_SECURE)
_client.client.setPreSharedKey(pskIdent, psKey);
#endif #endif
return *this; return *this;
} }
@@ -75,4 +120,9 @@ espMqttClient::espMqttClient()
, _client() { , _client() {
_transport = &_client; _transport = &_client;
} }
#elif defined(_WIN32) || defined(__APPLE__)
// Windows
espMqttClient::espMqttClient()
: MqttClientSetup(espMqttClientTypes::UseInternalTask::NO) {
}
#endif #endif

View File

@@ -65,16 +65,10 @@ class espMqttClientSecure : public MqttClientSetup<espMqttClientSecure> {
espMqttClientSecure & setPreSharedKey(const char * pskIdent, const char * psKey); espMqttClientSecure & setPreSharedKey(const char * pskIdent, const char * psKey);
protected: protected:
#ifndef NO_TLS_SUPPORT
espMqttClientInternals::ClientSecureSync _client; espMqttClientInternals::ClientSecureSync _client;
#else
espMqttClientInternals::ClientSync _client;
#endif
}; };
#endif #elif defined(__linux__)
#if defined(__linux__)
class espMqttClient : public MqttClientSetup<espMqttClient> { class espMqttClient : public MqttClientSetup<espMqttClient> {
public: public:
espMqttClient(); espMqttClient();
@@ -82,4 +76,10 @@ class espMqttClient : public MqttClientSetup<espMqttClient> {
protected: protected:
espMqttClientInternals::ClientPosix _client; espMqttClientInternals::ClientPosix _client;
}; };
#elif defined(_WIN32) || defined(__APPLE__)
class espMqttClient : public MqttClientSetup<espMqttClient> {
public:
espMqttClient();
};
#endif #endif

View File

@@ -1,22 +0,0 @@
The MIT License (MIT)
Copyright (c) 2015 Marko Živanović
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -1,7 +0,0 @@
# About
This project is MIT-licensed, C++14 implementation of [semantic versioning](http://semver.org) parser and comparator with support for modifying parsed version strings. Semantic versioning 2.0.0 specification is supported out-of-the-box and the code should be flexible-enough to support future revisions or other similar versioning schemes.
Copyright (c) 2015 Marko Zivanovic
Based on https://github.com/zmarko/semver

View File

@@ -1,121 +0,0 @@
/*
The MIT License (MIT)
Copyright (c) 2015 Marko Zivanovic
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <algorithm>
#include <functional>
#include <map>
#include "semver200.h"
using namespace std;
namespace version {
namespace {
// Compare normal version identifiers.
int compare_normal(const Version_data & l, const Version_data & r) {
if (l.major > r.major)
return 1;
if (l.major < r.major)
return -1;
if (l.minor > r.minor)
return 1;
if (l.minor < r.minor)
return -1;
if (l.patch > r.patch)
return 1;
if (l.patch < r.patch)
return -1;
return 0;
}
// Compare alphanumeric prerelease identifiers.
inline int cmp_alnum_prerel_ids(const string & l, const string & r) {
auto cmp = l.compare(r);
if (cmp == 0) {
return cmp;
} else {
return cmp > 0 ? 1 : -1;
}
}
// Compare numeric prerelease identifiers.
inline int cmp_num_prerel_ids(const string & l, const string & r) {
long long li = stoll(l);
long long ri = stoll(r);
if (li == ri)
return 0;
return li > ri ? 1 : -1;
}
using Prerel_type_pair = pair<Id_type, Id_type>;
using Prerel_id_comparator = function<int(const string &, const string &)>;
const map<Prerel_type_pair, Prerel_id_comparator> comparators = {{{Id_type::alnum, Id_type::alnum}, cmp_alnum_prerel_ids},
{{Id_type::alnum, Id_type::num}, [](const string &, const string &) { return 1; }},
{{Id_type::num, Id_type::alnum}, [](const string &, const string &) { return -1; }},
{{Id_type::num, Id_type::num}, cmp_num_prerel_ids}};
// Compare prerelease identifiers based on their types.
inline int compare_prerel_identifiers(const Prerelease_identifier & l, const Prerelease_identifier & r) {
auto cmp = comparators.at({l.second, r.second});
return cmp(l.first, r.first);
}
inline int cmp_rel_prerel(const Prerelease_identifiers & l, const Prerelease_identifiers & r) {
if (l.empty() && !r.empty())
return 1;
if (r.empty() && !l.empty())
return -1;
return 0;
}
} // namespace
int Semver200_comparator::compare(const Version_data & l, const Version_data & r) const {
// Compare normal version components.
int cmp = compare_normal(l, r);
if (cmp != 0)
return cmp;
// Compare if one version is release and the other prerelease - release is always higher.
cmp = cmp_rel_prerel(l.prerelease_ids, r.prerelease_ids);
if (cmp != 0)
return cmp;
// Compare prerelease by looking at each identifier: numeric ones are compared as numbers,
// alphanum as ASCII strings.
auto shorter = min(l.prerelease_ids.size(), r.prerelease_ids.size());
for (size_t i = 0; i < shorter; i++) {
cmp = compare_prerel_identifiers(l.prerelease_ids[i], r.prerelease_ids[i]);
if (cmp != 0)
return cmp;
}
// Prerelease identifiers are the same, to the length of the shorter version string;
// if they are the same length, then versions are equal, otherwise, longer one wins.
if (l.prerelease_ids.size() == r.prerelease_ids.size())
return 0;
return l.prerelease_ids.size() > r.prerelease_ids.size() ? 1 : -1;
}
} // namespace version

View File

@@ -1,187 +0,0 @@
/*
The MIT License (MIT)
Copyright (c) 2015 Marko Zivanovic
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <functional>
#include <map>
#include "semver200.h"
using namespace std;
namespace version {
namespace {
enum class Parser_state { major, minor, patch, prerelease, build };
using Validator = function<void(const string &, const char)>;
using State_transition_hook = function<void(string &)>;
/// State transition is described by a character that triggers it, a state to transition to and
/// optional hook to be invoked on transition.
using Transition = tuple<const char, Parser_state, State_transition_hook>;
using Transitions = vector<Transition>;
using State = tuple<Transitions, string &, Validator>;
using State_machine = std::map<Parser_state, State>;
// Ranges of characters allowed in prerelease and build identifiers.
const vector<pair<char, char>> allowed_prerel_id_chars = {{'0', '9'}, {'A', 'Z'}, {'a', 'z'}, {'-', '-'}};
inline Transition mkx(const char c, Parser_state p, State_transition_hook pth) {
return make_tuple(c, p, pth);
}
inline void Parse_error(const std::string & s) {
// EMSESP::logger().err("parse error: %s", s.c_str());
}
/// Advance parser state machine by a single step.
/**
Perform single step of parser state machine: if character matches one from transition tables -
trigger transition to next state; otherwise, validate if current token is in legal state
(throw Parse_error if not) and then add character to current token; State transition includes
preparing various vars for next state and invoking state transition hook (if specified) which is
where whole tokens are validated.
*/
inline void process_char(const char c, Parser_state & cstate, Parser_state & pstate, const Transitions & transitions, string & target, Validator validate) {
for (const auto & transition : transitions) {
if (c == get<0>(transition)) {
if (get<2>(transition))
get<2>(transition)(target);
pstate = cstate;
cstate = get<1>(transition);
return;
}
}
validate(target, c);
target.push_back(c);
}
/// Validate normal (major, minor, patch) version components.
inline void normal_version_validator(const string & tgt, const char c) {
if (c < '0' || c > '9')
Parse_error("invalid character encountered: " + string(1, c));
if (tgt.compare(0, 1, "0") == 0)
Parse_error("leading 0 not allowed");
}
/// Validate that prerelease and build version identifiers are comprised of allowed chars only.
inline void prerelease_version_validator(const string &, const char c) {
bool res = false;
for (const auto & r : allowed_prerel_id_chars) {
res |= (c >= r.first && c <= r.second);
}
if (!res)
Parse_error("invalid character encountered: " + string(1, c));
}
inline bool is_identifier_numeric(const string & id) {
return id.find_first_not_of("0123456789") == string::npos;
}
inline bool check_for_leading_0(const string & str) {
return str.length() > 1 && str[0] == '0';
}
/// Validate every individual prerelease identifier, determine it's type and add it to collection.
void prerelease_hook_impl(string & id, Prerelease_identifiers & prerelease) {
if (id.empty())
Parse_error("version identifier cannot be empty");
Id_type t = Id_type::alnum;
if (is_identifier_numeric(id)) {
t = Id_type::num;
if (check_for_leading_0(id)) {
Parse_error("numeric identifiers cannot have leading 0");
}
}
prerelease.push_back(Prerelease_identifier(id, t));
id.clear();
}
/// Validate every individual build identifier and add it to collection.
void build_hook_impl(string & id, Parser_state & pstate, Build_identifiers & build, std::string & prerelease_id, Prerelease_identifiers & prerelease) {
// process last token left from parsing prerelease data
if (pstate == Parser_state::prerelease)
prerelease_hook_impl(prerelease_id, prerelease);
if (id.empty())
Parse_error("version identifier cannot be empty");
build.push_back(id);
id.clear();
}
} // namespace
/// Parse semver 2.0.0-compatible string to Version_data structure.
/**
Version text parser is implemented as a state machine. In each step one successive character from version
string is consumed and is either added to current token or triggers state transition. Hooks can be
injected into state transitions for validation/customization purposes.
*/
Version_data Semver200_parser::parse(const string & s) const {
string major;
string minor;
string patch;
string prerelease_id;
string build_id;
Prerelease_identifiers prerelease;
Build_identifiers build;
Parser_state cstate{Parser_state::major};
Parser_state pstate;
auto prerelease_hook = [&](string & id) { prerelease_hook_impl(id, prerelease); };
auto build_hook = [&](string & id) { build_hook_impl(id, pstate, build, prerelease_id, prerelease); };
// State transition tables
auto major_trans = {mkx('.', Parser_state::minor, {})};
auto minor_trans = {mkx('.', Parser_state::patch, {})};
auto patch_trans = {mkx('-', Parser_state::prerelease, {}), mkx('+', Parser_state::build, {})};
auto prerelease_trans = {// When identifier separator (.) is found, stay in the same state but invoke hook
// in order to process each individual identifier separately.
mkx('.', Parser_state::prerelease, prerelease_hook),
mkx('+', Parser_state::build, {})};
auto build_trans = {// Same stay-in-the-same-state-but-invoke-hook trick from above.
mkx('.', Parser_state::build, build_hook)};
State_machine state_machine = {{Parser_state::major, State{major_trans, major, normal_version_validator}},
{Parser_state::minor, State{minor_trans, minor, normal_version_validator}},
{Parser_state::patch, State{patch_trans, patch, normal_version_validator}},
{Parser_state::prerelease, State{prerelease_trans, prerelease_id, prerelease_version_validator}},
{Parser_state::build, State{build_trans, build_id, prerelease_version_validator}}};
// Main loop.
for (const auto & c : s) {
auto state = state_machine.at(cstate);
process_char(c, cstate, pstate, get<0>(state), get<1>(state), get<2>(state));
}
// Trigger appropriate hooks in order to process last token, because no state transition was
// triggered for it.
if (cstate == Parser_state::prerelease) {
prerelease_hook(prerelease_id);
} else if (cstate == Parser_state::build) {
build_hook(build_id);
}
return Version_data{stoi(major), stoi(minor), stoi(patch), prerelease, build};
}
} // namespace version

View File

@@ -1,53 +0,0 @@
/*
The MIT License (MIT)
Copyright (c) 2015 Marko Zivanovic
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#pragma once
#include "version.h"
namespace version {
/// Parse string into Version_data structure according to semantic versioning 2.0.0 rules.
struct Semver200_parser {
Version_data parse(const std::string &) const;
};
/// Compare Version_data to another using semantic versioning 2.0.0 rules.
struct Semver200_comparator {
int compare(const Version_data &, const Version_data &) const;
};
/// Concrete version class that binds all semver 2.0.0 functionality together.
class Semver200_version : public Basic_version<Semver200_parser, Semver200_comparator> {
public:
Semver200_version()
: Basic_version{Semver200_parser(), Semver200_comparator()} {
}
Semver200_version(const std::string & v)
: Basic_version{v, Semver200_parser(), Semver200_comparator()} {
}
};
} // namespace version

View File

@@ -1,149 +0,0 @@
/*
The MIT License (MIT)
Copyright (c) 2015 Marko Zivanovic
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#pragma once
#include <string>
#include <vector>
namespace version {
/// Type of prerelease identifier: alphanumeric or numeric.
/**
Type of identifier affects comparison: alphanumeric identifiers are compared as ASCII strings, while
numeric identifiers are compared as numbers.
*/
enum class Id_type {
alnum, ///< Identifier is alphanumerical
num ///< Identifier is numeric
};
/// Container for prerelease identifier value and it's type.
/**
Prerelease version string consist of an optional series of dot-separated identifiers.
These identifiers can be either numerical or alphanumerical.
This structure describes one such identifier.
*/
using Prerelease_identifier = std::pair<std::string, Id_type>;
/// Container for all prerelease identifiers for a given version string.
using Prerelease_identifiers = std::vector<Prerelease_identifier>;
/// Build identifier is arbitrary string with no special meaning with regards to version precedence.
using Build_identifier = std::string;
/// Container for all build identifiers of a given version string.
using Build_identifiers = std::vector<Build_identifier>;
/// Description of version broken into parts, as per semantic versioning specification.
struct Version_data {
Version_data(const int M, const int m, const int p, const Prerelease_identifiers & pr, const Build_identifiers & b)
: major{M}
, minor{m}
, patch{p}
, prerelease_ids{pr}
, build_ids{b} {
}
int major; ///< Major version, change only on incompatible API modifications.
int minor; ///< Minor version, change on backwards-compatible API modifications.
int patch; ///< Patch version, change only on bugfixes.
/// Optional series of prerelease identifiers.
Prerelease_identifiers prerelease_ids;
/// Optional series of build identifiers.
Build_identifiers build_ids;
};
// Forward declaration required for operators' template declarations.
template <typename Parser, typename Comparator>
class Basic_version;
/// Test if left-hand version operand is of lower precedence than the right-hand version.
template <typename Parser, typename Comparator>
bool operator<(const Basic_version<Parser, Comparator> &, const Basic_version<Parser, Comparator> &);
/// Test if left-hand version operand if of equal precedence as the right-hand version.
template <typename Parser, typename Comparator>
bool operator==(const Basic_version<Parser, Comparator> &, const Basic_version<Parser, Comparator> &);
/// Test if left-hand version and right-hand version are of different precedence.
template <typename Parser, typename Comparator>
bool operator!=(const Basic_version<Parser, Comparator> &, const Basic_version<Parser, Comparator> &);
/// Test if left-hand version operand is of higher precedence than the right-hand version.
template <typename Parser, typename Comparator>
bool operator>(const Basic_version<Parser, Comparator> &, const Basic_version<Parser, Comparator> &);
/// Test if left-hand version operand is of higher or equal precedence as the right-hand version.
template <typename Parser, typename Comparator>
bool operator>=(const Basic_version<Parser, Comparator> &, const Basic_version<Parser, Comparator> &);
/// Test if left-hand version operand is of lower or equal precedence as the right-hand version.
template <typename Parser, typename Comparator>
bool operator<=(const Basic_version<Parser, Comparator> &, const Basic_version<Parser, Comparator> &);
/// Base class for various version parsing, precedence ordering and data manipulation schemes.
/**
Basic_version class describes general version object without prescribing parsing,
validation, comparison and modification rules. These rules are implemented by supplied Parser, Comparator
and Modifier objects.
*/
template <typename Parser, typename Comparator>
class Basic_version {
public:
/// Construct Basic_version object using Parser object to parse default ("0.0.0") version string, Comparator for comparison and Modifier for modification.
Basic_version(Parser, Comparator);
/// Construct Basic_version object using Parser to parse supplied version string, Comparator for comparison and Modifier for modification.
Basic_version(const std::string &, Parser, Comparator);
/// Construct Basic_version object using supplied Version_data, Parser, Comparator and Modifier objects.
Basic_version(const Version_data &, Parser, Comparator);
/// Construct Basic_version by copying data from another one.
Basic_version(const Basic_version &);
/// Copy version data from another Basic_version to this one.
Basic_version & operator=(const Basic_version &);
int major() const; ///< Get major version.
int minor() const; ///< Get minor version.
int patch() const; ///< Get patch version.
const std::string prerelease() const; ///< Get prerelease version string.
const std::string build() const; ///< Get build version string.
friend bool operator< <>(const Basic_version &, const Basic_version &);
friend bool operator==<>(const Basic_version &, const Basic_version &);
private:
Parser parser_;
Comparator comparator_;
Version_data ver_;
};
} // namespace version
#include "version.inl"

View File

@@ -1,132 +0,0 @@
/*
The MIT License (MIT)
Copyright (c) 2015 Marko Zivanovic
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#pragma once
#include "version.h"
namespace version {
namespace {
/// Utility function to splice all vector elements to output stream, using designated separator
/// between elements and function object for getting values from vector elements.
template <typename T, typename F>
std::string & splice(std::string & ss, const std::vector<T> & vec, const std::string & sep, F read) {
if (!vec.empty()) {
for (auto it = vec.cbegin(); it < vec.cend() - 1; ++it) {
ss += read(*it) + sep;
}
ss += read(*vec.crbegin());
}
return ss;
}
} // namespace
template <typename Parser, typename Comparator>
Basic_version<Parser, Comparator>::Basic_version(Parser p, Comparator c)
: parser_(p)
, comparator_(c)
, ver_(parser_.parse("0.0.0")) {
}
template <typename Parser, typename Comparator>
Basic_version<Parser, Comparator>::Basic_version(const std::string & v, Parser p, Comparator c)
: parser_(p)
, comparator_(c)
, ver_(parser_.parse(v)) {
}
template <typename Parser, typename Comparator>
Basic_version<Parser, Comparator>::Basic_version(const Version_data & v, Parser p, Comparator c)
: parser_(p)
, comparator_(c)
, ver_(v) {
}
template <typename Parser, typename Comparator>
Basic_version<Parser, Comparator>::Basic_version(const Basic_version<Parser, Comparator> &) = default;
template <typename Parser, typename Comparator>
Basic_version<Parser, Comparator> & Basic_version<Parser, Comparator>::operator=(const Basic_version<Parser, Comparator> &) = default;
template <typename Parser, typename Comparator>
int Basic_version<Parser, Comparator>::major() const {
return ver_.major;
}
template <typename Parser, typename Comparator>
int Basic_version<Parser, Comparator>::minor() const {
return ver_.minor;
}
template <typename Parser, typename Comparator>
int Basic_version<Parser, Comparator>::patch() const {
return ver_.patch;
}
template <typename Parser, typename Comparator>
const std::string Basic_version<Parser, Comparator>::prerelease() const {
std::string ss;
return splice(ss, ver_.prerelease_ids, ".", [](const Prerelease_identifier & id) { return id.first; });
}
template <typename Parser, typename Comparator>
const std::string Basic_version<Parser, Comparator>::build() const {
std::string ss;
return splice(ss, ver_.build_ids, ".", [](const std::string & id) { return id; });
}
template <typename Parser, typename Comparator>
bool operator<(const Basic_version<Parser, Comparator> & l, const Basic_version<Parser, Comparator> & r) {
return l.comparator_.compare(l.ver_, r.ver_) == -1;
}
template <typename Parser, typename Comparator>
bool operator==(const Basic_version<Parser, Comparator> & l, const Basic_version<Parser, Comparator> & r) {
return l.comparator_.compare(l.ver_, r.ver_) == 0;
}
template <typename Parser, typename Comparator>
inline bool operator!=(const Basic_version<Parser, Comparator> & l, const Basic_version<Parser, Comparator> & r) {
return !(l == r);
}
template <typename Parser, typename Comparator>
inline bool operator>(const Basic_version<Parser, Comparator> & l, const Basic_version<Parser, Comparator> & r) {
return r < l;
}
template <typename Parser, typename Comparator>
inline bool operator>=(const Basic_version<Parser, Comparator> & l, const Basic_version<Parser, Comparator> & r) {
return !(l < r);
}
template <typename Parser, typename Comparator>
inline bool operator<=(const Basic_version<Parser, Comparator> & l, const Basic_version<Parser, Comparator> & r) {
return !(l > r);
}
} // namespace version

View File

@@ -1,5 +1,5 @@
// ArduinoJson - https://arduinojson.org // ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON // Copyright © 2014-2026, Benoit BLANCHON
// MIT License // MIT License
#pragma once #pragma once
@@ -239,11 +239,11 @@
#define ARDUINOJSON_BIN2ALPHA_1111() P #define ARDUINOJSON_BIN2ALPHA_1111() P
#define ARDUINOJSON_BIN2ALPHA_(A, B, C, D) ARDUINOJSON_BIN2ALPHA_##A##B##C##D() #define ARDUINOJSON_BIN2ALPHA_(A, B, C, D) ARDUINOJSON_BIN2ALPHA_##A##B##C##D()
#define ARDUINOJSON_BIN2ALPHA(A, B, C, D) ARDUINOJSON_BIN2ALPHA_(A, B, C, D) #define ARDUINOJSON_BIN2ALPHA(A, B, C, D) ARDUINOJSON_BIN2ALPHA_(A, B, C, D)
#define ARDUINOJSON_VERSION "7.4.2" #define ARDUINOJSON_VERSION "7.4.3"
#define ARDUINOJSON_VERSION_MAJOR 7 #define ARDUINOJSON_VERSION_MAJOR 7
#define ARDUINOJSON_VERSION_MINOR 4 #define ARDUINOJSON_VERSION_MINOR 4
#define ARDUINOJSON_VERSION_REVISION 2 #define ARDUINOJSON_VERSION_REVISION 3
#define ARDUINOJSON_VERSION_MACRO V742 #define ARDUINOJSON_VERSION_MACRO V743
#ifndef ARDUINOJSON_VERSION_NAMESPACE #ifndef ARDUINOJSON_VERSION_NAMESPACE
#define ARDUINOJSON_VERSION_NAMESPACE \ #define ARDUINOJSON_VERSION_NAMESPACE \
ARDUINOJSON_CONCAT5(ARDUINOJSON_VERSION_MACRO, \ ARDUINOJSON_CONCAT5(ARDUINOJSON_VERSION_MACRO, \
@@ -1541,6 +1541,7 @@ struct FloatTraits<T, 8 /*64bits*/> {
static const mantissa_type mantissa_max = (mantissa_type(1) << mantissa_bits) - 1; static const mantissa_type mantissa_max = (mantissa_type(1) << mantissa_bits) - 1;
using exponent_type = int16_t; using exponent_type = int16_t;
static const exponent_type exponent_max = 308; static const exponent_type exponent_max = 308;
static const size_t binaryPowersOfTen = 9;
static pgm_ptr<T> positiveBinaryPowersOfTen() { static pgm_ptr<T> positiveBinaryPowersOfTen() {
ARDUINOJSON_DEFINE_PROGMEM_ARRAY( // ARDUINOJSON_DEFINE_PROGMEM_ARRAY( //
uint64_t, uint64_t,
@@ -1606,6 +1607,7 @@ struct FloatTraits<T, 4 /*32bits*/> {
static const mantissa_type mantissa_max = (mantissa_type(1) << mantissa_bits) - 1; static const mantissa_type mantissa_max = (mantissa_type(1) << mantissa_bits) - 1;
using exponent_type = int8_t; using exponent_type = int8_t;
static const exponent_type exponent_max = 38; static const exponent_type exponent_max = 38;
static const size_t binaryPowersOfTen = 6;
static pgm_ptr<T> positiveBinaryPowersOfTen() { static pgm_ptr<T> positiveBinaryPowersOfTen() {
ARDUINOJSON_DEFINE_PROGMEM_ARRAY(uint32_t, ARDUINOJSON_DEFINE_PROGMEM_ARRAY(uint32_t,
factors, factors,
@@ -1668,9 +1670,12 @@ template <typename TFloat, typename TExponent>
inline TFloat make_float(TFloat m, TExponent e) { inline TFloat make_float(TFloat m, TExponent e) {
using traits = FloatTraits<TFloat>; using traits = FloatTraits<TFloat>;
auto powersOfTen = e > 0 ? traits::positiveBinaryPowersOfTen() : traits::negativeBinaryPowersOfTen(); auto powersOfTen = e > 0 ? traits::positiveBinaryPowersOfTen() : traits::negativeBinaryPowersOfTen();
auto count = traits::binaryPowersOfTen;
if (e <= 0) if (e <= 0)
e = TExponent(-e); e = TExponent(-e);
for (uint8_t index = 0; e != 0; index++) { for (uint8_t index = 0; e != 0; index++) {
if (index >= count)
return traits::nan();
if (e & 1) if (e & 1)
m *= powersOfTen[index]; m *= powersOfTen[index];
e >>= 1; e >>= 1;
@@ -2422,7 +2427,7 @@ class VariantData {
return; return;
var->removeMember(key, resources); var->removeMember(key, resources);
} }
void reset() { void reset() { // TODO: remove
type_ = VariantType::Null; type_ = VariantType::Null;
} }
void setBoolean(bool value) { void setBoolean(bool value) {
@@ -4214,7 +4219,7 @@ template <typename T, typename Enable = void>
struct Comparer; struct Comparer;
template <typename T> template <typename T>
struct Comparer<T, enable_if_t<IsString<T>::value>> : ComparerBase { struct Comparer<T, enable_if_t<IsString<T>::value>> : ComparerBase {
T rhs; T rhs; // TODO: store adapted string?
explicit Comparer(T value) explicit Comparer(T value)
: rhs(value) { : rhs(value) {
} }
@@ -5298,7 +5303,7 @@ class StringBuilder {
append(*s++); append(*s++);
} }
void append(const char * s, size_t n) { void append(const char * s, size_t n) {
while (n-- > 0) while (n-- > 0) // TODO: memcpy
append(*s++); append(*s++);
} }
void append(char c) { void append(char c) {

View File

@@ -22,10 +22,11 @@
#include "Arduino.h" #include "Arduino.h"
#define EMS_TXMODE_OFF 0 #define EMS_TXMODE_OFF 0
#define EMS_TXMODE_DEFAULT 1 #define EMS_TXMODE_EMS 1
#define EMS_TXMODE_EMSPLUS 2 #define EMS_TXMODE_EMSPLUS 2
#define EMS_TXMODE_HT3 3 #define EMS_TXMODE_HT3 3
#define EMS_TXMODE_HW 4 #define EMS_TXMODE_HW 4
#define EMS_TXMODE_AUTO 5
namespace emsesp { namespace emsesp {

View File

@@ -1,6 +1,6 @@
{ {
"name": "mock-api", "name": "mock-api",
"version": "3.8.0", "version": "3.8.2",
"description": "mock api for EMS-ESP", "description": "mock api for EMS-ESP",
"author": "proddy, emsesp.org", "author": "proddy, emsesp.org",
"license": "MIT", "license": "MIT",
@@ -13,7 +13,7 @@
"@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.23",
"prettier": "^3.8.1" "prettier": "^3.8.3"
}, },
"packageManager": "pnpm@10.32.1+sha512.a706938f0e89ac1456b6563eab4edf1d1faf3368d1191fc5c59790e96dc918e4456ab2e67d613de1043d2e8c81f87303e6b40d4ffeca9df15ef1ad567348f2be" "packageManager": "pnpm@10.33.1+sha512.05ba3c1d5d1c18f68df06470d74055e62d41fc110a0c660db1b2dfb2785327f04cf0f68345d4609bc52089e7fa0343c31593b2f9594e2c5d5da426230acc9820"
} }

View File

@@ -13,7 +13,7 @@ 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.1) version: 6.0.2(prettier@3.8.3)
formidable: formidable:
specifier: ^3.5.4 specifier: ^3.5.4
version: 3.5.4 version: 3.5.4
@@ -21,8 +21,8 @@ importers:
specifier: ^5.0.23 specifier: ^5.0.23
version: 5.0.23 version: 5.0.23
prettier: prettier:
specifier: ^3.8.1 specifier: ^3.8.3
version: 3.8.1 version: 3.8.3
packages: packages:
@@ -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.0.2: brace-expansion@2.1.0:
resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} resolution: {integrity: sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==}
debug@4.4.3: debug@4.4.3:
resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
@@ -145,8 +145,8 @@ packages:
engines: {node: '>=6'} engines: {node: '>=6'}
hasBin: true hasBin: true
lodash-es@4.17.23: lodash-es@4.18.1:
resolution: {integrity: sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==} resolution: {integrity: sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==}
minimatch@9.0.9: minimatch@9.0.9:
resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==} resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==}
@@ -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.1: prettier@3.8.3:
resolution: {integrity: sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==} resolution: {integrity: sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==}
engines: {node: '>=14'} engines: {node: '>=14'}
hasBin: true hasBin: true
@@ -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.1)': '@trivago/prettier-plugin-sort-imports@6.0.2(prettier@3.8.3)':
dependencies: dependencies:
'@babel/generator': 7.29.1 '@babel/generator': 7.29.1
'@babel/parser': 7.29.2 '@babel/parser': 7.29.2
'@babel/traverse': 7.29.0 '@babel/traverse': 7.29.0
'@babel/types': 7.29.0 '@babel/types': 7.29.0
javascript-natural-sort: 0.7.1 javascript-natural-sort: 0.7.1
lodash-es: 4.17.23 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.1 prettier: 3.8.3
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@@ -264,7 +264,7 @@ snapshots:
balanced-match@1.0.2: {} balanced-match@1.0.2: {}
brace-expansion@2.0.2: brace-expansion@2.1.0:
dependencies: dependencies:
balanced-match: 1.0.2 balanced-match: 1.0.2
@@ -291,11 +291,11 @@ snapshots:
jsesc@3.1.0: {} jsesc@3.1.0: {}
lodash-es@4.17.23: {} lodash-es@4.18.1: {}
minimatch@9.0.9: minimatch@9.0.9:
dependencies: dependencies:
brace-expansion: 2.0.2 brace-expansion: 2.1.0
ms@2.1.3: {} ms@2.1.3: {}
@@ -311,6 +311,6 @@ snapshots:
picocolors@1.1.1: {} picocolors@1.1.1: {}
prettier@3.8.1: {} prettier@3.8.3: {}
wrappy@1.0.2: {} wrappy@1.0.2: {}

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.0'; let LATEST_STABLE_VERSION = '3.8.2';
let LATEST_DEV_VERSION = '3.8.1-dev.2'; let LATEST_DEV_VERSION = '3.8.3-dev.2';
// 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
@@ -388,13 +388,34 @@ function custom_support() {
'', '',
"For help and questions please <a target='_blank' href='https://emsesp.org'>contact</a> your installer." "For help and questions please <a target='_blank' href='https://emsesp.org'>contact</a> your installer."
], ],
img_url: 'https://emsesp.org/_media/images/designer.png' img_url: 'https://emsesp.org/media/images/designer.png'
// img_url: 'https://picsum.photos/200/300' // img_url: 'https://picsum.photos/200/300'
} }
}; };
} }
// called by Action endpoint // called by Action endpoint upgradeImportantMessages
function upgradeImportantMessages(version: string) {
// 0 is do nothing
// 1 means 3.9 and factory reset required
// 2 means a major version upgrade
let upgradeImportantMessageType_n = 0;
// see if its a filename with a .bin extension
if (version.endsWith('.bin')) {
upgradeImportantMessageType_n = 1; // 1 means 3.9 and factory reset required
} else if (version.endsWith('.md')) {
upgradeImportantMessageType_n = 0;
} else {
// this is a version string like "3.9.0"
upgradeImportantMessageType_n = 2;
}
console.log('upgradeImportantMessageType: ' + upgradeImportantMessageType_n);
return { upgradeImportantMessageType: upgradeImportantMessageType_n };
}
// called by Action endpoint checkUpgrade
function check_upgrade(version: string) { function check_upgrade(version: string) {
let data = {}; let data = {};
if (version) { if (version) {
@@ -5170,6 +5191,9 @@ router
// set partition // set partition
console.log('setting partition to', content.param); console.log('setting partition to', content.param);
return status(200); return status(200);
} else if (action === 'upgradeImportantMessages') {
// check upgrade important messages
return upgradeImportantMessages(content.param);
} }
} }
return status(404); // cmd not found return status(404); // cmd not found

View File

@@ -20,7 +20,7 @@ my_build_flags =
[platformio] [platformio]
default_envs = s_16M_P ; BBQKees E32V2 default_envs = s_16M_P ; BBQKees E32V2
; default_envs = build_webUI ; build the web interface only ; default_envs = build-webUI ; build the web interface only
; default_envs = s3_16M_P ; BBQKees S3 ; default_envs = s3_16M_P ; BBQKees S3
; default_envs = s_4M ; BBQKees older S32, 4MB no psram ; default_envs = s_4M ; BBQKees older S32, 4MB no psram
; default_envs = s_16M ; BBQKees newer S32 V2, 16MB no psram ; default_envs = s_16M ; BBQKees newer S32 V2, 16MB no psram

Some files were not shown because too many files have changed in this diff Show More