This commit is contained in:
MichaelDvP
2025-10-23 13:32:39 +02:00
94 changed files with 1698 additions and 1100 deletions

View File

@@ -69,7 +69,7 @@ Format: `<type>(<scope>): <subject>`
## Example
```
```text
feat: add hat wobble
^--^ ^------------^
| |
@@ -96,7 +96,7 @@ References:
## Contributor License Agreement (CLA)
```
```text
By making a contribution to this project, I certify that:
(a) The contribution was created in whole or in part by me and I

View File

@@ -35,7 +35,7 @@
[![chat](https://img.shields.io/discord/816637840644505620.svg?style=flat-square&color=blueviolet)](https://discord.gg/3J3GgnzpyT)
[![GitHub stars](https://img.shields.io/github/stars/emsesp/EMS-ESP32.svg?style=social&label=Star)](https://github.com/emsesp/EMS-ESP32/stargazers)
[![GitHub forks](https://img.shields.io/github/forks/emsesp/EMS-ESP32.svg?style=social&label=Fork)](https://github.com/emsesp/EMS-ES32P/network)
[![GitHub forks](https://img.shields.io/github/forks/emsesp/EMS-ESP32.svg?style=social&label=Fork)](https://github.com/emsesp/EMS-ESP32/network)
[![donate](https://img.shields.io/badge/donate-PayPal-blue.svg)](https://www.paypal.com/paypalme/prderbyshire/2)
**EMS-ESP** is an open-source firmware for the Espressif ESP32 microcontroller to communicate with **EMS** (Energy Management System) compatible equipment from manufacturers such as Bosch, Buderus, Nefit, Junkers, Worcester, Sieger, elm.leblanc and iVT.

View File

@@ -33,6 +33,7 @@
"src/core/modbus_entity_parameters.hpp",
"sdkconfig.*",
"managed_components/**",
"pnpm-*.yaml"
"pnpm-*.yaml",
"vite.config.ts"
]
}

View File

@@ -421,8 +421,8 @@
| dhw.comfoff | comfort switch off | uint8 (>=15<=65) | C | true | DHW | 18 | 1 | 1 |
| dhw.ecooff | eco switch off | uint8 (>=15<=65) | C | true | DHW | 19 | 1 | 1 |
| dhw.ecoplusoff | eco+ switch off | uint8 (>=48<=63) | C | true | DHW | 20 | 1 | 1 |
| dhw.comfdiff | comfort diff | uint8 (>=6<=12) | K | true | DHW | 21 | 1 | 1 |
| dhw.ecodiff | eco diff | uint8 (>=6<=12) | K | true | DHW | 22 | 1 | 1 |
| dhw.comfdiff | comfort diff | uint8 (>=4<=12) | K | true | DHW | 21 | 1 | 1 |
| dhw.ecodiff | eco diff | uint8 (>=4<=12) | K | true | DHW | 22 | 1 | 1 |
| dhw.ecoplusdiff | eco+ diff | uint8 (>=6<=12) | K | true | DHW | 23 | 1 | 1 |
| dhw.comfstop | comfort stop temp | uint8 (>=0<=254) | C | true | DHW | 24 | 1 | 1 |
| dhw.ecostop | eco stop temp | uint8 (>=0<=254) | C | true | DHW | 25 | 1 | 1 |
@@ -1704,8 +1704,8 @@
| dhw.comfoff | comfort switch off | uint8 (>=15<=65) | C | true | DHW | 18 | 1 | 1 |
| dhw.ecooff | eco switch off | uint8 (>=15<=65) | C | true | DHW | 19 | 1 | 1 |
| dhw.ecoplusoff | eco+ switch off | uint8 (>=48<=63) | C | true | DHW | 20 | 1 | 1 |
| dhw.comfdiff | comfort diff | uint8 (>=6<=12) | K | true | DHW | 21 | 1 | 1 |
| dhw.ecodiff | eco diff | uint8 (>=6<=12) | K | true | DHW | 22 | 1 | 1 |
| dhw.comfdiff | comfort diff | uint8 (>=4<=12) | K | true | DHW | 21 | 1 | 1 |
| dhw.ecodiff | eco diff | uint8 (>=4<=12) | K | true | DHW | 22 | 1 | 1 |
| dhw.ecoplusdiff | eco+ diff | uint8 (>=6<=12) | K | true | DHW | 23 | 1 | 1 |
| dhw.comfstop | comfort stop temp | uint8 (>=0<=254) | C | true | DHW | 24 | 1 | 1 |
| dhw.ecostop | eco stop temp | uint8 (>=0<=254) | C | true | DHW | 25 | 1 | 1 |
@@ -2273,8 +2273,8 @@
| dhw.comfoff | comfort switch off | uint8 (>=15<=65) | C | true | DHW | 18 | 1 | 1 |
| dhw.ecooff | eco switch off | uint8 (>=15<=65) | C | true | DHW | 19 | 1 | 1 |
| dhw.ecoplusoff | eco+ switch off | uint8 (>=48<=63) | C | true | DHW | 20 | 1 | 1 |
| dhw.comfdiff | comfort diff | uint8 (>=6<=12) | K | true | DHW | 21 | 1 | 1 |
| dhw.ecodiff | eco diff | uint8 (>=6<=12) | K | true | DHW | 22 | 1 | 1 |
| dhw.comfdiff | comfort diff | uint8 (>=4<=12) | K | true | DHW | 21 | 1 | 1 |
| dhw.ecodiff | eco diff | uint8 (>=4<=12) | K | true | DHW | 22 | 1 | 1 |
| dhw.ecoplusdiff | eco+ diff | uint8 (>=6<=12) | K | true | DHW | 23 | 1 | 1 |
| dhw.comfstop | comfort stop temp | uint8 (>=0<=254) | C | true | DHW | 24 | 1 | 1 |
| dhw.ecostop | eco stop temp | uint8 (>=0<=254) | C | true | DHW | 25 | 1 | 1 |
@@ -3967,6 +3967,13 @@
| nrgheat | energy heating | uint24 (>=0<=10000000) | kWh | true | DEVICE_DATA | 85 | 2 | 1/100 |
| dhw.nrg | energy | uint24 (>=0<=10000000) | kWh | true | DHW | 0 | 2 | 1/100 |
## Devices of type *connect*
### MX400
| shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor |
|-|-|-|-|-|-|-|-|-|
| datetime | date/time | string | | false | DEVICE_DATA | 0 | 13 | 1 |
| outdoortemp | outside temperature | int16 (>=-3199<=3199) | C | false | DEVICE_DATA | 13 | 1 | 1/10 |
## Devices of type *controller*
### Rego 3000
| shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor |
@@ -4704,18 +4711,18 @@
| hc1.switchtime1 | own1 program switchtime | string | | true | HC | 91 | 8 | 1 |
| hc1.switchtime2 | own2 program switchtime | string | | true | HC | 99 | 8 | 1 |
| dhw.mode | operating mode | enum [off\|on\|auto] | | true | DHW | 0 | 1 | 1 |
| dhw.circmode | circulation pump mode | enum [off\|on\|auto] | | true | DHW | 3 | 1 | 1 |
| dhw.progmode | program | enum [std prog\|own prog] | | true | DHW | 12 | 1 | 1 |
| dhw.circprog | circulation program | enum [std prog\|own prog] | | true | DHW | 13 | 1 | 1 |
| dhw.disinfecting | disinfecting | boolean | | true | DHW | 7 | 1 | 1 |
| dhw.disinfectday | disinfection day | enum [mo\|tu\|we\|th\|fr\|sa\|su\|all] | | true | DHW | 8 | 1 | 1 |
| dhw.disinfecthour | disinfection hour | uint8 (>=0<=23) | | true | DHW | 14 | 1 | 1 |
| dhw.maxtemp | maximum temperature | uint8 (>=0<=254) | C | true | DHW | 15 | 1 | 1 |
| dhw.onetimekey | one time key function | boolean | | true | DHW | 16 | 1 | 1 |
| dhw.switchtime | program switchtime | string | | true | DHW | 17 | 8 | 1 |
| dhw.circswitchtime | circulation program switchtime | string | | true | DHW | 25 | 8 | 1 |
| dhw.holidays | holiday dates | string | | true | DHW | 33 | 13 | 1 |
| dhw.vacations | vacation dates | string | | true | DHW | 46 | 13 | 1 |
| dhw.circmode | circulation pump mode | enum [off\|on\|auto] | | true | DHW | 4 | 1 | 1 |
| dhw.progmode | program | enum [std prog\|own prog] | | true | DHW | 13 | 1 | 1 |
| dhw.circprog | circulation program | enum [std prog\|own prog] | | true | DHW | 14 | 1 | 1 |
| dhw.disinfecting | disinfecting | boolean | | true | DHW | 8 | 1 | 1 |
| dhw.disinfectday | disinfection day | enum [mo\|tu\|we\|th\|fr\|sa\|su\|all] | | true | DHW | 9 | 1 | 1 |
| dhw.disinfecthour | disinfection hour | uint8 (>=0<=23) | | true | DHW | 15 | 1 | 1 |
| dhw.maxtemp | maximum temperature | uint8 (>=0<=254) | C | true | DHW | 16 | 1 | 1 |
| dhw.onetimekey | one time key function | boolean | | true | DHW | 17 | 1 | 1 |
| dhw.switchtime | program switchtime | string | | true | DHW | 18 | 8 | 1 |
| dhw.circswitchtime | circulation program switchtime | string | | true | DHW | 26 | 8 | 1 |
| dhw.holidays | holiday dates | string | | true | DHW | 34 | 13 | 1 |
| dhw.vacations | vacation dates | string | | true | DHW | 47 | 13 | 1 |
### ES79
| shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor |
@@ -4770,18 +4777,18 @@
| hc1.switchtime1 | own1 program switchtime | string | | true | HC | 91 | 8 | 1 |
| hc1.switchtime2 | own2 program switchtime | string | | true | HC | 99 | 8 | 1 |
| dhw.mode | operating mode | enum [off\|on\|auto] | | true | DHW | 0 | 1 | 1 |
| dhw.circmode | circulation pump mode | enum [off\|on\|auto] | | true | DHW | 3 | 1 | 1 |
| dhw.progmode | program | enum [std prog\|own prog] | | true | DHW | 12 | 1 | 1 |
| dhw.circprog | circulation program | enum [std prog\|own prog] | | true | DHW | 13 | 1 | 1 |
| dhw.disinfecting | disinfecting | boolean | | true | DHW | 7 | 1 | 1 |
| dhw.disinfectday | disinfection day | enum [mo\|tu\|we\|th\|fr\|sa\|su\|all] | | true | DHW | 8 | 1 | 1 |
| dhw.disinfecthour | disinfection hour | uint8 (>=0<=23) | | true | DHW | 14 | 1 | 1 |
| dhw.maxtemp | maximum temperature | uint8 (>=60<=80) | C | true | DHW | 15 | 1 | 1 |
| dhw.onetimekey | one time key function | boolean | | true | DHW | 16 | 1 | 1 |
| dhw.switchtime | program switchtime | string | | true | DHW | 17 | 8 | 1 |
| dhw.circswitchtime | circulation program switchtime | string | | true | DHW | 25 | 8 | 1 |
| dhw.holidays | holiday dates | string | | true | DHW | 33 | 13 | 1 |
| dhw.vacations | vacation dates | string | | true | DHW | 46 | 13 | 1 |
| dhw.circmode | circulation pump mode | enum [off\|on\|auto] | | true | DHW | 4 | 1 | 1 |
| dhw.progmode | program | enum [std prog\|own prog] | | true | DHW | 13 | 1 | 1 |
| dhw.circprog | circulation program | enum [std prog\|own prog] | | true | DHW | 14 | 1 | 1 |
| dhw.disinfecting | disinfecting | boolean | | true | DHW | 8 | 1 | 1 |
| dhw.disinfectday | disinfection day | enum [mo\|tu\|we\|th\|fr\|sa\|su\|all] | | true | DHW | 9 | 1 | 1 |
| dhw.disinfecthour | disinfection hour | uint8 (>=0<=23) | | true | DHW | 15 | 1 | 1 |
| dhw.maxtemp | maximum temperature | uint8 (>=60<=80) | C | true | DHW | 16 | 1 | 1 |
| dhw.onetimekey | one time key function | boolean | | true | DHW | 17 | 1 | 1 |
| dhw.switchtime | program switchtime | string | | true | DHW | 18 | 8 | 1 |
| dhw.circswitchtime | circulation program switchtime | string | | true | DHW | 26 | 8 | 1 |
| dhw.holidays | holiday dates | string | | true | DHW | 34 | 13 | 1 |
| dhw.vacations | vacation dates | string | | true | DHW | 47 | 13 | 1 |
### EasyControl, CT200
| shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor |
@@ -4832,7 +4839,7 @@
| hc1.roominflfactor | room influence factor | uint8 (>=0<=100) | % | true | HC | 14 | 1 | 10 |
| hc1.heatingtype | heating type | enum [off\|heatingcurve\|radiator\|convector\|floor] | | true | HC | 19 | 1 | 1 |
| hc1.controlmode | control mode | enum [off\|unmixed\|unmixed IPM\|mixed IPM] | | true | HC | 25 | 1 | 1 |
| dhw.charge | charge | boolean | | true | DHW | 5 | 1 | 1 |
| dhw.charge | charge | boolean | | true | DHW | 6 | 1 | 1 |
### FB100
| shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor |
@@ -4873,7 +4880,7 @@
| hc1.roominflfactor | room influence factor | uint8 (>=0<=100) | % | true | HC | 14 | 1 | 10 |
| hc1.heatingtype | heating type | enum [off\|heatingcurve\|radiator\|convector\|floor] | | true | HC | 19 | 1 | 1 |
| hc1.controlmode | control mode | enum [off\|unmixed\|unmixed IPM\|mixed IPM] | | true | HC | 25 | 1 | 1 |
| dhw.charge | charge | boolean | | true | DHW | 5 | 1 | 1 |
| dhw.charge | charge | boolean | | true | DHW | 6 | 1 | 1 |
### FR10
| shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor |
@@ -4912,7 +4919,7 @@
| hc1.roominflfactor | room influence factor | uint8 (>=0<=100) | % | true | HC | 14 | 1 | 10 |
| hc1.heatingtype | heating type | enum [off\|heatingcurve\|radiator\|convector\|floor] | | true | HC | 19 | 1 | 1 |
| hc1.controlmode | control mode | enum [off\|unmixed\|unmixed IPM\|mixed IPM] | | true | HC | 25 | 1 | 1 |
| dhw.charge | charge | boolean | | true | DHW | 5 | 1 | 1 |
| dhw.charge | charge | boolean | | true | DHW | 6 | 1 | 1 |
### FR100
| shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor |
@@ -4951,7 +4958,7 @@
| hc1.roominflfactor | room influence factor | uint8 (>=0<=100) | % | true | HC | 14 | 1 | 10 |
| hc1.heatingtype | heating type | enum [off\|heatingcurve\|radiator\|convector\|floor] | | true | HC | 19 | 1 | 1 |
| hc1.controlmode | control mode | enum [off\|unmixed\|unmixed IPM\|mixed IPM] | | true | HC | 25 | 1 | 1 |
| dhw.charge | charge | boolean | | true | DHW | 5 | 1 | 1 |
| dhw.charge | charge | boolean | | true | DHW | 6 | 1 | 1 |
### FR110
| shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor |
@@ -4990,7 +4997,7 @@
| hc1.roominflfactor | room influence factor | uint8 (>=0<=100) | % | true | HC | 14 | 1 | 10 |
| hc1.heatingtype | heating type | enum [off\|heatingcurve\|radiator\|convector\|floor] | | true | HC | 19 | 1 | 1 |
| hc1.controlmode | control mode | enum [off\|unmixed\|unmixed IPM\|mixed IPM] | | true | HC | 25 | 1 | 1 |
| dhw.charge | charge | boolean | | true | DHW | 5 | 1 | 1 |
| dhw.charge | charge | boolean | | true | DHW | 6 | 1 | 1 |
### FR120
| shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor |
@@ -5029,7 +5036,7 @@
| hc1.roominflfactor | room influence factor | uint8 (>=0<=100) | % | true | HC | 14 | 1 | 10 |
| hc1.heatingtype | heating type | enum [off\|heatingcurve\|radiator\|convector\|floor] | | true | HC | 19 | 1 | 1 |
| hc1.controlmode | control mode | enum [off\|unmixed\|unmixed IPM\|mixed IPM] | | true | HC | 25 | 1 | 1 |
| dhw.charge | charge | boolean | | true | DHW | 5 | 1 | 1 |
| dhw.charge | charge | boolean | | true | DHW | 6 | 1 | 1 |
### FR50
| shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor |
@@ -5068,7 +5075,7 @@
| hc1.roominflfactor | room influence factor | uint8 (>=0<=100) | % | true | HC | 14 | 1 | 10 |
| hc1.heatingtype | heating type | enum [off\|heatingcurve\|radiator\|convector\|floor] | | true | HC | 19 | 1 | 1 |
| hc1.controlmode | control mode | enum [off\|unmixed\|unmixed IPM\|mixed IPM] | | true | HC | 25 | 1 | 1 |
| dhw.charge | charge | boolean | | true | DHW | 5 | 1 | 1 |
| dhw.charge | charge | boolean | | true | DHW | 6 | 1 | 1 |
### FW100
| shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor |
@@ -5109,7 +5116,7 @@
| hc1.roominflfactor | room influence factor | uint8 (>=0<=100) | % | true | HC | 14 | 1 | 10 |
| hc1.heatingtype | heating type | enum [off\|heatingcurve\|radiator\|convector\|floor] | | true | HC | 19 | 1 | 1 |
| hc1.controlmode | control mode | enum [off\|unmixed\|unmixed IPM\|mixed IPM] | | true | HC | 25 | 1 | 1 |
| dhw.charge | charge | boolean | | true | DHW | 5 | 1 | 1 |
| dhw.charge | charge | boolean | | true | DHW | 6 | 1 | 1 |
### FW120
| shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor |
@@ -5150,7 +5157,7 @@
| hc1.roominflfactor | room influence factor | uint8 (>=0<=100) | % | true | HC | 14 | 1 | 10 |
| hc1.heatingtype | heating type | enum [off\|heatingcurve\|radiator\|convector\|floor] | | true | HC | 19 | 1 | 1 |
| hc1.controlmode | control mode | enum [off\|unmixed\|unmixed IPM\|mixed IPM] | | true | HC | 25 | 1 | 1 |
| dhw.charge | charge | boolean | | true | DHW | 5 | 1 | 1 |
| dhw.charge | charge | boolean | | true | DHW | 6 | 1 | 1 |
### FW200
| shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor |
@@ -5191,7 +5198,7 @@
| hc1.roominflfactor | room influence factor | uint8 (>=0<=100) | % | true | HC | 14 | 1 | 10 |
| hc1.heatingtype | heating type | enum [off\|heatingcurve\|radiator\|convector\|floor] | | true | HC | 19 | 1 | 1 |
| hc1.controlmode | control mode | enum [off\|unmixed\|unmixed IPM\|mixed IPM] | | true | HC | 25 | 1 | 1 |
| dhw.charge | charge | boolean | | true | DHW | 5 | 1 | 1 |
| dhw.charge | charge | boolean | | true | DHW | 6 | 1 | 1 |
### FW500
| shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor |
@@ -5232,7 +5239,7 @@
| hc1.roominflfactor | room influence factor | uint8 (>=0<=100) | % | true | HC | 14 | 1 | 10 |
| hc1.heatingtype | heating type | enum [off\|heatingcurve\|radiator\|convector\|floor] | | true | HC | 19 | 1 | 1 |
| hc1.controlmode | control mode | enum [off\|unmixed\|unmixed IPM\|mixed IPM] | | true | HC | 25 | 1 | 1 |
| dhw.charge | charge | boolean | | true | DHW | 5 | 1 | 1 |
| dhw.charge | charge | boolean | | true | DHW | 6 | 1 | 1 |
### Logamatic TC100, Moduline Easy
| shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor |
@@ -5303,10 +5310,10 @@
| hc1.manualtemp | manual temperature | uint8 (>=0<=127) | C | true | HC | 6 | 1 | 1/2 |
| hc1.offtemp | temperature when mode is off | uint8 (>=0<=127) | C | true | HC | 107 | 1 | 1/2 |
| dhw.mode | operating mode | enum [on\|off\|auto] | | true | DHW | 0 | 1 | 1 |
| dhw.whenmodeoff | when thermostat mode off | boolean | | true | DHW | 59 | 1 | 1 |
| dhw.disinfecting | disinfecting | boolean | | true | DHW | 7 | 1 | 1 |
| dhw.disinfectday | disinfection day | enum [mo\|tu\|we\|th\|fr\|sa\|su\|all] | | true | DHW | 8 | 1 | 1 |
| dhw.disinfecthour | disinfection hour | uint8 (>=0<=23) | | true | DHW | 14 | 1 | 1 |
| dhw.whenmodeoff | when thermostat mode off | boolean | | true | DHW | 60 | 1 | 1 |
| dhw.disinfecting | disinfecting | boolean | | true | DHW | 8 | 1 | 1 |
| dhw.disinfectday | disinfection day | enum [mo\|tu\|we\|th\|fr\|sa\|su\|all] | | true | DHW | 9 | 1 | 1 |
| dhw.disinfecthour | disinfection hour | uint8 (>=0<=23) | | true | DHW | 15 | 1 | 1 |
### RC10
| shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor |
@@ -5487,17 +5494,18 @@
| hc1.solarinfl | solar influence | uint8 (>=-5<=4294967295) | C | true | HC | 54 | 1 | 1 |
| hc1.currsolarinfl | curent solar influence | uint8 (>=0<=25) | C | false | HC | 55 | 1 | 1/10 |
| dhw.mode | operating mode | enum [off\|on\|auto] | | true | DHW | 0 | 1 | 1 |
| dhw.settemp | set temperature | uint8 (>=0<=254) | C | true | DHW | 1 | 1 | 1 |
| dhw.settemplow | set low temperature | uint8 (>=0<=254) | C | true | DHW | 2 | 1 | 1 |
| dhw.circmode | circulation pump mode | enum [off\|on\|auto\|own prog] | | true | DHW | 3 | 1 | 1 |
| dhw.chargeduration | charge duration | uint8 (>=0<=3810) | minutes | true | DHW | 4 | 1 | 15 |
| dhw.charge | charge | boolean | | true | DHW | 5 | 1 | 1 |
| dhw.extra | extra | boolean | | false | DHW | 6 | 1 | 1 |
| dhw.disinfecting | disinfecting | boolean | | true | DHW | 7 | 1 | 1 |
| dhw.disinfectday | disinfection day | enum [mo\|tu\|we\|th\|fr\|sa\|su\|all] | | true | DHW | 8 | 1 | 1 |
| dhw.disinfecttime | disinfection time | uint8 (>=0<=1431) | minutes | true | DHW | 9 | 1 | 15 |
| dhw.dailyheating | daily heating | boolean | | true | DHW | 10 | 1 | 1 |
| dhw.dailyheattime | daily heating time | uint8 (>=0<=1431) | minutes | true | DHW | 11 | 1 | 15 |
| dhw.modetype | mode type | enum [off\|eco\|comfort\|eco+] | | false | DHW | 1 | 1 | 1 |
| dhw.settemp | set temperature | uint8 (>=0<=254) | C | true | DHW | 2 | 1 | 1 |
| dhw.settemplow | set low temperature | uint8 (>=0<=254) | C | true | DHW | 3 | 1 | 1 |
| dhw.circmode | circulation pump mode | enum [off\|on\|auto\|own prog] | | true | DHW | 4 | 1 | 1 |
| dhw.chargeduration | charge duration | uint8 (>=0<=3810) | minutes | true | DHW | 5 | 1 | 15 |
| dhw.charge | charge | boolean | | true | DHW | 6 | 1 | 1 |
| dhw.extra | extra | boolean | | false | DHW | 7 | 1 | 1 |
| dhw.disinfecting | disinfecting | boolean | | true | DHW | 8 | 1 | 1 |
| dhw.disinfectday | disinfection day | enum [mo\|tu\|we\|th\|fr\|sa\|su\|all] | | true | DHW | 9 | 1 | 1 |
| dhw.disinfecttime | disinfection time | uint8 (>=0<=1431) | minutes | true | DHW | 10 | 1 | 15 |
| dhw.dailyheating | daily heating | boolean | | true | DHW | 11 | 1 | 1 |
| dhw.dailyheattime | daily heating time | uint8 (>=0<=1431) | minutes | true | DHW | 12 | 1 | 15 |
### RC20RF
| shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor |
@@ -5621,17 +5629,18 @@
| hc1.solarinfl | solar influence | uint8 (>=-5<=4294967295) | C | true | HC | 54 | 1 | 1 |
| hc1.currsolarinfl | curent solar influence | uint8 (>=0<=25) | C | false | HC | 55 | 1 | 1/10 |
| dhw.mode | operating mode | enum [off\|normal\|comfort\|auto\|own prog] | | true | DHW | 0 | 1 | 1 |
| dhw.settemp | set temperature | uint8 (>=0<=254) | C | true | DHW | 1 | 1 | 1 |
| dhw.settemplow | set low temperature | uint8 (>=0<=254) | C | true | DHW | 2 | 1 | 1 |
| dhw.circmode | circulation pump mode | enum [off\|on\|auto\|own prog] | | true | DHW | 3 | 1 | 1 |
| dhw.chargeduration | charge duration | uint8 (>=0<=3810) | minutes | true | DHW | 4 | 1 | 15 |
| dhw.charge | charge | boolean | | true | DHW | 5 | 1 | 1 |
| dhw.extra | extra | boolean | | false | DHW | 6 | 1 | 1 |
| dhw.disinfecting | disinfecting | boolean | | true | DHW | 7 | 1 | 1 |
| dhw.disinfectday | disinfection day | enum [mo\|tu\|we\|th\|fr\|sa\|su\|all] | | true | DHW | 8 | 1 | 1 |
| dhw.disinfecttime | disinfection time | uint8 (>=0<=1431) | minutes | true | DHW | 9 | 1 | 15 |
| dhw.dailyheating | daily heating | boolean | | true | DHW | 10 | 1 | 1 |
| dhw.dailyheattime | daily heating time | uint8 (>=0<=1431) | minutes | true | DHW | 11 | 1 | 15 |
| dhw.modetype | mode type | enum [off\|eco\|comfort\|eco+] | | false | DHW | 1 | 1 | 1 |
| dhw.settemp | set temperature | uint8 (>=0<=254) | C | true | DHW | 2 | 1 | 1 |
| dhw.settemplow | set low temperature | uint8 (>=0<=254) | C | true | DHW | 3 | 1 | 1 |
| dhw.circmode | circulation pump mode | enum [off\|on\|auto\|own prog] | | true | DHW | 4 | 1 | 1 |
| dhw.chargeduration | charge duration | uint8 (>=0<=3810) | minutes | true | DHW | 5 | 1 | 15 |
| dhw.charge | charge | boolean | | true | DHW | 6 | 1 | 1 |
| dhw.extra | extra | boolean | | false | DHW | 7 | 1 | 1 |
| dhw.disinfecting | disinfecting | boolean | | true | DHW | 8 | 1 | 1 |
| dhw.disinfectday | disinfection day | enum [mo\|tu\|we\|th\|fr\|sa\|su\|all] | | true | DHW | 9 | 1 | 1 |
| dhw.disinfecttime | disinfection time | uint8 (>=0<=1431) | minutes | true | DHW | 10 | 1 | 15 |
| dhw.dailyheating | daily heating | boolean | | true | DHW | 11 | 1 | 1 |
| dhw.dailyheattime | daily heating time | uint8 (>=0<=1431) | minutes | true | DHW | 12 | 1 | 15 |
### RC30
| shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor |
@@ -5689,18 +5698,18 @@
| hc1.switchtime1 | own1 program switchtime | string | | true | HC | 91 | 8 | 1 |
| hc1.switchtime2 | own2 program switchtime | string | | true | HC | 99 | 8 | 1 |
| dhw.mode | operating mode | enum [off\|on\|auto] | | true | DHW | 0 | 1 | 1 |
| dhw.circmode | circulation pump mode | enum [off\|on\|auto] | | true | DHW | 3 | 1 | 1 |
| dhw.progmode | program | enum [std prog\|own prog] | | true | DHW | 12 | 1 | 1 |
| dhw.circprog | circulation program | enum [std prog\|own prog] | | true | DHW | 13 | 1 | 1 |
| dhw.disinfecting | disinfecting | boolean | | true | DHW | 7 | 1 | 1 |
| dhw.disinfectday | disinfection day | enum [mo\|tu\|we\|th\|fr\|sa\|su\|all] | | true | DHW | 8 | 1 | 1 |
| dhw.disinfecthour | disinfection hour | uint8 (>=0<=23) | | true | DHW | 14 | 1 | 1 |
| dhw.maxtemp | maximum temperature | uint8 (>=0<=254) | C | true | DHW | 15 | 1 | 1 |
| dhw.onetimekey | one time key function | boolean | | true | DHW | 16 | 1 | 1 |
| dhw.switchtime | program switchtime | string | | true | DHW | 17 | 8 | 1 |
| dhw.circswitchtime | circulation program switchtime | string | | true | DHW | 25 | 8 | 1 |
| dhw.holidays | holiday dates | string | | true | DHW | 33 | 13 | 1 |
| dhw.vacations | vacation dates | string | | true | DHW | 46 | 13 | 1 |
| dhw.circmode | circulation pump mode | enum [off\|on\|auto] | | true | DHW | 4 | 1 | 1 |
| dhw.progmode | program | enum [std prog\|own prog] | | true | DHW | 13 | 1 | 1 |
| dhw.circprog | circulation program | enum [std prog\|own prog] | | true | DHW | 14 | 1 | 1 |
| dhw.disinfecting | disinfecting | boolean | | true | DHW | 8 | 1 | 1 |
| dhw.disinfectday | disinfection day | enum [mo\|tu\|we\|th\|fr\|sa\|su\|all] | | true | DHW | 9 | 1 | 1 |
| dhw.disinfecthour | disinfection hour | uint8 (>=0<=23) | | true | DHW | 15 | 1 | 1 |
| dhw.maxtemp | maximum temperature | uint8 (>=0<=254) | C | true | DHW | 16 | 1 | 1 |
| dhw.onetimekey | one time key function | boolean | | true | DHW | 17 | 1 | 1 |
| dhw.switchtime | program switchtime | string | | true | DHW | 18 | 8 | 1 |
| dhw.circswitchtime | circulation program switchtime | string | | true | DHW | 26 | 8 | 1 |
| dhw.holidays | holiday dates | string | | true | DHW | 34 | 13 | 1 |
| dhw.vacations | vacation dates | string | | true | DHW | 47 | 13 | 1 |
### RC35
| shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor |
@@ -5755,18 +5764,18 @@
| hc1.switchtime1 | own1 program switchtime | string | | true | HC | 91 | 8 | 1 |
| hc1.switchtime2 | own2 program switchtime | string | | true | HC | 99 | 8 | 1 |
| dhw.mode | operating mode | enum [off\|on\|auto] | | true | DHW | 0 | 1 | 1 |
| dhw.circmode | circulation pump mode | enum [off\|on\|auto] | | true | DHW | 3 | 1 | 1 |
| dhw.progmode | program | enum [std prog\|own prog] | | true | DHW | 12 | 1 | 1 |
| dhw.circprog | circulation program | enum [std prog\|own prog] | | true | DHW | 13 | 1 | 1 |
| dhw.disinfecting | disinfecting | boolean | | true | DHW | 7 | 1 | 1 |
| dhw.disinfectday | disinfection day | enum [mo\|tu\|we\|th\|fr\|sa\|su\|all] | | true | DHW | 8 | 1 | 1 |
| dhw.disinfecthour | disinfection hour | uint8 (>=0<=23) | | true | DHW | 14 | 1 | 1 |
| dhw.maxtemp | maximum temperature | uint8 (>=60<=80) | C | true | DHW | 15 | 1 | 1 |
| dhw.onetimekey | one time key function | boolean | | true | DHW | 16 | 1 | 1 |
| dhw.switchtime | program switchtime | string | | true | DHW | 17 | 8 | 1 |
| dhw.circswitchtime | circulation program switchtime | string | | true | DHW | 25 | 8 | 1 |
| dhw.holidays | holiday dates | string | | true | DHW | 33 | 13 | 1 |
| dhw.vacations | vacation dates | string | | true | DHW | 46 | 13 | 1 |
| dhw.circmode | circulation pump mode | enum [off\|on\|auto] | | true | DHW | 4 | 1 | 1 |
| dhw.progmode | program | enum [std prog\|own prog] | | true | DHW | 13 | 1 | 1 |
| dhw.circprog | circulation program | enum [std prog\|own prog] | | true | DHW | 14 | 1 | 1 |
| dhw.disinfecting | disinfecting | boolean | | true | DHW | 8 | 1 | 1 |
| dhw.disinfectday | disinfection day | enum [mo\|tu\|we\|th\|fr\|sa\|su\|all] | | true | DHW | 9 | 1 | 1 |
| dhw.disinfecthour | disinfection hour | uint8 (>=0<=23) | | true | DHW | 15 | 1 | 1 |
| dhw.maxtemp | maximum temperature | uint8 (>=60<=80) | C | true | DHW | 16 | 1 | 1 |
| dhw.onetimekey | one time key function | boolean | | true | DHW | 17 | 1 | 1 |
| dhw.switchtime | program switchtime | string | | true | DHW | 18 | 8 | 1 |
| dhw.circswitchtime | circulation program switchtime | string | | true | DHW | 26 | 8 | 1 |
| dhw.holidays | holiday dates | string | | true | DHW | 34 | 13 | 1 |
| dhw.vacations | vacation dates | string | | true | DHW | 47 | 13 | 1 |
### RFM20 Remote
| shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor |
@@ -5871,17 +5880,18 @@
| hc1.solarinfl | solar influence | uint8 (>=-5<=4294967295) | C | true | HC | 54 | 1 | 1 |
| hc1.currsolarinfl | curent solar influence | uint8 (>=0<=25) | C | false | HC | 55 | 1 | 1/10 |
| dhw.mode | operating mode | enum [normal\|comfort\|eco+] | | true | DHW | 0 | 1 | 1 |
| dhw.settemp | set temperature | uint8 (>=0<=254) | C | true | DHW | 1 | 1 | 1 |
| dhw.settemplow | set low temperature | uint8 (>=0<=254) | C | true | DHW | 2 | 1 | 1 |
| dhw.circmode | circulation pump mode | enum [off\|on\|auto\|own prog] | | true | DHW | 3 | 1 | 1 |
| dhw.chargeduration | charge duration | uint8 (>=0<=3810) | minutes | true | DHW | 4 | 1 | 15 |
| dhw.charge | charge | boolean | | true | DHW | 5 | 1 | 1 |
| dhw.extra | extra | boolean | | false | DHW | 6 | 1 | 1 |
| dhw.disinfecting | disinfecting | boolean | | true | DHW | 7 | 1 | 1 |
| dhw.disinfectday | disinfection day | enum [mo\|tu\|we\|th\|fr\|sa\|su\|all] | | true | DHW | 8 | 1 | 1 |
| dhw.disinfecttime | disinfection time | uint8 (>=0<=1431) | minutes | true | DHW | 9 | 1 | 15 |
| dhw.dailyheating | daily heating | boolean | | true | DHW | 10 | 1 | 1 |
| dhw.dailyheattime | daily heating time | uint8 (>=0<=1431) | minutes | true | DHW | 11 | 1 | 15 |
| dhw.modetype | mode type | enum [off\|eco\|comfort\|eco+] | | false | DHW | 1 | 1 | 1 |
| dhw.settemp | set temperature | uint8 (>=0<=254) | C | true | DHW | 2 | 1 | 1 |
| dhw.settemplow | set low temperature | uint8 (>=0<=254) | C | true | DHW | 3 | 1 | 1 |
| dhw.circmode | circulation pump mode | enum [off\|on\|auto\|own prog] | | true | DHW | 4 | 1 | 1 |
| dhw.chargeduration | charge duration | uint8 (>=0<=3810) | minutes | true | DHW | 5 | 1 | 15 |
| dhw.charge | charge | boolean | | true | DHW | 6 | 1 | 1 |
| dhw.extra | extra | boolean | | false | DHW | 7 | 1 | 1 |
| dhw.disinfecting | disinfecting | boolean | | true | DHW | 8 | 1 | 1 |
| dhw.disinfectday | disinfection day | enum [mo\|tu\|we\|th\|fr\|sa\|su\|all] | | true | DHW | 9 | 1 | 1 |
| dhw.disinfecttime | disinfection time | uint8 (>=0<=1431) | minutes | true | DHW | 10 | 1 | 15 |
| dhw.dailyheating | daily heating | boolean | | true | DHW | 11 | 1 | 1 |
| dhw.dailyheattime | daily heating time | uint8 (>=0<=1431) | minutes | true | DHW | 12 | 1 | 15 |
### Rego 3000, UI800, Logamatic BC400
| shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor |
@@ -5965,17 +5975,18 @@
| hc1.solarinfl | solar influence | uint8 (>=-5<=4294967295) | C | true | HC | 54 | 1 | 1 |
| hc1.currsolarinfl | curent solar influence | uint8 (>=0<=25) | C | false | HC | 55 | 1 | 1/10 |
| dhw.mode | operating mode | enum [off\|eco+\|eco\|comfort\|auto] | | true | DHW | 0 | 1 | 1 |
| dhw.settemp | set temperature | uint8 (>=0<=254) | C | true | DHW | 1 | 1 | 1 |
| dhw.settemplow | set low temperature | uint8 (>=0<=254) | C | true | DHW | 2 | 1 | 1 |
| dhw.circmode | circulation pump mode | enum [off\|on\|auto\|own prog] | | true | DHW | 3 | 1 | 1 |
| dhw.chargeduration | charge duration | uint8 (>=0<=3810) | minutes | true | DHW | 4 | 1 | 15 |
| dhw.charge | charge | boolean | | true | DHW | 5 | 1 | 1 |
| dhw.extra | extra | boolean | | false | DHW | 6 | 1 | 1 |
| dhw.disinfecting | disinfecting | boolean | | true | DHW | 7 | 1 | 1 |
| dhw.disinfectday | disinfection day | enum [mo\|tu\|we\|th\|fr\|sa\|su\|all] | | true | DHW | 8 | 1 | 1 |
| dhw.disinfecttime | disinfection time | uint8 (>=0<=1431) | minutes | true | DHW | 9 | 1 | 15 |
| dhw.dailyheating | daily heating | boolean | | true | DHW | 10 | 1 | 1 |
| dhw.dailyheattime | daily heating time | uint8 (>=0<=1431) | minutes | true | DHW | 11 | 1 | 15 |
| dhw.modetype | mode type | enum [off\|eco\|comfort\|eco+] | | false | DHW | 1 | 1 | 1 |
| dhw.settemp | set temperature | uint8 (>=0<=254) | C | true | DHW | 2 | 1 | 1 |
| dhw.settemplow | set low temperature | uint8 (>=0<=254) | C | true | DHW | 3 | 1 | 1 |
| dhw.circmode | circulation pump mode | enum [off\|on\|auto\|own prog] | | true | DHW | 4 | 1 | 1 |
| dhw.chargeduration | charge duration | uint8 (>=0<=3810) | minutes | true | DHW | 5 | 1 | 15 |
| dhw.charge | charge | boolean | | true | DHW | 6 | 1 | 1 |
| dhw.extra | extra | boolean | | false | DHW | 7 | 1 | 1 |
| dhw.disinfecting | disinfecting | boolean | | true | DHW | 8 | 1 | 1 |
| dhw.disinfectday | disinfection day | enum [mo\|tu\|we\|th\|fr\|sa\|su\|all] | | true | DHW | 9 | 1 | 1 |
| dhw.disinfecttime | disinfection time | uint8 (>=0<=1431) | minutes | true | DHW | 10 | 1 | 15 |
| dhw.dailyheating | daily heating | boolean | | true | DHW | 11 | 1 | 1 |
| dhw.dailyheattime | daily heating time | uint8 (>=0<=1431) | minutes | true | DHW | 12 | 1 | 15 |
### TR120RF, CR20RF
| shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor |
@@ -6069,17 +6080,18 @@
| hc1.solarinfl | solar influence | uint8 (>=-5<=4294967295) | C | true | HC | 54 | 1 | 1 |
| hc1.currsolarinfl | curent solar influence | uint8 (>=0<=25) | C | false | HC | 55 | 1 | 1/10 |
| dhw.mode | operating mode | enum [off\|eco+\|eco\|comfort\|auto] | | true | DHW | 0 | 1 | 1 |
| dhw.settemp | set temperature | uint8 (>=0<=254) | C | true | DHW | 1 | 1 | 1 |
| dhw.settemplow | set low temperature | uint8 (>=0<=254) | C | true | DHW | 2 | 1 | 1 |
| dhw.circmode | circulation pump mode | enum [off\|on\|auto\|own prog] | | true | DHW | 3 | 1 | 1 |
| dhw.chargeduration | charge duration | uint8 (>=0<=3810) | minutes | true | DHW | 4 | 1 | 15 |
| dhw.charge | charge | boolean | | true | DHW | 5 | 1 | 1 |
| dhw.extra | extra | boolean | | false | DHW | 6 | 1 | 1 |
| dhw.disinfecting | disinfecting | boolean | | true | DHW | 7 | 1 | 1 |
| dhw.disinfectday | disinfection day | enum [mo\|tu\|we\|th\|fr\|sa\|su\|all] | | true | DHW | 8 | 1 | 1 |
| dhw.disinfecttime | disinfection time | uint8 (>=0<=1431) | minutes | true | DHW | 9 | 1 | 15 |
| dhw.dailyheating | daily heating | boolean | | true | DHW | 10 | 1 | 1 |
| dhw.dailyheattime | daily heating time | uint8 (>=0<=1431) | minutes | true | DHW | 11 | 1 | 15 |
| dhw.modetype | mode type | enum [off\|eco\|comfort\|eco+] | | false | DHW | 1 | 1 | 1 |
| dhw.settemp | set temperature | uint8 (>=0<=254) | C | true | DHW | 2 | 1 | 1 |
| dhw.settemplow | set low temperature | uint8 (>=0<=254) | C | true | DHW | 3 | 1 | 1 |
| dhw.circmode | circulation pump mode | enum [off\|on\|auto\|own prog] | | true | DHW | 4 | 1 | 1 |
| dhw.chargeduration | charge duration | uint8 (>=0<=3810) | minutes | true | DHW | 5 | 1 | 15 |
| dhw.charge | charge | boolean | | true | DHW | 6 | 1 | 1 |
| dhw.extra | extra | boolean | | false | DHW | 7 | 1 | 1 |
| dhw.disinfecting | disinfecting | boolean | | true | DHW | 8 | 1 | 1 |
| dhw.disinfectday | disinfection day | enum [mo\|tu\|we\|th\|fr\|sa\|su\|all] | | true | DHW | 9 | 1 | 1 |
| dhw.disinfecttime | disinfection time | uint8 (>=0<=1431) | minutes | true | DHW | 10 | 1 | 15 |
| dhw.dailyheating | daily heating | boolean | | true | DHW | 11 | 1 | 1 |
| dhw.dailyheattime | daily heating time | uint8 (>=0<=1431) | minutes | true | DHW | 12 | 1 | 15 |
## Devices of type *ventilation*
### HRV176, HRV156, 5000c, MV200

View File

@@ -160,8 +160,8 @@ device name,device type,product id,shortname,fullname,type [options...] \| (min/
"CS5800i, CS6800i, WLW176i, WLW186i",boiler,8,dhw.comfoff,comfort switch off,uint8 (>=15<=65),C,true,number.boiler_dhw_comfort_switch_off,number.boiler_dhw_comfoff,5,9,1,18,1
"CS5800i, CS6800i, WLW176i, WLW186i",boiler,8,dhw.ecooff,eco switch off,uint8 (>=15<=65),C,true,number.boiler_dhw_eco_switch_off,number.boiler_dhw_ecooff,5,9,1,19,1
"CS5800i, CS6800i, WLW176i, WLW186i",boiler,8,dhw.ecoplusoff,eco+ switch off,uint8 (>=48<=63),C,true,number.boiler_dhw_eco+_switch_off,number.boiler_dhw_ecoplusoff,5,9,1,20,1
"CS5800i, CS6800i, WLW176i, WLW186i",boiler,8,dhw.comfdiff,comfort diff,uint8 (>=6<=12),K,true,number.boiler_dhw_comfort_diff,number.boiler_dhw_comfdiff,5,9,1,21,1
"CS5800i, CS6800i, WLW176i, WLW186i",boiler,8,dhw.ecodiff,eco diff,uint8 (>=6<=12),K,true,number.boiler_dhw_eco_diff,number.boiler_dhw_ecodiff,5,9,1,22,1
"CS5800i, CS6800i, WLW176i, WLW186i",boiler,8,dhw.comfdiff,comfort diff,uint8 (>=4<=12),K,true,number.boiler_dhw_comfort_diff,number.boiler_dhw_comfdiff,5,9,1,21,1
"CS5800i, CS6800i, WLW176i, WLW186i",boiler,8,dhw.ecodiff,eco diff,uint8 (>=4<=12),K,true,number.boiler_dhw_eco_diff,number.boiler_dhw_ecodiff,5,9,1,22,1
"CS5800i, CS6800i, WLW176i, WLW186i",boiler,8,dhw.ecoplusdiff,eco+ diff,uint8 (>=6<=12),K,true,number.boiler_dhw_eco+_diff,number.boiler_dhw_ecoplusdiff,5,9,1,23,1
"CS5800i, CS6800i, WLW176i, WLW186i",boiler,8,dhw.comfstop,comfort stop temp,uint8 (>=0<=254),C,true,number.boiler_dhw_comfort_stop_temp,number.boiler_dhw_comfstop,5,9,1,24,1
"CS5800i, CS6800i, WLW176i, WLW186i",boiler,8,dhw.ecostop,eco stop temp,uint8 (>=0<=254),C,true,number.boiler_dhw_eco_stop_temp,number.boiler_dhw_ecostop,5,9,1,25,1
@@ -2668,8 +2668,8 @@ device name,device type,product id,shortname,fullname,type [options...] \| (min/
"Enviline, Compress 6000AW, Hybrid 3000-7000iAW, SupraEco/Geo 5xx, WLW196i/WSW196i",boiler,172,dhw.comfoff,comfort switch off,uint8 (>=15<=65),C,true,number.boiler_dhw_comfort_switch_off,number.boiler_dhw_comfoff,5,9,1,18,1
"Enviline, Compress 6000AW, Hybrid 3000-7000iAW, SupraEco/Geo 5xx, WLW196i/WSW196i",boiler,172,dhw.ecooff,eco switch off,uint8 (>=15<=65),C,true,number.boiler_dhw_eco_switch_off,number.boiler_dhw_ecooff,5,9,1,19,1
"Enviline, Compress 6000AW, Hybrid 3000-7000iAW, SupraEco/Geo 5xx, WLW196i/WSW196i",boiler,172,dhw.ecoplusoff,eco+ switch off,uint8 (>=48<=63),C,true,number.boiler_dhw_eco+_switch_off,number.boiler_dhw_ecoplusoff,5,9,1,20,1
"Enviline, Compress 6000AW, Hybrid 3000-7000iAW, SupraEco/Geo 5xx, WLW196i/WSW196i",boiler,172,dhw.comfdiff,comfort diff,uint8 (>=6<=12),K,true,number.boiler_dhw_comfort_diff,number.boiler_dhw_comfdiff,5,9,1,21,1
"Enviline, Compress 6000AW, Hybrid 3000-7000iAW, SupraEco/Geo 5xx, WLW196i/WSW196i",boiler,172,dhw.ecodiff,eco diff,uint8 (>=6<=12),K,true,number.boiler_dhw_eco_diff,number.boiler_dhw_ecodiff,5,9,1,22,1
"Enviline, Compress 6000AW, Hybrid 3000-7000iAW, SupraEco/Geo 5xx, WLW196i/WSW196i",boiler,172,dhw.comfdiff,comfort diff,uint8 (>=4<=12),K,true,number.boiler_dhw_comfort_diff,number.boiler_dhw_comfdiff,5,9,1,21,1
"Enviline, Compress 6000AW, Hybrid 3000-7000iAW, SupraEco/Geo 5xx, WLW196i/WSW196i",boiler,172,dhw.ecodiff,eco diff,uint8 (>=4<=12),K,true,number.boiler_dhw_eco_diff,number.boiler_dhw_ecodiff,5,9,1,22,1
"Enviline, Compress 6000AW, Hybrid 3000-7000iAW, SupraEco/Geo 5xx, WLW196i/WSW196i",boiler,172,dhw.ecoplusdiff,eco+ diff,uint8 (>=6<=12),K,true,number.boiler_dhw_eco+_diff,number.boiler_dhw_ecoplusdiff,5,9,1,23,1
"Enviline, Compress 6000AW, Hybrid 3000-7000iAW, SupraEco/Geo 5xx, WLW196i/WSW196i",boiler,172,dhw.comfstop,comfort stop temp,uint8 (>=0<=254),C,true,number.boiler_dhw_comfort_stop_temp,number.boiler_dhw_comfstop,5,9,1,24,1
"Enviline, Compress 6000AW, Hybrid 3000-7000iAW, SupraEco/Geo 5xx, WLW196i/WSW196i",boiler,172,dhw.ecostop,eco stop temp,uint8 (>=0<=254),C,true,number.boiler_dhw_eco_stop_temp,number.boiler_dhw_ecostop,5,9,1,25,1
@@ -2876,8 +2876,8 @@ device name,device type,product id,shortname,fullname,type [options...] \| (min/
"Geo 5xx",boiler,173,dhw.comfoff,comfort switch off,uint8 (>=15<=65),C,true,number.boiler_dhw_comfort_switch_off,number.boiler_dhw_comfoff,5,9,1,18,1
"Geo 5xx",boiler,173,dhw.ecooff,eco switch off,uint8 (>=15<=65),C,true,number.boiler_dhw_eco_switch_off,number.boiler_dhw_ecooff,5,9,1,19,1
"Geo 5xx",boiler,173,dhw.ecoplusoff,eco+ switch off,uint8 (>=48<=63),C,true,number.boiler_dhw_eco+_switch_off,number.boiler_dhw_ecoplusoff,5,9,1,20,1
"Geo 5xx",boiler,173,dhw.comfdiff,comfort diff,uint8 (>=6<=12),K,true,number.boiler_dhw_comfort_diff,number.boiler_dhw_comfdiff,5,9,1,21,1
"Geo 5xx",boiler,173,dhw.ecodiff,eco diff,uint8 (>=6<=12),K,true,number.boiler_dhw_eco_diff,number.boiler_dhw_ecodiff,5,9,1,22,1
"Geo 5xx",boiler,173,dhw.comfdiff,comfort diff,uint8 (>=4<=12),K,true,number.boiler_dhw_comfort_diff,number.boiler_dhw_comfdiff,5,9,1,21,1
"Geo 5xx",boiler,173,dhw.ecodiff,eco diff,uint8 (>=4<=12),K,true,number.boiler_dhw_eco_diff,number.boiler_dhw_ecodiff,5,9,1,22,1
"Geo 5xx",boiler,173,dhw.ecoplusdiff,eco+ diff,uint8 (>=6<=12),K,true,number.boiler_dhw_eco+_diff,number.boiler_dhw_ecoplusdiff,5,9,1,23,1
"Geo 5xx",boiler,173,dhw.comfstop,comfort stop temp,uint8 (>=0<=254),C,true,number.boiler_dhw_comfort_stop_temp,number.boiler_dhw_comfstop,5,9,1,24,1
"Geo 5xx",boiler,173,dhw.ecostop,eco stop temp,uint8 (>=0<=254),C,true,number.boiler_dhw_eco_stop_temp,number.boiler_dhw_ecostop,5,9,1,25,1
@@ -3914,17 +3914,18 @@ device name,device type,product id,shortname,fullname,type [options...] \| (min/
"UI800, BC400",thermostat,4,hc1.solarinfl,solar influence,uint8 (>=-5<=4294967295),C,true,number.thermostat_hc1_solar_influence,number.thermostat_hc1_solarinfl,6,1,1,54,1
"UI800, BC400",thermostat,4,hc1.currsolarinfl,curent solar influence,uint8 (>=0<=25),C,false,sensor.thermostat_hc1_curent_solar_influence,sensor.thermostat_hc1_currsolarinfl,6,1,1/10,55,1
"UI800, BC400",thermostat,4,dhw.mode,operating mode,enum [off\|eco+\|eco\|comfort\|auto], ,true,select.thermostat_dhw_operating_mode,select.thermostat_dhw_mode,6,9,1,0,1
"UI800, BC400",thermostat,4,dhw.settemp,set temperature,uint8 (>=0<=254),C,true,number.thermostat_dhw_set_temperature,number.thermostat_dhw_settemp,6,9,1,1,1
"UI800, BC400",thermostat,4,dhw.settemplow,set low temperature,uint8 (>=0<=254),C,true,number.thermostat_dhw_set_low_temperature,number.thermostat_dhw_settemplow,6,9,1,2,1
"UI800, BC400",thermostat,4,dhw.circmode,circulation pump mode,enum [off\|on\|auto\|own prog], ,true,select.thermostat_dhw_circulation_pump_mode,select.thermostat_dhw_circmode,6,9,1,3,1
"UI800, BC400",thermostat,4,dhw.chargeduration,charge duration,uint8 (>=0<=3810),minutes,true,number.thermostat_dhw_charge_duration,number.thermostat_dhw_chargeduration,6,9,15,4,1
"UI800, BC400",thermostat,4,dhw.charge,charge,boolean, ,true,switch.thermostat_dhw_charge,switch.thermostat_dhw_charge,6,9,1,5,1
"UI800, BC400",thermostat,4,dhw.extra,extra,boolean, ,false,binary_sensor.thermostat_dhw_extra,binary_sensor.thermostat_dhw_extra,6,9,1,6,1
"UI800, BC400",thermostat,4,dhw.disinfecting,disinfecting,boolean, ,true,switch.thermostat_dhw_disinfecting,switch.thermostat_dhw_disinfecting,6,9,1,7,1
"UI800, BC400",thermostat,4,dhw.disinfectday,disinfection day,enum [mo\|tu\|we\|th\|fr\|sa\|su\|all], ,true,select.thermostat_dhw_disinfection_day,select.thermostat_dhw_disinfectday,6,9,1,8,1
"UI800, BC400",thermostat,4,dhw.disinfecttime,disinfection time,uint8 (>=0<=1431),minutes,true,number.thermostat_dhw_disinfection_time,number.thermostat_dhw_disinfecttime,6,9,15,9,1
"UI800, BC400",thermostat,4,dhw.dailyheating,daily heating,boolean, ,true,switch.thermostat_dhw_daily_heating,switch.thermostat_dhw_dailyheating,6,9,1,10,1
"UI800, BC400",thermostat,4,dhw.dailyheattime,daily heating time,uint8 (>=0<=1431),minutes,true,number.thermostat_dhw_daily_heating_time,number.thermostat_dhw_dailyheattime,6,9,15,11,1
"UI800, BC400",thermostat,4,dhw.modetype,mode type,enum [off\|eco\|comfort\|eco+], ,false,sensor.thermostat_dhw_mode_type,sensor.thermostat_dhw_modetype,6,9,1,1,1
"UI800, BC400",thermostat,4,dhw.settemp,set temperature,uint8 (>=0<=254),C,true,number.thermostat_dhw_set_temperature,number.thermostat_dhw_settemp,6,9,1,2,1
"UI800, BC400",thermostat,4,dhw.settemplow,set low temperature,uint8 (>=0<=254),C,true,number.thermostat_dhw_set_low_temperature,number.thermostat_dhw_settemplow,6,9,1,3,1
"UI800, BC400",thermostat,4,dhw.circmode,circulation pump mode,enum [off\|on\|auto\|own prog], ,true,select.thermostat_dhw_circulation_pump_mode,select.thermostat_dhw_circmode,6,9,1,4,1
"UI800, BC400",thermostat,4,dhw.chargeduration,charge duration,uint8 (>=0<=3810),minutes,true,number.thermostat_dhw_charge_duration,number.thermostat_dhw_chargeduration,6,9,15,5,1
"UI800, BC400",thermostat,4,dhw.charge,charge,boolean, ,true,switch.thermostat_dhw_charge,switch.thermostat_dhw_charge,6,9,1,6,1
"UI800, BC400",thermostat,4,dhw.extra,extra,boolean, ,false,binary_sensor.thermostat_dhw_extra,binary_sensor.thermostat_dhw_extra,6,9,1,7,1
"UI800, BC400",thermostat,4,dhw.disinfecting,disinfecting,boolean, ,true,switch.thermostat_dhw_disinfecting,switch.thermostat_dhw_disinfecting,6,9,1,8,1
"UI800, BC400",thermostat,4,dhw.disinfectday,disinfection day,enum [mo\|tu\|we\|th\|fr\|sa\|su\|all], ,true,select.thermostat_dhw_disinfection_day,select.thermostat_dhw_disinfectday,6,9,1,9,1
"UI800, BC400",thermostat,4,dhw.disinfecttime,disinfection time,uint8 (>=0<=1431),minutes,true,number.thermostat_dhw_disinfection_time,number.thermostat_dhw_disinfecttime,6,9,15,10,1
"UI800, BC400",thermostat,4,dhw.dailyheating,daily heating,boolean, ,true,switch.thermostat_dhw_daily_heating,switch.thermostat_dhw_dailyheating,6,9,1,11,1
"UI800, BC400",thermostat,4,dhw.dailyheattime,daily heating time,uint8 (>=0<=1431),minutes,true,number.thermostat_dhw_daily_heating_time,number.thermostat_dhw_dailyheattime,6,9,15,12,1
"CR11",thermostat,10,errorcode,error code,string, ,false,sensor.thermostat_error_code,sensor.thermostat_errorcode,6,0,1,0,8
"CR11",thermostat,10,lastcode,last error code,string, ,false,sensor.thermostat_last_error_code,sensor.thermostat_lastcode,6,0,1,8,25
"CR11",thermostat,10,hc1.seltemp,selected room temperature,int16 (>=0<=30),C,true,number.thermostat_hc1_selected_room_temperature,number.thermostat_hc1_seltemp,6,1,1/2,0,1
@@ -4004,18 +4005,18 @@ device name,device type,product id,shortname,fullname,type [options...] \| (min/
"RC30",thermostat,67,hc1.switchtime1,own1 program switchtime,string, ,true,sensor.thermostat_hc1_own1_program_switchtime,sensor.thermostat_hc1_switchtime1,6,1,1,91,8
"RC30",thermostat,67,hc1.switchtime2,own2 program switchtime,string, ,true,sensor.thermostat_hc1_own2_program_switchtime,sensor.thermostat_hc1_switchtime2,6,1,1,99,8
"RC30",thermostat,67,dhw.mode,operating mode,enum [off\|on\|auto], ,true,select.thermostat_dhw_operating_mode,select.thermostat_dhw_mode,6,9,1,0,1
"RC30",thermostat,67,dhw.circmode,circulation pump mode,enum [off\|on\|auto], ,true,select.thermostat_dhw_circulation_pump_mode,select.thermostat_dhw_circmode,6,9,1,3,1
"RC30",thermostat,67,dhw.progmode,program,enum [std prog\|own prog], ,true,select.thermostat_dhw_program,select.thermostat_dhw_progmode,6,9,1,12,1
"RC30",thermostat,67,dhw.circprog,circulation program,enum [std prog\|own prog], ,true,select.thermostat_dhw_circulation_program,select.thermostat_dhw_circprog,6,9,1,13,1
"RC30",thermostat,67,dhw.disinfecting,disinfecting,boolean, ,true,switch.thermostat_dhw_disinfecting,switch.thermostat_dhw_disinfecting,6,9,1,7,1
"RC30",thermostat,67,dhw.disinfectday,disinfection day,enum [mo\|tu\|we\|th\|fr\|sa\|su\|all], ,true,select.thermostat_dhw_disinfection_day,select.thermostat_dhw_disinfectday,6,9,1,8,1
"RC30",thermostat,67,dhw.disinfecthour,disinfection hour,uint8 (>=0<=23), ,true,number.thermostat_dhw_disinfection_hour,number.thermostat_dhw_disinfecthour,6,9,1,14,1
"RC30",thermostat,67,dhw.maxtemp,maximum temperature,uint8 (>=0<=254),C,true,number.thermostat_dhw_maximum_temperature,number.thermostat_dhw_maxtemp,6,9,1,15,1
"RC30",thermostat,67,dhw.onetimekey,one time key function,boolean, ,true,switch.thermostat_dhw_one_time_key_function,switch.thermostat_dhw_onetimekey,6,9,1,16,1
"RC30",thermostat,67,dhw.switchtime,program switchtime,string, ,true,sensor.thermostat_dhw_program_switchtime,sensor.thermostat_dhw_switchtime,6,9,1,17,8
"RC30",thermostat,67,dhw.circswitchtime,circulation program switchtime,string, ,true,sensor.thermostat_dhw_circulation_program_switchtime,sensor.thermostat_dhw_circswitchtime,6,9,1,25,8
"RC30",thermostat,67,dhw.holidays,holiday dates,string, ,true,sensor.thermostat_dhw_holiday_dates,sensor.thermostat_dhw_holidays,6,9,1,33,13
"RC30",thermostat,67,dhw.vacations,vacation dates,string, ,true,sensor.thermostat_dhw_vacation_dates,sensor.thermostat_dhw_vacations,6,9,1,46,13
"RC30",thermostat,67,dhw.circmode,circulation pump mode,enum [off\|on\|auto], ,true,select.thermostat_dhw_circulation_pump_mode,select.thermostat_dhw_circmode,6,9,1,4,1
"RC30",thermostat,67,dhw.progmode,program,enum [std prog\|own prog], ,true,select.thermostat_dhw_program,select.thermostat_dhw_progmode,6,9,1,13,1
"RC30",thermostat,67,dhw.circprog,circulation program,enum [std prog\|own prog], ,true,select.thermostat_dhw_circulation_program,select.thermostat_dhw_circprog,6,9,1,14,1
"RC30",thermostat,67,dhw.disinfecting,disinfecting,boolean, ,true,switch.thermostat_dhw_disinfecting,switch.thermostat_dhw_disinfecting,6,9,1,8,1
"RC30",thermostat,67,dhw.disinfectday,disinfection day,enum [mo\|tu\|we\|th\|fr\|sa\|su\|all], ,true,select.thermostat_dhw_disinfection_day,select.thermostat_dhw_disinfectday,6,9,1,9,1
"RC30",thermostat,67,dhw.disinfecthour,disinfection hour,uint8 (>=0<=23), ,true,number.thermostat_dhw_disinfection_hour,number.thermostat_dhw_disinfecthour,6,9,1,15,1
"RC30",thermostat,67,dhw.maxtemp,maximum temperature,uint8 (>=0<=254),C,true,number.thermostat_dhw_maximum_temperature,number.thermostat_dhw_maxtemp,6,9,1,16,1
"RC30",thermostat,67,dhw.onetimekey,one time key function,boolean, ,true,switch.thermostat_dhw_one_time_key_function,switch.thermostat_dhw_onetimekey,6,9,1,17,1
"RC30",thermostat,67,dhw.switchtime,program switchtime,string, ,true,sensor.thermostat_dhw_program_switchtime,sensor.thermostat_dhw_switchtime,6,9,1,18,8
"RC30",thermostat,67,dhw.circswitchtime,circulation program switchtime,string, ,true,sensor.thermostat_dhw_circulation_program_switchtime,sensor.thermostat_dhw_circswitchtime,6,9,1,26,8
"RC30",thermostat,67,dhw.holidays,holiday dates,string, ,true,sensor.thermostat_dhw_holiday_dates,sensor.thermostat_dhw_holidays,6,9,1,34,13
"RC30",thermostat,67,dhw.vacations,vacation dates,string, ,true,sensor.thermostat_dhw_vacation_dates,sensor.thermostat_dhw_vacations,6,9,1,47,13
"RC20, Moduline 300",thermostat,77,errorcode,error code,string, ,false,sensor.thermostat_error_code,sensor.thermostat_errorcode,6,0,1,0,8
"RC20, Moduline 300",thermostat,77,lastcode,last error code,string, ,false,sensor.thermostat_last_error_code,sensor.thermostat_lastcode,6,0,1,8,25
"RC20, Moduline 300",thermostat,77,datetime,date/time,string, ,false,sensor.thermostat_date/time,sensor.thermostat_datetime,6,0,1,33,13
@@ -4068,10 +4069,10 @@ device name,device type,product id,shortname,fullname,type [options...] \| (min/
"Moduline 400",thermostat,78,hc1.manualtemp,manual temperature,uint8 (>=0<=127),C,true,number.thermostat_hc1_manual_temperature,number.thermostat_hc1_manualtemp,6,1,1/2,6,1
"Moduline 400",thermostat,78,hc1.offtemp,temperature when mode is off,uint8 (>=0<=127),C,true,number.thermostat_hc1_temperature_when_mode_is_off,number.thermostat_hc1_offtemp,6,1,1/2,107,1
"Moduline 400",thermostat,78,dhw.mode,operating mode,enum [on\|off\|auto], ,true,select.thermostat_dhw_operating_mode,select.thermostat_dhw_mode,6,9,1,0,1
"Moduline 400",thermostat,78,dhw.whenmodeoff,when thermostat mode off,boolean, ,true,switch.thermostat_dhw_when_thermostat_mode_off,switch.thermostat_dhw_whenmodeoff,6,9,1,59,1
"Moduline 400",thermostat,78,dhw.disinfecting,disinfecting,boolean, ,true,switch.thermostat_dhw_disinfecting,switch.thermostat_dhw_disinfecting,6,9,1,7,1
"Moduline 400",thermostat,78,dhw.disinfectday,disinfection day,enum [mo\|tu\|we\|th\|fr\|sa\|su\|all], ,true,select.thermostat_dhw_disinfection_day,select.thermostat_dhw_disinfectday,6,9,1,8,1
"Moduline 400",thermostat,78,dhw.disinfecthour,disinfection hour,uint8 (>=0<=23), ,true,number.thermostat_dhw_disinfection_hour,number.thermostat_dhw_disinfecthour,6,9,1,14,1
"Moduline 400",thermostat,78,dhw.whenmodeoff,when thermostat mode off,boolean, ,true,switch.thermostat_dhw_when_thermostat_mode_off,switch.thermostat_dhw_whenmodeoff,6,9,1,60,1
"Moduline 400",thermostat,78,dhw.disinfecting,disinfecting,boolean, ,true,switch.thermostat_dhw_disinfecting,switch.thermostat_dhw_disinfecting,6,9,1,8,1
"Moduline 400",thermostat,78,dhw.disinfectday,disinfection day,enum [mo\|tu\|we\|th\|fr\|sa\|su\|all], ,true,select.thermostat_dhw_disinfection_day,select.thermostat_dhw_disinfectday,6,9,1,9,1
"Moduline 400",thermostat,78,dhw.disinfecthour,disinfection hour,uint8 (>=0<=23), ,true,number.thermostat_dhw_disinfection_hour,number.thermostat_dhw_disinfecthour,6,9,1,15,1
"RC10, Moduline 100",thermostat,79,errorcode,error code,string, ,false,sensor.thermostat_error_code,sensor.thermostat_errorcode,6,0,1,0,8
"RC10, Moduline 100",thermostat,79,lastcode,last error code,string, ,false,sensor.thermostat_last_error_code,sensor.thermostat_lastcode,6,0,1,8,25
"RC10, Moduline 100",thermostat,79,intoffset,internal temperature offset,int8 (>=-12<=12),C,true,number.thermostat_internal_temperature_offset,number.thermostat_intoffset,6,0,1/10,46,1
@@ -4150,18 +4151,18 @@ device name,device type,product id,shortname,fullname,type [options...] \| (min/
"RC35",thermostat,86,hc1.switchtime1,own1 program switchtime,string, ,true,sensor.thermostat_hc1_own1_program_switchtime,sensor.thermostat_hc1_switchtime1,6,1,1,91,8
"RC35",thermostat,86,hc1.switchtime2,own2 program switchtime,string, ,true,sensor.thermostat_hc1_own2_program_switchtime,sensor.thermostat_hc1_switchtime2,6,1,1,99,8
"RC35",thermostat,86,dhw.mode,operating mode,enum [off\|on\|auto], ,true,select.thermostat_dhw_operating_mode,select.thermostat_dhw_mode,6,9,1,0,1
"RC35",thermostat,86,dhw.circmode,circulation pump mode,enum [off\|on\|auto], ,true,select.thermostat_dhw_circulation_pump_mode,select.thermostat_dhw_circmode,6,9,1,3,1
"RC35",thermostat,86,dhw.progmode,program,enum [std prog\|own prog], ,true,select.thermostat_dhw_program,select.thermostat_dhw_progmode,6,9,1,12,1
"RC35",thermostat,86,dhw.circprog,circulation program,enum [std prog\|own prog], ,true,select.thermostat_dhw_circulation_program,select.thermostat_dhw_circprog,6,9,1,13,1
"RC35",thermostat,86,dhw.disinfecting,disinfecting,boolean, ,true,switch.thermostat_dhw_disinfecting,switch.thermostat_dhw_disinfecting,6,9,1,7,1
"RC35",thermostat,86,dhw.disinfectday,disinfection day,enum [mo\|tu\|we\|th\|fr\|sa\|su\|all], ,true,select.thermostat_dhw_disinfection_day,select.thermostat_dhw_disinfectday,6,9,1,8,1
"RC35",thermostat,86,dhw.disinfecthour,disinfection hour,uint8 (>=0<=23), ,true,number.thermostat_dhw_disinfection_hour,number.thermostat_dhw_disinfecthour,6,9,1,14,1
"RC35",thermostat,86,dhw.maxtemp,maximum temperature,uint8 (>=60<=80),C,true,number.thermostat_dhw_maximum_temperature,number.thermostat_dhw_maxtemp,6,9,1,15,1
"RC35",thermostat,86,dhw.onetimekey,one time key function,boolean, ,true,switch.thermostat_dhw_one_time_key_function,switch.thermostat_dhw_onetimekey,6,9,1,16,1
"RC35",thermostat,86,dhw.switchtime,program switchtime,string, ,true,sensor.thermostat_dhw_program_switchtime,sensor.thermostat_dhw_switchtime,6,9,1,17,8
"RC35",thermostat,86,dhw.circswitchtime,circulation program switchtime,string, ,true,sensor.thermostat_dhw_circulation_program_switchtime,sensor.thermostat_dhw_circswitchtime,6,9,1,25,8
"RC35",thermostat,86,dhw.holidays,holiday dates,string, ,true,sensor.thermostat_dhw_holiday_dates,sensor.thermostat_dhw_holidays,6,9,1,33,13
"RC35",thermostat,86,dhw.vacations,vacation dates,string, ,true,sensor.thermostat_dhw_vacation_dates,sensor.thermostat_dhw_vacations,6,9,1,46,13
"RC35",thermostat,86,dhw.circmode,circulation pump mode,enum [off\|on\|auto], ,true,select.thermostat_dhw_circulation_pump_mode,select.thermostat_dhw_circmode,6,9,1,4,1
"RC35",thermostat,86,dhw.progmode,program,enum [std prog\|own prog], ,true,select.thermostat_dhw_program,select.thermostat_dhw_progmode,6,9,1,13,1
"RC35",thermostat,86,dhw.circprog,circulation program,enum [std prog\|own prog], ,true,select.thermostat_dhw_circulation_program,select.thermostat_dhw_circprog,6,9,1,14,1
"RC35",thermostat,86,dhw.disinfecting,disinfecting,boolean, ,true,switch.thermostat_dhw_disinfecting,switch.thermostat_dhw_disinfecting,6,9,1,8,1
"RC35",thermostat,86,dhw.disinfectday,disinfection day,enum [mo\|tu\|we\|th\|fr\|sa\|su\|all], ,true,select.thermostat_dhw_disinfection_day,select.thermostat_dhw_disinfectday,6,9,1,9,1
"RC35",thermostat,86,dhw.disinfecthour,disinfection hour,uint8 (>=0<=23), ,true,number.thermostat_dhw_disinfection_hour,number.thermostat_dhw_disinfecthour,6,9,1,15,1
"RC35",thermostat,86,dhw.maxtemp,maximum temperature,uint8 (>=60<=80),C,true,number.thermostat_dhw_maximum_temperature,number.thermostat_dhw_maxtemp,6,9,1,16,1
"RC35",thermostat,86,dhw.onetimekey,one time key function,boolean, ,true,switch.thermostat_dhw_one_time_key_function,switch.thermostat_dhw_onetimekey,6,9,1,17,1
"RC35",thermostat,86,dhw.switchtime,program switchtime,string, ,true,sensor.thermostat_dhw_program_switchtime,sensor.thermostat_dhw_switchtime,6,9,1,18,8
"RC35",thermostat,86,dhw.circswitchtime,circulation program switchtime,string, ,true,sensor.thermostat_dhw_circulation_program_switchtime,sensor.thermostat_dhw_circswitchtime,6,9,1,26,8
"RC35",thermostat,86,dhw.holidays,holiday dates,string, ,true,sensor.thermostat_dhw_holiday_dates,sensor.thermostat_dhw_holidays,6,9,1,34,13
"RC35",thermostat,86,dhw.vacations,vacation dates,string, ,true,sensor.thermostat_dhw_vacation_dates,sensor.thermostat_dhw_vacations,6,9,1,47,13
"RC10, Moduline 100",thermostat,90,errorcode,error code,string, ,false,sensor.thermostat_error_code,sensor.thermostat_errorcode,6,0,1,0,8
"RC10, Moduline 100",thermostat,90,lastcode,last error code,string, ,false,sensor.thermostat_last_error_code,sensor.thermostat_lastcode,6,0,1,8,25
"RC10, Moduline 100",thermostat,90,datetime,date/time,string, ,false,sensor.thermostat_date/time,sensor.thermostat_datetime,6,0,1,33,13
@@ -4296,17 +4297,18 @@ device name,device type,product id,shortname,fullname,type [options...] \| (min/
"RC200, CW100, CR120, CR50",thermostat,157,hc1.solarinfl,solar influence,uint8 (>=-5<=4294967295),C,true,number.thermostat_hc1_solar_influence,number.thermostat_hc1_solarinfl,6,1,1,54,1
"RC200, CW100, CR120, CR50",thermostat,157,hc1.currsolarinfl,curent solar influence,uint8 (>=0<=25),C,false,sensor.thermostat_hc1_curent_solar_influence,sensor.thermostat_hc1_currsolarinfl,6,1,1/10,55,1
"RC200, CW100, CR120, CR50",thermostat,157,dhw.mode,operating mode,enum [off\|on\|auto], ,true,select.thermostat_dhw_operating_mode,select.thermostat_dhw_mode,6,9,1,0,1
"RC200, CW100, CR120, CR50",thermostat,157,dhw.settemp,set temperature,uint8 (>=0<=254),C,true,number.thermostat_dhw_set_temperature,number.thermostat_dhw_settemp,6,9,1,1,1
"RC200, CW100, CR120, CR50",thermostat,157,dhw.settemplow,set low temperature,uint8 (>=0<=254),C,true,number.thermostat_dhw_set_low_temperature,number.thermostat_dhw_settemplow,6,9,1,2,1
"RC200, CW100, CR120, CR50",thermostat,157,dhw.circmode,circulation pump mode,enum [off\|on\|auto\|own prog], ,true,select.thermostat_dhw_circulation_pump_mode,select.thermostat_dhw_circmode,6,9,1,3,1
"RC200, CW100, CR120, CR50",thermostat,157,dhw.chargeduration,charge duration,uint8 (>=0<=3810),minutes,true,number.thermostat_dhw_charge_duration,number.thermostat_dhw_chargeduration,6,9,15,4,1
"RC200, CW100, CR120, CR50",thermostat,157,dhw.charge,charge,boolean, ,true,switch.thermostat_dhw_charge,switch.thermostat_dhw_charge,6,9,1,5,1
"RC200, CW100, CR120, CR50",thermostat,157,dhw.extra,extra,boolean, ,false,binary_sensor.thermostat_dhw_extra,binary_sensor.thermostat_dhw_extra,6,9,1,6,1
"RC200, CW100, CR120, CR50",thermostat,157,dhw.disinfecting,disinfecting,boolean, ,true,switch.thermostat_dhw_disinfecting,switch.thermostat_dhw_disinfecting,6,9,1,7,1
"RC200, CW100, CR120, CR50",thermostat,157,dhw.disinfectday,disinfection day,enum [mo\|tu\|we\|th\|fr\|sa\|su\|all], ,true,select.thermostat_dhw_disinfection_day,select.thermostat_dhw_disinfectday,6,9,1,8,1
"RC200, CW100, CR120, CR50",thermostat,157,dhw.disinfecttime,disinfection time,uint8 (>=0<=1431),minutes,true,number.thermostat_dhw_disinfection_time,number.thermostat_dhw_disinfecttime,6,9,15,9,1
"RC200, CW100, CR120, CR50",thermostat,157,dhw.dailyheating,daily heating,boolean, ,true,switch.thermostat_dhw_daily_heating,switch.thermostat_dhw_dailyheating,6,9,1,10,1
"RC200, CW100, CR120, CR50",thermostat,157,dhw.dailyheattime,daily heating time,uint8 (>=0<=1431),minutes,true,number.thermostat_dhw_daily_heating_time,number.thermostat_dhw_dailyheattime,6,9,15,11,1
"RC200, CW100, CR120, CR50",thermostat,157,dhw.modetype,mode type,enum [off\|eco\|comfort\|eco+], ,false,sensor.thermostat_dhw_mode_type,sensor.thermostat_dhw_modetype,6,9,1,1,1
"RC200, CW100, CR120, CR50",thermostat,157,dhw.settemp,set temperature,uint8 (>=0<=254),C,true,number.thermostat_dhw_set_temperature,number.thermostat_dhw_settemp,6,9,1,2,1
"RC200, CW100, CR120, CR50",thermostat,157,dhw.settemplow,set low temperature,uint8 (>=0<=254),C,true,number.thermostat_dhw_set_low_temperature,number.thermostat_dhw_settemplow,6,9,1,3,1
"RC200, CW100, CR120, CR50",thermostat,157,dhw.circmode,circulation pump mode,enum [off\|on\|auto\|own prog], ,true,select.thermostat_dhw_circulation_pump_mode,select.thermostat_dhw_circmode,6,9,1,4,1
"RC200, CW100, CR120, CR50",thermostat,157,dhw.chargeduration,charge duration,uint8 (>=0<=3810),minutes,true,number.thermostat_dhw_charge_duration,number.thermostat_dhw_chargeduration,6,9,15,5,1
"RC200, CW100, CR120, CR50",thermostat,157,dhw.charge,charge,boolean, ,true,switch.thermostat_dhw_charge,switch.thermostat_dhw_charge,6,9,1,6,1
"RC200, CW100, CR120, CR50",thermostat,157,dhw.extra,extra,boolean, ,false,binary_sensor.thermostat_dhw_extra,binary_sensor.thermostat_dhw_extra,6,9,1,7,1
"RC200, CW100, CR120, CR50",thermostat,157,dhw.disinfecting,disinfecting,boolean, ,true,switch.thermostat_dhw_disinfecting,switch.thermostat_dhw_disinfecting,6,9,1,8,1
"RC200, CW100, CR120, CR50",thermostat,157,dhw.disinfectday,disinfection day,enum [mo\|tu\|we\|th\|fr\|sa\|su\|all], ,true,select.thermostat_dhw_disinfection_day,select.thermostat_dhw_disinfectday,6,9,1,9,1
"RC200, CW100, CR120, CR50",thermostat,157,dhw.disinfecttime,disinfection time,uint8 (>=0<=1431),minutes,true,number.thermostat_dhw_disinfection_time,number.thermostat_dhw_disinfecttime,6,9,15,10,1
"RC200, CW100, CR120, CR50",thermostat,157,dhw.dailyheating,daily heating,boolean, ,true,switch.thermostat_dhw_daily_heating,switch.thermostat_dhw_dailyheating,6,9,1,11,1
"RC200, CW100, CR120, CR50",thermostat,157,dhw.dailyheattime,daily heating time,uint8 (>=0<=1431),minutes,true,number.thermostat_dhw_daily_heating_time,number.thermostat_dhw_dailyheattime,6,9,15,12,1
"RC3*0, Moduline 3000/1010H, CW400, Sense II, HPC410",thermostat,158,errorcode,error code,string, ,false,sensor.thermostat_error_code,sensor.thermostat_errorcode,6,0,1,0,8
"RC3*0, Moduline 3000/1010H, CW400, Sense II, HPC410",thermostat,158,lastcode,last error code,string, ,false,sensor.thermostat_last_error_code,sensor.thermostat_lastcode,6,0,1,8,25
"RC3*0, Moduline 3000/1010H, CW400, Sense II, HPC410",thermostat,158,datetime,date/time,string, ,true,sensor.thermostat_date/time,sensor.thermostat_datetime,6,0,1,33,13
@@ -4386,17 +4388,18 @@ device name,device type,product id,shortname,fullname,type [options...] \| (min/
"RC3*0, Moduline 3000/1010H, CW400, Sense II, HPC410",thermostat,158,hc1.solarinfl,solar influence,uint8 (>=-5<=4294967295),C,true,number.thermostat_hc1_solar_influence,number.thermostat_hc1_solarinfl,6,1,1,54,1
"RC3*0, Moduline 3000/1010H, CW400, Sense II, HPC410",thermostat,158,hc1.currsolarinfl,curent solar influence,uint8 (>=0<=25),C,false,sensor.thermostat_hc1_curent_solar_influence,sensor.thermostat_hc1_currsolarinfl,6,1,1/10,55,1
"RC3*0, Moduline 3000/1010H, CW400, Sense II, HPC410",thermostat,158,dhw.mode,operating mode,enum [off\|normal\|comfort\|auto\|own prog], ,true,select.thermostat_dhw_operating_mode,select.thermostat_dhw_mode,6,9,1,0,1
"RC3*0, Moduline 3000/1010H, CW400, Sense II, HPC410",thermostat,158,dhw.settemp,set temperature,uint8 (>=0<=254),C,true,number.thermostat_dhw_set_temperature,number.thermostat_dhw_settemp,6,9,1,1,1
"RC3*0, Moduline 3000/1010H, CW400, Sense II, HPC410",thermostat,158,dhw.settemplow,set low temperature,uint8 (>=0<=254),C,true,number.thermostat_dhw_set_low_temperature,number.thermostat_dhw_settemplow,6,9,1,2,1
"RC3*0, Moduline 3000/1010H, CW400, Sense II, HPC410",thermostat,158,dhw.circmode,circulation pump mode,enum [off\|on\|auto\|own prog], ,true,select.thermostat_dhw_circulation_pump_mode,select.thermostat_dhw_circmode,6,9,1,3,1
"RC3*0, Moduline 3000/1010H, CW400, Sense II, HPC410",thermostat,158,dhw.chargeduration,charge duration,uint8 (>=0<=3810),minutes,true,number.thermostat_dhw_charge_duration,number.thermostat_dhw_chargeduration,6,9,15,4,1
"RC3*0, Moduline 3000/1010H, CW400, Sense II, HPC410",thermostat,158,dhw.charge,charge,boolean, ,true,switch.thermostat_dhw_charge,switch.thermostat_dhw_charge,6,9,1,5,1
"RC3*0, Moduline 3000/1010H, CW400, Sense II, HPC410",thermostat,158,dhw.extra,extra,boolean, ,false,binary_sensor.thermostat_dhw_extra,binary_sensor.thermostat_dhw_extra,6,9,1,6,1
"RC3*0, Moduline 3000/1010H, CW400, Sense II, HPC410",thermostat,158,dhw.disinfecting,disinfecting,boolean, ,true,switch.thermostat_dhw_disinfecting,switch.thermostat_dhw_disinfecting,6,9,1,7,1
"RC3*0, Moduline 3000/1010H, CW400, Sense II, HPC410",thermostat,158,dhw.disinfectday,disinfection day,enum [mo\|tu\|we\|th\|fr\|sa\|su\|all], ,true,select.thermostat_dhw_disinfection_day,select.thermostat_dhw_disinfectday,6,9,1,8,1
"RC3*0, Moduline 3000/1010H, CW400, Sense II, HPC410",thermostat,158,dhw.disinfecttime,disinfection time,uint8 (>=0<=1431),minutes,true,number.thermostat_dhw_disinfection_time,number.thermostat_dhw_disinfecttime,6,9,15,9,1
"RC3*0, Moduline 3000/1010H, CW400, Sense II, HPC410",thermostat,158,dhw.dailyheating,daily heating,boolean, ,true,switch.thermostat_dhw_daily_heating,switch.thermostat_dhw_dailyheating,6,9,1,10,1
"RC3*0, Moduline 3000/1010H, CW400, Sense II, HPC410",thermostat,158,dhw.dailyheattime,daily heating time,uint8 (>=0<=1431),minutes,true,number.thermostat_dhw_daily_heating_time,number.thermostat_dhw_dailyheattime,6,9,15,11,1
"RC3*0, Moduline 3000/1010H, CW400, Sense II, HPC410",thermostat,158,dhw.modetype,mode type,enum [off\|eco\|comfort\|eco+], ,false,sensor.thermostat_dhw_mode_type,sensor.thermostat_dhw_modetype,6,9,1,1,1
"RC3*0, Moduline 3000/1010H, CW400, Sense II, HPC410",thermostat,158,dhw.settemp,set temperature,uint8 (>=0<=254),C,true,number.thermostat_dhw_set_temperature,number.thermostat_dhw_settemp,6,9,1,2,1
"RC3*0, Moduline 3000/1010H, CW400, Sense II, HPC410",thermostat,158,dhw.settemplow,set low temperature,uint8 (>=0<=254),C,true,number.thermostat_dhw_set_low_temperature,number.thermostat_dhw_settemplow,6,9,1,3,1
"RC3*0, Moduline 3000/1010H, CW400, Sense II, HPC410",thermostat,158,dhw.circmode,circulation pump mode,enum [off\|on\|auto\|own prog], ,true,select.thermostat_dhw_circulation_pump_mode,select.thermostat_dhw_circmode,6,9,1,4,1
"RC3*0, Moduline 3000/1010H, CW400, Sense II, HPC410",thermostat,158,dhw.chargeduration,charge duration,uint8 (>=0<=3810),minutes,true,number.thermostat_dhw_charge_duration,number.thermostat_dhw_chargeduration,6,9,15,5,1
"RC3*0, Moduline 3000/1010H, CW400, Sense II, HPC410",thermostat,158,dhw.charge,charge,boolean, ,true,switch.thermostat_dhw_charge,switch.thermostat_dhw_charge,6,9,1,6,1
"RC3*0, Moduline 3000/1010H, CW400, Sense II, HPC410",thermostat,158,dhw.extra,extra,boolean, ,false,binary_sensor.thermostat_dhw_extra,binary_sensor.thermostat_dhw_extra,6,9,1,7,1
"RC3*0, Moduline 3000/1010H, CW400, Sense II, HPC410",thermostat,158,dhw.disinfecting,disinfecting,boolean, ,true,switch.thermostat_dhw_disinfecting,switch.thermostat_dhw_disinfecting,6,9,1,8,1
"RC3*0, Moduline 3000/1010H, CW400, Sense II, HPC410",thermostat,158,dhw.disinfectday,disinfection day,enum [mo\|tu\|we\|th\|fr\|sa\|su\|all], ,true,select.thermostat_dhw_disinfection_day,select.thermostat_dhw_disinfectday,6,9,1,9,1
"RC3*0, Moduline 3000/1010H, CW400, Sense II, HPC410",thermostat,158,dhw.disinfecttime,disinfection time,uint8 (>=0<=1431),minutes,true,number.thermostat_dhw_disinfection_time,number.thermostat_dhw_disinfecttime,6,9,15,10,1
"RC3*0, Moduline 3000/1010H, CW400, Sense II, HPC410",thermostat,158,dhw.dailyheating,daily heating,boolean, ,true,switch.thermostat_dhw_daily_heating,switch.thermostat_dhw_dailyheating,6,9,1,11,1
"RC3*0, Moduline 3000/1010H, CW400, Sense II, HPC410",thermostat,158,dhw.dailyheattime,daily heating time,uint8 (>=0<=1431),minutes,true,number.thermostat_dhw_daily_heating_time,number.thermostat_dhw_dailyheattime,6,9,15,12,1
"RC100, CR10, Moduline 1000/1010",thermostat,165,errorcode,error code,string, ,false,sensor.thermostat_error_code,sensor.thermostat_errorcode,6,0,1,0,8
"RC100, CR10, Moduline 1000/1010",thermostat,165,lastcode,last error code,string, ,false,sensor.thermostat_last_error_code,sensor.thermostat_lastcode,6,0,1,8,25
"RC100, CR10, Moduline 1000/1010",thermostat,165,hc1.seltemp,selected room temperature,int16 (>=0<=30),C,true,number.thermostat_hc1_selected_room_temperature,number.thermostat_hc1_seltemp,6,1,1/2,0,1
@@ -4485,17 +4488,18 @@ device name,device type,product id,shortname,fullname,type [options...] \| (min/
"Rego 2000/3000",thermostat,172,hc1.solarinfl,solar influence,uint8 (>=-5<=4294967295),C,true,number.thermostat_hc1_solar_influence,number.thermostat_hc1_solarinfl,6,1,1,54,1
"Rego 2000/3000",thermostat,172,hc1.currsolarinfl,curent solar influence,uint8 (>=0<=25),C,false,sensor.thermostat_hc1_curent_solar_influence,sensor.thermostat_hc1_currsolarinfl,6,1,1/10,55,1
"Rego 2000/3000",thermostat,172,dhw.mode,operating mode,enum [normal\|comfort\|eco+], ,true,select.thermostat_dhw_operating_mode,select.thermostat_dhw_mode,6,9,1,0,1
"Rego 2000/3000",thermostat,172,dhw.settemp,set temperature,uint8 (>=0<=254),C,true,number.thermostat_dhw_set_temperature,number.thermostat_dhw_settemp,6,9,1,1,1
"Rego 2000/3000",thermostat,172,dhw.settemplow,set low temperature,uint8 (>=0<=254),C,true,number.thermostat_dhw_set_low_temperature,number.thermostat_dhw_settemplow,6,9,1,2,1
"Rego 2000/3000",thermostat,172,dhw.circmode,circulation pump mode,enum [off\|on\|auto\|own prog], ,true,select.thermostat_dhw_circulation_pump_mode,select.thermostat_dhw_circmode,6,9,1,3,1
"Rego 2000/3000",thermostat,172,dhw.chargeduration,charge duration,uint8 (>=0<=3810),minutes,true,number.thermostat_dhw_charge_duration,number.thermostat_dhw_chargeduration,6,9,15,4,1
"Rego 2000/3000",thermostat,172,dhw.charge,charge,boolean, ,true,switch.thermostat_dhw_charge,switch.thermostat_dhw_charge,6,9,1,5,1
"Rego 2000/3000",thermostat,172,dhw.extra,extra,boolean, ,false,binary_sensor.thermostat_dhw_extra,binary_sensor.thermostat_dhw_extra,6,9,1,6,1
"Rego 2000/3000",thermostat,172,dhw.disinfecting,disinfecting,boolean, ,true,switch.thermostat_dhw_disinfecting,switch.thermostat_dhw_disinfecting,6,9,1,7,1
"Rego 2000/3000",thermostat,172,dhw.disinfectday,disinfection day,enum [mo\|tu\|we\|th\|fr\|sa\|su\|all], ,true,select.thermostat_dhw_disinfection_day,select.thermostat_dhw_disinfectday,6,9,1,8,1
"Rego 2000/3000",thermostat,172,dhw.disinfecttime,disinfection time,uint8 (>=0<=1431),minutes,true,number.thermostat_dhw_disinfection_time,number.thermostat_dhw_disinfecttime,6,9,15,9,1
"Rego 2000/3000",thermostat,172,dhw.dailyheating,daily heating,boolean, ,true,switch.thermostat_dhw_daily_heating,switch.thermostat_dhw_dailyheating,6,9,1,10,1
"Rego 2000/3000",thermostat,172,dhw.dailyheattime,daily heating time,uint8 (>=0<=1431),minutes,true,number.thermostat_dhw_daily_heating_time,number.thermostat_dhw_dailyheattime,6,9,15,11,1
"Rego 2000/3000",thermostat,172,dhw.modetype,mode type,enum [off\|eco\|comfort\|eco+], ,false,sensor.thermostat_dhw_mode_type,sensor.thermostat_dhw_modetype,6,9,1,1,1
"Rego 2000/3000",thermostat,172,dhw.settemp,set temperature,uint8 (>=0<=254),C,true,number.thermostat_dhw_set_temperature,number.thermostat_dhw_settemp,6,9,1,2,1
"Rego 2000/3000",thermostat,172,dhw.settemplow,set low temperature,uint8 (>=0<=254),C,true,number.thermostat_dhw_set_low_temperature,number.thermostat_dhw_settemplow,6,9,1,3,1
"Rego 2000/3000",thermostat,172,dhw.circmode,circulation pump mode,enum [off\|on\|auto\|own prog], ,true,select.thermostat_dhw_circulation_pump_mode,select.thermostat_dhw_circmode,6,9,1,4,1
"Rego 2000/3000",thermostat,172,dhw.chargeduration,charge duration,uint8 (>=0<=3810),minutes,true,number.thermostat_dhw_charge_duration,number.thermostat_dhw_chargeduration,6,9,15,5,1
"Rego 2000/3000",thermostat,172,dhw.charge,charge,boolean, ,true,switch.thermostat_dhw_charge,switch.thermostat_dhw_charge,6,9,1,6,1
"Rego 2000/3000",thermostat,172,dhw.extra,extra,boolean, ,false,binary_sensor.thermostat_dhw_extra,binary_sensor.thermostat_dhw_extra,6,9,1,7,1
"Rego 2000/3000",thermostat,172,dhw.disinfecting,disinfecting,boolean, ,true,switch.thermostat_dhw_disinfecting,switch.thermostat_dhw_disinfecting,6,9,1,8,1
"Rego 2000/3000",thermostat,172,dhw.disinfectday,disinfection day,enum [mo\|tu\|we\|th\|fr\|sa\|su\|all], ,true,select.thermostat_dhw_disinfection_day,select.thermostat_dhw_disinfectday,6,9,1,9,1
"Rego 2000/3000",thermostat,172,dhw.disinfecttime,disinfection time,uint8 (>=0<=1431),minutes,true,number.thermostat_dhw_disinfection_time,number.thermostat_dhw_disinfecttime,6,9,15,10,1
"Rego 2000/3000",thermostat,172,dhw.dailyheating,daily heating,boolean, ,true,switch.thermostat_dhw_daily_heating,switch.thermostat_dhw_dailyheating,6,9,1,11,1
"Rego 2000/3000",thermostat,172,dhw.dailyheattime,daily heating time,uint8 (>=0<=1431),minutes,true,number.thermostat_dhw_daily_heating_time,number.thermostat_dhw_dailyheattime,6,9,15,12,1
"Comfort RF",thermostat,215,errorcode,error code,string, ,false,sensor.thermostat_error_code,sensor.thermostat_errorcode,6,0,1,0,8
"Comfort RF",thermostat,215,lastcode,last error code,string, ,false,sensor.thermostat_last_error_code,sensor.thermostat_lastcode,6,0,1,8,25
"Comfort RF",thermostat,215,datetime,date/time,string, ,false,sensor.thermostat_date/time,sensor.thermostat_datetime,6,0,1,33,13
@@ -4602,17 +4606,18 @@ device name,device type,product id,shortname,fullname,type [options...] \| (min/
"Rego 3000, UI800, Logamatic BC400",thermostat,253,hc1.solarinfl,solar influence,uint8 (>=-5<=4294967295),C,true,number.thermostat_hc1_solar_influence,number.thermostat_hc1_solarinfl,6,1,1,54,1
"Rego 3000, UI800, Logamatic BC400",thermostat,253,hc1.currsolarinfl,curent solar influence,uint8 (>=0<=25),C,false,sensor.thermostat_hc1_curent_solar_influence,sensor.thermostat_hc1_currsolarinfl,6,1,1/10,55,1
"Rego 3000, UI800, Logamatic BC400",thermostat,253,dhw.mode,operating mode,enum [off\|eco+\|eco\|comfort\|auto], ,true,select.thermostat_dhw_operating_mode,select.thermostat_dhw_mode,6,9,1,0,1
"Rego 3000, UI800, Logamatic BC400",thermostat,253,dhw.settemp,set temperature,uint8 (>=0<=254),C,true,number.thermostat_dhw_set_temperature,number.thermostat_dhw_settemp,6,9,1,1,1
"Rego 3000, UI800, Logamatic BC400",thermostat,253,dhw.settemplow,set low temperature,uint8 (>=0<=254),C,true,number.thermostat_dhw_set_low_temperature,number.thermostat_dhw_settemplow,6,9,1,2,1
"Rego 3000, UI800, Logamatic BC400",thermostat,253,dhw.circmode,circulation pump mode,enum [off\|on\|auto\|own prog], ,true,select.thermostat_dhw_circulation_pump_mode,select.thermostat_dhw_circmode,6,9,1,3,1
"Rego 3000, UI800, Logamatic BC400",thermostat,253,dhw.chargeduration,charge duration,uint8 (>=0<=3810),minutes,true,number.thermostat_dhw_charge_duration,number.thermostat_dhw_chargeduration,6,9,15,4,1
"Rego 3000, UI800, Logamatic BC400",thermostat,253,dhw.charge,charge,boolean, ,true,switch.thermostat_dhw_charge,switch.thermostat_dhw_charge,6,9,1,5,1
"Rego 3000, UI800, Logamatic BC400",thermostat,253,dhw.extra,extra,boolean, ,false,binary_sensor.thermostat_dhw_extra,binary_sensor.thermostat_dhw_extra,6,9,1,6,1
"Rego 3000, UI800, Logamatic BC400",thermostat,253,dhw.disinfecting,disinfecting,boolean, ,true,switch.thermostat_dhw_disinfecting,switch.thermostat_dhw_disinfecting,6,9,1,7,1
"Rego 3000, UI800, Logamatic BC400",thermostat,253,dhw.disinfectday,disinfection day,enum [mo\|tu\|we\|th\|fr\|sa\|su\|all], ,true,select.thermostat_dhw_disinfection_day,select.thermostat_dhw_disinfectday,6,9,1,8,1
"Rego 3000, UI800, Logamatic BC400",thermostat,253,dhw.disinfecttime,disinfection time,uint8 (>=0<=1431),minutes,true,number.thermostat_dhw_disinfection_time,number.thermostat_dhw_disinfecttime,6,9,15,9,1
"Rego 3000, UI800, Logamatic BC400",thermostat,253,dhw.dailyheating,daily heating,boolean, ,true,switch.thermostat_dhw_daily_heating,switch.thermostat_dhw_dailyheating,6,9,1,10,1
"Rego 3000, UI800, Logamatic BC400",thermostat,253,dhw.dailyheattime,daily heating time,uint8 (>=0<=1431),minutes,true,number.thermostat_dhw_daily_heating_time,number.thermostat_dhw_dailyheattime,6,9,15,11,1
"Rego 3000, UI800, Logamatic BC400",thermostat,253,dhw.modetype,mode type,enum [off\|eco\|comfort\|eco+], ,false,sensor.thermostat_dhw_mode_type,sensor.thermostat_dhw_modetype,6,9,1,1,1
"Rego 3000, UI800, Logamatic BC400",thermostat,253,dhw.settemp,set temperature,uint8 (>=0<=254),C,true,number.thermostat_dhw_set_temperature,number.thermostat_dhw_settemp,6,9,1,2,1
"Rego 3000, UI800, Logamatic BC400",thermostat,253,dhw.settemplow,set low temperature,uint8 (>=0<=254),C,true,number.thermostat_dhw_set_low_temperature,number.thermostat_dhw_settemplow,6,9,1,3,1
"Rego 3000, UI800, Logamatic BC400",thermostat,253,dhw.circmode,circulation pump mode,enum [off\|on\|auto\|own prog], ,true,select.thermostat_dhw_circulation_pump_mode,select.thermostat_dhw_circmode,6,9,1,4,1
"Rego 3000, UI800, Logamatic BC400",thermostat,253,dhw.chargeduration,charge duration,uint8 (>=0<=3810),minutes,true,number.thermostat_dhw_charge_duration,number.thermostat_dhw_chargeduration,6,9,15,5,1
"Rego 3000, UI800, Logamatic BC400",thermostat,253,dhw.charge,charge,boolean, ,true,switch.thermostat_dhw_charge,switch.thermostat_dhw_charge,6,9,1,6,1
"Rego 3000, UI800, Logamatic BC400",thermostat,253,dhw.extra,extra,boolean, ,false,binary_sensor.thermostat_dhw_extra,binary_sensor.thermostat_dhw_extra,6,9,1,7,1
"Rego 3000, UI800, Logamatic BC400",thermostat,253,dhw.disinfecting,disinfecting,boolean, ,true,switch.thermostat_dhw_disinfecting,switch.thermostat_dhw_disinfecting,6,9,1,8,1
"Rego 3000, UI800, Logamatic BC400",thermostat,253,dhw.disinfectday,disinfection day,enum [mo\|tu\|we\|th\|fr\|sa\|su\|all], ,true,select.thermostat_dhw_disinfection_day,select.thermostat_dhw_disinfectday,6,9,1,9,1
"Rego 3000, UI800, Logamatic BC400",thermostat,253,dhw.disinfecttime,disinfection time,uint8 (>=0<=1431),minutes,true,number.thermostat_dhw_disinfection_time,number.thermostat_dhw_disinfecttime,6,9,15,10,1
"Rego 3000, UI800, Logamatic BC400",thermostat,253,dhw.dailyheating,daily heating,boolean, ,true,switch.thermostat_dhw_daily_heating,switch.thermostat_dhw_dailyheating,6,9,1,11,1
"Rego 3000, UI800, Logamatic BC400",thermostat,253,dhw.dailyheattime,daily heating time,uint8 (>=0<=1431),minutes,true,number.thermostat_dhw_daily_heating_time,number.thermostat_dhw_dailyheattime,6,9,15,12,1
"ES72, RC20",thermostat,66,errorcode,error code,string, ,false,sensor.thermostat_error_code,sensor.thermostat_errorcode,6,0,1,0,8
"ES72, RC20",thermostat,66,lastcode,last error code,string, ,false,sensor.thermostat_last_error_code,sensor.thermostat_lastcode,6,0,1,8,25
"ES72, RC20",thermostat,66,datetime,date/time,string, ,false,sensor.thermostat_date/time,sensor.thermostat_datetime,6,0,1,33,13
@@ -4684,18 +4689,18 @@ device name,device type,product id,shortname,fullname,type [options...] \| (min/
"ES73",thermostat,76,hc1.switchtime1,own1 program switchtime,string, ,true,sensor.thermostat_hc1_own1_program_switchtime,sensor.thermostat_hc1_switchtime1,6,1,1,91,8
"ES73",thermostat,76,hc1.switchtime2,own2 program switchtime,string, ,true,sensor.thermostat_hc1_own2_program_switchtime,sensor.thermostat_hc1_switchtime2,6,1,1,99,8
"ES73",thermostat,76,dhw.mode,operating mode,enum [off\|on\|auto], ,true,select.thermostat_dhw_operating_mode,select.thermostat_dhw_mode,6,9,1,0,1
"ES73",thermostat,76,dhw.circmode,circulation pump mode,enum [off\|on\|auto], ,true,select.thermostat_dhw_circulation_pump_mode,select.thermostat_dhw_circmode,6,9,1,3,1
"ES73",thermostat,76,dhw.progmode,program,enum [std prog\|own prog], ,true,select.thermostat_dhw_program,select.thermostat_dhw_progmode,6,9,1,12,1
"ES73",thermostat,76,dhw.circprog,circulation program,enum [std prog\|own prog], ,true,select.thermostat_dhw_circulation_program,select.thermostat_dhw_circprog,6,9,1,13,1
"ES73",thermostat,76,dhw.disinfecting,disinfecting,boolean, ,true,switch.thermostat_dhw_disinfecting,switch.thermostat_dhw_disinfecting,6,9,1,7,1
"ES73",thermostat,76,dhw.disinfectday,disinfection day,enum [mo\|tu\|we\|th\|fr\|sa\|su\|all], ,true,select.thermostat_dhw_disinfection_day,select.thermostat_dhw_disinfectday,6,9,1,8,1
"ES73",thermostat,76,dhw.disinfecthour,disinfection hour,uint8 (>=0<=23), ,true,number.thermostat_dhw_disinfection_hour,number.thermostat_dhw_disinfecthour,6,9,1,14,1
"ES73",thermostat,76,dhw.maxtemp,maximum temperature,uint8 (>=0<=254),C,true,number.thermostat_dhw_maximum_temperature,number.thermostat_dhw_maxtemp,6,9,1,15,1
"ES73",thermostat,76,dhw.onetimekey,one time key function,boolean, ,true,switch.thermostat_dhw_one_time_key_function,switch.thermostat_dhw_onetimekey,6,9,1,16,1
"ES73",thermostat,76,dhw.switchtime,program switchtime,string, ,true,sensor.thermostat_dhw_program_switchtime,sensor.thermostat_dhw_switchtime,6,9,1,17,8
"ES73",thermostat,76,dhw.circswitchtime,circulation program switchtime,string, ,true,sensor.thermostat_dhw_circulation_program_switchtime,sensor.thermostat_dhw_circswitchtime,6,9,1,25,8
"ES73",thermostat,76,dhw.holidays,holiday dates,string, ,true,sensor.thermostat_dhw_holiday_dates,sensor.thermostat_dhw_holidays,6,9,1,33,13
"ES73",thermostat,76,dhw.vacations,vacation dates,string, ,true,sensor.thermostat_dhw_vacation_dates,sensor.thermostat_dhw_vacations,6,9,1,46,13
"ES73",thermostat,76,dhw.circmode,circulation pump mode,enum [off\|on\|auto], ,true,select.thermostat_dhw_circulation_pump_mode,select.thermostat_dhw_circmode,6,9,1,4,1
"ES73",thermostat,76,dhw.progmode,program,enum [std prog\|own prog], ,true,select.thermostat_dhw_program,select.thermostat_dhw_progmode,6,9,1,13,1
"ES73",thermostat,76,dhw.circprog,circulation program,enum [std prog\|own prog], ,true,select.thermostat_dhw_circulation_program,select.thermostat_dhw_circprog,6,9,1,14,1
"ES73",thermostat,76,dhw.disinfecting,disinfecting,boolean, ,true,switch.thermostat_dhw_disinfecting,switch.thermostat_dhw_disinfecting,6,9,1,8,1
"ES73",thermostat,76,dhw.disinfectday,disinfection day,enum [mo\|tu\|we\|th\|fr\|sa\|su\|all], ,true,select.thermostat_dhw_disinfection_day,select.thermostat_dhw_disinfectday,6,9,1,9,1
"ES73",thermostat,76,dhw.disinfecthour,disinfection hour,uint8 (>=0<=23), ,true,number.thermostat_dhw_disinfection_hour,number.thermostat_dhw_disinfecthour,6,9,1,15,1
"ES73",thermostat,76,dhw.maxtemp,maximum temperature,uint8 (>=0<=254),C,true,number.thermostat_dhw_maximum_temperature,number.thermostat_dhw_maxtemp,6,9,1,16,1
"ES73",thermostat,76,dhw.onetimekey,one time key function,boolean, ,true,switch.thermostat_dhw_one_time_key_function,switch.thermostat_dhw_onetimekey,6,9,1,17,1
"ES73",thermostat,76,dhw.switchtime,program switchtime,string, ,true,sensor.thermostat_dhw_program_switchtime,sensor.thermostat_dhw_switchtime,6,9,1,18,8
"ES73",thermostat,76,dhw.circswitchtime,circulation program switchtime,string, ,true,sensor.thermostat_dhw_circulation_program_switchtime,sensor.thermostat_dhw_circswitchtime,6,9,1,26,8
"ES73",thermostat,76,dhw.holidays,holiday dates,string, ,true,sensor.thermostat_dhw_holiday_dates,sensor.thermostat_dhw_holidays,6,9,1,34,13
"ES73",thermostat,76,dhw.vacations,vacation dates,string, ,true,sensor.thermostat_dhw_vacation_dates,sensor.thermostat_dhw_vacations,6,9,1,47,13
"ES72, RC20",thermostat,113,errorcode,error code,string, ,false,sensor.thermostat_error_code,sensor.thermostat_errorcode,6,0,1,0,8
"ES72, RC20",thermostat,113,lastcode,last error code,string, ,false,sensor.thermostat_last_error_code,sensor.thermostat_lastcode,6,0,1,8,25
"ES72, RC20",thermostat,113,datetime,date/time,string, ,false,sensor.thermostat_date/time,sensor.thermostat_datetime,6,0,1,33,13
@@ -4764,18 +4769,18 @@ device name,device type,product id,shortname,fullname,type [options...] \| (min/
"ES79",thermostat,156,hc1.switchtime1,own1 program switchtime,string, ,true,sensor.thermostat_hc1_own1_program_switchtime,sensor.thermostat_hc1_switchtime1,6,1,1,91,8
"ES79",thermostat,156,hc1.switchtime2,own2 program switchtime,string, ,true,sensor.thermostat_hc1_own2_program_switchtime,sensor.thermostat_hc1_switchtime2,6,1,1,99,8
"ES79",thermostat,156,dhw.mode,operating mode,enum [off\|on\|auto], ,true,select.thermostat_dhw_operating_mode,select.thermostat_dhw_mode,6,9,1,0,1
"ES79",thermostat,156,dhw.circmode,circulation pump mode,enum [off\|on\|auto], ,true,select.thermostat_dhw_circulation_pump_mode,select.thermostat_dhw_circmode,6,9,1,3,1
"ES79",thermostat,156,dhw.progmode,program,enum [std prog\|own prog], ,true,select.thermostat_dhw_program,select.thermostat_dhw_progmode,6,9,1,12,1
"ES79",thermostat,156,dhw.circprog,circulation program,enum [std prog\|own prog], ,true,select.thermostat_dhw_circulation_program,select.thermostat_dhw_circprog,6,9,1,13,1
"ES79",thermostat,156,dhw.disinfecting,disinfecting,boolean, ,true,switch.thermostat_dhw_disinfecting,switch.thermostat_dhw_disinfecting,6,9,1,7,1
"ES79",thermostat,156,dhw.disinfectday,disinfection day,enum [mo\|tu\|we\|th\|fr\|sa\|su\|all], ,true,select.thermostat_dhw_disinfection_day,select.thermostat_dhw_disinfectday,6,9,1,8,1
"ES79",thermostat,156,dhw.disinfecthour,disinfection hour,uint8 (>=0<=23), ,true,number.thermostat_dhw_disinfection_hour,number.thermostat_dhw_disinfecthour,6,9,1,14,1
"ES79",thermostat,156,dhw.maxtemp,maximum temperature,uint8 (>=60<=80),C,true,number.thermostat_dhw_maximum_temperature,number.thermostat_dhw_maxtemp,6,9,1,15,1
"ES79",thermostat,156,dhw.onetimekey,one time key function,boolean, ,true,switch.thermostat_dhw_one_time_key_function,switch.thermostat_dhw_onetimekey,6,9,1,16,1
"ES79",thermostat,156,dhw.switchtime,program switchtime,string, ,true,sensor.thermostat_dhw_program_switchtime,sensor.thermostat_dhw_switchtime,6,9,1,17,8
"ES79",thermostat,156,dhw.circswitchtime,circulation program switchtime,string, ,true,sensor.thermostat_dhw_circulation_program_switchtime,sensor.thermostat_dhw_circswitchtime,6,9,1,25,8
"ES79",thermostat,156,dhw.holidays,holiday dates,string, ,true,sensor.thermostat_dhw_holiday_dates,sensor.thermostat_dhw_holidays,6,9,1,33,13
"ES79",thermostat,156,dhw.vacations,vacation dates,string, ,true,sensor.thermostat_dhw_vacation_dates,sensor.thermostat_dhw_vacations,6,9,1,46,13
"ES79",thermostat,156,dhw.circmode,circulation pump mode,enum [off\|on\|auto], ,true,select.thermostat_dhw_circulation_pump_mode,select.thermostat_dhw_circmode,6,9,1,4,1
"ES79",thermostat,156,dhw.progmode,program,enum [std prog\|own prog], ,true,select.thermostat_dhw_program,select.thermostat_dhw_progmode,6,9,1,13,1
"ES79",thermostat,156,dhw.circprog,circulation program,enum [std prog\|own prog], ,true,select.thermostat_dhw_circulation_program,select.thermostat_dhw_circprog,6,9,1,14,1
"ES79",thermostat,156,dhw.disinfecting,disinfecting,boolean, ,true,switch.thermostat_dhw_disinfecting,switch.thermostat_dhw_disinfecting,6,9,1,8,1
"ES79",thermostat,156,dhw.disinfectday,disinfection day,enum [mo\|tu\|we\|th\|fr\|sa\|su\|all], ,true,select.thermostat_dhw_disinfection_day,select.thermostat_dhw_disinfectday,6,9,1,9,1
"ES79",thermostat,156,dhw.disinfecthour,disinfection hour,uint8 (>=0<=23), ,true,number.thermostat_dhw_disinfection_hour,number.thermostat_dhw_disinfecthour,6,9,1,15,1
"ES79",thermostat,156,dhw.maxtemp,maximum temperature,uint8 (>=60<=80),C,true,number.thermostat_dhw_maximum_temperature,number.thermostat_dhw_maxtemp,6,9,1,16,1
"ES79",thermostat,156,dhw.onetimekey,one time key function,boolean, ,true,switch.thermostat_dhw_one_time_key_function,switch.thermostat_dhw_onetimekey,6,9,1,17,1
"ES79",thermostat,156,dhw.switchtime,program switchtime,string, ,true,sensor.thermostat_dhw_program_switchtime,sensor.thermostat_dhw_switchtime,6,9,1,18,8
"ES79",thermostat,156,dhw.circswitchtime,circulation program switchtime,string, ,true,sensor.thermostat_dhw_circulation_program_switchtime,sensor.thermostat_dhw_circswitchtime,6,9,1,26,8
"ES79",thermostat,156,dhw.holidays,holiday dates,string, ,true,sensor.thermostat_dhw_holiday_dates,sensor.thermostat_dhw_holidays,6,9,1,34,13
"ES79",thermostat,156,dhw.vacations,vacation dates,string, ,true,sensor.thermostat_dhw_vacation_dates,sensor.thermostat_dhw_vacations,6,9,1,47,13
"FW100",thermostat,105,errorcode,error code,string, ,false,sensor.thermostat_error_code,sensor.thermostat_errorcode,6,0,1,0,8
"FW100",thermostat,105,lastcode,last error code,string, ,false,sensor.thermostat_last_error_code,sensor.thermostat_lastcode,6,0,1,8,25
"FW100",thermostat,105,datetime,date/time,string, ,true,sensor.thermostat_date/time,sensor.thermostat_datetime,6,0,1,33,13
@@ -4812,7 +4817,7 @@ device name,device type,product id,shortname,fullname,type [options...] \| (min/
"FW100",thermostat,105,hc1.roominflfactor,room influence factor,uint8 (>=0<=100),%,true,number.thermostat_hc1_room_influence_factor,number.thermostat_hc1_roominflfactor,6,1,10,14,1
"FW100",thermostat,105,hc1.heatingtype,heating type,enum [off\|heatingcurve\|radiator\|convector\|floor], ,true,select.thermostat_hc1_heating_type,select.thermostat_hc1_heatingtype,6,1,1,19,1
"FW100",thermostat,105,hc1.controlmode,control mode,enum [off\|unmixed\|unmixed IPM\|mixed IPM], ,true,select.thermostat_hc1_control_mode,select.thermostat_hc1_controlmode,6,1,1,25,1
"FW100",thermostat,105,dhw.charge,charge,boolean, ,true,switch.thermostat_dhw_charge,switch.thermostat_dhw_charge,6,9,1,5,1
"FW100",thermostat,105,dhw.charge,charge,boolean, ,true,switch.thermostat_dhw_charge,switch.thermostat_dhw_charge,6,9,1,6,1
"FW200",thermostat,106,errorcode,error code,string, ,false,sensor.thermostat_error_code,sensor.thermostat_errorcode,6,0,1,0,8
"FW200",thermostat,106,lastcode,last error code,string, ,false,sensor.thermostat_last_error_code,sensor.thermostat_lastcode,6,0,1,8,25
"FW200",thermostat,106,datetime,date/time,string, ,true,sensor.thermostat_date/time,sensor.thermostat_datetime,6,0,1,33,13
@@ -4849,7 +4854,7 @@ device name,device type,product id,shortname,fullname,type [options...] \| (min/
"FW200",thermostat,106,hc1.roominflfactor,room influence factor,uint8 (>=0<=100),%,true,number.thermostat_hc1_room_influence_factor,number.thermostat_hc1_roominflfactor,6,1,10,14,1
"FW200",thermostat,106,hc1.heatingtype,heating type,enum [off\|heatingcurve\|radiator\|convector\|floor], ,true,select.thermostat_hc1_heating_type,select.thermostat_hc1_heatingtype,6,1,1,19,1
"FW200",thermostat,106,hc1.controlmode,control mode,enum [off\|unmixed\|unmixed IPM\|mixed IPM], ,true,select.thermostat_hc1_control_mode,select.thermostat_hc1_controlmode,6,1,1,25,1
"FW200",thermostat,106,dhw.charge,charge,boolean, ,true,switch.thermostat_dhw_charge,switch.thermostat_dhw_charge,6,9,1,5,1
"FW200",thermostat,106,dhw.charge,charge,boolean, ,true,switch.thermostat_dhw_charge,switch.thermostat_dhw_charge,6,9,1,6,1
"FR100",thermostat,107,errorcode,error code,string, ,false,sensor.thermostat_error_code,sensor.thermostat_errorcode,6,0,1,0,8
"FR100",thermostat,107,lastcode,last error code,string, ,false,sensor.thermostat_last_error_code,sensor.thermostat_lastcode,6,0,1,8,25
"FR100",thermostat,107,datetime,date/time,string, ,false,sensor.thermostat_date/time,sensor.thermostat_datetime,6,0,1,33,13
@@ -4884,7 +4889,7 @@ device name,device type,product id,shortname,fullname,type [options...] \| (min/
"FR100",thermostat,107,hc1.roominflfactor,room influence factor,uint8 (>=0<=100),%,true,number.thermostat_hc1_room_influence_factor,number.thermostat_hc1_roominflfactor,6,1,10,14,1
"FR100",thermostat,107,hc1.heatingtype,heating type,enum [off\|heatingcurve\|radiator\|convector\|floor], ,true,select.thermostat_hc1_heating_type,select.thermostat_hc1_heatingtype,6,1,1,19,1
"FR100",thermostat,107,hc1.controlmode,control mode,enum [off\|unmixed\|unmixed IPM\|mixed IPM], ,true,select.thermostat_hc1_control_mode,select.thermostat_hc1_controlmode,6,1,1,25,1
"FR100",thermostat,107,dhw.charge,charge,boolean, ,true,switch.thermostat_dhw_charge,switch.thermostat_dhw_charge,6,9,1,5,1
"FR100",thermostat,107,dhw.charge,charge,boolean, ,true,switch.thermostat_dhw_charge,switch.thermostat_dhw_charge,6,9,1,6,1
"FR110",thermostat,108,errorcode,error code,string, ,false,sensor.thermostat_error_code,sensor.thermostat_errorcode,6,0,1,0,8
"FR110",thermostat,108,lastcode,last error code,string, ,false,sensor.thermostat_last_error_code,sensor.thermostat_lastcode,6,0,1,8,25
"FR110",thermostat,108,datetime,date/time,string, ,false,sensor.thermostat_date/time,sensor.thermostat_datetime,6,0,1,33,13
@@ -4919,7 +4924,7 @@ device name,device type,product id,shortname,fullname,type [options...] \| (min/
"FR110",thermostat,108,hc1.roominflfactor,room influence factor,uint8 (>=0<=100),%,true,number.thermostat_hc1_room_influence_factor,number.thermostat_hc1_roominflfactor,6,1,10,14,1
"FR110",thermostat,108,hc1.heatingtype,heating type,enum [off\|heatingcurve\|radiator\|convector\|floor], ,true,select.thermostat_hc1_heating_type,select.thermostat_hc1_heatingtype,6,1,1,19,1
"FR110",thermostat,108,hc1.controlmode,control mode,enum [off\|unmixed\|unmixed IPM\|mixed IPM], ,true,select.thermostat_hc1_control_mode,select.thermostat_hc1_controlmode,6,1,1,25,1
"FR110",thermostat,108,dhw.charge,charge,boolean, ,true,switch.thermostat_dhw_charge,switch.thermostat_dhw_charge,6,9,1,5,1
"FR110",thermostat,108,dhw.charge,charge,boolean, ,true,switch.thermostat_dhw_charge,switch.thermostat_dhw_charge,6,9,1,6,1
"FB10",thermostat,109,errorcode,error code,string, ,false,sensor.thermostat_error_code,sensor.thermostat_errorcode,6,0,1,0,8
"FB10",thermostat,109,lastcode,last error code,string, ,false,sensor.thermostat_last_error_code,sensor.thermostat_lastcode,6,0,1,8,25
"FB10",thermostat,109,datetime,date/time,string, ,true,sensor.thermostat_date/time,sensor.thermostat_datetime,6,0,1,33,13
@@ -4956,7 +4961,7 @@ device name,device type,product id,shortname,fullname,type [options...] \| (min/
"FB10",thermostat,109,hc1.roominflfactor,room influence factor,uint8 (>=0<=100),%,true,number.thermostat_hc1_room_influence_factor,number.thermostat_hc1_roominflfactor,6,1,10,14,1
"FB10",thermostat,109,hc1.heatingtype,heating type,enum [off\|heatingcurve\|radiator\|convector\|floor], ,true,select.thermostat_hc1_heating_type,select.thermostat_hc1_heatingtype,6,1,1,19,1
"FB10",thermostat,109,hc1.controlmode,control mode,enum [off\|unmixed\|unmixed IPM\|mixed IPM], ,true,select.thermostat_hc1_control_mode,select.thermostat_hc1_controlmode,6,1,1,25,1
"FB10",thermostat,109,dhw.charge,charge,boolean, ,true,switch.thermostat_dhw_charge,switch.thermostat_dhw_charge,6,9,1,5,1
"FB10",thermostat,109,dhw.charge,charge,boolean, ,true,switch.thermostat_dhw_charge,switch.thermostat_dhw_charge,6,9,1,6,1
"FB100",thermostat,110,errorcode,error code,string, ,false,sensor.thermostat_error_code,sensor.thermostat_errorcode,6,0,1,0,8
"FB100",thermostat,110,lastcode,last error code,string, ,false,sensor.thermostat_last_error_code,sensor.thermostat_lastcode,6,0,1,8,25
"FB100",thermostat,110,datetime,date/time,string, ,true,sensor.thermostat_date/time,sensor.thermostat_datetime,6,0,1,33,13
@@ -4993,7 +4998,7 @@ device name,device type,product id,shortname,fullname,type [options...] \| (min/
"FB100",thermostat,110,hc1.roominflfactor,room influence factor,uint8 (>=0<=100),%,true,number.thermostat_hc1_room_influence_factor,number.thermostat_hc1_roominflfactor,6,1,10,14,1
"FB100",thermostat,110,hc1.heatingtype,heating type,enum [off\|heatingcurve\|radiator\|convector\|floor], ,true,select.thermostat_hc1_heating_type,select.thermostat_hc1_heatingtype,6,1,1,19,1
"FB100",thermostat,110,hc1.controlmode,control mode,enum [off\|unmixed\|unmixed IPM\|mixed IPM], ,true,select.thermostat_hc1_control_mode,select.thermostat_hc1_controlmode,6,1,1,25,1
"FB100",thermostat,110,dhw.charge,charge,boolean, ,true,switch.thermostat_dhw_charge,switch.thermostat_dhw_charge,6,9,1,5,1
"FB100",thermostat,110,dhw.charge,charge,boolean, ,true,switch.thermostat_dhw_charge,switch.thermostat_dhw_charge,6,9,1,6,1
"FR10",thermostat,111,errorcode,error code,string, ,false,sensor.thermostat_error_code,sensor.thermostat_errorcode,6,0,1,0,8
"FR10",thermostat,111,lastcode,last error code,string, ,false,sensor.thermostat_last_error_code,sensor.thermostat_lastcode,6,0,1,8,25
"FR10",thermostat,111,datetime,date/time,string, ,false,sensor.thermostat_date/time,sensor.thermostat_datetime,6,0,1,33,13
@@ -5028,7 +5033,7 @@ device name,device type,product id,shortname,fullname,type [options...] \| (min/
"FR10",thermostat,111,hc1.roominflfactor,room influence factor,uint8 (>=0<=100),%,true,number.thermostat_hc1_room_influence_factor,number.thermostat_hc1_roominflfactor,6,1,10,14,1
"FR10",thermostat,111,hc1.heatingtype,heating type,enum [off\|heatingcurve\|radiator\|convector\|floor], ,true,select.thermostat_hc1_heating_type,select.thermostat_hc1_heatingtype,6,1,1,19,1
"FR10",thermostat,111,hc1.controlmode,control mode,enum [off\|unmixed\|unmixed IPM\|mixed IPM], ,true,select.thermostat_hc1_control_mode,select.thermostat_hc1_controlmode,6,1,1,25,1
"FR10",thermostat,111,dhw.charge,charge,boolean, ,true,switch.thermostat_dhw_charge,switch.thermostat_dhw_charge,6,9,1,5,1
"FR10",thermostat,111,dhw.charge,charge,boolean, ,true,switch.thermostat_dhw_charge,switch.thermostat_dhw_charge,6,9,1,6,1
"FW500",thermostat,116,errorcode,error code,string, ,false,sensor.thermostat_error_code,sensor.thermostat_errorcode,6,0,1,0,8
"FW500",thermostat,116,lastcode,last error code,string, ,false,sensor.thermostat_last_error_code,sensor.thermostat_lastcode,6,0,1,8,25
"FW500",thermostat,116,datetime,date/time,string, ,true,sensor.thermostat_date/time,sensor.thermostat_datetime,6,0,1,33,13
@@ -5065,7 +5070,7 @@ device name,device type,product id,shortname,fullname,type [options...] \| (min/
"FW500",thermostat,116,hc1.roominflfactor,room influence factor,uint8 (>=0<=100),%,true,number.thermostat_hc1_room_influence_factor,number.thermostat_hc1_roominflfactor,6,1,10,14,1
"FW500",thermostat,116,hc1.heatingtype,heating type,enum [off\|heatingcurve\|radiator\|convector\|floor], ,true,select.thermostat_hc1_heating_type,select.thermostat_hc1_heatingtype,6,1,1,19,1
"FW500",thermostat,116,hc1.controlmode,control mode,enum [off\|unmixed\|unmixed IPM\|mixed IPM], ,true,select.thermostat_hc1_control_mode,select.thermostat_hc1_controlmode,6,1,1,25,1
"FW500",thermostat,116,dhw.charge,charge,boolean, ,true,switch.thermostat_dhw_charge,switch.thermostat_dhw_charge,6,9,1,5,1
"FW500",thermostat,116,dhw.charge,charge,boolean, ,true,switch.thermostat_dhw_charge,switch.thermostat_dhw_charge,6,9,1,6,1
"FR50",thermostat,147,errorcode,error code,string, ,false,sensor.thermostat_error_code,sensor.thermostat_errorcode,6,0,1,0,8
"FR50",thermostat,147,lastcode,last error code,string, ,false,sensor.thermostat_last_error_code,sensor.thermostat_lastcode,6,0,1,8,25
"FR50",thermostat,147,datetime,date/time,string, ,false,sensor.thermostat_date/time,sensor.thermostat_datetime,6,0,1,33,13
@@ -5100,7 +5105,7 @@ device name,device type,product id,shortname,fullname,type [options...] \| (min/
"FR50",thermostat,147,hc1.roominflfactor,room influence factor,uint8 (>=0<=100),%,true,number.thermostat_hc1_room_influence_factor,number.thermostat_hc1_roominflfactor,6,1,10,14,1
"FR50",thermostat,147,hc1.heatingtype,heating type,enum [off\|heatingcurve\|radiator\|convector\|floor], ,true,select.thermostat_hc1_heating_type,select.thermostat_hc1_heatingtype,6,1,1,19,1
"FR50",thermostat,147,hc1.controlmode,control mode,enum [off\|unmixed\|unmixed IPM\|mixed IPM], ,true,select.thermostat_hc1_control_mode,select.thermostat_hc1_controlmode,6,1,1,25,1
"FR50",thermostat,147,dhw.charge,charge,boolean, ,true,switch.thermostat_dhw_charge,switch.thermostat_dhw_charge,6,9,1,5,1
"FR50",thermostat,147,dhw.charge,charge,boolean, ,true,switch.thermostat_dhw_charge,switch.thermostat_dhw_charge,6,9,1,6,1
"FR120",thermostat,191,errorcode,error code,string, ,false,sensor.thermostat_error_code,sensor.thermostat_errorcode,6,0,1,0,8
"FR120",thermostat,191,lastcode,last error code,string, ,false,sensor.thermostat_last_error_code,sensor.thermostat_lastcode,6,0,1,8,25
"FR120",thermostat,191,datetime,date/time,string, ,false,sensor.thermostat_date/time,sensor.thermostat_datetime,6,0,1,33,13
@@ -5135,7 +5140,7 @@ device name,device type,product id,shortname,fullname,type [options...] \| (min/
"FR120",thermostat,191,hc1.roominflfactor,room influence factor,uint8 (>=0<=100),%,true,number.thermostat_hc1_room_influence_factor,number.thermostat_hc1_roominflfactor,6,1,10,14,1
"FR120",thermostat,191,hc1.heatingtype,heating type,enum [off\|heatingcurve\|radiator\|convector\|floor], ,true,select.thermostat_hc1_heating_type,select.thermostat_hc1_heatingtype,6,1,1,19,1
"FR120",thermostat,191,hc1.controlmode,control mode,enum [off\|unmixed\|unmixed IPM\|mixed IPM], ,true,select.thermostat_hc1_control_mode,select.thermostat_hc1_controlmode,6,1,1,25,1
"FR120",thermostat,191,dhw.charge,charge,boolean, ,true,switch.thermostat_dhw_charge,switch.thermostat_dhw_charge,6,9,1,5,1
"FR120",thermostat,191,dhw.charge,charge,boolean, ,true,switch.thermostat_dhw_charge,switch.thermostat_dhw_charge,6,9,1,6,1
"FW120",thermostat,192,errorcode,error code,string, ,false,sensor.thermostat_error_code,sensor.thermostat_errorcode,6,0,1,0,8
"FW120",thermostat,192,lastcode,last error code,string, ,false,sensor.thermostat_last_error_code,sensor.thermostat_lastcode,6,0,1,8,25
"FW120",thermostat,192,datetime,date/time,string, ,true,sensor.thermostat_date/time,sensor.thermostat_datetime,6,0,1,33,13
@@ -5172,7 +5177,7 @@ device name,device type,product id,shortname,fullname,type [options...] \| (min/
"FW120",thermostat,192,hc1.roominflfactor,room influence factor,uint8 (>=0<=100),%,true,number.thermostat_hc1_room_influence_factor,number.thermostat_hc1_roominflfactor,6,1,10,14,1
"FW120",thermostat,192,hc1.heatingtype,heating type,enum [off\|heatingcurve\|radiator\|convector\|floor], ,true,select.thermostat_hc1_heating_type,select.thermostat_hc1_heatingtype,6,1,1,19,1
"FW120",thermostat,192,hc1.controlmode,control mode,enum [off\|unmixed\|unmixed IPM\|mixed IPM], ,true,select.thermostat_hc1_control_mode,select.thermostat_hc1_controlmode,6,1,1,25,1
"FW120",thermostat,192,dhw.charge,charge,boolean, ,true,switch.thermostat_dhw_charge,switch.thermostat_dhw_charge,6,9,1,5,1
"FW120",thermostat,192,dhw.charge,charge,boolean, ,true,switch.thermostat_dhw_charge,switch.thermostat_dhw_charge,6,9,1,6,1
"RT800, RC220",thermostat,3,errorcode,error code,string, ,false,sensor.thermostat_error_code,sensor.thermostat_errorcode,6,0,1,0,8
"RT800, RC220",thermostat,3,lastcode,last error code,string, ,false,sensor.thermostat_last_error_code,sensor.thermostat_lastcode,6,0,1,8,25
"RT800, RC220",thermostat,3,datetime,date/time,string, ,false,sensor.thermostat_date/time,sensor.thermostat_datetime,6,0,1,33,13
@@ -5621,6 +5626,8 @@ device name,device type,product id,shortname,fullname,type [options...] \| (min/
"WM10",switch,71,flowtemphc,flow temperature (TC1),uint16 (>=0<=3199),C,false,sensor.switch_flow_temperature_(TC1),sensor.switch_flowtemphc,11,0,1/10,1,1
"WM10",switch,71,status,status,int8 (>=-126<=126), ,false,sensor.switch_status,sensor.switch_status,11,0,1,2,1
"Rego 3000",controller,240,datetime,date/time,string, ,false,sensor.controller_date/time,sensor.controller_datetime,12,0,1,0,13
"MX400",connect,17,datetime,date/time,string, ,false,sensor.connect_date/time,sensor.connect_datetime,13,0,1,0,13
"MX400",connect,17,outdoortemp,outside temperature,int16 (>=-3199<=3199),C,false,sensor.connect_outside_temperature,sensor.connect_outdoortemp,13,0,1/10,13,1
"EM10",alert,74,setflowtemp,set flow temperature,uint8 (>=0<=254),C,false,sensor.alert_set_flow_temperature,sensor.alert_setflowtemp,14,0,1,0,1
"EM10",alert,74,setburnpow,burner set power,uint8 (>=0<=100),%,false,sensor.alert_burner_set_power,sensor.alert_setburnpow,14,0,1,1,1
"EM10, EM100",extension,243,flowtempvf,flow temperature in header (T0/Vf),int16 (>=-3199<=3199),C,false,sensor.extension_flow_temperature_in_header_(T0/Vf),sensor.extension_flowtempvf,15,0,1/10,0,1
Can't render this file because it is too large.

View File

@@ -10,8 +10,7 @@ export default tseslint.config(
{
languageOptions: {
parserOptions: {
project: true,
tsconfigRootDir: import.meta.dirname
project: true
}
}
},

View File

@@ -1,6 +1,6 @@
{
"name": "EMS-ESP",
"version": "3.7.2",
"version": "3.7.3",
"description": "EMS-ESP WebUI",
"homepage": "https://emsesp.org",
"author": "proddy, emsesp.org",
@@ -63,5 +63,5 @@
"vite-plugin-imagemin": "^0.6.1",
"vite-tsconfig-paths": "^5.1.4"
},
"packageManager": "pnpm@10.19.0"
"packageManager": "pnpm@10.19.0+sha512.c9fc7236e92adf5c8af42fd5bf1612df99c2ceb62f27047032f4720b33f8eacdde311865e91c411f2774f618d82f320808ecb51718bfa82c060c4ba7c76a32b8"
}

View File

@@ -1324,8 +1324,8 @@ packages:
duplexer3@0.1.5:
resolution: {integrity: sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==}
electron-to-chromium@1.5.238:
resolution: {integrity: sha512-khBdc+w/Gv+cS8e/Pbnaw/FXcBUeKrRVik9IxfXtgREOWyJhR4tj43n3amkVogJ/yeQUqzkrZcFhtIxIdqmmcQ==}
electron-to-chromium@1.5.239:
resolution: {integrity: sha512-1y5w0Zsq39MSPmEjHjbizvhYoTaulVtivpxkp5q5kaPmQtsK6/2nvAzGRxNMS9DoYySp9PkW0MAQDwU1m764mg==}
emoji-regex@8.0.0:
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
@@ -3995,7 +3995,7 @@ snapshots:
dependencies:
baseline-browser-mapping: 2.8.19
caniuse-lite: 1.0.30001751
electron-to-chromium: 1.5.238
electron-to-chromium: 1.5.239
node-releases: 2.0.26
update-browserslist-db: 1.1.4(browserslist@4.27.0)
@@ -4340,7 +4340,7 @@ snapshots:
duplexer3@0.1.5: {}
electron-to-chromium@1.5.238: {}
electron-to-chromium@1.5.239: {}
emoji-regex@8.0.0: {}

View File

@@ -4,6 +4,7 @@ import {
existsSync,
readFileSync,
readdirSync,
statSync,
unlinkSync
} from 'fs';
import mime from 'mime-types';
@@ -15,67 +16,79 @@ const INDENT = ' ';
const outputPath = '../src/ESP32React/WWWData.h';
const sourcePath = './dist';
const bytesPerLine = 20;
var totalSize = 0;
let totalSize = 0;
let bundleStats = {
js: { count: 0, uncompressed: 0, compressed: 0 },
css: { count: 0, uncompressed: 0, compressed: 0 },
html: { count: 0, uncompressed: 0, compressed: 0 },
svg: { count: 0, uncompressed: 0, compressed: 0 },
other: { count: 0, uncompressed: 0, compressed: 0 }
};
const generateWWWClass = () =>
`typedef std::function<void(const char * uri, const String & contentType, const uint8_t * content, size_t len, const String & hash)> RouteRegistrationHandler;
// Total size is ${totalSize} bytes
const generateWWWClass =
() => `typedef std::function<void(const char * uri, const String & contentType, const uint8_t * content, size_t len, const String & hash)> RouteRegistrationHandler;
// Bundle Statistics:
// - 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
// - 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()}
class WWWData {
${indent}public:
${indent.repeat(2)}static void registerRoutes(RouteRegistrationHandler handler) {
${fileInfo.map((file) => `${indent.repeat(3)}handler("${file.uri}", "${file.mimeType}", ${file.variable}, ${file.size}, "${file.hash}");`).join('\n')}
${indent.repeat(2)}}
${INDENT}public:
${INDENT.repeat(2)}static void registerRoutes(RouteRegistrationHandler handler) {
${fileInfo.map((f) => `${INDENT.repeat(3)}handler("${f.uri}", "${f.mimeType}", ${f.variable}, ${f.size}, "${f.hash}");`).join('\n')}
${INDENT.repeat(2)}}
};
`;
function getFilesSync(dir, files = []) {
const getFilesSync = (dir, files = []) => {
readdirSync(dir, { withFileTypes: true }).forEach((entry) => {
const entryPath = resolve(dir, entry.name);
if (entry.isDirectory()) {
getFilesSync(entryPath, files);
} else {
files.push(entryPath);
}
entry.isDirectory() ? getFilesSync(entryPath, files) : files.push(entryPath);
});
return files;
}
};
function cleanAndOpen(path) {
if (existsSync(path)) {
unlinkSync(path);
}
const cleanAndOpen = (path) => {
existsSync(path) && unlinkSync(path);
return createWriteStream(path, { flags: 'w+' });
}
};
const getFileType = (filePath) => {
const ext = filePath.split('.').pop().toLowerCase();
if (ext === 'js') return 'js';
if (ext === 'css') return 'css';
if (ext === 'html') return 'html';
if (ext === 'svg') return 'svg';
return 'other';
};
const writeFile = (relativeFilePath, buffer) => {
const variable = 'ESP_REACT_DATA_' + fileInfo.length;
const variable = `ESP_REACT_DATA_${fileInfo.length}`;
const mimeType = mime.lookup(relativeFilePath);
var size = 0;
writeStream.write('const uint8_t ' + variable + '[] = {');
// const zipBuffer = zlib.brotliCompressSync(buffer, { quality: 1 });
const zipBuffer = zlib.gzipSync(buffer, { level: 9 });
const fileType = getFileType(relativeFilePath);
let size = 0;
writeStream.write(`const uint8_t ${variable}[] = {`);
// create sha
const hashSum = crypto.createHash('sha256');
hashSum.update(zipBuffer);
const hash = hashSum.digest('hex');
const zipBuffer = zlib.gzipSync(buffer, { level: 9 });
const hash = crypto.createHash('sha256').update(zipBuffer).digest('hex');
zipBuffer.forEach((b) => {
if (!(size % bytesPerLine)) {
writeStream.write('\n');
writeStream.write(indent);
writeStream.write('\n' + INDENT);
}
writeStream.write('0x' + ('00' + b.toString(16).toUpperCase()).slice(-2) + ',');
writeStream.write('0x' + b.toString(16).toUpperCase().padStart(2, '0') + ',');
size++;
});
if (size % bytesPerLine) {
writeStream.write('\n');
}
size % bytesPerLine && writeStream.write('\n');
writeStream.write('};\n\n');
// Update bundle statistics
bundleStats[fileType].count++;
bundleStats[fileType].uncompressed += buffer.length;
bundleStats[fileType].compressed += zipBuffer.length;
fileInfo.push({
uri: '/' + relativeFilePath.replace(sep, '/'),
mimeType,
@@ -84,32 +97,52 @@ const writeFile = (relativeFilePath, buffer) => {
hash
});
// console.log(relativeFilePath + ' (size ' + size + ' bytes)');
totalSize += size;
};
// start
console.log('Generating ' + outputPath + ' from ' + sourcePath);
const includes = ARDUINO_INCLUDES;
const indent = INDENT;
console.log(`Generating ${outputPath} from ${sourcePath}`);
const fileInfo = [];
const writeStream = cleanAndOpen(resolve(outputPath));
// includes
writeStream.write(includes);
writeStream.write(ARDUINO_INCLUDES);
// process static files
const buildPath = resolve(sourcePath);
for (const filePath of getFilesSync(buildPath)) {
const readStream = readFileSync(filePath);
const relativeFilePath = relative(buildPath, filePath);
writeFile(relativeFilePath, readStream);
writeFile(relative(buildPath, filePath), readFileSync(filePath));
}
// add class
writeStream.write(generateWWWClass());
// end
writeStream.end();
console.log('Total size: ' + totalSize / 1000 + ' KB');
// Calculate and display bundle statistics
const totalUncompressed = Object.values(bundleStats).reduce(
(sum, stat) => sum + stat.uncompressed,
0
);
const totalCompressed = Object.values(bundleStats).reduce(
(sum, stat) => sum + stat.compressed,
0
);
const compressionRatio = (
((totalUncompressed - totalCompressed) / totalUncompressed) *
100
).toFixed(1);
console.log('\n📊 Bundle Size Analysis:');
console.log('='.repeat(50));
console.log(`Total compressed size: ${(totalSize / 1000).toFixed(1)} KB`);
console.log(`Total uncompressed size: ${(totalUncompressed / 1000).toFixed(1)} KB`);
console.log(`Compression ratio: ${compressionRatio}%`);
console.log('\n📁 File Type Breakdown:');
Object.entries(bundleStats).forEach(([type, stats]) => {
if (stats.count > 0) {
const ratio = (
((stats.uncompressed - stats.compressed) / stats.uncompressed) *
100
).toFixed(1);
console.log(
`${type.toUpperCase().padEnd(4)}: ${stats.count} files, ${(stats.uncompressed / 1000).toFixed(1)} KB → ${(stats.compressed / 1000).toFixed(1)} KB (${ratio}% compression)`
);
}
});
console.log('='.repeat(50));

View File

@@ -37,10 +37,9 @@ const AuthenticatedRouting = () => {
<Route path="/dashboard/*" element={<Dashboard />} />
<Route path="/devices/*" element={<Devices />} />
<Route path="/sensors/*" element={<Sensors />} />
<Route path="/status/*" element={<Status />} />
<Route path="/help/*" element={<Help />} />
<Route path="/*" element={<Navigate to="/" />} />
<Route path="/status/*" element={<Status />} />
<Route path="/status/hardwarestatus/*" element={<HardwareStatus />} />
<Route path="/status/activity" element={<Activity />} />
<Route path="/status/log" element={<SystemLog />} />
@@ -68,6 +67,8 @@ const AuthenticatedRouting = () => {
<Route path="/customentities" element={<CustomEntities />} />
</>
)}
<Route path="/*" element={<Navigate to="/" />} />
</Routes>
</Layout>
);

View File

@@ -98,7 +98,7 @@ const SignIn = () => {
<Box display="flex" flexDirection="column" alignItems="center">
<ValidatedTextField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
disabled={processing}
sx={{
width: 240
@@ -117,7 +117,7 @@ const SignIn = () => {
}}
/>
<ValidatedPasswordField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
disabled={processing}
sx={{
width: 240

View File

@@ -70,6 +70,7 @@ export const readDeviceEntities = (id: number) =>
alovaInstance.Get<DeviceEntity[]>(`/rest/deviceEntities`, {
params: { id },
responseType: 'arraybuffer',
// @ts-expect-error - exactOptionalPropertyTypes compatibility issue
transform(data) {
return (data as DeviceEntity[]).map((de: DeviceEntity) => ({
...de,
@@ -92,6 +93,7 @@ export const writeDeviceName = (data: { id: number; name: string }) =>
// SettingsScheduler
export const readSchedule = () =>
alovaInstance.Get<ScheduleItem[]>('/rest/schedule', {
// @ts-expect-error - exactOptionalPropertyTypes compatibility issue
transform(data) {
return (data as Schedule).schedule.map((si: ScheduleItem) => ({
...si,
@@ -129,6 +131,7 @@ export const writeModules = (data: {
// CustomEntities
export const readCustomEntities = () =>
alovaInstance.Get<EntityItem[]>('/rest/customEntities', {
// @ts-expect-error - exactOptionalPropertyTypes compatibility issue
transform(data) {
return (data as Entities).entities.map((ei: EntityItem) => ({
...ei,

View File

@@ -30,7 +30,7 @@ export const getDevVersion = () =>
cacheFor: 60 * 10 * 1000,
transform(response: { data: { name: string; published_at: string } }) {
return {
name: response.data.name.split(/\s+/).splice(-1)[0].substring(1),
name: response.data.name.split(/\s+/).splice(-1)[0]?.substring(1) || '',
published_at: response.data.published_at
};
}

View File

@@ -1,40 +1,37 @@
let decoder;
// @ts-nocheck - Optimized MessagePack unpacking library for EMS-ESP32
let decoder,
src,
srcEnd,
position = 0,
strings = [],
stringPosition = 0,
currentUnpackr = {},
currentStructures,
srcString,
srcStringStart = 0,
srcStringEnd = 0,
bundledStrings,
referenceMap,
dataView;
const EMPTY_ARRAY = [],
currentExtensions = [];
const defaultOptions = { useRecords: false, mapsAsObjects: true };
try {
decoder = new TextDecoder();
} catch (error) {}
let src;
let srcEnd;
let position = 0;
const EMPTY_ARRAY = [];
let strings = EMPTY_ARRAY;
let stringPosition = 0;
let currentUnpackr = {};
let currentStructures;
let srcString;
let srcStringStart = 0;
let srcStringEnd = 0;
let bundledStrings;
let referenceMap;
const currentExtensions = [];
let dataView;
const defaultOptions = {
useRecords: false,
mapsAsObjects: true
};
export class C1Type {}
export const C1 = new C1Type();
class C1Type {}
const C1 = new C1Type();
C1.name = 'MessagePack 0xC1';
let sequentialMode = false;
let inlineObjectReadThreshold = 2;
let readStruct, onLoadedStructures, onSaveState;
// no-eval build
let sequentialMode = false,
inlineObjectReadThreshold = 2,
readStruct,
onLoadedStructures,
onSaveState;
try {
new Function('');
} catch (error) {
// if eval variants are not supported, do not create inline object readers ever
inlineObjectReadThreshold = Infinity;
}
export class Unpackr {
constructor(options) {
if (options) {
@@ -50,19 +47,15 @@ export class Unpackr {
if (options.structures)
options.structures.sharedLength = options.structures.length;
else if (options.getStructures) {
(options.structures = []).uninitialized = true; // this is what we use to denote an uninitialized structures
(options.structures = []).uninitialized = true;
options.structures.sharedLength = 0;
}
if (options.int64AsNumber) {
options.int64AsType = 'number';
}
if (options.int64AsNumber) options.int64AsType = 'number';
}
Object.assign(this, options);
}
unpack(source, options?: any) {
if (src) {
// re-entrant execution, save the state and restore it after we do this unpack
return saveState(() => {
clearSource();
return this
@@ -86,9 +79,6 @@ export class Unpackr {
strings = EMPTY_ARRAY;
bundledStrings = null;
src = source;
// this provides cached access to the data view for a buffer if it is getting reused, which is a recommend
// technique for getting data from a database where it can be copied into an existing buffer instead of creating
// new ones
try {
dataView =
source.dataView ||
@@ -191,10 +181,10 @@ export class Unpackr {
return this.unpack(source, end);
}
}
export function getPosition() {
function getPosition() {
return position;
}
export function checkedRead(options: any) {
function checkedRead(options: any) {
try {
if (!currentUnpackr.trusted && !sequentialMode) {
const sharedLength = currentStructures.sharedLength || 0;
@@ -264,7 +254,7 @@ function restoreStructures() {
currentStructures.restoreStructures = null;
}
export function read() {
function read() {
let token = src[position++];
if (token < 0xa0) {
if (token < 0x80) {
@@ -589,7 +579,7 @@ const createSecondByteReader = (firstId, read0) =>
return structure.read();
};
export function loadStructures() {
function loadStructures() {
const loadedStructures = saveState(() => {
// save the state in case getStructures modifies our buffer
src = null;
@@ -605,9 +595,8 @@ var readFixedString = readStringJS;
var readString8 = readStringJS;
var readString16 = readStringJS;
var readString32 = readStringJS;
export let isNativeAccelerationEnabled = false;
export function setExtractor(extractStrings) {
let isNativeAccelerationEnabled = false;
function setExtractor(extractStrings) {
isNativeAccelerationEnabled = true;
readFixedString = readString(1);
readString8 = readString(2);
@@ -701,7 +690,7 @@ function readStringJS(length) {
return result;
}
export function readString(source, start, length) {
function readString(source, start, length) {
const existingSrc = src;
src = source;
position = start;
@@ -1065,7 +1054,7 @@ currentExtensions[0x70] = (data) => {
currentExtensions[0x73] = () => new Set(read());
export const typedArrays = [
const typedArrays = [
'Int8',
'Uint8',
'Uint8Clamped',
@@ -1177,44 +1166,20 @@ function saveState(callback) {
dataView = new DataView(src.buffer, src.byteOffset, src.byteLength);
return value;
}
export function clearSource() {
function clearSource() {
src = null;
referenceMap = null;
currentStructures = null;
}
export function addExtension(extension) {
function addExtension(extension) {
if (extension.unpack) currentExtensions[extension.type] = extension.unpack;
else currentExtensions[extension.type] = extension;
}
export const mult10 = new Array(147); // this is a table matching binary exponents to the multiplier to determine significant digit rounding
const mult10 = new Array(147);
for (let i = 0; i < 256; i++) {
mult10[i] = +('1e' + Math.floor(45.15 - i * 0.30103));
}
export const Decoder = Unpackr;
var defaultUnpackr = new Unpackr({ useRecords: false });
const defaultUnpackr = new Unpackr({ useRecords: false });
export const unpack = defaultUnpackr.unpack;
export const unpackMultiple = defaultUnpackr.unpackMultiple;
export const decode = defaultUnpackr.unpack;
export const FLOAT32_OPTIONS = {
NEVER: 0,
ALWAYS: 1,
DECIMAL_ROUND: 3,
DECIMAL_FIT: 4
};
const f32Array = new Float32Array(1);
const u8Array = new Uint8Array(f32Array.buffer, 0, 4);
export function roundFloat32(float32Number) {
f32Array[0] = float32Number;
const multiplier = mult10[((u8Array[3] & 0x7f) << 1) | (u8Array[2] >> 7)];
return (
((multiplier * float32Number + (float32Number > 0 ? 0.5 : -0.5)) >> 0) /
multiplier
);
}
export function setReadStruct(updatedReadStruct, loadedStructs, saveState) {
readStruct = updatedReadStruct;
onLoadedStructures = loadedStructs;
onSaveState = saveState;
}

View File

@@ -137,8 +137,8 @@ const CustomEntities = () => {
const saveEntities = async () => {
await writeEntities({
entities: entities
.filter((ei) => !ei.deleted)
.map((condensed_ei) => ({
.filter((ei: EntityItem) => !ei.deleted)
.map((condensed_ei: EntityItem) => ({
id: condensed_ei.id,
ram: condensed_ei.ram,
name: condensed_ei.name,
@@ -231,6 +231,7 @@ const CustomEntities = () => {
value_type: 0,
writeable: false,
deleted: false,
hide: false,
value: ''
});
setDialogOpen(true);
@@ -251,15 +252,17 @@ const CustomEntities = () => {
const renderEntity = () => {
if (!entities) {
return <FormLoader onRetry={fetchEntities} errorMessage={error?.message} />;
return (
<FormLoader onRetry={fetchEntities} errorMessage={error?.message || ''} />
);
}
return (
<Table
data={{
nodes: entities
.filter((ei) => !ei.deleted)
.sort((a, b) => a.name.localeCompare(b.name))
.filter((ei: EntityItem) => !ei.deleted)
.sort((a: EntityItem, b: EntityItem) => a.name.localeCompare(b.name))
}}
theme={entity_theme}
layout={{ custom: true }}

View File

@@ -74,7 +74,10 @@ const CustomEntitiesDialog = ({
}
}, [open, selectedItem]);
const handleClose = (_event, reason: 'backdropClick' | 'escapeKeyDown') => {
const handleClose = (
_event: React.SyntheticEvent,
reason: 'backdropClick' | 'escapeKeyDown'
) => {
if (reason !== 'backdropClick') {
onClose();
}
@@ -123,7 +126,7 @@ const CustomEntitiesDialog = ({
<Grid container spacing={2} rowSpacing={0}>
<Grid size={12}>
<ValidatedTextField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="name"
label={LL.NAME(0)}
value={editItem.name}
@@ -211,7 +214,7 @@ const CustomEntitiesDialog = ({
</Grid>
<Grid>
<ValidatedTextField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="device_id"
label={LL.ID_OF(LL.DEVICE())}
margin="normal"
@@ -231,7 +234,7 @@ const CustomEntitiesDialog = ({
</Grid>
<Grid>
<ValidatedTextField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="type_id"
label={LL.ID_OF(LL.TYPE(1))}
margin="normal"
@@ -251,7 +254,7 @@ const CustomEntitiesDialog = ({
</Grid>
<Grid>
<ValidatedTextField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="offset"
label={LL.OFFSET()}
margin="normal"
@@ -343,7 +346,7 @@ const CustomEntitiesDialog = ({
editItem.device_id !== '0' && (
<Grid>
<ValidatedTextField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="factor"
label={LL.BYTES()}
value={numberValue(editItem.factor as number)}
@@ -361,7 +364,7 @@ const CustomEntitiesDialog = ({
{editItem.value_type === DeviceValueType.BOOL && (
<Grid>
<ValidatedTextField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="factor"
label={LL.BITMASK()}
value={editItem.factor as string}

View File

@@ -125,13 +125,22 @@ const Customizations = () => {
const setOriginalSettings = (data: DeviceEntity[]) => {
setDeviceEntities(
data.map((de) => ({
data.map((de) => {
const result: DeviceEntity = {
...de,
o_m: de.m,
o_cn: de.cn,
o_mi: de.mi,
o_ma: de.ma
}))
o_m: de.m
};
if (de.cn !== undefined) {
result.o_cn = de.cn;
}
if (de.mi !== undefined) {
result.o_mi = de.mi;
}
if (de.ma !== undefined) {
result.o_ma = de.ma;
}
return result;
})
);
};
@@ -244,8 +253,11 @@ const Customizations = () => {
setSelectedDevice(-1);
setSelectedDeviceTypeNameURL('');
} else {
setSelectedDeviceTypeNameURL(devices.devices[index].url || '');
setSelectedDeviceName(devices.devices[index].n);
const device = devices.devices[index];
if (device) {
setSelectedDeviceTypeNameURL(device.url || '');
setSelectedDeviceName(device.n);
}
setNumChanges(0);
setRestartNeeded(false);
}
@@ -396,14 +408,20 @@ const Customizations = () => {
await sendCustomizationEntities({
id: selectedDevice,
entity_ids: masked_entities
}).catch((error: Error) => {
})
.then(() => {
toast.success(LL.CUSTOMIZATIONS_SAVED());
})
.catch((error: Error) => {
if (error.message === 'Reboot required') {
setRestartNeeded(true);
} else {
toast.error(error.message);
}
});
})
.finally(() => {
setOriginalSettings(deviceEntities);
});
}
};
@@ -545,7 +563,7 @@ const Customizations = () => {
size="small"
color="secondary"
value={getMaskString(selectedFilters)}
onChange={(event, mask: string[]) => {
onChange={(_, mask: string[]) => {
setSelectedFilters(getMaskNumber(mask));
}}
>

View File

@@ -54,7 +54,10 @@ const CustomizationsDialog = ({
}
}, [open, selectedItem]);
const handleClose = (_event, reason: 'backdropClick' | 'escapeKeyDown') => {
const handleClose = (
_event: React.SyntheticEvent,
reason: 'backdropClick' | 'escapeKeyDown'
) => {
if (reason !== 'backdropClick') {
onClose();
}

View File

@@ -1,4 +1,4 @@
import { useContext, useEffect, useState } from 'react';
import { useCallback, useContext, useEffect, useMemo, useState } from 'react';
import { IconContext } from 'react-icons/lib';
import { Link } from 'react-router';
import { toast } from 'react-toastify';
@@ -76,11 +76,12 @@ const Dashboard = () => {
}
);
const deviceValueDialogSave = async (devicevalue: DeviceValue) => {
const deviceValueDialogSave = useCallback(
async (devicevalue: DeviceValue) => {
if (!selectedDashboardItem) {
return;
}
const id = selectedDashboardItem.parentNode.id; // this is the parent ID
const id = selectedDashboardItem.id; // this is the parent ID
await sendDeviceValue({ id, c: devicevalue.c ?? '', v: devicevalue.v })
.then(() => {
toast.success(LL.WRITE_CMD_SENT());
@@ -92,9 +93,13 @@ const Dashboard = () => {
setDeviceValueDialogOpen(false);
setSelectedDashboardItem(undefined);
});
};
},
[selectedDashboardItem, sendDeviceValue, LL]
);
const dashboard_theme = useTheme({
const dashboard_theme = useMemo(
() =>
useTheme({
Table: `
--data-table-library_grid-template-columns: minmax(80px, auto) 120px 32px;
`,
@@ -122,12 +127,14 @@ const Dashboard = () => {
text-align: right;
}
`
});
}),
[]
);
const tree = useTree(
{ nodes: data.nodes },
{
onChange: undefined // not used but needed
onChange: () => {} // not used but needed
},
{
treeIcon: {
@@ -162,7 +169,8 @@ const Dashboard = () => {
: tree.fns.onRemoveAll(); // collapse tree
}, [parentNodes]);
const showType = (n?: string, t?: number) => {
const showType = useCallback(
(n?: string, t?: number) => {
// if we have a name show it
if (n) {
return n;
@@ -183,7 +191,9 @@ const Dashboard = () => {
}
}
return '';
};
},
[LL]
);
const showName = (di: DashboardItem) => {
if (di.id < 100) {
@@ -201,20 +211,24 @@ const Dashboard = () => {
if (di.dv) {
return <span>{di.dv.id.slice(2)}</span>;
}
return null;
};
const hasMask = (id: string, mask: number) =>
(parseInt(id.slice(0, 2), 16) & mask) === mask;
const editDashboardValue = (di: DashboardItem) => {
const editDashboardValue = useCallback(
(di: DashboardItem) => {
if (me.admin && di.dv?.c) {
setSelectedDashboardItem(di);
setDeviceValueDialogOpen(true);
}
};
},
[me.admin]
);
const handleShowAll = (
event: React.MouseEvent<HTMLElement>,
_event: React.MouseEvent<HTMLElement>,
toggle: boolean | null
) => {
if (toggle !== null) {
@@ -225,7 +239,9 @@ const Dashboard = () => {
const renderContent = () => {
if (!data) {
return <FormLoader onRetry={fetchDashboard} errorMessage={error?.message} />;
return (
<FormLoader onRetry={fetchDashboard} errorMessage={error?.message || ''} />
);
}
const hasFavEntities = data.nodes.filter(

View File

@@ -329,13 +329,16 @@ const Devices = () => {
const handleDownloadCsv = () => {
const deviceIndex = coreData.devices.findIndex(
(d) => d.id === device_select.state.id
(d: Device) => d.id === device_select.state.id
);
if (deviceIndex === -1) {
return;
}
const filename =
coreData.devices[deviceIndex].tn + '_' + coreData.devices[deviceIndex].n;
const selectedDevice = coreData.devices[deviceIndex];
if (!selectedDevice) {
return;
}
const filename = selectedDevice.tn + '_' + selectedDevice.n;
const columns = [
{
@@ -350,7 +353,7 @@ const Devices = () => {
{
accessor: (dv: DeviceValue) =>
dv.u !== undefined && DeviceValueUOM_s[dv.u]
? DeviceValueUOM_s[dv.u].replace(/[^a-zA-Z0-9]/g, '')
? DeviceValueUOM_s[dv.u]?.replace(/[^a-zA-Z0-9]/g, '')
: '',
name: 'UoM'
},
@@ -373,7 +376,9 @@ const Devices = () => {
];
const data = onlyFav
? deviceData.nodes.filter((dv) => hasMask(dv.id, DeviceEntityMask.DV_FAVORITE))
? deviceData.nodes.filter((dv: DeviceValue) =>
hasMask(dv.id, DeviceEntityMask.DV_FAVORITE)
)
: deviceData.nodes;
const csvData = data.reduce(
@@ -433,10 +438,14 @@ const Devices = () => {
const renderDeviceDetails = () => {
if (showDeviceInfo) {
const deviceIndex = coreData.devices.findIndex(
(d) => d.id === device_select.state.id
(d: Device) => d.id === device_select.state.id
);
if (deviceIndex === -1) {
return;
return null;
}
const deviceDetails = coreData.devices[deviceIndex];
if (!deviceDetails) {
return null;
}
return (
@@ -449,47 +458,35 @@ const Devices = () => {
<DialogContent dividers>
<List dense={true}>
<ListItem>
<ListItemText
primary={LL.TYPE(0)}
secondary={coreData.devices[deviceIndex].tn}
/>
<ListItemText primary={LL.TYPE(0)} secondary={deviceDetails.tn} />
</ListItem>
<ListItem>
<ListItemText
primary={LL.NAME(0)}
secondary={coreData.devices[deviceIndex].n}
/>
<ListItemText primary={LL.NAME(0)} secondary={deviceDetails.n} />
</ListItem>
{coreData.devices[deviceIndex].t !== DeviceType.CUSTOM && (
{deviceDetails.t !== DeviceType.CUSTOM && (
<>
<ListItem>
<ListItemText
primary={LL.BRAND()}
secondary={coreData.devices[deviceIndex].b}
/>
<ListItemText primary={LL.BRAND()} secondary={deviceDetails.b} />
</ListItem>
<ListItem>
<ListItemText
primary={LL.ID_OF(LL.DEVICE())}
secondary={
'0x' +
(
'00' +
coreData.devices[deviceIndex].d.toString(16).toUpperCase()
).slice(-2)
('00' + deviceDetails.d.toString(16).toUpperCase()).slice(-2)
}
/>
</ListItem>
<ListItem>
<ListItemText
primary={LL.ID_OF(LL.PRODUCT())}
secondary={coreData.devices[deviceIndex].p}
secondary={deviceDetails.p}
/>
</ListItem>
<ListItem>
<ListItemText
primary={LL.VERSION()}
secondary={coreData.devices[deviceIndex].v}
secondary={deviceDetails.v}
/>
</ListItem>
</>
@@ -508,6 +505,7 @@ const Devices = () => {
</Dialog>
);
}
return null;
};
const renderCoreData = () => (
@@ -598,21 +596,26 @@ const Devices = () => {
const shown_data = onlyFav
? deviceData.nodes.filter(
(dv) =>
(dv: DeviceValue) =>
hasMask(dv.id, DeviceEntityMask.DV_FAVORITE) &&
dv.id.slice(2).toLowerCase().includes(search.toLowerCase())
)
: deviceData.nodes.filter((dv) =>
: deviceData.nodes.filter((dv: DeviceValue) =>
dv.id.slice(2).toLowerCase().includes(search.toLowerCase())
);
const deviceIndex = coreData.devices.findIndex(
(d) => d.id === device_select.state.id
(d: Device) => d.id === device_select.state.id
);
if (deviceIndex === -1) {
return;
}
const deviceInfo = coreData.devices[deviceIndex];
if (!deviceInfo) {
return;
}
const [, height] = size;
return (
<Box
sx={{
@@ -623,15 +626,15 @@ const Devices = () => {
bottom: 0,
top: 64,
zIndex: 'modal',
maxHeight: () => size[1] - 126,
maxHeight: () => (height || 0) - 126,
border: '1px solid #177ac9'
}}
>
<Box sx={{ p: 1 }}>
<Grid container justifyContent="space-between">
<Typography noWrap variant="subtitle1" color="warning.main">
{coreData.devices[deviceIndex].n}&nbsp;(
{coreData.devices[deviceIndex].tn})
{deviceInfo.n}&nbsp;(
{deviceInfo.tn})
</Typography>
<Grid justifyContent="flex-end">
<ButtonTooltip title={LL.CLOSE()}>
@@ -701,7 +704,7 @@ const Devices = () => {
' ' +
shown_data.length +
'/' +
coreData.devices[deviceIndex].e +
deviceInfo.e +
' ' +
LL.ENTITIES(shown_data.length)}
</span>

View File

@@ -120,7 +120,7 @@ const DevicesDialog = ({
{editItem.l ? (
<TextField
name="v"
label={LL.VALUE(0)}
// label={LL.VALUE(0)}
value={editItem.v}
disabled={!writeable}
sx={{ width: '30ch' }}
@@ -135,7 +135,7 @@ const DevicesDialog = ({
</TextField>
) : editItem.s || editItem.u !== DeviceValueUOM.NONE ? (
<ValidatedTextField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="v"
label={LL.VALUE(0)}
value={numberValue(Math.round((editItem.v as number) * 10) / 10)}
@@ -159,7 +159,7 @@ const DevicesDialog = ({
/>
) : (
<ValidatedTextField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="v"
label={LL.VALUE(0)}
value={editItem.v}

View File

@@ -43,7 +43,7 @@ const EntityMaskToggle = ({ onUpdate, de }: EntityMaskToggleProps) => {
size="small"
color="secondary"
value={getMaskString(de.m)}
onChange={(event, mask: string[]) => {
onChange={(_event, mask: string[]) => {
de.m = getMaskNumber(mask);
if (de.n === '' && de.m & DeviceEntityMask.DV_READONLY) {
de.m = de.m | DeviceEntityMask.DV_WEB_EXCLUDE;

View File

@@ -133,13 +133,15 @@ const Modules = () => {
};
const saveModules = async () => {
await updateModules({
modules: modules.map((condensed_mi) => ({
await Promise.all(
modules.map((condensed_mi: ModuleItem) =>
updateModules({
key: condensed_mi.key,
enabled: condensed_mi.enabled,
license: condensed_mi.license
}))
})
)
)
.then(() => {
toast.success(LL.MODULES_UPDATED());
})
@@ -154,7 +156,9 @@ const Modules = () => {
const renderContent = () => {
if (!modules) {
return <FormLoader onRetry={fetchModules} errorMessage={error?.message} />;
return (
<FormLoader onRetry={fetchModules} errorMessage={error?.message || ''} />
);
}
if (modules.length === 0) {

View File

@@ -135,8 +135,8 @@ const Scheduler = () => {
const saveSchedule = async () => {
await updateSchedule({
schedule: schedule
.filter((si) => !si.deleted)
.map((condensed_si) => ({
.filter((si: ScheduleItem) => !si.deleted)
.map((condensed_si: ScheduleItem) => ({
id: condensed_si.id,
active: condensed_si.active,
flags: condensed_si.flags,
@@ -212,7 +212,9 @@ const Scheduler = () => {
const renderSchedule = () => {
if (!schedule) {
return <FormLoader onRetry={fetchSchedule} errorMessage={error?.message} />;
return (
<FormLoader onRetry={fetchSchedule} errorMessage={error?.message || ''} />
);
}
const dayBox = (si: ScheduleItem, flag: number) => (
@@ -251,8 +253,8 @@ const Scheduler = () => {
<Table
data={{
nodes: schedule
.filter((si) => !si.deleted)
.sort((a, b) => a.flags - b.flags)
.filter((si: ScheduleItem) => !si.deleted)
.sort((a: ScheduleItem, b: ScheduleItem) => a.flags - b.flags)
}}
theme={schedule_theme}
layout={{ custom: true }}

View File

@@ -144,7 +144,10 @@ const SchedulerDialog = ({
</Typography>
);
const handleClose = (_event, reason: 'backdropClick' | 'escapeKeyDown') => {
const handleClose = (
_event: React.SyntheticEvent,
reason: 'backdropClick' | 'escapeKeyDown'
) => {
if (reason !== 'backdropClick') {
onClose();
}
@@ -325,7 +328,7 @@ const SchedulerDialog = ({
</>
)}
<ValidatedTextField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="cmd"
label={LL.COMMAND(0)}
multiline
@@ -344,7 +347,7 @@ const SchedulerDialog = ({
onChange={updateFormValue}
/>
<ValidatedTextField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="name"
label={LL.NAME(0) + ' (' + LL.OPTIONAL() + ')'}
value={editItem.name}

View File

@@ -57,7 +57,10 @@ const SensorsAnalogDialog = ({
}
}, [open, selectedItem]);
const handleClose = (_event, reason: 'backdropClick' | 'escapeKeyDown') => {
const handleClose = (
_event: React.SyntheticEvent,
reason: 'backdropClick' | 'escapeKeyDown'
) => {
if (reason !== 'backdropClick') {
onClose();
}
@@ -88,7 +91,7 @@ const SensorsAnalogDialog = ({
<Grid container spacing={2}>
<Grid>
<ValidatedTextField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="g"
label="GPIO"
sx={{ width: '11ch' }}
@@ -107,7 +110,7 @@ const SensorsAnalogDialog = ({
)}
<Grid>
<ValidatedTextField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="n"
label={LL.NAME(0)}
value={editItem.n}

View File

@@ -52,7 +52,10 @@ const SensorsTemperatureDialog = ({
}
}, [open, selectedItem]);
const handleClose = (_event, reason: 'backdropClick' | 'escapeKeyDown') => {
const handleClose = (
_event: React.SyntheticEvent,
reason: 'backdropClick' | 'escapeKeyDown'
) => {
if (reason !== 'backdropClick') {
onClose();
}
@@ -82,7 +85,7 @@ const SensorsTemperatureDialog = ({
<Grid container spacing={2}>
<Grid>
<ValidatedTextField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="n"
label={LL.NAME(0)}
value={editItem.n}

View File

@@ -13,7 +13,7 @@ import type {
export const GPIO_VALIDATOR = {
validator(
rule: InternalRuleItem,
_rule: InternalRuleItem,
value: number,
callback: (error?: string) => void
) {
@@ -36,7 +36,7 @@ export const GPIO_VALIDATOR = {
export const GPIO_VALIDATORR = {
validator(
rule: InternalRuleItem,
_rule: InternalRuleItem,
value: number,
callback: (error?: string) => void
) {
@@ -60,7 +60,7 @@ export const GPIO_VALIDATORR = {
export const GPIO_VALIDATORC3 = {
validator(
rule: InternalRuleItem,
_rule: InternalRuleItem,
value: number,
callback: (error?: string) => void
) {
@@ -74,7 +74,7 @@ export const GPIO_VALIDATORC3 = {
export const GPIO_VALIDATORS2 = {
validator(
rule: InternalRuleItem,
_rule: InternalRuleItem,
value: number,
callback: (error?: string) => void
) {
@@ -94,7 +94,7 @@ export const GPIO_VALIDATORS2 = {
export const GPIO_VALIDATORS3 = {
validator(
rule: InternalRuleItem,
_rule: InternalRuleItem,
value: number,
callback: (error?: string) => void
) {
@@ -279,7 +279,7 @@ export const createSettingsValidator = (settings: Settings) =>
export const uniqueNameValidator = (schedule: ScheduleItem[], o_name?: string) => ({
validator(
rule: InternalRuleItem,
_rule: InternalRuleItem,
name: string,
callback: (error?: string) => void
) {
@@ -324,7 +324,7 @@ export const uniqueCustomNameValidator = (
o_name?: string
) => ({
validator(
rule: InternalRuleItem,
_rule: InternalRuleItem,
name: string,
callback: (error?: string) => void
) {
@@ -353,7 +353,7 @@ export const entityItemValidation = (entity: EntityItem[], entityItem: EntityIte
device_id: [
{
validator(
rule: InternalRuleItem,
_rule: InternalRuleItem,
value: string,
callback: (error?: string) => void
) {
@@ -367,7 +367,7 @@ export const entityItemValidation = (entity: EntityItem[], entityItem: EntityIte
type_id: [
{
validator(
rule: InternalRuleItem,
_rule: InternalRuleItem,
value: string,
callback: (error?: string) => void
) {
@@ -389,7 +389,7 @@ export const uniqueTemperatureNameValidator = (
sensors: TemperatureSensor[],
o_name?: string
) => ({
validator(rule: InternalRuleItem, n: string, callback: (error?: string) => void) {
validator(_rule: InternalRuleItem, n: string, callback: (error?: string) => void) {
if (
(o_name === undefined || o_name.toLowerCase() !== n.toLowerCase()) &&
n !== '' &&
@@ -419,7 +419,7 @@ export const temperatureSensorItemValidation = (
export const isGPIOUniqueValidator = (sensors: AnalogSensor[]) => ({
validator(
rule: InternalRuleItem,
_rule: InternalRuleItem,
gpio: number,
callback: (error?: string) => void
) {
@@ -435,7 +435,7 @@ export const uniqueAnalogNameValidator = (
sensors: AnalogSensor[],
o_name?: string
) => ({
validator(rule: InternalRuleItem, n: string, callback: (error?: string) => void) {
validator(_rule: InternalRuleItem, n: string, callback: (error?: string) => void) {
if (
(o_name === undefined || o_name.toLowerCase() !== n.toLowerCase()) &&
n !== '' &&
@@ -482,7 +482,7 @@ export const deviceValueItemValidation = (dv: DeviceValue) =>
{ required: true, message: 'Value is required' },
{
validator(
rule: InternalRuleItem,
_rule: InternalRuleItem,
value: unknown,
callback: (error?: string) => void
) {

View File

@@ -54,12 +54,12 @@ const APSettings = () => {
origData,
dirtyFlags,
setDirtyFlags,
updateDataValue
updateDataValue as (value: unknown) => void
);
const content = () => {
if (!data) {
return <FormLoader onRetry={loadData} errorMessage={errorMessage} />;
return <FormLoader onRetry={loadData} errorMessage={errorMessage || ''} />;
}
const validateAndSubmit = async () => {
@@ -80,7 +80,7 @@ const APSettings = () => {
return (
<>
<ValidatedTextField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="provision_mode"
label={LL.AP_PROVIDE() + '...'}
value={data.provision_mode}
@@ -103,7 +103,7 @@ const APSettings = () => {
{isAPEnabled(data) && (
<>
<ValidatedTextField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="ssid"
label={LL.ACCESS_POINT(2) + ' SSID'}
fullWidth
@@ -113,7 +113,7 @@ const APSettings = () => {
margin="normal"
/>
<ValidatedPasswordField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="password"
label={LL.ACCESS_POINT(2) + ' ' + LL.PASSWORD()}
fullWidth
@@ -123,7 +123,7 @@ const APSettings = () => {
margin="normal"
/>
<ValidatedTextField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="channel"
label={LL.AP_PREFERRED_CHANNEL()}
value={numberValue(data.channel)}
@@ -151,7 +151,7 @@ const APSettings = () => {
label={LL.AP_HIDE_SSID()}
/>
<ValidatedTextField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="max_clients"
label={LL.AP_MAX_CLIENTS()}
value={numberValue(data.max_clients)}
@@ -169,7 +169,7 @@ const APSettings = () => {
))}
</ValidatedTextField>
<ValidatedTextField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="local_ip"
label={LL.AP_LOCAL_IP()}
fullWidth
@@ -179,7 +179,7 @@ const APSettings = () => {
margin="normal"
/>
<ValidatedTextField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="gateway_ip"
label={LL.NETWORK_GATEWAY()}
fullWidth
@@ -189,7 +189,7 @@ const APSettings = () => {
margin="normal"
/>
<ValidatedTextField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="subnet_mask"
label={LL.NETWORK_SUBNET()}
fullWidth

View File

@@ -75,7 +75,7 @@ const ApplicationSettings = () => {
origData,
dirtyFlags,
setDirtyFlags,
updateDataValue
updateDataValue as (value: unknown) => void
);
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
@@ -135,7 +135,7 @@ const ApplicationSettings = () => {
const content = () => {
if (!data || !hardwareData) {
return <FormLoader onRetry={loadData} errorMessage={errorMessage} />;
return <FormLoader onRetry={loadData} errorMessage={errorMessage || ''} />;
}
const validateAndSubmit = async () => {
@@ -219,7 +219,7 @@ const ApplicationSettings = () => {
<Grid container spacing={2} rowSpacing={0}>
<Grid>
<ValidatedTextField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="modbus_max_clients"
label={LL.AP_MAX_CLIENTS()}
variant="outlined"
@@ -231,7 +231,7 @@ const ApplicationSettings = () => {
</Grid>
<Grid>
<ValidatedTextField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="modbus_port"
label="Port"
variant="outlined"
@@ -243,7 +243,7 @@ const ApplicationSettings = () => {
</Grid>
<Grid>
<ValidatedTextField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="modbus_timeout"
label="Timeout"
slotProps={{
@@ -273,7 +273,7 @@ const ApplicationSettings = () => {
<Grid container spacing={2} rowSpacing={0}>
<Grid>
<ValidatedTextField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="syslog_host"
label="Host"
variant="outlined"
@@ -284,7 +284,7 @@ const ApplicationSettings = () => {
</Grid>
<Grid>
<ValidatedTextField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="syslog_port"
label="Port"
variant="outlined"
@@ -315,7 +315,7 @@ const ApplicationSettings = () => {
</Grid>
<Grid>
<ValidatedTextField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="syslog_mark_interval"
label={LL.MARK_INTERVAL()}
slotProps={{
@@ -485,7 +485,7 @@ const ApplicationSettings = () => {
<Grid container spacing={2} rowSpacing={0}>
<Grid>
<ValidatedTextField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="rx_gpio"
label={LL.GPIO_OF('Rx')}
fullWidth
@@ -498,7 +498,7 @@ const ApplicationSettings = () => {
</Grid>
<Grid>
<ValidatedTextField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="tx_gpio"
label={LL.GPIO_OF('Tx')}
fullWidth
@@ -511,7 +511,7 @@ const ApplicationSettings = () => {
</Grid>
<Grid>
<ValidatedTextField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="pbutton_gpio"
label={LL.GPIO_OF(LL.BUTTON())}
fullWidth
@@ -524,7 +524,7 @@ const ApplicationSettings = () => {
</Grid>
<Grid>
<ValidatedTextField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="dallas_gpio"
label={
LL.GPIO_OF(LL.TEMPERATURE()) + ' (0=' + LL.DISABLED(1) + ')'
@@ -539,7 +539,7 @@ const ApplicationSettings = () => {
</Grid>
<Grid>
<ValidatedTextField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="led_gpio"
label={LL.GPIO_OF('LED') + ' (0=' + LL.DISABLED(1) + ')'}
fullWidth
@@ -743,7 +743,7 @@ const ApplicationSettings = () => {
{data.remote_timeout_en && (
<Box mt={2}>
<ValidatedTextField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="remote_timeout"
label={LL.REMOTE_TIMEOUT()}
slotProps={{
@@ -783,7 +783,7 @@ const ApplicationSettings = () => {
{data.shower_timer && (
<Grid>
<ValidatedTextField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="shower_min_duration"
label={LL.MIN_DURATION()}
slotProps={{
@@ -801,7 +801,7 @@ const ApplicationSettings = () => {
<>
<Grid>
<ValidatedTextField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="shower_alert_trigger"
label={LL.TRIGGER_TIME()}
slotProps={{
@@ -817,7 +817,7 @@ const ApplicationSettings = () => {
</Grid>
<Grid>
<ValidatedTextField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="shower_alert_coldshot"
label={LL.COLD_SHOT_DURATION()}
slotProps={{

View File

@@ -57,7 +57,7 @@ const DownloadUpload = () => {
const content = () => {
if (!data) {
return <FormLoader onRetry={loadData} errorMessage={error?.message} />;
return <FormLoader onRetry={loadData} errorMessage={error?.message || ''} />;
}
return (

View File

@@ -56,7 +56,7 @@ const MqttSettings = () => {
origData,
dirtyFlags,
setDirtyFlags,
updateDataValue
updateDataValue as (value: unknown) => void
);
const SecondsInputProps = {
@@ -65,7 +65,7 @@ const MqttSettings = () => {
const content = () => {
if (!data) {
return <FormLoader onRetry={loadData} errorMessage={errorMessage} />;
return <FormLoader onRetry={loadData} errorMessage={errorMessage || ''} />;
}
const validateAndSubmit = async () => {
@@ -93,7 +93,7 @@ const MqttSettings = () => {
<Grid container spacing={2} rowSpacing={0}>
<Grid>
<ValidatedTextField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="host"
label={LL.ADDRESS_OF(LL.BROKER())}
multiline
@@ -105,7 +105,7 @@ const MqttSettings = () => {
</Grid>
<Grid>
<ValidatedTextField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="port"
label="Port"
variant="outlined"
@@ -117,7 +117,7 @@ const MqttSettings = () => {
</Grid>
<Grid>
<ValidatedTextField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="base"
label={LL.BASE_TOPIC()}
variant="outlined"
@@ -158,7 +158,7 @@ const MqttSettings = () => {
</Grid>
<Grid>
<ValidatedTextField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="keep_alive"
label="Keep Alive"
slotProps={{
@@ -354,7 +354,7 @@ const MqttSettings = () => {
<Grid container spacing={2} rowSpacing={0}>
<Grid>
<ValidatedTextField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="publish_time_heartbeat"
label="Heartbeat"
slotProps={{

View File

@@ -76,7 +76,7 @@ const NTPSettings = () => {
origData,
dirtyFlags,
setDirtyFlags,
updateDataValue
updateDataValue as (value: unknown) => void
);
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
@@ -155,7 +155,7 @@ const NTPSettings = () => {
const content = () => {
if (!data) {
return <FormLoader onRetry={loadData} errorMessage={errorMessage} />;
return <FormLoader onRetry={loadData} errorMessage={errorMessage || ''} />;
}
const validateAndSubmit = async () => {
@@ -190,7 +190,7 @@ const NTPSettings = () => {
label={LL.ENABLE_NTP()}
/>
<ValidatedTextField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="server"
label={LL.NTP_SERVER()}
fullWidth
@@ -200,7 +200,7 @@ const NTPSettings = () => {
margin="normal"
/>
<ValidatedTextField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="tz_label"
label={LL.TIME_ZONE()}
fullWidth

View File

@@ -35,7 +35,7 @@ const Network = () => {
],
useLocation()
);
const routerTab = matchedRoutes?.[0].route.path || false;
const routerTab = matchedRoutes?.[0]?.route.path || false;
const navigate = useNavigate();
@@ -56,7 +56,7 @@ const Network = () => {
return (
<WiFiConnectionContext.Provider
value={{
selectedNetwork,
...(selectedNetwork && { selectedNetwork }),
selectNetwork,
deselectNetwork
}}

View File

@@ -104,7 +104,7 @@ const NetworkSettings = () => {
origData,
dirtyFlags,
setDirtyFlags,
updateDataValue
updateDataValue as (value: unknown) => void
);
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
@@ -113,7 +113,7 @@ const NetworkSettings = () => {
const content = () => {
if (!data) {
return <FormLoader onRetry={loadData} errorMessage={errorMessage} />;
return <FormLoader onRetry={loadData} errorMessage={errorMessage || ''} />;
}
const validateAndSubmit = async () => {
@@ -172,7 +172,7 @@ const NetworkSettings = () => {
</List>
) : (
<ValidatedTextField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="ssid"
label={'SSID (' + LL.NETWORK_BLANK_SSID() + ')'}
fullWidth
@@ -183,7 +183,7 @@ const NetworkSettings = () => {
/>
)}
<ValidatedTextField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="bssid"
label={'BSSID (' + LL.NETWORK_BLANK_BSSID() + ')'}
fullWidth
@@ -194,7 +194,7 @@ const NetworkSettings = () => {
/>
{(!selectedNetwork || !isNetworkOpen(selectedNetwork)) && (
<ValidatedPasswordField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="password"
label={LL.PASSWORD()}
fullWidth
@@ -251,7 +251,7 @@ const NetworkSettings = () => {
{LL.GENERAL_OPTIONS()}
</Typography>
<ValidatedTextField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="hostname"
label={LL.HOSTNAME()}
fullWidth
@@ -304,7 +304,7 @@ const NetworkSettings = () => {
{data.static_ip_config && (
<>
<ValidatedTextField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="local_ip"
label={LL.AP_LOCAL_IP()}
fullWidth
@@ -314,7 +314,7 @@ const NetworkSettings = () => {
margin="normal"
/>
<ValidatedTextField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="gateway_ip"
label={LL.NETWORK_GATEWAY()}
fullWidth
@@ -324,7 +324,7 @@ const NetworkSettings = () => {
margin="normal"
/>
<ValidatedTextField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="subnet_mask"
label={LL.NETWORK_SUBNET()}
fullWidth
@@ -334,7 +334,7 @@ const NetworkSettings = () => {
margin="normal"
/>
<ValidatedTextField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="dns_ip_1"
label="DNS #1"
fullWidth
@@ -344,7 +344,7 @@ const NetworkSettings = () => {
margin="normal"
/>
<ValidatedTextField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="dns_ip_2"
label="DNS #2"
fullWidth

View File

@@ -50,9 +50,7 @@ const WiFiNetworkScanner = () => {
const renderNetworkScanner = () => {
if (!networkList) {
return (
<FormLoader message={LL.SCANNING() + '...'} errorMessage={errorMessage} />
);
return <FormLoader errorMessage={errorMessage || ''} />;
}
return <WiFiNetworkSelector networkList={networkList} />;
};

View File

@@ -97,7 +97,7 @@ const ManageUsers = () => {
const content = () => {
if (!data) {
return <FormLoader onRetry={loadData} errorMessage={errorMessage} />;
return <FormLoader onRetry={loadData} errorMessage={errorMessage || ''} />;
}
const noAdminConfigured = () => !data.users.find((u) => u.admin);
@@ -260,7 +260,11 @@ const ManageUsers = () => {
</Box>
</Box>
<GenerateToken username={generatingToken} onClose={closeGenerateToken} />
<GenerateToken
username={generatingToken || ''}
onClose={closeGenerateToken}
/>
{user && (
<User
user={user}
setUser={setUser}
@@ -269,6 +273,7 @@ const ManageUsers = () => {
onCancelEditing={cancelEditingUser}
validator={createUserValidator(data.users, creating)}
/>
)}
</>
);
};

View File

@@ -19,7 +19,7 @@ const Security = () => {
],
useLocation()
);
const routerTab = matchedRoutes?.[0].route.path || false;
const routerTab = matchedRoutes?.[0]?.route.path || false;
return (
<>

View File

@@ -47,12 +47,12 @@ const SecuritySettings = () => {
origData,
dirtyFlags,
setDirtyFlags,
updateDataValue
updateDataValue as (value: unknown) => void
);
const content = () => {
if (!data) {
return <FormLoader onRetry={loadData} errorMessage={errorMessage} />;
return <FormLoader onRetry={loadData} errorMessage={errorMessage || ''} />;
}
const validateAndSubmit = async () => {
@@ -69,7 +69,7 @@ const SecuritySettings = () => {
return (
<>
<ValidatedPasswordField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="jwt_secret"
label={LL.SU_PASSWORD()}
fullWidth

View File

@@ -82,7 +82,7 @@ const User: FC<UserFormProps> = ({
</DialogTitle>
<DialogContent dividers>
<ValidatedTextField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="username"
label={LL.USERNAME(1)}
fullWidth
@@ -93,7 +93,7 @@ const User: FC<UserFormProps> = ({
margin="normal"
/>
<ValidatedPasswordField
fieldErrors={fieldErrors}
fieldErrors={fieldErrors || {}}
name="password"
label={LL.PASSWORD()}
fullWidth

View File

@@ -61,7 +61,7 @@ const APStatus = () => {
const content = () => {
if (!data) {
return <FormLoader onRetry={loadData} errorMessage={error?.message} />;
return <FormLoader onRetry={loadData} errorMessage={error?.message || ''} />;
}
return (

View File

@@ -67,7 +67,8 @@ const SystemActivity = () => {
});
const showName = (id: number) => {
const name: keyof Translation['STATUS_NAMES'] = id;
const name: keyof Translation['STATUS_NAMES'] =
id.toString() as keyof Translation['STATUS_NAMES'];
return LL.STATUS_NAMES[name]();
};
@@ -87,7 +88,7 @@ const SystemActivity = () => {
const content = () => {
if (!data) {
return <FormLoader onRetry={loadData} errorMessage={error?.message} />;
return <FormLoader onRetry={loadData} errorMessage={error?.message || ''} />;
}
return (

View File

@@ -41,7 +41,7 @@ const HardwareStatus = () => {
const content = () => {
if (!data) {
return <FormLoader onRetry={loadData} errorMessage={error?.message} />;
return <FormLoader onRetry={loadData} errorMessage={error?.message || ''} />;
}
return (

View File

@@ -99,7 +99,7 @@ const MqttStatus = () => {
const content = () => {
if (!data) {
return <FormLoader onRetry={loadData} errorMessage={error?.message} />;
return <FormLoader onRetry={loadData} errorMessage={error?.message || ''} />;
}
const renderConnectionStatus = () => (

View File

@@ -68,7 +68,7 @@ const NTPStatus = () => {
const content = () => {
if (!data) {
return <FormLoader onRetry={loadData} errorMessage={error?.message} />;
return <FormLoader onRetry={loadData} errorMessage={error?.message || ''} />;
}
return (

View File

@@ -120,7 +120,7 @@ const NetworkStatus = () => {
const content = () => {
if (!data) {
return <FormLoader onRetry={loadData} errorMessage={error?.message} />;
return <FormLoader onRetry={loadData} errorMessage={error?.message || ''} />;
}
return (

View File

@@ -248,7 +248,7 @@ const SystemStatus = () => {
const content = () => {
if (!data || !LL) {
return <FormLoader onRetry={loadData} errorMessage={error?.message} />;
return <FormLoader onRetry={loadData} errorMessage={error?.message || ''} />;
}
return (

View File

@@ -31,13 +31,14 @@ import type { LogEntry, LogSettings } from 'types';
import { LogLevel } from 'types';
import { updateValueDirty, useRest } from 'utils';
const TextColors = {
const TextColors: Record<LogLevel, string> = {
[LogLevel.ERROR]: '#ff0000', // red
[LogLevel.WARNING]: '#ff0000', // red
[LogLevel.NOTICE]: '#ffffff', // white
[LogLevel.INFO]: '#ffcc00', // yellow
[LogLevel.DEBUG]: '#00ffff', // cyan
[LogLevel.TRACE]: '#00ffff' // cyan
[LogLevel.TRACE]: '#00ffff', // cyan
[LogLevel.ALL]: '#ffffff' // white
};
const LogEntryLine = styled('span')(
@@ -109,7 +110,7 @@ const SystemLog = () => {
origData,
dirtyFlags,
setDirtyFlags,
updateDataValue
updateDataValue as (value: unknown) => void
);
useSSE(fetchLogES, {
@@ -190,7 +191,7 @@ const SystemLog = () => {
const content = () => {
if (!data) {
return <FormLoader onRetry={loadData} errorMessage={errorMessage} />;
return <FormLoader onRetry={loadData} errorMessage={errorMessage || ''} />;
}
return (

View File

@@ -110,7 +110,7 @@ const Version = () => {
}, [latestVersion, latestDevVersion]);
const rtf = new Intl.RelativeTimeFormat(locale, { numeric: 'auto' });
const DIVISIONS = [
const DIVISIONS: Array<{ amount: number; name: string }> = [
{ amount: 60, name: 'seconds' },
{ amount: 60, name: 'minutes' },
{ amount: 24, name: 'hours' },
@@ -119,19 +119,22 @@ const Version = () => {
{ amount: 12, name: 'months' },
{ amount: Number.POSITIVE_INFINITY, name: 'years' }
];
function formatTimeAgo(date) {
function formatTimeAgo(date: Date) {
let duration = (date.getTime() - new Date().getTime()) / 1000;
for (let i = 0; i < DIVISIONS.length; i++) {
const division = DIVISIONS[i];
if (Math.abs(duration) < division.amount) {
if (division && Math.abs(duration) < division.amount) {
return rtf.format(
Math.round(duration),
division.name as Intl.RelativeTimeFormatUnit
);
}
if (division) {
duration /= division.amount;
}
}
return rtf.format(0, 'seconds');
}
const { send: sendAPI } = useRequest((data: APIcall) => API(data), {
immediate: false
@@ -270,6 +273,14 @@ const Version = () => {
<span style={{ color: '#66bb6a', fontSize: '0.8em' }}>
{LL.LATEST_VERSION(usingDevVersion ? LL.DEVELOPMENT() : LL.STABLE())}
</span>
<Button
sx={{ ml: 2 }}
variant="outlined"
size="small"
onClick={() => showFirmwareDialog(showingDev)}
>
{LL.REINSTALL()}
</Button>
</>
);
}
@@ -293,7 +304,7 @@ const Version = () => {
const content = () => {
if (!data) {
return <FormLoader onRetry={loadData} errorMessage={error?.message} />;
return <FormLoader onRetry={loadData} errorMessage={error?.message || ''} />;
}
const isDev = data.emsesp_version.includes('dev');

View File

@@ -1,26 +1,24 @@
import type { FC } from 'react';
import { memo } from 'react';
import { Box } from '@mui/material';
import type { BoxProps } from '@mui/material';
const ButtonRow: FC<BoxProps> = ({ children, ...rest }) => (
const ButtonRow = memo<BoxProps>(({ children, ...rest }) => (
<Box
sx={{
'& button, & a, & .MuiCard-root': {
mt: 2,
mx: 0.6,
'&:last-child': {
mr: 0
},
'&:first-of-type': {
ml: 0
}
'&:last-child': { mr: 0 },
'&:first-of-type': { ml: 0 }
}
}}
{...rest}
>
{children}
</Box>
);
));
ButtonRow.displayName = 'ButtonRow';
export default ButtonRow;

View File

@@ -1,7 +1,12 @@
import { Tooltip, type TooltipProps, styled, tooltipClasses } from '@mui/material';
export const ButtonTooltip = styled(({ className, ...props }: TooltipProps) => (
<Tooltip {...props} placement="top" arrow classes={{ popper: className }} />
<Tooltip
{...props}
placement="top"
arrow
classes={{ ...(className && { popper: className }) }}
/>
))(({ theme }) => ({
[`& .${tooltipClasses.arrow}`]: {
color: theme.palette.success.main

View File

@@ -1,10 +1,15 @@
// Optimized exports - use direct exports to reduce bundle size
export { default as SectionContent } from './SectionContent';
export { default as ButtonRow } from './ButtonRow';
export { default as MessageBox } from './MessageBox';
export { default as ButtonTooltip } from './ButtonTooltip';
// Re-export sub-modules
export * from './inputs';
export * from './layout';
export * from './loading';
export * from './routing';
export * from './upload';
export { default as SectionContent } from './SectionContent';
export { default as ButtonRow } from './ButtonRow';
export { default as MessageBox } from './MessageBox';
// Specific routing exports
export { default as BlockNavigation } from './routing/BlockNavigation';
export { default as ButtonTooltip } from './ButtonTooltip';

View File

@@ -16,14 +16,14 @@ const ValidatedTextField: FC<ValidatedTextFieldProps> = ({
fieldErrors,
...rest
}) => {
const errors = fieldErrors && fieldErrors[rest.name];
const renderErrors = () =>
errors &&
errors.map((e) => <FormHelperText key={e.message}>{e.message}</FormHelperText>);
const errors = fieldErrors?.[rest.name];
return (
<>
<TextField error={!!errors} {...rest} />
{renderErrors()}
{errors?.map((e) => (
<FormHelperText key={e.message}>{e.message}</FormHelperText>
))}
</>
);
};

View File

@@ -23,7 +23,12 @@ const LayoutMenuItem = ({
const selected = routeMatches(to, pathname);
return (
<ListItemButton component={Link} to={to} disabled={disabled} selected={selected}>
<ListItemButton
component={Link}
to={to}
disabled={disabled || false}
selected={selected}
>
<ListItemIcon sx={{ color: selected ? '#90caf9' : '#9e9e9e' }}>
<Icon />
</ListItemIcon>

View File

@@ -58,12 +58,22 @@ const LayoutMenuItem = ({
}
>
<ListItemButton component={Link} to={to}>
<RenderIcon icon={icon} bgcolor={bgcolor} label={label} text={text} />
<RenderIcon
icon={icon}
{...(bgcolor && { bgcolor })}
label={label}
text={text}
/>
</ListItemButton>
</ListItem>
) : (
<ListItem>
<RenderIcon icon={icon} bgcolor={bgcolor} label={label} text={text} />
<RenderIcon
icon={icon}
{...(bgcolor && { bgcolor })}
label={label}
text={text}
/>
</ListItem>
)}
</>

View File

@@ -0,0 +1,20 @@
import { Box, CircularProgress } from '@mui/material';
const LazyLoader = () => (
<Box
display="flex"
justifyContent="center"
alignItems="center"
minHeight="200px"
sx={{
backgroundColor: 'background.default',
borderRadius: 1,
border: '1px solid',
borderColor: 'divider'
}}
>
<CircularProgress size={40} />
</Box>
);
export default LazyLoader;

View File

@@ -1,2 +1,3 @@
export { default as LoadingSpinner } from './LoadingSpinner';
export { default as FormLoader } from './FormLoader';
export { default as LazyLoader } from './LazyLoader';

View File

@@ -1,6 +1,5 @@
import type { Path } from 'react-router';
import type * as H from 'history';
import { jwtDecode } from 'jwt-decode';
import type { Me, SignInRequest, SignInResponse } from 'types';
@@ -18,10 +17,10 @@ export function getStorage() {
return localStorage || sessionStorage;
}
export function storeLoginRedirect(location?: H.Location) {
export function storeLoginRedirect(location?: { pathname: string; search: string }) {
if (location) {
getStorage().setItem(SIGN_IN_PATHNAME, location.pathname as string);
getStorage().setItem(SIGN_IN_SEARCH, location.search as string);
getStorage().setItem(SIGN_IN_PATHNAME, location.pathname);
getStorage().setItem(SIGN_IN_SEARCH, location.search);
}
}
@@ -36,7 +35,7 @@ export function fetchLoginRedirect(): Partial<Path> {
clearLoginRedirect();
return {
pathname: signInPathname || `/dashboard`,
search: (signInPathname && signInSearch) || undefined
...(signInPathname && signInSearch && { search: signInSearch })
};
}

View File

@@ -1,16 +1,59 @@
// 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 { type ChangeEvent, useRef, useState } from 'react';
import {
type ChangeEvent,
type DragEvent,
type MouseEvent,
useRef,
useState
} from 'react';
import CancelIcon from '@mui/icons-material/Cancel';
import CloudUploadIcon from '@mui/icons-material/CloudUpload';
import UploadIcon from '@mui/icons-material/Upload';
import { Box, Button } from '@mui/material';
import { Box, Button, Typography, styled } from '@mui/material';
import { useI18nContext } from 'i18n/i18n-react';
import './dragNdrop.css';
const DocumentUploader = styled(Box)<{ active?: boolean }>(({ theme, active }) => ({
border: `2px dashed ${active ? '#6dc24b' : '#4282fe'}`,
backgroundColor: '#2e3339',
padding: theme.spacing(1.25),
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
position: 'relative',
borderRadius: theme.spacing(1),
cursor: 'pointer',
minHeight: '120px',
transition: 'border-color 0.2s ease-in-out'
}));
const DragNdrop = ({ text, onFileSelected }) => {
const UploadInfo = styled(Box)({
display: 'flex',
alignItems: 'center'
});
const FileInfo = styled(Box)({
display: 'flex',
flexDirection: 'column',
width: '100%',
justifyContent: 'space-between',
alignItems: 'center'
});
const FileName = styled(Typography)(({ theme }) => ({
fontSize: '14px',
color: '#6dc24b',
margin: theme.spacing(1, 0)
}));
interface DragNdropProps {
text: string;
onFileSelected: (file: File) => void;
}
const DragNdrop = ({ text, onFileSelected }: DragNdropProps) => {
const [file, setFile] = useState<File>();
const [dragged, setDragged] = useState(false);
const inputRef = useRef<HTMLInputElement | null>(null);
@@ -28,14 +71,17 @@ const DragNdrop = ({ text, onFileSelected }) => {
};
const handleFileChange = (e: ChangeEvent<HTMLInputElement>) => {
if (!e.target.files) {
if (!e.target.files || e.target.files.length === 0) {
return;
}
checkFileExtension(e.target.files[0]);
const selectedFile = e.target.files[0];
if (selectedFile) {
checkFileExtension(selectedFile);
}
e.target.value = ''; // this is to allow the same file to be selected again
};
const handleDrop = (event) => {
const handleDrop = (event: DragEvent<HTMLDivElement>) => {
event.preventDefault();
const droppedFiles = event.dataTransfer.files;
if (droppedFiles.length > 0) {
@@ -43,38 +89,40 @@ const DragNdrop = ({ text, onFileSelected }) => {
}
};
const handleRemoveFile = (event) => {
const handleRemoveFile = (event: MouseEvent<HTMLButtonElement>) => {
event.stopPropagation();
setFile(undefined);
setDragged(false);
};
const handleUploadClick = (event) => {
const handleUploadClick = (event: MouseEvent<HTMLButtonElement>) => {
event.stopPropagation();
if (file) {
onFileSelected(file);
}
};
const handleBrowseClick = () => {
inputRef.current?.click();
};
const handleDragOver = (event) => {
const handleDragOver = (event: DragEvent<HTMLDivElement>) => {
event.preventDefault(); // prevent file from being opened
setDragged(true);
};
return (
<div
className={`document-uploader ${file || dragged ? 'active' : ''}`}
<DocumentUploader
active={!!(file || dragged)}
onDrop={handleDrop}
onDragOver={handleDragOver}
onDragLeave={() => setDragged(false)}
onClick={handleBrowseClick}
>
<div className="upload-info">
<UploadInfo>
<CloudUploadIcon sx={{ marginRight: 4 }} color="primary" fontSize="large" />
<p>{text}</p>
</div>
<Typography>{text}</Typography>
</UploadInfo>
<input
type="file"
@@ -88,9 +136,9 @@ const DragNdrop = ({ text, onFileSelected }) => {
{file && (
<>
<div className="file-info">
<p>{file.name}</p>
</div>
<FileInfo>
<FileName>{file.name}</FileName>
</FileInfo>
<Box>
<Button
startIcon={<CancelIcon />}
@@ -112,7 +160,7 @@ const DragNdrop = ({ text, onFileSelected }) => {
</Box>
</>
)}
</div>
</DocumentUploader>
);
};

View File

@@ -12,7 +12,12 @@ import { useI18nContext } from 'i18n/i18n-react';
import DragNdrop from './DragNdrop';
import { LinearProgressWithLabel } from './LinearProgressWithLabel';
const SingleUpload = ({ text, doRestart }) => {
interface SingleUploadProps {
text: string;
doRestart: () => void;
}
const SingleUpload = ({ text, doRestart }: SingleUploadProps) => {
const [md5, setMd5] = useState<string>();
const [file, setFile] = useState<File>();
const { LL } = useI18nContext();
@@ -25,8 +30,8 @@ const SingleUpload = ({ text, doRestart }) => {
} = useRequest(SystemApi.uploadFile, {
immediate: false
}).onSuccess(({ data }) => {
if (data) {
setMd5(data.md5 as string);
if (data && typeof data === 'object' && 'md5' in data) {
setMd5((data as { md5: string }).md5);
toast.success(LL.UPLOAD() + ' MD5 ' + LL.SUCCESSFUL());
setFile(undefined);
} else {
@@ -34,16 +39,19 @@ const SingleUpload = ({ text, doRestart }) => {
}
});
useEffect(async () => {
useEffect(() => {
const uploadFile = async () => {
if (file) {
await sendUpload(file).catch((error: Error) => {
if (error.message === 'The user aborted a request') {
if (error.message.includes('The user aborted a request')) {
toast.warning(LL.UPLOAD() + ' ' + LL.ABORTED());
} else {
toast.warning('Invalid file extension or incompatible bin file');
}
});
}
};
void uploadFile();
}, [file]);
return (

View File

@@ -1,33 +0,0 @@
.document-uploader {
border: 2px dashed #4282fe;
background-color: #2e3339;
padding: 10px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
position: relative;
border-radius: 8px;
cursor: pointer;
&.active {
border-color: #6dc24b;
}
.upload-info {
display: flex;
align-items: center;
}
.file-info {
display: flex;
flex-direction: column;
width: 100%;
justify-content: space-between;
align-items: center;
p {
font-size: 14px;
color: #6dc24b;
}
}
}

View File

@@ -69,7 +69,12 @@ const Authentication: FC<RequiredChildrenProps> = ({ children }) => {
// cache object to prevent re-renders
const obj = useMemo(
() => ({ signIn, signOut, me, refresh }),
() => ({
signIn,
signOut,
refresh,
...(me && { me })
}),
[signIn, signOut, me, refresh]
);

View File

@@ -162,6 +162,7 @@ const cz: Translation = {
UPLOAD: 'Nahrát',
DOWNLOAD: '{{S|s|s}}táhnout',
INSTALL: 'Instalovat',
REINSTALL: 'Znovu instalovat',
ABORTED: 'přerušeno',
FAILED: 'neúspěšné',
SUCCESSFUL: 'úspěšné',

View File

@@ -162,6 +162,7 @@ const de: Translation = {
UPLOAD: 'Hochladen',
DOWNLOAD: '{{Herunterladen|heruntergeladen|}}',
INSTALL: 'Installieren',
REINSTALL: 'Neu installieren',
ABORTED: 'abgebrochen',
FAILED: 'gescheitert',
SUCCESSFUL: 'erfolgreich',

View File

@@ -162,6 +162,7 @@ const en: Translation = {
UPLOAD: 'Upload',
DOWNLOAD: '{{D|d|d}}ownload',
INSTALL: 'Install',
REINSTALL: 'Reinstall',
ABORTED: 'aborted',
FAILED: 'failed',
SUCCESSFUL: 'successful',

View File

@@ -162,6 +162,7 @@ const fr: Translation = {
UPLOAD: 'Upload',
DOWNLOAD: '{{D|d|d}}ownload',
INSTALL: 'Installer',
REINSTALL: 'Réinstaller',
ABORTED: 'annulé',
FAILED: 'échoué',
SUCCESSFUL: 'réussi',

View File

@@ -162,6 +162,7 @@ const it: Translation = {
UPLOAD: 'Carica',
DOWNLOAD: 'Scarica',
INSTALL: 'Installare {0}',
REINSTALL: 'Riavviare',
ABORTED: 'Annullato',
FAILED: 'Fallito',
SUCCESSFUL: 'Riuscito',

View File

@@ -162,6 +162,7 @@ const nl: Translation = {
UPLOAD: 'Upload',
DOWNLOAD: '{{D|d|d}}ownload',
INSTALL: 'Installeren',
REINSTALL: 'Opnieuw installeren',
ABORTED: 'afgebroken',
FAILED: 'mislukt',
SUCCESSFUL: 'successvol',

View File

@@ -162,6 +162,7 @@ const no: Translation = {
UPLOAD: 'Opplasning',
DOWNLOAD: '{{N|n|n}}edlasting',
INSTALL: 'Installer',
REINSTALL: 'Ominstaller',
ABORTED: 'avbrutt',
FAILED: 'feilet',
SUCCESSFUL: 'vellykket',

View File

@@ -162,6 +162,7 @@ const pl: BaseTranslation = {
UPLOAD: 'Wysyłanie',
DOWNLOAD: '{{P|p||P}}obier{{anie|z||z}}',
INSTALL: 'Zainstalować',
REINSTALL: 'Zainstalować ponownie',
ABORTED: 'zostało przerwane!',
FAILED: 'nie powiodł{{o|a|}} się!',
SUCCESSFUL: 'powiodło się.',

View File

@@ -162,6 +162,7 @@ const sk: Translation = {
UPLOAD: 'Nahrať',
DOWNLOAD: '{{S|s|s}}tiahnuť',
INSTALL: 'Inštalovať',
REINSTALL: 'Inštalovať znova',
ABORTED: 'zrušené',
FAILED: 'chybné',
SUCCESSFUL: 'úspešné',

View File

@@ -162,6 +162,7 @@ const sv: Translation = {
UPLOAD: 'Uppladdning',
DOWNLOAD: '{{N|n|n}}edladdning',
INSTALL: 'Installera',
REINSTALL: 'Återinstallera',
ABORTED: 'Avbruten',
FAILED: 'Misslyckades',
SUCCESSFUL: 'Lyckades',

View File

@@ -162,6 +162,7 @@ const tr: Translation = {
UPLOAD: 'Yükleme',
DOWNLOAD: '{{İ|i|i}}İndirme',
INSTALL: 'Düzenlemek',
REINSTALL: 'Yeniden düzenlemek',
ABORTED: 'iptal edildi',
FAILED: 'başarısız',
SUCCESSFUL: 'başarılı',

View File

@@ -13,7 +13,7 @@ const router = createBrowserRouter(
createRoutesFromElements(<Route path="/*" element={<App />} />)
);
createRoot(document.getElementById('root') as HTMLElement).render(
createRoot(document.getElementById('root')!).render(
<StrictMode>
<RouterProvider router={router} />
</StrictMode>

View File

@@ -29,10 +29,10 @@ export const updateValue =
export const updateValueDirty =
(
origData,
origData: unknown,
dirtyFlags: string[],
setDirtyFlags: React.Dispatch<React.SetStateAction<string[]>>,
updateDataValue: (unknown) => void
updateDataValue: (value: unknown) => void
) =>
(event: React.ChangeEvent<HTMLInputElement>) => {
const updated_value = extractEventValue(event);

View File

@@ -1,31 +1,108 @@
{
"compilerOptions": {
"target": "ESNext",
// Target modern browsers for better performance
"target": "ES2022",
"useDefineForClassFields": true,
"lib": ["DOM", "DOM.Iterable", "ESNext"],
"types": ["node"],
// Optimized library selection
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"types": ["node", "vite/client"],
// JavaScript handling
"allowJs": false,
"skipLibCheck": true,
"esModuleInterop": false,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"composite": true,
"checkJs": false,
// Module system optimized for Vite
"module": "ESNext",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"resolveJsonModule": true,
"isolatedModules": true,
// Emit configuration
"noEmit": true,
"useUnknownInCatchVariables": false,
"declaration": false,
"declarationMap": false,
"sourceMap": false,
// React/JSX configuration
"jsx": "react-jsx",
"noImplicitAny": false,
"baseUrl": "src",
"jsxImportSource": "react",
// Strict type checking for better code quality
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictBindCallApply": true,
"strictPropertyInitialization": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"exactOptionalPropertyTypes": true,
// Additional checks
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedIndexedAccess": true,
"useUnknownInCatchVariables": true,
// Performance optimizations
"skipLibCheck": true,
"skipDefaultLibCheck": true,
"incremental": true,
"tsBuildInfoFile": ".tsbuildinfo",
// Path mapping for cleaner imports
"baseUrl": ".",
"paths": {
"@": ["src"],
"@/*": ["src/*"]
"@/*": ["src/*"],
"@/components/*": ["src/components/*"],
"@/utils/*": ["src/utils/*"],
"@/types/*": ["src/types/*"],
"@/hooks/*": ["src/hooks/*"],
"@/services/*": ["src/services/*"],
"@/assets/*": ["src/assets/*"],
// Support for bare imports from src directory
"App": ["src/App"],
"AppRouting": ["src/AppRouting"],
"CustomTheme": ["src/CustomTheme"],
"SignIn": ["src/SignIn"],
"AuthenticatedRouting": ["src/AuthenticatedRouting"],
"env": ["src/env"],
"components": ["src/components"],
"contexts": ["src/contexts"],
"i18n": ["src/i18n"],
"utils": ["src/utils"],
"validators": ["src/validators"],
"types": ["src/types"],
"api": ["src/api"],
"app": ["src/app"],
// Wildcard patterns for subdirectories
"components/*": ["src/components/*"],
"contexts/*": ["src/contexts/*"],
"i18n/*": ["src/i18n/*"],
"utils/*": ["src/utils/*"],
"validators/*": ["src/validators/*"],
"types/*": ["src/types/*"],
"api/*": ["src/api/*"],
"app/*": ["src/app/*"]
}
},
"include": ["src/**/*", "vite.config.ts"],
"exclude": ["node_modules", "dist"]
"include": ["src/**/*", "vite.config.ts", "progmem-generator.js"],
"exclude": [
"node_modules",
"dist",
"build",
".tsbuildinfo",
"**/*.test.ts",
"**/*.test.tsx",
"**/*.spec.ts",
"**/*.spec.tsx"
],
"ts-node": {
"esm": true
}
}

View File

@@ -1,16 +1,106 @@
import preact from '@preact/preset-vite';
import fs from 'fs';
import path from 'path';
import { visualizer } from 'rollup-plugin-visualizer';
import { defineConfig } from 'vite';
// import viteImagemin from 'vite-plugin-imagemin';
import { Plugin } from 'vite';
import viteImagemin from 'vite-plugin-imagemin';
import viteTsconfigPaths from 'vite-tsconfig-paths';
import zlib from 'zlib';
// @ts-expect-error - mock server doesn't have type declarations
import mockServer from '../mock-api/mockServer.js';
export default defineConfig(({ command, mode }) => {
// Plugin to display bundle size information
const bundleSizeReporter = (): Plugin => {
return {
name: 'bundle-size-reporter',
// eslint-disable-next-line @typescript-eslint/no-explicit-any
writeBundle(options: any, bundle: any) {
console.log('\n📦 Bundle Size Report:');
console.log('='.repeat(50));
let totalSize = 0;
const files: Array<{ name: string; size: number; gzipSize?: number }> = [];
for (const [fileName, chunk] of Object.entries(
bundle as Record<string, unknown>
)) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
if ((chunk as any).type === 'chunk' || (chunk as any).type === 'asset') {
const filePath = path.join((options.dir as string) || 'dist', fileName);
let size = 0;
let gzipSize = 0;
try {
const stats = fs.statSync(filePath);
size = stats.size;
totalSize += size;
// Calculate gzip size
const fileContent = fs.readFileSync(filePath);
gzipSize = zlib.gzipSync(fileContent).length;
files.push({
name: fileName,
size,
gzipSize
});
} catch (error) {
console.warn(`Could not read file ${fileName}:`, error);
}
}
}
// Sort files by size (largest first)
files.sort((a, b) => b.size - a.size);
// Display individual file sizes
files.forEach((file) => {
const sizeKB = (file.size / 1024).toFixed(2);
const gzipKB = file.gzipSize ? (file.gzipSize / 1024).toFixed(2) : 'N/A';
console.log(
`📄 ${file.name.padEnd(30)} ${sizeKB.padStart(8)} KB (${gzipKB} KB gzipped)`
);
});
console.log('='.repeat(50));
console.log(`📊 Total Bundle Size: ${(totalSize / 1024).toFixed(2)} KB`);
// Calculate and display gzip total
const totalGzipSize = files.reduce(
(sum, file) => sum + (file.gzipSize || 0),
0
);
console.log(`🗜️ Total Gzipped Size: ${(totalGzipSize / 1024).toFixed(2)} KB`);
// Show compression ratio
const compressionRatio = (
((totalSize - totalGzipSize) / totalSize) *
100
).toFixed(1);
console.log(`📈 Compression Ratio: ${compressionRatio}%`);
console.log('='.repeat(50));
}
};
};
export default defineConfig(
({ command, mode }: { command: string; mode: string }) => {
if (command === 'serve') {
console.log('Preparing for standalone build with server, mode=' + mode);
return {
plugins: [preact(), viteTsconfigPaths(), mockServer()],
plugins: [
preact({
// Keep dev tools enabled for development
devToolsEnabled: true,
prefreshEnabled: true
}),
viteTsconfigPaths(),
bundleSizeReporter(), // Add bundle size reporting
mockServer()
],
server: {
open: true,
port: mode == 'production' ? 4173 : 3000,
@@ -23,6 +113,12 @@ export default defineConfig(({ command, mode }) => {
'/rest': 'http://localhost:3080',
'/gh': 'http://localhost:3080' // mock for GitHub API
}
},
// Optimize development builds
build: {
target: 'es2020',
minify: false, // Disable minification for faster dev builds
sourcemap: true // Enable source maps for debugging
}
};
}
@@ -30,9 +126,50 @@ export default defineConfig(({ command, mode }) => {
if (mode === 'hosted') {
console.log('Preparing for hosted build');
return {
plugins: [preact(), viteTsconfigPaths()],
plugins: [
preact({
// Enable Preact optimizations for hosted build
devToolsEnabled: false,
prefreshEnabled: false
}),
viteTsconfigPaths(),
bundleSizeReporter() // Add bundle size reporting
],
build: {
chunkSizeWarningLimit: 1024
target: 'es2020',
chunkSizeWarningLimit: 512,
minify: 'terser',
cssMinify: true,
assetsInlineLimit: 4096,
terserOptions: {
compress: {
passes: 3,
drop_console: true,
drop_debugger: true,
dead_code: true,
unused: true
},
mangle: {
toplevel: true
},
ecma: 2020
},
rollupOptions: {
treeshake: {
moduleSideEffects: false
},
output: {
manualChunks(id: string) {
if (id.includes('node_modules')) {
if (id.includes('preact')) {
return '@preact';
}
return 'vendor';
}
return undefined;
}
}
}
}
};
}
@@ -41,90 +178,154 @@ export default defineConfig(({ command, mode }) => {
return {
plugins: [
preact(),
preact({
// Enable Preact optimizations
devToolsEnabled: false,
prefreshEnabled: false
}),
viteTsconfigPaths(),
// {
// ...viteImagemin({
// verbose: false,
// gifsicle: {
// optimizationLevel: 7,
// interlaced: false
// },
// optipng: {
// optimizationLevel: 7
// },
// mozjpeg: {
// quality: 20
// },
// pngquant: {
// quality: [0.8, 0.9],
// speed: 4
// },
// svgo: {
// plugins: [
// {
// name: 'removeViewBox'
// },
// {
// name: 'removeEmptyAttrs',
// active: false
// }
// ]
// }
// }),
// enforce: 'pre'
// },
// Enable image optimization for size reduction
{
...viteImagemin({
verbose: false,
gifsicle: {
optimizationLevel: 7,
interlaced: false
},
optipng: {
optimizationLevel: 7
},
mozjpeg: {
quality: 20
},
pngquant: {
quality: [0.8, 0.9],
speed: 4
},
svgo: {
plugins: [
{
name: 'removeViewBox'
},
{
name: 'removeEmptyAttrs',
active: false
}
]
}
}),
enforce: 'pre'
},
visualizer({
template: 'treemap', // or sunburst
open: false,
gzipSize: true,
brotliSize: true,
filename: '../analyse.html' // will be saved in project's root
})
}),
bundleSizeReporter() // Add bundle size reporting
],
build: {
// target: 'es2022',
chunkSizeWarningLimit: 1024,
// Target modern browsers for smaller bundles
target: 'es2020',
chunkSizeWarningLimit: 512,
minify: 'terser',
// Enable CSS minification
cssMinify: true,
// Optimize asset handling
assetsInlineLimit: 4096, // Inline small assets
terserOptions: {
compress: {
passes: 4,
passes: 6,
arrows: true,
drop_console: true,
drop_debugger: true,
sequences: true
// Additional aggressive compression options
// dead_code: true,
// hoist_funs: true,
// hoist_vars: true,
// if_return: true,
// join_vars: true,
// loops: true,
// pure_getters: true,
// reduce_vars: true,
// side_effects: false,
// switches: true,
// unsafe: true,
// unsafe_arrows: true,
// unsafe_comps: true,
// unsafe_Function: true,
// unsafe_math: true,
// unsafe_proto: true,
// unsafe_regexp: true,
// unsafe_undefined: true,
// unused: true
},
mangle: {
// toplevel: true
// module: true
toplevel: true, // Enable top-level mangling
module: true // Enable module mangling
// properties: {
// regex: /^_/
// regex: /^_/ // Mangle properties starting with _
// }
},
ecma: 5,
ecma: 2020, // Updated to modern ECMAScript
enclose: false,
keep_classnames: false,
keep_fnames: false,
ie8: false,
module: false,
safari10: false,
toplevel: false
toplevel: true // Enable top-level optimization
},
rollupOptions: {
// Enable tree shaking
treeshake: {
moduleSideEffects: false,
propertyReadSideEffects: false,
tryCatchDeoptimization: false
},
output: {
// Optimize chunk naming for better caching
chunkFileNames: 'assets/[name]-[hash].js',
entryFileNames: 'assets/[name]-[hash].js',
assetFileNames: 'assets/[name]-[hash].[ext]',
manualChunks(id: string) {
if (id.includes('node_modules')) {
// creating a chunk to react routes deps. Reducing the vendor chunk size
// More granular chunk splitting for better caching
if (id.includes('react-router')) {
return '@react-router';
}
if (id.includes('preact')) {
return '@preact';
}
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';
}
// Split large application modules
if (id.includes('components/')) {
return 'components';
}
if (id.includes('pages/') || id.includes('routes/')) {
return 'pages';
}
return undefined;
},
// Enable source maps for debugging (optional)
sourcemap: false // Disable for production to save space
}
}
}
};
});
}
);

View File

@@ -1,33 +1,24 @@
// used to simulate
// - file uploads
// - EventSource (SSE) for log messages
// Mock server for development
// Simulates file uploads and EventSource (SSE) for log messages
import formidable from 'formidable';
function pad(number) {
let r = String(number);
if (r.length === 1) {
r = '0' + r;
}
return r;
}
// Optimized padding function
const pad = (number) => String(number).padStart(2, '0');
// e.g. 2024-03-29 07:02:37.856
Date.prototype.toISOString = function () {
return (
this.getUTCFullYear() +
'-' +
pad(this.getUTCMonth() + 1) +
'-' +
pad(this.getUTCDate()) +
' ' +
pad(this.getUTCHours()) +
':' +
pad(this.getUTCMinutes()) +
':' +
pad(this.getUTCSeconds()) +
'.' +
String((this.getUTCMilliseconds() / 1000).toFixed(3)).slice(2, 5)
// Cached date formatter to avoid prototype pollution
const formatDate = (date) => {
const year = date.getUTCFullYear();
const month = pad(date.getUTCMonth() + 1);
const day = pad(date.getUTCDate());
const hours = pad(date.getUTCHours());
const minutes = pad(date.getUTCMinutes());
const seconds = pad(date.getUTCSeconds());
const milliseconds = String((date.getUTCMilliseconds() / 1000).toFixed(3)).slice(
2,
5
);
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}.${milliseconds}`;
};
export default () => {
@@ -35,97 +26,129 @@ export default () => {
name: 'vite:mockserver',
configureServer: async (server) => {
server.middlewares.use(async (req, res, next) => {
// catch any file uploads
// Handle file uploads
if (req.url.startsWith('/rest/uploadFile')) {
// show progress
const fileSize = parseInt(req.headers['content-length'] || '0', 10);
let progress = 0;
const file_size = req.headers['content-length'];
console.log('File size: ' + file_size);
req.on('data', async (chunk) => {
// Track upload progress
req.on('data', (chunk) => {
progress += chunk.length;
const percentage = (progress / file_size) * 100;
console.log(`Progress: ${Math.round(percentage)}%`);
// await new Promise((resolve) => setTimeout(() => resolve(), 3000)); // slow it down
if (fileSize > 0) {
const percentage = Math.round((progress / fileSize) * 100);
console.log(`Upload progress: ${percentage}%`);
}
});
const form = formidable({});
let fields;
let files;
try {
[fields, files] = await form.parse(req);
} catch (err) {
console.error('Not json form content');
res.writeHead(err.httpCode || 400, {
'Content-Type': 'text/plain'
const form = formidable({
maxFileSize: 50 * 1024 * 1024, // 50MB limit
keepExtensions: true
});
res.end(String(err));
const [fields, files] = await form.parse(req);
if (Object.keys(files).length === 0) {
res.statusCode = 400;
res.end('No file uploaded');
return;
}
// only process when we have a file
if (Object.keys(files).length > 0) {
const uploaded_file = files.file[0];
const file_name = uploaded_file.originalFilename;
const file_extension = file_name.substring(
file_name.lastIndexOf('.') + 1
const uploadedFile = files.file[0];
const fileName = uploadedFile.originalFilename;
const fileExtension = fileName
.substring(fileName.lastIndexOf('.') + 1)
.toLowerCase();
console.log(
`File uploaded: ${fileName} (${fileExtension}, ${fileSize} bytes)`
);
console.log('Filename: ' + file_name);
console.log('Extension: ' + file_extension);
console.log('File size: ' + file_size);
// Validate file extension
const validExtensions = new Set(['bin', 'json', 'md5']);
if (!validExtensions.has(fileExtension)) {
res.statusCode = 406;
res.end('Invalid file extension');
return;
}
if (file_extension === 'bin' || file_extension === 'json') {
console.log('File uploaded successfully!');
} else if (file_extension === 'md5') {
console.log('MD5 hash generated successfully!');
// Handle different file types
if (fileExtension === 'md5') {
res.setHeader('Content-Type', 'application/json');
res.end(
JSON.stringify({
md5: 'ef4304fc4d9025a58dcf25d71c882d2c'
})
);
} else {
res.statusCode = 406;
console.log('Invalid file extension!');
}
}
console.log('File uploaded successfully!');
res.end();
}
} catch (err) {
console.error('Upload error:', err.message);
res.statusCode = err.httpCode || 400;
res.setHeader('Content-Type', 'text/plain');
res.end(err.message);
}
}
// SSE Eventsource
// Handle Server-Sent Events (SSE) for log streaming
else if (req.url.startsWith('/es/log')) {
// Set SSE headers
res.writeHead(200, {
Connection: 'keep-alive',
'Cache-Control': 'no-cache',
'Content-Type': 'text/event-stream'
'Content-Type': 'text/event-stream',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'Cache-Control'
});
let count = 0;
const interval = setInterval(() => {
let message = 'message #' + count;
if (count % 6 === 1) {
let messageCount = 0;
const logLevels = [3, 4, 5, 6, 7, 8]; // Different log levels
const logNames = ['system', 'ems', 'wifi', 'mqtt', 'ntp', 'api'];
const sendLogMessage = () => {
const level = logLevels[messageCount % logLevels.length];
const name = logNames[messageCount % logNames.length];
let message = `Log message #${messageCount}`;
// Add long message every 6th message
if (messageCount % 6 === 1) {
message +=
' that is a long message that will be wrapped, to test if it gets truncated';
' - This is a longer message to test text wrapping and truncation behavior in the UI';
}
const data = {
t: new Date().toISOString(),
l: 3 + (count % 6),
i: count,
n: 'system',
const logData = {
t: formatDate(new Date()),
l: level,
i: messageCount,
n: name,
m: message
};
count++;
res.write(`data: ${JSON.stringify(data)}\n\n`);
}, 1000);
// if client closes connection
res.on('close', () => {
console.log('Closing ES connection');
res.write(`data: ${JSON.stringify(logData)}\n\n`);
messageCount++;
};
// Send initial message
sendLogMessage();
// Set up interval for periodic messages
const interval = setInterval(sendLogMessage, 1000);
// Clean up on connection close
const cleanup = () => {
console.log('SSE connection closed');
clearInterval(interval);
if (!res.destroyed) {
res.end();
});
}
};
res.on('close', cleanup);
res.on('error', cleanup);
} else {
next(); // move on to the next middleware function in chain
next(); // Continue to next middleware
}
});
}

View File

@@ -1,6 +1,6 @@
{
"name": "mock-api",
"version": "3.7.2",
"version": "3.7.3",
"description": "mock api for EMS-ESP",
"author": "proddy, emsesp.org",
"license": "MIT",
@@ -15,5 +15,5 @@
"itty-router": "^5.0.22",
"prettier": "^3.6.2"
},
"packageManager": "pnpm@10.18.1+sha512.77a884a165cbba2d8d1c19e3b4880eee6d2fcabd0d879121e282196b80042351d5eb3ca0935fa599da1dc51265cc68816ad2bddd2a2de5ea9fdf92adbec7cd34"
"packageManager": "pnpm@10.19.0+sha512.c9fc7236e92adf5c8af42fd5bf1612df99c2ceb62f27047032f4720b33f8eacdde311865e91c411f2774f618d82f320808ecb51718bfa82c060c4ba7c76a32b8"
}

View File

@@ -116,17 +116,15 @@ let system_status = {
let DEV_VERSION_IS_UPGRADEABLE: boolean;
let STABLE_VERSION_IS_UPGRADEABLE: boolean;
let THIS_VERSION: string;
let version_test: number;
let LATEST_STABLE_VERSION = '3.7.2';
let LATEST_DEV_VERSION = '3.7.3-dev.6';
// scenarios for testing versioning
version_test = 0; // on latest stable, or switch to dev
// version_test = 1; // on latest dev, or switch back to stable
// version_test = 2; // upgrade an older stable to latest stable or switch to latest dev
// version_test = 3; // upgrade dev to latest, or switch to stable
// version_test = 4; // downgrade to an older dev, or switch back to stable
let version_test = 0; // on latest stable, or switch to dev
// let version_test = 1; // on latest dev, or switch back to stable
// let version_test = 2; // upgrade an older stable to latest stable or switch to latest dev
// let version_test = 3; // upgrade dev to latest, or switch to stable
// let version_test = 4; // downgrade to an older dev, or switch back to stable
switch (version_test as number) {
case 0:
@@ -4380,78 +4378,124 @@ router
function deviceData(id: number) {
if (id == 1) {
return new Response(encoder.encode(emsesp_devicedata_1), { headers });
return new Response(encoder.encode(emsesp_devicedata_1) as BodyInit, {
headers
});
}
if (id == 2) {
return new Response(encoder.encode(emsesp_devicedata_2), { headers });
return new Response(encoder.encode(emsesp_devicedata_2) as BodyInit, {
headers
});
}
if (id == 3) {
return new Response(encoder.encode(emsesp_devicedata_3), { headers });
return new Response(encoder.encode(emsesp_devicedata_3) as BodyInit, {
headers
});
}
if (id == 4) {
return new Response(encoder.encode(emsesp_devicedata_4), { headers });
return new Response(encoder.encode(emsesp_devicedata_4) as BodyInit, {
headers
});
}
if (id == 5) {
return new Response(encoder.encode(emsesp_devicedata_5), { headers });
return new Response(encoder.encode(emsesp_devicedata_5) as BodyInit, {
headers
});
}
if (id == 6) {
return new Response(encoder.encode(emsesp_devicedata_6), { headers });
return new Response(encoder.encode(emsesp_devicedata_6) as BodyInit, {
headers
});
}
if (id == 7) {
return new Response(encoder.encode(emsesp_devicedata_7), { headers });
return new Response(encoder.encode(emsesp_devicedata_7) as BodyInit, {
headers
});
}
if (id == 8) {
// test changing the selected flow temp on a Bosch Compress 7000i AW Heat Pump (Boiler/HP)
emsesp_devicedata_8.nodes[4].v = Math.floor(Math.random() * 100);
return new Response(encoder.encode(emsesp_devicedata_8), { headers });
return new Response(encoder.encode(emsesp_devicedata_8) as BodyInit, {
headers
});
}
if (id == 9) {
return new Response(encoder.encode(emsesp_devicedata_9), { headers });
return new Response(encoder.encode(emsesp_devicedata_9) as BodyInit, {
headers
});
}
if (id == 10) {
return new Response(encoder.encode(emsesp_devicedata_10), { headers });
return new Response(encoder.encode(emsesp_devicedata_10) as BodyInit, {
headers
});
}
if (id == 11) {
return new Response(encoder.encode(emsesp_devicedata_11), { headers });
return new Response(encoder.encode(emsesp_devicedata_11) as BodyInit, {
headers
});
}
if (id == 99) {
return new Response(encoder.encode(emsesp_devicedata_99), { headers });
return new Response(encoder.encode(emsesp_devicedata_99) as BodyInit, {
headers
});
}
}
function deviceEntities(id: number) {
if (id == 1) {
return new Response(encoder.encode(emsesp_deviceentities_1), { headers });
return new Response(encoder.encode(emsesp_deviceentities_1) as BodyInit, {
headers
});
}
if (id == 2) {
return new Response(encoder.encode(emsesp_deviceentities_2), { headers });
return new Response(encoder.encode(emsesp_deviceentities_2) as BodyInit, {
headers
});
}
if (id == 3) {
return new Response(encoder.encode(emsesp_deviceentities_3), { headers });
return new Response(encoder.encode(emsesp_deviceentities_3) as BodyInit, {
headers
});
}
if (id == 4) {
return new Response(encoder.encode(emsesp_deviceentities_4), { headers });
return new Response(encoder.encode(emsesp_deviceentities_4) as BodyInit, {
headers
});
}
if (id == 5) {
return new Response(encoder.encode(emsesp_deviceentities_5), { headers });
return new Response(encoder.encode(emsesp_deviceentities_5) as BodyInit, {
headers
});
}
if (id == 6) {
return new Response(encoder.encode(emsesp_deviceentities_6), { headers });
return new Response(encoder.encode(emsesp_deviceentities_6) as BodyInit, {
headers
});
}
if (id == 7) {
return new Response(encoder.encode(emsesp_deviceentities_7), { headers });
return new Response(encoder.encode(emsesp_deviceentities_7) as BodyInit, {
headers
});
}
if (id == 8) {
return new Response(encoder.encode(emsesp_deviceentities_8), { headers });
return new Response(encoder.encode(emsesp_deviceentities_8) as BodyInit, {
headers
});
}
if (id == 9) {
return new Response(encoder.encode(emsesp_deviceentities_9), { headers });
return new Response(encoder.encode(emsesp_deviceentities_9) as BodyInit, {
headers
});
}
if (id == 10) {
return new Response(encoder.encode(emsesp_deviceentities_10), { headers });
return new Response(encoder.encode(emsesp_deviceentities_10) as BodyInit, {
headers
});
}
// not found, return empty
return new Response(encoder.encode(emsesp_deviceentities_none), { headers });
return new Response(encoder.encode(emsesp_deviceentities_none) as BodyInit, {
headers
});
}
// prepare dashboard data
@@ -4558,8 +4602,8 @@ router
}
// add temperature sensor data. no command c
let sensor_data: any[] = [];
sensor_data = emsesp_sensordata.ts.map((item, index) => ({
if (emsesp_sensordata.ts.length > 0) {
const sensor_data = emsesp_sensordata.ts.map((item, index) => ({
id: DeviceTypeUniqueID.TEMPERATURESENSOR_UID * 100 + index,
dv: {
id: '00' + item.n,
@@ -4572,15 +4616,16 @@ router
t: DeviceType.TEMPERATURESENSOR,
nodes: sensor_data
};
// only add to dashboard if we have values
if ((dashboard_object.nodes ?? []).length > 0) {
dashboard_nodes.push(dashboard_object);
}
// add analog sensor data. no command c
// remove disabled sensors first (t = 0)
sensor_data = emsesp_sensordata.as.filter((item) => item.t !== 0);
sensor_data = sensor_data.map((item, index) => ({
// remove disabled sensors first (t = 0) and create data in one pass
const enabledAnalogSensors = emsesp_sensordata.as.filter(
(item) => item.t !== 0
);
if (enabledAnalogSensors.length > 0) {
const sensor_data = enabledAnalogSensors.map((item, index) => ({
id: DeviceTypeUniqueID.ANALOGSENSOR_UID * 100 + index,
dv: {
id: '00' + item.n,
@@ -4593,15 +4638,14 @@ router
t: DeviceType.ANALOGSENSOR,
nodes: sensor_data
};
// only add to dashboard if we have values
if ((dashboard_object.nodes ?? []).length > 0) {
dashboard_nodes.push(dashboard_object);
}
// add the scheduler data
// filter emsesp_schedule with only if it has a name
let scheduler_data = emsesp_schedule.schedule.filter((item) => item.name);
let scheduler_data2 = scheduler_data.map((item, index) => ({
// filter emsesp_schedule with only if it has a name and create data in one pass
const namedSchedules = emsesp_schedule.schedule.filter((item) => item.name);
if (namedSchedules.length > 0) {
const scheduler_data = namedSchedules.map((item, index) => ({
id: DeviceTypeUniqueID.SCHEDULER_UID * 100 + index,
dv: {
id: '00' + item.name,
@@ -4613,10 +4657,8 @@ router
dashboard_object = {
id: DeviceTypeUniqueID.SCHEDULER_UID,
t: DeviceType.SCHEDULER,
nodes: scheduler_data2
nodes: scheduler_data
};
// only add to dashboard if we have values
if ((dashboard_object.nodes ?? []).length > 0) {
dashboard_nodes.push(dashboard_object);
}
} else {
@@ -4660,8 +4702,12 @@ router
};
// console.log('dashboardData: ', dashboardData);
// Clear references to help with garbage collection
dashboard_nodes = [];
dashboard_object = {};
// return dashboard_data; // if not using msgpack
return new Response(encoder.encode(dashboardData), { headers }); // msgpack it
return new Response(encoder.encode(dashboardData) as BodyInit, { headers }); // msgpack it
})
// Customizations
@@ -4852,10 +4898,12 @@ router
} else {
if (as.deleted) {
emsesp_sensordata.as[objIndex].d = true;
var filtered = emsesp_sensordata.as.filter(function (value, index, arr) {
return !value.d;
});
emsesp_sensordata.as = filtered;
// Remove deleted items in-place to avoid creating new arrays
for (let i = emsesp_sensordata.as.length - 1; i >= 0; i--) {
if (emsesp_sensordata.as[i].d) {
emsesp_sensordata.as.splice(i, 1);
}
}
} else {
emsesp_sensordata.as[objIndex].n = as.name;
emsesp_sensordata.as[objIndex].f = as.factor;

View File

@@ -197,7 +197,7 @@ extra_scripts =
build_flags =
-DARDUINOJSON_ENABLE_ARDUINO_STRING=1
-DEMSESP_STANDALONE -DEMSESP_TEST
-DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_TX_MODE=8 -DEMSESP_DEFAULT_VERSION=\"3.7.2-dev.0\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32\"
-DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_TX_MODE=8 -DEMSESP_DEFAULT_VERSION=\"3.7.3-dev.0\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32\"
-std=gnu++17 -Og -ggdb
build_unflags = -std=gnu++11 -std=gnu++14
build_type = debug

View File

@@ -1426,3 +1426,11 @@ Roomparams
Roomdata
Roomschedule
dewtemp
chefhat
sofasingle
bowlmix
bedsingle
beddouble
teddybear
washingmachine
switchprogram

View File

@@ -211,7 +211,7 @@ for entity in entities:
if int(entity["modbus count"]) <= 0:
raise Exception('Entity "' + entity_dev_name + ' (' + entity_shortname + ')' +
'" does not have a size - string sizes need to be added manually to update_modbus_registers.py/string_sizes')
'" does not have a size - string sizes need to be added manually to update_modbus_registers.py/string_sizes[]')
# if entity["modbus count"] == "0":
# print("ignoring " + entity_dev_name + " - it has a register length of zero")

View File

@@ -203,7 +203,7 @@
{163, DeviceType::WATER, "SM100, MS100", DeviceFlags::EMS_DEVICE_FLAG_SM100},
{164, DeviceType::WATER, "SM200, MS200", DeviceFlags::EMS_DEVICE_FLAG_SM100},
{248, DeviceType::MIXER, "HM210", DeviceFlags::EMS_DEVICE_FLAG_MMPLUS},
{17, DeviceType::CONNECT, "MX400", DeviceFlags::EMS_DEVICE_FLAG_NONE} // 0x50 Wirelss Base
{17, DeviceType::CONNECT, "MX400", DeviceFlags::EMS_DEVICE_FLAG_NONE} // 0x50 Wireless Base
// {157, DeviceType::THERMOSTAT, "RC120", DeviceFlags::EMS_DEVICE_FLAG_CR120}
#endif

View File

@@ -29,7 +29,7 @@ static_assert(uuid::console::thread_safe, "uuid-console must be thread-safe");
namespace emsesp {
// Static member definitions
std::deque<std::unique_ptr<EMSdevice>> EMSESP::emsdevices{};
std::vector<std::unique_ptr<EMSdevice>> EMSESP::emsdevices{};
std::vector<EMSESP::Device_record> EMSESP::device_library_;
uuid::log::Logger EMSESP::logger_{F_(emsesp), uuid::log::Facility::KERN};
uint16_t EMSESP::watch_id_ = WATCH_ID_NONE;

View File

@@ -222,7 +222,7 @@ class EMSESP {
static void scan_devices();
static void clear_all_devices();
static std::deque<std::unique_ptr<EMSdevice>> emsdevices;
static std::vector<std::unique_ptr<EMSdevice>> emsdevices;
// services
static Mqtt mqtt_;

View File

@@ -787,8 +787,8 @@ std::string Helpers::toUpper(std::string const & s) {
}
// capitalizes one UTF-8 character in char array
// works with Latin1 (1 byte), Polish amd some other (2 bytes) characters
// TODO add special characters that occur in other supported languages
// works with Latin1 (1 byte), Polish and other (2 bytes) characters
// supports special characters for all 11 supported languages: EN, DE, NL, SV, PL, NO, FR, TR, IT, SK, CZ
#if defined(EMSESP_STANDALONE)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wtype-limits"
@@ -803,23 +803,77 @@ void Helpers::CharToUpperUTF8(char * c) {
if ((p_v >= (char)0xA0) && (p_v <= (char)0xBE)) {
*p -= 0x20;
}
// Additional special characters for supported languages
switch (p_v) {
case (char)0xA0: // à -> À
case (char)0xA1: // á -> Á
case (char)0xA2: // â -> Â
case (char)0xA3: // ã -> Ã
case (char)0xA4: // ä -> Ä (German, Swedish)
case (char)0xA5: // å -> Å (Swedish, Norwegian)
case (char)0xA6: // æ -> Æ (Norwegian)
case (char)0xA7: // ç -> Ç (French, Turkish)
case (char)0xA8: // è -> È (French, Italian)
case (char)0xA9: // é -> É (French, Italian)
case (char)0xAA: // ê -> Ê (French)
case (char)0xAB: // ë -> Ë (French)
case (char)0xAC: // ì -> Ì (Italian)
case (char)0xAD: // í -> Í (Slovak, Czech)
case (char)0xAE: // î -> Î (French)
case (char)0xAF: // ï -> Ï (French)
case (char)0xB0: // ð -> Ð (Icelandic)
case (char)0xB1: // ñ -> Ñ (Spanish)
case (char)0xB2: // ò -> Ò (Italian)
case (char)0xB3: // ó -> Ó (Slovak, Czech)
case (char)0xB4: // ô -> Ô (French, Slovak)
case (char)0xB5: // õ -> Õ (Portuguese)
case (char)0xB6: // ö -> Ö (German, Swedish, Turkish)
case (char)0xB8: // ø -> Ø (Norwegian)
case (char)0xB9: // ù -> Ù (French, Italian)
case (char)0xBA: // ú -> Ú (Slovak, Czech)
case (char)0xBB: // û -> Û (French)
case (char)0xBC: // ü -> Ü (German, French, Turkish)
case (char)0xBD: // ý -> Ý (Slovak, Czech)
case (char)0xBE: // þ -> Þ (Icelandic)
case (char)0xBF: // ÿ -> Ÿ (French)
*p -= 0x20;
break;
}
break;
case (char)0xC4:
switch (p_v) {
case (char)0x85: //ą (0xC4,0x85) -> Ą (0xC4,0x84)
case (char)0x87: //ć (0xC4,0x87) -> Ć (0xC4,0x86)
case (char)0x99: //ę (0xC4,0x99) -> Ę (0xC4,0x98)
case (char)0x85: //ą (0xC4,0x85) -> Ą (0xC4,0x84) (Polish)
case (char)0x87: //ć (0xC4,0x87) -> Ć (0xC4,0x86) (Polish)
case (char)0x8D: //č (0xC4,0x8D) -> Č (0xC4,0x8C) (Slovak, Czech)
case (char)0x8F: //ď (0xC4,0x8F) -> Ď (0xC4,0x8E) (Slovak, Czech)
case (char)0x9F: //ğ (0xC4,0x9F) -> Ğ (0xC4,0x9E) (Turkish)
case (char)0x99: //ę (0xC4,0x99) -> Ę (0xC4,0x98) (Polish)
case (char)0x9B: //ě (0xC4,0x9B) -> Ě (0xC4,0x9A) (Czech)
case (char)0xAF: //ı (0xC4,0xAF) -> I (0xC4,0xAE) (Turkish)
case (char)0xB1: //ı (0xC4,0xB1) -> I (0xC4,0xB0) (Turkish)
case (char)0xB3: //ij (0xC4,0xB3) -> IJ (0xC4,0xB2) (Dutch)
*p -= 1;
break;
}
break;
case (char)0xC5:
switch (p_v) {
case (char)0x82: //ł (0xC5,0x82) -> Ł (0xC5,0x81)
case (char)0x84: //ń (0xC5,0x84) -> Ń (0xC5,0x83)
case (char)0x9B: //ś (0xC5,0x9B) -> Ś (0xC5,0x9A)
case (char)0xBA: //ź (0xC5,0xBA) -> Ź (0xC5,0xB9)
case (char)0xBC: //ż (0xC5,0xBC) -> Ż (0xC5,0xBB)
case (char)0x81: //ł (0xC5,0x81) -> Ł (0xC5,0x80) (Polish)
case (char)0x82: //ł (0xC5,0x82) -> Ł (0xC5,0x81) (Polish)
case (char)0x83: //ń (0xC5,0x83) -> Ń (0xC5,0x82) (Polish)
case (char)0x84: //ń (0xC5,0x84) -> Ń (0xC5,0x83) (Polish)
case (char)0x88: //ň (0xC5,0x88) -> Ň (0xC5,0x87) (Slovak, Czech)
case (char)0x95: //ŕ (0xC5,0x95) -> Ŕ (0xC5,0x94) (Slovak)
case (char)0x99: //ř (0xC5,0x99) -> Ř (0xC5,0x98) (Czech)
case (char)0x9A: //ś (0xC5,0x9A) -> Ś (0xC5,0x99) (Polish)
case (char)0x9B: //ś (0xC5,0x9B) -> Ś (0xC5,0x9A) (Polish)
case (char)0x9F: //ş (0xC5,0x9F) -> Ş (0xC5,0x9E) (Turkish)
case (char)0xA1: //š (0xC5,0xA1) -> Š (0xC5,0xA0) (Slovak, Czech)
case (char)0xA5: //ť (0xC5,0xA5) -> Ť (0xC5,0xA4) (Slovak, Czech)
case (char)0xAF: //ů (0xC5,0xAF) -> Ů (0xC5,0xAE) (Czech)
case (char)0xBA: //ź (0xC5,0xBA) -> Ź (0xC5,0xB9) (Polish)
case (char)0xBC: //ż (0xC5,0xBC) -> Ż (0xC5,0xBB) (Polish)
case (char)0xBE: //ž (0xC5,0xBE) -> Ž (0xC5,0xBD) (Slovak, Czech)
*p -= 1;
break;
}

View File

@@ -378,27 +378,28 @@ const std::initializer_list<Modbus::EntityModbusInfo> Modbus::modbus_register_ma
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(roomsensor), 199, 1), // roomsensor
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(heatup), 200, 1), // heatup
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(mode), 0, 1), // mode
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwSetTemp), 1, 1), // settemp
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwSetTempLow), 2, 1), // settemplow
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwCircMode), 3, 1), // circmode
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwChargeDuration), 4, 1), // chargeduration
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwCharge), 5, 1), // charge
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwExtra), 6, 1), // extra
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwDisinfecting), 7, 1), // disinfecting
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwDisinfectDay), 8, 1), // disinfectday
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwDisinfectTime), 9, 1), // disinfecttime
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwDailyHeating), 10, 1), // dailyheating
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwDailyHeatTime), 11, 1), // dailyheattime
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwProgMode), 12, 1), // progmode
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwCircProg), 13, 1), // circprog
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwDisinfectHour), 14, 1), // disinfecthour
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwMaxTemp), 15, 1), // maxtemp
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwOneTimeKey), 16, 1), // onetimekey
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(switchtime), 17, 8), // switchtime
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwcircswitchtime), 25, 8), // circswitchtime
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(holidays), 33, 13), // holidays
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(vacations), 46, 13), // vacations
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwWhenModeOff), 59, 1), // whenmodeoff
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(modetype), 1, 1), // modetype
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwSetTemp), 2, 1), // settemp
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwSetTempLow), 3, 1), // settemplow
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwCircMode), 4, 1), // circmode
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwChargeDuration), 5, 1), // chargeduration
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwCharge), 6, 1), // charge
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwExtra), 7, 1), // extra
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwDisinfecting), 8, 1), // disinfecting
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwDisinfectDay), 9, 1), // disinfectday
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwDisinfectTime), 10, 1), // disinfecttime
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwDailyHeating), 11, 1), // dailyheating
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwDailyHeatTime), 12, 1), // dailyheattime
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwProgMode), 13, 1), // progmode
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwCircProg), 14, 1), // circprog
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwDisinfectHour), 15, 1), // disinfecthour
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwMaxTemp), 16, 1), // maxtemp
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwOneTimeKey), 17, 1), // onetimekey
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(switchtime), 18, 8), // switchtime
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwcircswitchtime), 26, 8), // circswitchtime
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(holidays), 34, 13), // holidays
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(vacations), 47, 13), // vacations
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwWhenModeOff), 60, 1), // whenmodeoff
REGISTER_MAPPING(dt::MIXER, TAG_TYPE_HC, FL_(flowTempHc), 0, 1), // flowtemphc
REGISTER_MAPPING(dt::MIXER, TAG_TYPE_HC, FL_(valveStatus), 1, 1), // valvestatus
REGISTER_MAPPING(dt::MIXER, TAG_TYPE_HC, FL_(flowSetTemp), 2, 1), // flowsettemp
@@ -523,6 +524,8 @@ const std::initializer_list<Modbus::EntityModbusInfo> Modbus::modbus_register_ma
REGISTER_MAPPING(dt::SWITCH, TAG_TYPE_DEVICE_DATA, FL_(flowTempHc), 1, 1), // flowtemphc
REGISTER_MAPPING(dt::SWITCH, TAG_TYPE_DEVICE_DATA, FL_(status), 2, 1), // status
REGISTER_MAPPING(dt::CONTROLLER, TAG_TYPE_DEVICE_DATA, FL_(dateTime), 0, 13), // datetime
REGISTER_MAPPING(dt::CONNECT, TAG_TYPE_DEVICE_DATA, FL_(dateTime), 0, 13), // datetime
REGISTER_MAPPING(dt::CONNECT, TAG_TYPE_DEVICE_DATA, FL_(outdoorTemp), 13, 1), // outdoortemp
REGISTER_MAPPING(dt::ALERT, TAG_TYPE_DEVICE_DATA, FL_(setFlowTemp), 0, 1), // setflowtemp
REGISTER_MAPPING(dt::ALERT, TAG_TYPE_DEVICE_DATA, FL_(setBurnPow), 1, 1), // setburnpow
REGISTER_MAPPING(dt::EXTENSION, TAG_TYPE_DEVICE_DATA, FL_(flowTempVf), 0, 1), // flowtempvf

View File

@@ -151,7 +151,7 @@ void Shower::loop() {
// turn off hot water to send a shot of cold
void Shower::shower_alert_start() {
LOG_DEBUG("Shower Alert started");
(void)Command::call(EMSdevice::DeviceType::BOILER, "tapactivated", "false", 9);
(void)Command::call(EMSdevice::DeviceType::BOILER, "tapactivated", "false", DeviceValueTAG::TAG_DHW1);
doing_cold_shot_ = true;
force_coldshot = false;
alert_timer_start_ = uuid::get_uptime_sec(); // timer starts now
@@ -161,7 +161,7 @@ void Shower::shower_alert_start() {
void Shower::shower_alert_stop() {
if (doing_cold_shot_) {
LOG_DEBUG("Shower Alert stopped");
(void)Command::call(EMSdevice::DeviceType::BOILER, "tapactivated", "true", 9);
(void)Command::call(EMSdevice::DeviceType::BOILER, "tapactivated", "true", DeviceValueTAG::TAG_DHW1);
doing_cold_shot_ = false;
force_coldshot = false;
next_alert_ += shower_alert_trigger_;

View File

@@ -22,7 +22,7 @@
#include "shuntingYard.h"
// find tokens
// find tokens - optimized to reduce string allocations
std::deque<Token> exprToTokens(const std::string & expr) {
std::deque<Token> tokens;
@@ -40,13 +40,14 @@ std::deque<Token> exprToTokens(const std::string & expr) {
if (*p) {
++p;
}
auto s = std::string(b, p);
// Use string_view to avoid unnecessary string copies
std::string_view s(b, p - b);
auto n = s.find("\"\"");
while (n != std::string::npos) {
s.erase(n, 2);
while (n != std::string_view::npos) {
s.remove_prefix(n + 2);
n = s.find("\"\"");
}
tokens.emplace_back(Token::Type::String, s, -3);
tokens.emplace_back(Token::Type::String, std::string(s), -3);
if (*p == '\0') {
--p;
}
@@ -225,11 +226,14 @@ std::deque<Token> exprToTokens(const std::string & expr) {
return tokens;
}
// sort tokens to RPN form
// sort tokens to RPN form - optimized for memory usage
std::deque<Token> shuntingYard(const std::deque<Token> & tokens) {
std::deque<Token> queue;
std::vector<Token> stack;
// Reserve space for vector to reduce reallocations
stack.reserve(tokens.size() / 2);
// While there are tokens to be read:
for (auto const & token : tokens) {
// Read a token

View File

@@ -135,7 +135,7 @@ void Connect::process_roomThermostat(std::shared_ptr<const Telegram> telegram) {
has_update(rc->dewtemp_, dt);
}
// gateway(0x48) W gateway(0x50), ?(0x0B42), data: 01 // icon in ofset 0
// gateway(0x48) W gateway(0x50), ?(0x0B42), data: 01 // icon in offset 0
// gateway(0x48) W gateway(0x50), ?(0x0B42), data: 00 4B 00 FC 00 63 00 68 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 (offset 1)
void Connect::process_roomThermostatName(std::shared_ptr<const Telegram> telegram) {
auto rc = room_circuit(telegram->type_id - 0xB3D);

View File

@@ -1 +1 @@
#define EMSESP_APP_VERSION "3.7.3-dev.20"
#define EMSESP_APP_VERSION "3.7.3-dev.21"

View File

@@ -764,7 +764,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
test("boiler");
// device type, command, data
Command::call(EMSdevice::DeviceType::BOILER, "tapactivated", "false", 9);
Command::call(EMSdevice::DeviceType::BOILER, "tapactivated", "false", DeviceValueTAG::TAG_DHW1);
ok = true;
}

View File

@@ -318,7 +318,7 @@ const char * run_console_command(const char * command) {
}
void console_test1() {
auto expected_response = "Log level: DEBUG\n";
auto expected_response = "Log level: DEBUG";
TEST_ASSERT_EQUAL_STRING(expected_response, run_console_command("log"));
}
@@ -329,7 +329,7 @@ void console_test2() {
void console_test3() {
// test bad command
auto expected_response = "Bad syntax. Check arguments.\n";
auto expected_response = "Bad syntax. Check arguments.";
TEST_ASSERT_EQUAL_STRING(expected_response, run_console_command("call thermostat mode bad"));
}