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 ## Example
``` ```text
feat: add hat wobble feat: add hat wobble
^--^ ^------------^ ^--^ ^------------^
| | | |
@@ -96,7 +96,7 @@ References:
## Contributor License Agreement (CLA) ## Contributor License Agreement (CLA)
``` ```text
By making a contribution to this project, I certify that: By making a contribution to this project, I certify that:
(a) The contribution was created in whole or in part by me and I (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) [![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 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) [![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. **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", "src/core/modbus_entity_parameters.hpp",
"sdkconfig.*", "sdkconfig.*",
"managed_components/**", "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.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.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.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.comfdiff | comfort diff | uint8 (>=4<=12) | K | true | DHW | 21 | 1 | 1 |
| dhw.ecodiff | eco diff | uint8 (>=6<=12) | K | true | DHW | 22 | 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.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.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 | | 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.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.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.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.comfdiff | comfort diff | uint8 (>=4<=12) | K | true | DHW | 21 | 1 | 1 |
| dhw.ecodiff | eco diff | uint8 (>=6<=12) | K | true | DHW | 22 | 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.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.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 | | 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.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.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.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.comfdiff | comfort diff | uint8 (>=4<=12) | K | true | DHW | 21 | 1 | 1 |
| dhw.ecodiff | eco diff | uint8 (>=6<=12) | K | true | DHW | 22 | 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.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.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 | | 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 | | 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 | | 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* ## Devices of type *controller*
### Rego 3000 ### Rego 3000
| shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor | | shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor |
@@ -4704,18 +4711,18 @@
| hc1.switchtime1 | own1 program switchtime | string | | true | HC | 91 | 8 | 1 | | hc1.switchtime1 | own1 program switchtime | string | | true | HC | 91 | 8 | 1 |
| hc1.switchtime2 | own2 program switchtime | string | | true | HC | 99 | 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.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.circmode | circulation pump mode | enum [off\|on\|auto] | | true | DHW | 4 | 1 | 1 |
| dhw.progmode | program | enum [std prog\|own prog] | | true | DHW | 12 | 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 | 13 | 1 | 1 | | dhw.circprog | circulation program | enum [std prog\|own prog] | | true | DHW | 14 | 1 | 1 |
| dhw.disinfecting | disinfecting | boolean | | true | 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 | 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 | 14 | 1 | 1 | | dhw.disinfecthour | disinfection hour | uint8 (>=0<=23) | | true | DHW | 15 | 1 | 1 |
| dhw.maxtemp | maximum temperature | uint8 (>=0<=254) | C | 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 | 16 | 1 | 1 | | dhw.onetimekey | one time key function | boolean | | true | DHW | 17 | 1 | 1 |
| dhw.switchtime | program switchtime | string | | true | DHW | 17 | 8 | 1 | | dhw.switchtime | program switchtime | string | | true | DHW | 18 | 8 | 1 |
| dhw.circswitchtime | circulation program switchtime | string | | true | DHW | 25 | 8 | 1 | | dhw.circswitchtime | circulation program switchtime | string | | true | DHW | 26 | 8 | 1 |
| dhw.holidays | holiday dates | string | | true | DHW | 33 | 13 | 1 | | dhw.holidays | holiday dates | string | | true | DHW | 34 | 13 | 1 |
| dhw.vacations | vacation dates | string | | true | DHW | 46 | 13 | 1 | | dhw.vacations | vacation dates | string | | true | DHW | 47 | 13 | 1 |
### ES79 ### ES79
| shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor | | shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor |
@@ -4770,18 +4777,18 @@
| hc1.switchtime1 | own1 program switchtime | string | | true | HC | 91 | 8 | 1 | | hc1.switchtime1 | own1 program switchtime | string | | true | HC | 91 | 8 | 1 |
| hc1.switchtime2 | own2 program switchtime | string | | true | HC | 99 | 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.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.circmode | circulation pump mode | enum [off\|on\|auto] | | true | DHW | 4 | 1 | 1 |
| dhw.progmode | program | enum [std prog\|own prog] | | true | DHW | 12 | 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 | 13 | 1 | 1 | | dhw.circprog | circulation program | enum [std prog\|own prog] | | true | DHW | 14 | 1 | 1 |
| dhw.disinfecting | disinfecting | boolean | | true | 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 | 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 | 14 | 1 | 1 | | dhw.disinfecthour | disinfection hour | uint8 (>=0<=23) | | true | DHW | 15 | 1 | 1 |
| dhw.maxtemp | maximum temperature | uint8 (>=60<=80) | C | 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 | 16 | 1 | 1 | | dhw.onetimekey | one time key function | boolean | | true | DHW | 17 | 1 | 1 |
| dhw.switchtime | program switchtime | string | | true | DHW | 17 | 8 | 1 | | dhw.switchtime | program switchtime | string | | true | DHW | 18 | 8 | 1 |
| dhw.circswitchtime | circulation program switchtime | string | | true | DHW | 25 | 8 | 1 | | dhw.circswitchtime | circulation program switchtime | string | | true | DHW | 26 | 8 | 1 |
| dhw.holidays | holiday dates | string | | true | DHW | 33 | 13 | 1 | | dhw.holidays | holiday dates | string | | true | DHW | 34 | 13 | 1 |
| dhw.vacations | vacation dates | string | | true | DHW | 46 | 13 | 1 | | dhw.vacations | vacation dates | string | | true | DHW | 47 | 13 | 1 |
### EasyControl, CT200 ### EasyControl, CT200
| shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor | | shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor |
@@ -4832,7 +4839,7 @@
| hc1.roominflfactor | room influence factor | uint8 (>=0<=100) | % | true | HC | 14 | 1 | 10 | | 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.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 | | 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 ### FB100
| shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor | | shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor |
@@ -4873,7 +4880,7 @@
| hc1.roominflfactor | room influence factor | uint8 (>=0<=100) | % | true | HC | 14 | 1 | 10 | | 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.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 | | 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 ### FR10
| shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor | | shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor |
@@ -4912,7 +4919,7 @@
| hc1.roominflfactor | room influence factor | uint8 (>=0<=100) | % | true | HC | 14 | 1 | 10 | | 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.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 | | 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 ### FR100
| shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor | | shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor |
@@ -4951,7 +4958,7 @@
| hc1.roominflfactor | room influence factor | uint8 (>=0<=100) | % | true | HC | 14 | 1 | 10 | | 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.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 | | 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 ### FR110
| shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor | | shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor |
@@ -4990,7 +4997,7 @@
| hc1.roominflfactor | room influence factor | uint8 (>=0<=100) | % | true | HC | 14 | 1 | 10 | | 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.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 | | 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 ### FR120
| shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor | | shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor |
@@ -5029,7 +5036,7 @@
| hc1.roominflfactor | room influence factor | uint8 (>=0<=100) | % | true | HC | 14 | 1 | 10 | | 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.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 | | 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 ### FR50
| shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor | | shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor |
@@ -5068,7 +5075,7 @@
| hc1.roominflfactor | room influence factor | uint8 (>=0<=100) | % | true | HC | 14 | 1 | 10 | | 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.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 | | 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 ### FW100
| shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor | | shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor |
@@ -5109,7 +5116,7 @@
| hc1.roominflfactor | room influence factor | uint8 (>=0<=100) | % | true | HC | 14 | 1 | 10 | | 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.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 | | 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 ### FW120
| shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor | | shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor |
@@ -5150,7 +5157,7 @@
| hc1.roominflfactor | room influence factor | uint8 (>=0<=100) | % | true | HC | 14 | 1 | 10 | | 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.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 | | 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 ### FW200
| shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor | | shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor |
@@ -5191,7 +5198,7 @@
| hc1.roominflfactor | room influence factor | uint8 (>=0<=100) | % | true | HC | 14 | 1 | 10 | | 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.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 | | 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 ### FW500
| shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor | | shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor |
@@ -5232,7 +5239,7 @@
| hc1.roominflfactor | room influence factor | uint8 (>=0<=100) | % | true | HC | 14 | 1 | 10 | | 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.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 | | 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 ### Logamatic TC100, Moduline Easy
| shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor | | shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor |
@@ -5303,10 +5310,10 @@
| hc1.manualtemp | manual temperature | uint8 (>=0<=127) | C | true | HC | 6 | 1 | 1/2 | | 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 | | 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.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.whenmodeoff | when thermostat mode off | boolean | | true | DHW | 60 | 1 | 1 |
| dhw.disinfecting | disinfecting | boolean | | true | 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 | 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 | 14 | 1 | 1 | | dhw.disinfecthour | disinfection hour | uint8 (>=0<=23) | | true | DHW | 15 | 1 | 1 |
### RC10 ### RC10
| shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor | | shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor |
@@ -5487,17 +5494,18 @@
| hc1.solarinfl | solar influence | uint8 (>=-5<=4294967295) | C | true | HC | 54 | 1 | 1 | | 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 | | 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.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.modetype | mode type | enum [off\|eco\|comfort\|eco+] | | false | DHW | 1 | 1 | 1 |
| dhw.settemplow | set low temperature | uint8 (>=0<=254) | C | true | DHW | 2 | 1 | 1 | | dhw.settemp | set 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.settemplow | set low temperature | uint8 (>=0<=254) | C | true | DHW | 3 | 1 | 1 |
| dhw.chargeduration | charge duration | uint8 (>=0<=3810) | minutes | true | DHW | 4 | 1 | 15 | | dhw.circmode | circulation pump mode | enum [off\|on\|auto\|own prog] | | true | DHW | 4 | 1 | 1 |
| dhw.charge | charge | boolean | | true | DHW | 5 | 1 | 1 | | dhw.chargeduration | charge duration | uint8 (>=0<=3810) | minutes | true | DHW | 5 | 1 | 15 |
| dhw.extra | extra | boolean | | false | DHW | 6 | 1 | 1 | | dhw.charge | charge | boolean | | true | DHW | 6 | 1 | 1 |
| dhw.disinfecting | disinfecting | boolean | | true | DHW | 7 | 1 | 1 | | dhw.extra | extra | boolean | | false | DHW | 7 | 1 | 1 |
| dhw.disinfectday | disinfection day | enum [mo\|tu\|we\|th\|fr\|sa\|su\|all] | | true | DHW | 8 | 1 | 1 | | dhw.disinfecting | disinfecting | boolean | | true | DHW | 8 | 1 | 1 |
| dhw.disinfecttime | disinfection time | uint8 (>=0<=1431) | minutes | true | DHW | 9 | 1 | 15 | | dhw.disinfectday | disinfection day | enum [mo\|tu\|we\|th\|fr\|sa\|su\|all] | | true | DHW | 9 | 1 | 1 |
| dhw.dailyheating | daily heating | boolean | | true | DHW | 10 | 1 | 1 | | dhw.disinfecttime | disinfection time | uint8 (>=0<=1431) | minutes | true | DHW | 10 | 1 | 15 |
| dhw.dailyheattime | daily heating time | uint8 (>=0<=1431) | minutes | true | DHW | 11 | 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 ### RC20RF
| shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor | | shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor |
@@ -5621,17 +5629,18 @@
| hc1.solarinfl | solar influence | uint8 (>=-5<=4294967295) | C | true | HC | 54 | 1 | 1 | | 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 | | 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.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.modetype | mode type | enum [off\|eco\|comfort\|eco+] | | false | DHW | 1 | 1 | 1 |
| dhw.settemplow | set low temperature | uint8 (>=0<=254) | C | true | DHW | 2 | 1 | 1 | | dhw.settemp | set 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.settemplow | set low temperature | uint8 (>=0<=254) | C | true | DHW | 3 | 1 | 1 |
| dhw.chargeduration | charge duration | uint8 (>=0<=3810) | minutes | true | DHW | 4 | 1 | 15 | | dhw.circmode | circulation pump mode | enum [off\|on\|auto\|own prog] | | true | DHW | 4 | 1 | 1 |
| dhw.charge | charge | boolean | | true | DHW | 5 | 1 | 1 | | dhw.chargeduration | charge duration | uint8 (>=0<=3810) | minutes | true | DHW | 5 | 1 | 15 |
| dhw.extra | extra | boolean | | false | DHW | 6 | 1 | 1 | | dhw.charge | charge | boolean | | true | DHW | 6 | 1 | 1 |
| dhw.disinfecting | disinfecting | boolean | | true | DHW | 7 | 1 | 1 | | dhw.extra | extra | boolean | | false | DHW | 7 | 1 | 1 |
| dhw.disinfectday | disinfection day | enum [mo\|tu\|we\|th\|fr\|sa\|su\|all] | | true | DHW | 8 | 1 | 1 | | dhw.disinfecting | disinfecting | boolean | | true | DHW | 8 | 1 | 1 |
| dhw.disinfecttime | disinfection time | uint8 (>=0<=1431) | minutes | true | DHW | 9 | 1 | 15 | | dhw.disinfectday | disinfection day | enum [mo\|tu\|we\|th\|fr\|sa\|su\|all] | | true | DHW | 9 | 1 | 1 |
| dhw.dailyheating | daily heating | boolean | | true | DHW | 10 | 1 | 1 | | dhw.disinfecttime | disinfection time | uint8 (>=0<=1431) | minutes | true | DHW | 10 | 1 | 15 |
| dhw.dailyheattime | daily heating time | uint8 (>=0<=1431) | minutes | true | DHW | 11 | 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 ### RC30
| shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor | | shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor |
@@ -5689,18 +5698,18 @@
| hc1.switchtime1 | own1 program switchtime | string | | true | HC | 91 | 8 | 1 | | hc1.switchtime1 | own1 program switchtime | string | | true | HC | 91 | 8 | 1 |
| hc1.switchtime2 | own2 program switchtime | string | | true | HC | 99 | 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.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.circmode | circulation pump mode | enum [off\|on\|auto] | | true | DHW | 4 | 1 | 1 |
| dhw.progmode | program | enum [std prog\|own prog] | | true | DHW | 12 | 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 | 13 | 1 | 1 | | dhw.circprog | circulation program | enum [std prog\|own prog] | | true | DHW | 14 | 1 | 1 |
| dhw.disinfecting | disinfecting | boolean | | true | 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 | 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 | 14 | 1 | 1 | | dhw.disinfecthour | disinfection hour | uint8 (>=0<=23) | | true | DHW | 15 | 1 | 1 |
| dhw.maxtemp | maximum temperature | uint8 (>=0<=254) | C | 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 | 16 | 1 | 1 | | dhw.onetimekey | one time key function | boolean | | true | DHW | 17 | 1 | 1 |
| dhw.switchtime | program switchtime | string | | true | DHW | 17 | 8 | 1 | | dhw.switchtime | program switchtime | string | | true | DHW | 18 | 8 | 1 |
| dhw.circswitchtime | circulation program switchtime | string | | true | DHW | 25 | 8 | 1 | | dhw.circswitchtime | circulation program switchtime | string | | true | DHW | 26 | 8 | 1 |
| dhw.holidays | holiday dates | string | | true | DHW | 33 | 13 | 1 | | dhw.holidays | holiday dates | string | | true | DHW | 34 | 13 | 1 |
| dhw.vacations | vacation dates | string | | true | DHW | 46 | 13 | 1 | | dhw.vacations | vacation dates | string | | true | DHW | 47 | 13 | 1 |
### RC35 ### RC35
| shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor | | shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor |
@@ -5755,18 +5764,18 @@
| hc1.switchtime1 | own1 program switchtime | string | | true | HC | 91 | 8 | 1 | | hc1.switchtime1 | own1 program switchtime | string | | true | HC | 91 | 8 | 1 |
| hc1.switchtime2 | own2 program switchtime | string | | true | HC | 99 | 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.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.circmode | circulation pump mode | enum [off\|on\|auto] | | true | DHW | 4 | 1 | 1 |
| dhw.progmode | program | enum [std prog\|own prog] | | true | DHW | 12 | 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 | 13 | 1 | 1 | | dhw.circprog | circulation program | enum [std prog\|own prog] | | true | DHW | 14 | 1 | 1 |
| dhw.disinfecting | disinfecting | boolean | | true | 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 | 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 | 14 | 1 | 1 | | dhw.disinfecthour | disinfection hour | uint8 (>=0<=23) | | true | DHW | 15 | 1 | 1 |
| dhw.maxtemp | maximum temperature | uint8 (>=60<=80) | C | 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 | 16 | 1 | 1 | | dhw.onetimekey | one time key function | boolean | | true | DHW | 17 | 1 | 1 |
| dhw.switchtime | program switchtime | string | | true | DHW | 17 | 8 | 1 | | dhw.switchtime | program switchtime | string | | true | DHW | 18 | 8 | 1 |
| dhw.circswitchtime | circulation program switchtime | string | | true | DHW | 25 | 8 | 1 | | dhw.circswitchtime | circulation program switchtime | string | | true | DHW | 26 | 8 | 1 |
| dhw.holidays | holiday dates | string | | true | DHW | 33 | 13 | 1 | | dhw.holidays | holiday dates | string | | true | DHW | 34 | 13 | 1 |
| dhw.vacations | vacation dates | string | | true | DHW | 46 | 13 | 1 | | dhw.vacations | vacation dates | string | | true | DHW | 47 | 13 | 1 |
### RFM20 Remote ### RFM20 Remote
| shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor | | shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor |
@@ -5871,17 +5880,18 @@
| hc1.solarinfl | solar influence | uint8 (>=-5<=4294967295) | C | true | HC | 54 | 1 | 1 | | 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 | | 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.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.modetype | mode type | enum [off\|eco\|comfort\|eco+] | | false | DHW | 1 | 1 | 1 |
| dhw.settemplow | set low temperature | uint8 (>=0<=254) | C | true | DHW | 2 | 1 | 1 | | dhw.settemp | set 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.settemplow | set low temperature | uint8 (>=0<=254) | C | true | DHW | 3 | 1 | 1 |
| dhw.chargeduration | charge duration | uint8 (>=0<=3810) | minutes | true | DHW | 4 | 1 | 15 | | dhw.circmode | circulation pump mode | enum [off\|on\|auto\|own prog] | | true | DHW | 4 | 1 | 1 |
| dhw.charge | charge | boolean | | true | DHW | 5 | 1 | 1 | | dhw.chargeduration | charge duration | uint8 (>=0<=3810) | minutes | true | DHW | 5 | 1 | 15 |
| dhw.extra | extra | boolean | | false | DHW | 6 | 1 | 1 | | dhw.charge | charge | boolean | | true | DHW | 6 | 1 | 1 |
| dhw.disinfecting | disinfecting | boolean | | true | DHW | 7 | 1 | 1 | | dhw.extra | extra | boolean | | false | DHW | 7 | 1 | 1 |
| dhw.disinfectday | disinfection day | enum [mo\|tu\|we\|th\|fr\|sa\|su\|all] | | true | DHW | 8 | 1 | 1 | | dhw.disinfecting | disinfecting | boolean | | true | DHW | 8 | 1 | 1 |
| dhw.disinfecttime | disinfection time | uint8 (>=0<=1431) | minutes | true | DHW | 9 | 1 | 15 | | dhw.disinfectday | disinfection day | enum [mo\|tu\|we\|th\|fr\|sa\|su\|all] | | true | DHW | 9 | 1 | 1 |
| dhw.dailyheating | daily heating | boolean | | true | DHW | 10 | 1 | 1 | | dhw.disinfecttime | disinfection time | uint8 (>=0<=1431) | minutes | true | DHW | 10 | 1 | 15 |
| dhw.dailyheattime | daily heating time | uint8 (>=0<=1431) | minutes | true | DHW | 11 | 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 ### Rego 3000, UI800, Logamatic BC400
| shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor | | shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor |
@@ -5965,17 +5975,18 @@
| hc1.solarinfl | solar influence | uint8 (>=-5<=4294967295) | C | true | HC | 54 | 1 | 1 | | 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 | | 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.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.modetype | mode type | enum [off\|eco\|comfort\|eco+] | | false | DHW | 1 | 1 | 1 |
| dhw.settemplow | set low temperature | uint8 (>=0<=254) | C | true | DHW | 2 | 1 | 1 | | dhw.settemp | set 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.settemplow | set low temperature | uint8 (>=0<=254) | C | true | DHW | 3 | 1 | 1 |
| dhw.chargeduration | charge duration | uint8 (>=0<=3810) | minutes | true | DHW | 4 | 1 | 15 | | dhw.circmode | circulation pump mode | enum [off\|on\|auto\|own prog] | | true | DHW | 4 | 1 | 1 |
| dhw.charge | charge | boolean | | true | DHW | 5 | 1 | 1 | | dhw.chargeduration | charge duration | uint8 (>=0<=3810) | minutes | true | DHW | 5 | 1 | 15 |
| dhw.extra | extra | boolean | | false | DHW | 6 | 1 | 1 | | dhw.charge | charge | boolean | | true | DHW | 6 | 1 | 1 |
| dhw.disinfecting | disinfecting | boolean | | true | DHW | 7 | 1 | 1 | | dhw.extra | extra | boolean | | false | DHW | 7 | 1 | 1 |
| dhw.disinfectday | disinfection day | enum [mo\|tu\|we\|th\|fr\|sa\|su\|all] | | true | DHW | 8 | 1 | 1 | | dhw.disinfecting | disinfecting | boolean | | true | DHW | 8 | 1 | 1 |
| dhw.disinfecttime | disinfection time | uint8 (>=0<=1431) | minutes | true | DHW | 9 | 1 | 15 | | dhw.disinfectday | disinfection day | enum [mo\|tu\|we\|th\|fr\|sa\|su\|all] | | true | DHW | 9 | 1 | 1 |
| dhw.dailyheating | daily heating | boolean | | true | DHW | 10 | 1 | 1 | | dhw.disinfecttime | disinfection time | uint8 (>=0<=1431) | minutes | true | DHW | 10 | 1 | 15 |
| dhw.dailyheattime | daily heating time | uint8 (>=0<=1431) | minutes | true | DHW | 11 | 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 ### TR120RF, CR20RF
| shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor | | shortname | fullname | type | uom | writeable | tag type | register offset | register count | scale factor |
@@ -6069,17 +6080,18 @@
| hc1.solarinfl | solar influence | uint8 (>=-5<=4294967295) | C | true | HC | 54 | 1 | 1 | | 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 | | 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.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.modetype | mode type | enum [off\|eco\|comfort\|eco+] | | false | DHW | 1 | 1 | 1 |
| dhw.settemplow | set low temperature | uint8 (>=0<=254) | C | true | DHW | 2 | 1 | 1 | | dhw.settemp | set 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.settemplow | set low temperature | uint8 (>=0<=254) | C | true | DHW | 3 | 1 | 1 |
| dhw.chargeduration | charge duration | uint8 (>=0<=3810) | minutes | true | DHW | 4 | 1 | 15 | | dhw.circmode | circulation pump mode | enum [off\|on\|auto\|own prog] | | true | DHW | 4 | 1 | 1 |
| dhw.charge | charge | boolean | | true | DHW | 5 | 1 | 1 | | dhw.chargeduration | charge duration | uint8 (>=0<=3810) | minutes | true | DHW | 5 | 1 | 15 |
| dhw.extra | extra | boolean | | false | DHW | 6 | 1 | 1 | | dhw.charge | charge | boolean | | true | DHW | 6 | 1 | 1 |
| dhw.disinfecting | disinfecting | boolean | | true | DHW | 7 | 1 | 1 | | dhw.extra | extra | boolean | | false | DHW | 7 | 1 | 1 |
| dhw.disinfectday | disinfection day | enum [mo\|tu\|we\|th\|fr\|sa\|su\|all] | | true | DHW | 8 | 1 | 1 | | dhw.disinfecting | disinfecting | boolean | | true | DHW | 8 | 1 | 1 |
| dhw.disinfecttime | disinfection time | uint8 (>=0<=1431) | minutes | true | DHW | 9 | 1 | 15 | | dhw.disinfectday | disinfection day | enum [mo\|tu\|we\|th\|fr\|sa\|su\|all] | | true | DHW | 9 | 1 | 1 |
| dhw.dailyheating | daily heating | boolean | | true | DHW | 10 | 1 | 1 | | dhw.disinfecttime | disinfection time | uint8 (>=0<=1431) | minutes | true | DHW | 10 | 1 | 15 |
| dhw.dailyheattime | daily heating time | uint8 (>=0<=1431) | minutes | true | DHW | 11 | 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* ## Devices of type *ventilation*
### HRV176, HRV156, 5000c, MV200 ### 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.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.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.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.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 (>=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.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.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.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 "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.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.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.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.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 (>=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.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.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.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 "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.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.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.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.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 (>=6<=12),K,true,number.boiler_dhw_eco_diff,number.boiler_dhw_ecodiff,5,9,1,22,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.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.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 "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.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,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.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.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.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.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.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.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.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.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.charge,charge,boolean, ,true,switch.thermostat_dhw_charge,switch.thermostat_dhw_charge,6,9,1,5,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.extra,extra,boolean, ,false,binary_sensor.thermostat_dhw_extra,binary_sensor.thermostat_dhw_extra,6,9,1,6,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.disinfecting,disinfecting,boolean, ,true,switch.thermostat_dhw_disinfecting,switch.thermostat_dhw_disinfecting,6,9,1,7,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.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.disinfecting,disinfecting,boolean, ,true,switch.thermostat_dhw_disinfecting,switch.thermostat_dhw_disinfecting,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.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.dailyheating,daily heating,boolean, ,true,switch.thermostat_dhw_daily_heating,switch.thermostat_dhw_dailyheating,6,9,1,10,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.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.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,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,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 "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.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,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.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.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,12,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,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,7,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,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,14,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,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,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,17,8 "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,25,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,33,13 "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,46,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,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,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 "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.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,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.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.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,7,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,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,14,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,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,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 "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.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,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.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.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,12,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,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,7,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,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,14,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,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,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,17,8 "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,25,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,33,13 "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,46,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,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,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 "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.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,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.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.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.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.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.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.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.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.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.charge,charge,boolean, ,true,switch.thermostat_dhw_charge,switch.thermostat_dhw_charge,6,9,1,5,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.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.charge,charge,boolean, ,true,switch.thermostat_dhw_charge,switch.thermostat_dhw_charge,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.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.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.disinfecting,disinfecting,boolean, ,true,switch.thermostat_dhw_disinfecting,switch.thermostat_dhw_disinfecting,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.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.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.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.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.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,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,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 "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.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,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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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,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,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 "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.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,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.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.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.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.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.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.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.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.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.charge,charge,boolean, ,true,switch.thermostat_dhw_charge,switch.thermostat_dhw_charge,6,9,1,5,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.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.charge,charge,boolean, ,true,switch.thermostat_dhw_charge,switch.thermostat_dhw_charge,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.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.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.disinfecting,disinfecting,boolean, ,true,switch.thermostat_dhw_disinfecting,switch.thermostat_dhw_disinfecting,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.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.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.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.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.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,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,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 "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.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,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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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,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,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 "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.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,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.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.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,12,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,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,7,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,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,14,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,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,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,17,8 "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,25,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,33,13 "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,46,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,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,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 "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.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,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.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.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,12,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,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,7,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,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,14,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,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,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,17,8 "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,25,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,33,13 "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,46,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,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,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 "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.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.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,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,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,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 "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.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.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,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,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,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 "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.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.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,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,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,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 "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.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.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,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,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,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 "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.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.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,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,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,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 "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.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.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,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,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,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 "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.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.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,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,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,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 "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.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.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,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,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,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 "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.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.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,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,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,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 "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.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.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,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,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,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 "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.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.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,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,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,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 "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,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 "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 "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,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",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 "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: { languageOptions: {
parserOptions: { parserOptions: {
project: true, project: true
tsconfigRootDir: import.meta.dirname
} }
} }
}, },

View File

@@ -1,6 +1,6 @@
{ {
"name": "EMS-ESP", "name": "EMS-ESP",
"version": "3.7.2", "version": "3.7.3",
"description": "EMS-ESP WebUI", "description": "EMS-ESP WebUI",
"homepage": "https://emsesp.org", "homepage": "https://emsesp.org",
"author": "proddy, emsesp.org", "author": "proddy, emsesp.org",
@@ -63,5 +63,5 @@
"vite-plugin-imagemin": "^0.6.1", "vite-plugin-imagemin": "^0.6.1",
"vite-tsconfig-paths": "^5.1.4" "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: duplexer3@0.1.5:
resolution: {integrity: sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==} resolution: {integrity: sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==}
electron-to-chromium@1.5.238: electron-to-chromium@1.5.239:
resolution: {integrity: sha512-khBdc+w/Gv+cS8e/Pbnaw/FXcBUeKrRVik9IxfXtgREOWyJhR4tj43n3amkVogJ/yeQUqzkrZcFhtIxIdqmmcQ==} resolution: {integrity: sha512-1y5w0Zsq39MSPmEjHjbizvhYoTaulVtivpxkp5q5kaPmQtsK6/2nvAzGRxNMS9DoYySp9PkW0MAQDwU1m764mg==}
emoji-regex@8.0.0: emoji-regex@8.0.0:
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
@@ -3995,7 +3995,7 @@ snapshots:
dependencies: dependencies:
baseline-browser-mapping: 2.8.19 baseline-browser-mapping: 2.8.19
caniuse-lite: 1.0.30001751 caniuse-lite: 1.0.30001751
electron-to-chromium: 1.5.238 electron-to-chromium: 1.5.239
node-releases: 2.0.26 node-releases: 2.0.26
update-browserslist-db: 1.1.4(browserslist@4.27.0) update-browserslist-db: 1.1.4(browserslist@4.27.0)
@@ -4340,7 +4340,7 @@ snapshots:
duplexer3@0.1.5: {} duplexer3@0.1.5: {}
electron-to-chromium@1.5.238: {} electron-to-chromium@1.5.239: {}
emoji-regex@8.0.0: {} emoji-regex@8.0.0: {}

View File

@@ -4,6 +4,7 @@ import {
existsSync, existsSync,
readFileSync, readFileSync,
readdirSync, readdirSync,
statSync,
unlinkSync unlinkSync
} from 'fs'; } from 'fs';
import mime from 'mime-types'; import mime from 'mime-types';
@@ -15,67 +16,79 @@ const INDENT = ' ';
const outputPath = '../src/ESP32React/WWWData.h'; const outputPath = '../src/ESP32React/WWWData.h';
const sourcePath = './dist'; const sourcePath = './dist';
const bytesPerLine = 20; 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 = () => const generateWWWClass =
`typedef std::function<void(const char * uri, const String & contentType, const uint8_t * content, size_t len, const String & hash)> RouteRegistrationHandler; () => `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 // 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 { class WWWData {
${indent}public: ${INDENT}public:
${indent.repeat(2)}static void registerRoutes(RouteRegistrationHandler handler) { ${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')} ${fileInfo.map((f) => `${INDENT.repeat(3)}handler("${f.uri}", "${f.mimeType}", ${f.variable}, ${f.size}, "${f.hash}");`).join('\n')}
${indent.repeat(2)}} ${INDENT.repeat(2)}}
}; };
`; `;
function getFilesSync(dir, files = []) { const getFilesSync = (dir, files = []) => {
readdirSync(dir, { withFileTypes: true }).forEach((entry) => { readdirSync(dir, { withFileTypes: true }).forEach((entry) => {
const entryPath = resolve(dir, entry.name); const entryPath = resolve(dir, entry.name);
if (entry.isDirectory()) { entry.isDirectory() ? getFilesSync(entryPath, files) : files.push(entryPath);
getFilesSync(entryPath, files);
} else {
files.push(entryPath);
}
}); });
return files; return files;
} };
function cleanAndOpen(path) { const cleanAndOpen = (path) => {
if (existsSync(path)) { existsSync(path) && unlinkSync(path);
unlinkSync(path);
}
return createWriteStream(path, { flags: 'w+' }); 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 writeFile = (relativeFilePath, buffer) => {
const variable = 'ESP_REACT_DATA_' + fileInfo.length; const variable = `ESP_REACT_DATA_${fileInfo.length}`;
const mimeType = mime.lookup(relativeFilePath); const mimeType = mime.lookup(relativeFilePath);
var size = 0; const fileType = getFileType(relativeFilePath);
writeStream.write('const uint8_t ' + variable + '[] = {'); let size = 0;
// const zipBuffer = zlib.brotliCompressSync(buffer, { quality: 1 }); writeStream.write(`const uint8_t ${variable}[] = {`);
const zipBuffer = zlib.gzipSync(buffer, { level: 9 });
// create sha const zipBuffer = zlib.gzipSync(buffer, { level: 9 });
const hashSum = crypto.createHash('sha256'); const hash = crypto.createHash('sha256').update(zipBuffer).digest('hex');
hashSum.update(zipBuffer);
const hash = hashSum.digest('hex');
zipBuffer.forEach((b) => { zipBuffer.forEach((b) => {
if (!(size % bytesPerLine)) { if (!(size % bytesPerLine)) {
writeStream.write('\n'); writeStream.write('\n' + INDENT);
writeStream.write(indent);
} }
writeStream.write('0x' + ('00' + b.toString(16).toUpperCase()).slice(-2) + ','); writeStream.write('0x' + b.toString(16).toUpperCase().padStart(2, '0') + ',');
size++; size++;
}); });
if (size % bytesPerLine) { size % bytesPerLine && writeStream.write('\n');
writeStream.write('\n');
}
writeStream.write('};\n\n'); writeStream.write('};\n\n');
// Update bundle statistics
bundleStats[fileType].count++;
bundleStats[fileType].uncompressed += buffer.length;
bundleStats[fileType].compressed += zipBuffer.length;
fileInfo.push({ fileInfo.push({
uri: '/' + relativeFilePath.replace(sep, '/'), uri: '/' + relativeFilePath.replace(sep, '/'),
mimeType, mimeType,
@@ -84,32 +97,52 @@ const writeFile = (relativeFilePath, buffer) => {
hash hash
}); });
// console.log(relativeFilePath + ' (size ' + size + ' bytes)');
totalSize += size; totalSize += size;
}; };
// start console.log(`Generating ${outputPath} from ${sourcePath}`);
console.log('Generating ' + outputPath + ' from ' + sourcePath);
const includes = ARDUINO_INCLUDES;
const indent = INDENT;
const fileInfo = []; const fileInfo = [];
const writeStream = cleanAndOpen(resolve(outputPath)); const writeStream = cleanAndOpen(resolve(outputPath));
// includes writeStream.write(ARDUINO_INCLUDES);
writeStream.write(includes);
// process static files
const buildPath = resolve(sourcePath); const buildPath = resolve(sourcePath);
for (const filePath of getFilesSync(buildPath)) { for (const filePath of getFilesSync(buildPath)) {
const readStream = readFileSync(filePath); writeFile(relative(buildPath, filePath), readFileSync(filePath));
const relativeFilePath = relative(buildPath, filePath);
writeFile(relativeFilePath, readStream);
} }
// add class
writeStream.write(generateWWWClass()); writeStream.write(generateWWWClass());
// end
writeStream.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="/dashboard/*" element={<Dashboard />} />
<Route path="/devices/*" element={<Devices />} /> <Route path="/devices/*" element={<Devices />} />
<Route path="/sensors/*" element={<Sensors />} /> <Route path="/sensors/*" element={<Sensors />} />
<Route path="/status/*" element={<Status />} />
<Route path="/help/*" element={<Help />} /> <Route path="/help/*" element={<Help />} />
<Route path="/*" element={<Navigate to="/" />} />
<Route path="/status/*" element={<Status />} />
<Route path="/status/hardwarestatus/*" element={<HardwareStatus />} /> <Route path="/status/hardwarestatus/*" element={<HardwareStatus />} />
<Route path="/status/activity" element={<Activity />} /> <Route path="/status/activity" element={<Activity />} />
<Route path="/status/log" element={<SystemLog />} /> <Route path="/status/log" element={<SystemLog />} />
@@ -68,6 +67,8 @@ const AuthenticatedRouting = () => {
<Route path="/customentities" element={<CustomEntities />} /> <Route path="/customentities" element={<CustomEntities />} />
</> </>
)} )}
<Route path="/*" element={<Navigate to="/" />} />
</Routes> </Routes>
</Layout> </Layout>
); );

View File

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

View File

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

View File

@@ -30,7 +30,7 @@ export const getDevVersion = () =>
cacheFor: 60 * 10 * 1000, cacheFor: 60 * 10 * 1000,
transform(response: { data: { name: string; published_at: string } }) { transform(response: { data: { name: string; published_at: string } }) {
return { 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 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 { try {
decoder = new TextDecoder(); decoder = new TextDecoder();
} catch (error) {} } catch (error) {}
let src; class C1Type {}
let srcEnd; const C1 = new C1Type();
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();
C1.name = 'MessagePack 0xC1'; C1.name = 'MessagePack 0xC1';
let sequentialMode = false; let sequentialMode = false,
let inlineObjectReadThreshold = 2; inlineObjectReadThreshold = 2,
let readStruct, onLoadedStructures, onSaveState; readStruct,
// no-eval build onLoadedStructures,
onSaveState;
try { try {
new Function(''); new Function('');
} catch (error) { } catch (error) {
// if eval variants are not supported, do not create inline object readers ever
inlineObjectReadThreshold = Infinity; inlineObjectReadThreshold = Infinity;
} }
export class Unpackr { export class Unpackr {
constructor(options) { constructor(options) {
if (options) { if (options) {
@@ -50,19 +47,15 @@ export class Unpackr {
if (options.structures) if (options.structures)
options.structures.sharedLength = options.structures.length; options.structures.sharedLength = options.structures.length;
else if (options.getStructures) { 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; options.structures.sharedLength = 0;
} }
if (options.int64AsNumber) { if (options.int64AsNumber) options.int64AsType = 'number';
options.int64AsType = 'number';
}
} }
Object.assign(this, options); Object.assign(this, options);
} }
unpack(source, options?: any) { unpack(source, options?: any) {
if (src) { if (src) {
// re-entrant execution, save the state and restore it after we do this unpack
return saveState(() => { return saveState(() => {
clearSource(); clearSource();
return this return this
@@ -86,9 +79,6 @@ export class Unpackr {
strings = EMPTY_ARRAY; strings = EMPTY_ARRAY;
bundledStrings = null; bundledStrings = null;
src = source; 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 { try {
dataView = dataView =
source.dataView || source.dataView ||
@@ -191,10 +181,10 @@ export class Unpackr {
return this.unpack(source, end); return this.unpack(source, end);
} }
} }
export function getPosition() { function getPosition() {
return position; return position;
} }
export function checkedRead(options: any) { function checkedRead(options: any) {
try { try {
if (!currentUnpackr.trusted && !sequentialMode) { if (!currentUnpackr.trusted && !sequentialMode) {
const sharedLength = currentStructures.sharedLength || 0; const sharedLength = currentStructures.sharedLength || 0;
@@ -264,7 +254,7 @@ function restoreStructures() {
currentStructures.restoreStructures = null; currentStructures.restoreStructures = null;
} }
export function read() { function read() {
let token = src[position++]; let token = src[position++];
if (token < 0xa0) { if (token < 0xa0) {
if (token < 0x80) { if (token < 0x80) {
@@ -589,7 +579,7 @@ const createSecondByteReader = (firstId, read0) =>
return structure.read(); return structure.read();
}; };
export function loadStructures() { function loadStructures() {
const loadedStructures = saveState(() => { const loadedStructures = saveState(() => {
// save the state in case getStructures modifies our buffer // save the state in case getStructures modifies our buffer
src = null; src = null;
@@ -605,9 +595,8 @@ var readFixedString = readStringJS;
var readString8 = readStringJS; var readString8 = readStringJS;
var readString16 = readStringJS; var readString16 = readStringJS;
var readString32 = readStringJS; var readString32 = readStringJS;
export let isNativeAccelerationEnabled = false; let isNativeAccelerationEnabled = false;
function setExtractor(extractStrings) {
export function setExtractor(extractStrings) {
isNativeAccelerationEnabled = true; isNativeAccelerationEnabled = true;
readFixedString = readString(1); readFixedString = readString(1);
readString8 = readString(2); readString8 = readString(2);
@@ -701,7 +690,7 @@ function readStringJS(length) {
return result; return result;
} }
export function readString(source, start, length) { function readString(source, start, length) {
const existingSrc = src; const existingSrc = src;
src = source; src = source;
position = start; position = start;
@@ -1065,7 +1054,7 @@ currentExtensions[0x70] = (data) => {
currentExtensions[0x73] = () => new Set(read()); currentExtensions[0x73] = () => new Set(read());
export const typedArrays = [ const typedArrays = [
'Int8', 'Int8',
'Uint8', 'Uint8',
'Uint8Clamped', 'Uint8Clamped',
@@ -1177,44 +1166,20 @@ function saveState(callback) {
dataView = new DataView(src.buffer, src.byteOffset, src.byteLength); dataView = new DataView(src.buffer, src.byteOffset, src.byteLength);
return value; return value;
} }
export function clearSource() { function clearSource() {
src = null; src = null;
referenceMap = null; referenceMap = null;
currentStructures = null; currentStructures = null;
} }
export function addExtension(extension) { function addExtension(extension) {
if (extension.unpack) currentExtensions[extension.type] = extension.unpack; if (extension.unpack) currentExtensions[extension.type] = extension.unpack;
else currentExtensions[extension.type] = extension; 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++) { for (let i = 0; i < 256; i++) {
mult10[i] = +('1e' + Math.floor(45.15 - i * 0.30103)); mult10[i] = +('1e' + Math.floor(45.15 - i * 0.30103));
} }
export const Decoder = Unpackr; const defaultUnpackr = new Unpackr({ useRecords: false });
var defaultUnpackr = new Unpackr({ useRecords: false });
export const unpack = defaultUnpackr.unpack; 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 () => { const saveEntities = async () => {
await writeEntities({ await writeEntities({
entities: entities entities: entities
.filter((ei) => !ei.deleted) .filter((ei: EntityItem) => !ei.deleted)
.map((condensed_ei) => ({ .map((condensed_ei: EntityItem) => ({
id: condensed_ei.id, id: condensed_ei.id,
ram: condensed_ei.ram, ram: condensed_ei.ram,
name: condensed_ei.name, name: condensed_ei.name,
@@ -231,6 +231,7 @@ const CustomEntities = () => {
value_type: 0, value_type: 0,
writeable: false, writeable: false,
deleted: false, deleted: false,
hide: false,
value: '' value: ''
}); });
setDialogOpen(true); setDialogOpen(true);
@@ -251,15 +252,17 @@ const CustomEntities = () => {
const renderEntity = () => { const renderEntity = () => {
if (!entities) { if (!entities) {
return <FormLoader onRetry={fetchEntities} errorMessage={error?.message} />; return (
<FormLoader onRetry={fetchEntities} errorMessage={error?.message || ''} />
);
} }
return ( return (
<Table <Table
data={{ data={{
nodes: entities nodes: entities
.filter((ei) => !ei.deleted) .filter((ei: EntityItem) => !ei.deleted)
.sort((a, b) => a.name.localeCompare(b.name)) .sort((a: EntityItem, b: EntityItem) => a.name.localeCompare(b.name))
}} }}
theme={entity_theme} theme={entity_theme}
layout={{ custom: true }} layout={{ custom: true }}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,7 +1,12 @@
import { Tooltip, type TooltipProps, styled, tooltipClasses } from '@mui/material'; import { Tooltip, type TooltipProps, styled, tooltipClasses } from '@mui/material';
export const ButtonTooltip = styled(({ className, ...props }: TooltipProps) => ( 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 }) => ({ ))(({ theme }) => ({
[`& .${tooltipClasses.arrow}`]: { [`& .${tooltipClasses.arrow}`]: {
color: theme.palette.success.main 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 './inputs';
export * from './layout'; export * from './layout';
export * from './loading'; export * from './loading';
export * from './routing'; export * from './routing';
export * from './upload'; export * from './upload';
export { default as SectionContent } from './SectionContent';
export { default as ButtonRow } from './ButtonRow'; // Specific routing exports
export { default as MessageBox } from './MessageBox';
export { default as BlockNavigation } from './routing/BlockNavigation'; export { default as BlockNavigation } from './routing/BlockNavigation';
export { default as ButtonTooltip } from './ButtonTooltip';

View File

@@ -16,14 +16,14 @@ const ValidatedTextField: FC<ValidatedTextFieldProps> = ({
fieldErrors, fieldErrors,
...rest ...rest
}) => { }) => {
const errors = fieldErrors && fieldErrors[rest.name]; const errors = fieldErrors?.[rest.name];
const renderErrors = () =>
errors &&
errors.map((e) => <FormHelperText key={e.message}>{e.message}</FormHelperText>);
return ( return (
<> <>
<TextField error={!!errors} {...rest} /> <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); const selected = routeMatches(to, pathname);
return ( 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' }}> <ListItemIcon sx={{ color: selected ? '#90caf9' : '#9e9e9e' }}>
<Icon /> <Icon />
</ListItemIcon> </ListItemIcon>

View File

@@ -58,12 +58,22 @@ const LayoutMenuItem = ({
} }
> >
<ListItemButton component={Link} to={to}> <ListItemButton component={Link} to={to}>
<RenderIcon icon={icon} bgcolor={bgcolor} label={label} text={text} /> <RenderIcon
icon={icon}
{...(bgcolor && { bgcolor })}
label={label}
text={text}
/>
</ListItemButton> </ListItemButton>
</ListItem> </ListItem>
) : ( ) : (
<ListItem> <ListItem>
<RenderIcon icon={icon} bgcolor={bgcolor} label={label} text={text} /> <RenderIcon
icon={icon}
{...(bgcolor && { bgcolor })}
label={label}
text={text}
/>
</ListItem> </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 LoadingSpinner } from './LoadingSpinner';
export { default as FormLoader } from './FormLoader'; 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 { Path } from 'react-router';
import type * as H from 'history';
import { jwtDecode } from 'jwt-decode'; import { jwtDecode } from 'jwt-decode';
import type { Me, SignInRequest, SignInResponse } from 'types'; import type { Me, SignInRequest, SignInResponse } from 'types';
@@ -18,10 +17,10 @@ export function getStorage() {
return localStorage || sessionStorage; return localStorage || sessionStorage;
} }
export function storeLoginRedirect(location?: H.Location) { export function storeLoginRedirect(location?: { pathname: string; search: string }) {
if (location) { if (location) {
getStorage().setItem(SIGN_IN_PATHNAME, location.pathname as string); getStorage().setItem(SIGN_IN_PATHNAME, location.pathname);
getStorage().setItem(SIGN_IN_SEARCH, location.search as string); getStorage().setItem(SIGN_IN_SEARCH, location.search);
} }
} }
@@ -36,7 +35,7 @@ export function fetchLoginRedirect(): Partial<Path> {
clearLoginRedirect(); clearLoginRedirect();
return { return {
pathname: signInPathname || `/dashboard`, 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 // 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 CancelIcon from '@mui/icons-material/Cancel';
import CloudUploadIcon from '@mui/icons-material/CloudUpload'; import CloudUploadIcon from '@mui/icons-material/CloudUpload';
import UploadIcon from '@mui/icons-material/Upload'; import UploadIcon from '@mui/icons-material/Upload';
import { Box, Button } from '@mui/material'; import { Box, Button, Typography, styled } from '@mui/material';
import { useI18nContext } from 'i18n/i18n-react'; 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 [file, setFile] = useState<File>();
const [dragged, setDragged] = useState(false); const [dragged, setDragged] = useState(false);
const inputRef = useRef<HTMLInputElement | null>(null); const inputRef = useRef<HTMLInputElement | null>(null);
@@ -28,14 +71,17 @@ const DragNdrop = ({ text, onFileSelected }) => {
}; };
const handleFileChange = (e: ChangeEvent<HTMLInputElement>) => { const handleFileChange = (e: ChangeEvent<HTMLInputElement>) => {
if (!e.target.files) { if (!e.target.files || e.target.files.length === 0) {
return; 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 e.target.value = ''; // this is to allow the same file to be selected again
}; };
const handleDrop = (event) => { const handleDrop = (event: DragEvent<HTMLDivElement>) => {
event.preventDefault(); event.preventDefault();
const droppedFiles = event.dataTransfer.files; const droppedFiles = event.dataTransfer.files;
if (droppedFiles.length > 0) { if (droppedFiles.length > 0) {
@@ -43,38 +89,40 @@ const DragNdrop = ({ text, onFileSelected }) => {
} }
}; };
const handleRemoveFile = (event) => { const handleRemoveFile = (event: MouseEvent<HTMLButtonElement>) => {
event.stopPropagation(); event.stopPropagation();
setFile(undefined); setFile(undefined);
setDragged(false); setDragged(false);
}; };
const handleUploadClick = (event) => { const handleUploadClick = (event: MouseEvent<HTMLButtonElement>) => {
event.stopPropagation(); event.stopPropagation();
if (file) {
onFileSelected(file); onFileSelected(file);
}
}; };
const handleBrowseClick = () => { const handleBrowseClick = () => {
inputRef.current?.click(); inputRef.current?.click();
}; };
const handleDragOver = (event) => { const handleDragOver = (event: DragEvent<HTMLDivElement>) => {
event.preventDefault(); // prevent file from being opened event.preventDefault(); // prevent file from being opened
setDragged(true); setDragged(true);
}; };
return ( return (
<div <DocumentUploader
className={`document-uploader ${file || dragged ? 'active' : ''}`} active={!!(file || dragged)}
onDrop={handleDrop} onDrop={handleDrop}
onDragOver={handleDragOver} onDragOver={handleDragOver}
onDragLeave={() => setDragged(false)} onDragLeave={() => setDragged(false)}
onClick={handleBrowseClick} onClick={handleBrowseClick}
> >
<div className="upload-info"> <UploadInfo>
<CloudUploadIcon sx={{ marginRight: 4 }} color="primary" fontSize="large" /> <CloudUploadIcon sx={{ marginRight: 4 }} color="primary" fontSize="large" />
<p>{text}</p> <Typography>{text}</Typography>
</div> </UploadInfo>
<input <input
type="file" type="file"
@@ -88,9 +136,9 @@ const DragNdrop = ({ text, onFileSelected }) => {
{file && ( {file && (
<> <>
<div className="file-info"> <FileInfo>
<p>{file.name}</p> <FileName>{file.name}</FileName>
</div> </FileInfo>
<Box> <Box>
<Button <Button
startIcon={<CancelIcon />} startIcon={<CancelIcon />}
@@ -112,7 +160,7 @@ const DragNdrop = ({ text, onFileSelected }) => {
</Box> </Box>
</> </>
)} )}
</div> </DocumentUploader>
); );
}; };

View File

@@ -12,7 +12,12 @@ import { useI18nContext } from 'i18n/i18n-react';
import DragNdrop from './DragNdrop'; import DragNdrop from './DragNdrop';
import { LinearProgressWithLabel } from './LinearProgressWithLabel'; 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 [md5, setMd5] = useState<string>();
const [file, setFile] = useState<File>(); const [file, setFile] = useState<File>();
const { LL } = useI18nContext(); const { LL } = useI18nContext();
@@ -25,8 +30,8 @@ const SingleUpload = ({ text, doRestart }) => {
} = useRequest(SystemApi.uploadFile, { } = useRequest(SystemApi.uploadFile, {
immediate: false immediate: false
}).onSuccess(({ data }) => { }).onSuccess(({ data }) => {
if (data) { if (data && typeof data === 'object' && 'md5' in data) {
setMd5(data.md5 as string); setMd5((data as { md5: string }).md5);
toast.success(LL.UPLOAD() + ' MD5 ' + LL.SUCCESSFUL()); toast.success(LL.UPLOAD() + ' MD5 ' + LL.SUCCESSFUL());
setFile(undefined); setFile(undefined);
} else { } else {
@@ -34,16 +39,19 @@ const SingleUpload = ({ text, doRestart }) => {
} }
}); });
useEffect(async () => { useEffect(() => {
const uploadFile = async () => {
if (file) { if (file) {
await sendUpload(file).catch((error: Error) => { 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()); toast.warning(LL.UPLOAD() + ' ' + LL.ABORTED());
} else { } else {
toast.warning('Invalid file extension or incompatible bin file'); toast.warning('Invalid file extension or incompatible bin file');
} }
}); });
} }
};
void uploadFile();
}, [file]); }, [file]);
return ( 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 // cache object to prevent re-renders
const obj = useMemo( const obj = useMemo(
() => ({ signIn, signOut, me, refresh }), () => ({
signIn,
signOut,
refresh,
...(me && { me })
}),
[signIn, signOut, me, refresh] [signIn, signOut, me, refresh]
); );

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,31 +1,108 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "ESNext", // Target modern browsers for better performance
"target": "ES2022",
"useDefineForClassFields": true, "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, "allowJs": false,
"skipLibCheck": true, "checkJs": false,
"esModuleInterop": false,
"allowSyntheticDefaultImports": true, // Module system optimized for Vite
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"composite": true,
"module": "ESNext", "module": "ESNext",
"moduleResolution": "bundler", "moduleResolution": "bundler",
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"resolveJsonModule": true, "resolveJsonModule": true,
"isolatedModules": true, "isolatedModules": true,
// Emit configuration
"noEmit": true, "noEmit": true,
"useUnknownInCatchVariables": false, "declaration": false,
"declarationMap": false,
"sourceMap": false,
// React/JSX configuration
"jsx": "react-jsx", "jsx": "react-jsx",
"noImplicitAny": false, "jsxImportSource": "react",
"baseUrl": "src",
// 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": { "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"], "include": ["src/**/*", "vite.config.ts", "progmem-generator.js"],
"exclude": ["node_modules", "dist"] "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 preact from '@preact/preset-vite';
import fs from 'fs';
import path from 'path';
import { visualizer } from 'rollup-plugin-visualizer'; import { visualizer } from 'rollup-plugin-visualizer';
import { defineConfig } from 'vite'; 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 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'; 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') { if (command === 'serve') {
console.log('Preparing for standalone build with server, mode=' + mode); console.log('Preparing for standalone build with server, mode=' + mode);
return { 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: { server: {
open: true, open: true,
port: mode == 'production' ? 4173 : 3000, port: mode == 'production' ? 4173 : 3000,
@@ -23,6 +113,12 @@ export default defineConfig(({ command, mode }) => {
'/rest': 'http://localhost:3080', '/rest': 'http://localhost:3080',
'/gh': 'http://localhost:3080' // mock for GitHub API '/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') { if (mode === 'hosted') {
console.log('Preparing for hosted build'); console.log('Preparing for hosted build');
return { return {
plugins: [preact(), viteTsconfigPaths()], plugins: [
preact({
// Enable Preact optimizations for hosted build
devToolsEnabled: false,
prefreshEnabled: false
}),
viteTsconfigPaths(),
bundleSizeReporter() // Add bundle size reporting
],
build: { 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 { return {
plugins: [ plugins: [
preact(), preact({
// Enable Preact optimizations
devToolsEnabled: false,
prefreshEnabled: false
}),
viteTsconfigPaths(), viteTsconfigPaths(),
// { // Enable image optimization for size reduction
// ...viteImagemin({ {
// verbose: false, ...viteImagemin({
// gifsicle: { verbose: false,
// optimizationLevel: 7, gifsicle: {
// interlaced: false optimizationLevel: 7,
// }, interlaced: false
// optipng: { },
// optimizationLevel: 7 optipng: {
// }, optimizationLevel: 7
// mozjpeg: { },
// quality: 20 mozjpeg: {
// }, quality: 20
// pngquant: { },
// quality: [0.8, 0.9], pngquant: {
// speed: 4 quality: [0.8, 0.9],
// }, speed: 4
// svgo: { },
// plugins: [ svgo: {
// { plugins: [
// name: 'removeViewBox' {
// }, name: 'removeViewBox'
// { },
// name: 'removeEmptyAttrs', {
// active: false name: 'removeEmptyAttrs',
// } active: false
// ] }
// } ]
// }), }
// enforce: 'pre' }),
// }, enforce: 'pre'
},
visualizer({ visualizer({
template: 'treemap', // or sunburst template: 'treemap', // or sunburst
open: false, open: false,
gzipSize: true, gzipSize: true,
brotliSize: true, brotliSize: true,
filename: '../analyse.html' // will be saved in project's root filename: '../analyse.html' // will be saved in project's root
}) }),
bundleSizeReporter() // Add bundle size reporting
], ],
build: { build: {
// target: 'es2022', // Target modern browsers for smaller bundles
chunkSizeWarningLimit: 1024, target: 'es2020',
chunkSizeWarningLimit: 512,
minify: 'terser', minify: 'terser',
// Enable CSS minification
cssMinify: true,
// Optimize asset handling
assetsInlineLimit: 4096, // Inline small assets
terserOptions: { terserOptions: {
compress: { compress: {
passes: 4, passes: 6,
arrows: true, arrows: true,
drop_console: true, drop_console: true,
drop_debugger: true, drop_debugger: true,
sequences: 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: { mangle: {
// toplevel: true toplevel: true, // Enable top-level mangling
// module: true module: true // Enable module mangling
// properties: { // properties: {
// regex: /^_/ // regex: /^_/ // Mangle properties starting with _
// } // }
}, },
ecma: 5, ecma: 2020, // Updated to modern ECMAScript
enclose: false, enclose: false,
keep_classnames: false, keep_classnames: false,
keep_fnames: false, keep_fnames: false,
ie8: false, ie8: false,
module: false, module: false,
safari10: false, safari10: false,
toplevel: false toplevel: true // Enable top-level optimization
}, },
rollupOptions: { rollupOptions: {
// Enable tree shaking
treeshake: {
moduleSideEffects: false,
propertyReadSideEffects: false,
tryCatchDeoptimization: false
},
output: { 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) { manualChunks(id: string) {
if (id.includes('node_modules')) { 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')) { if (id.includes('react-router')) {
return '@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'; 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 // Mock server for development
// - file uploads // Simulates file uploads and EventSource (SSE) for log messages
// - EventSource (SSE) for log messages
import formidable from 'formidable'; import formidable from 'formidable';
function pad(number) { // Optimized padding function
let r = String(number); const pad = (number) => String(number).padStart(2, '0');
if (r.length === 1) {
r = '0' + r;
}
return r;
}
// e.g. 2024-03-29 07:02:37.856 // Cached date formatter to avoid prototype pollution
Date.prototype.toISOString = function () { const formatDate = (date) => {
return ( const year = date.getUTCFullYear();
this.getUTCFullYear() + const month = pad(date.getUTCMonth() + 1);
'-' + const day = pad(date.getUTCDate());
pad(this.getUTCMonth() + 1) + const hours = pad(date.getUTCHours());
'-' + const minutes = pad(date.getUTCMinutes());
pad(this.getUTCDate()) + const seconds = pad(date.getUTCSeconds());
' ' + const milliseconds = String((date.getUTCMilliseconds() / 1000).toFixed(3)).slice(
pad(this.getUTCHours()) + 2,
':' + 5
pad(this.getUTCMinutes()) +
':' +
pad(this.getUTCSeconds()) +
'.' +
String((this.getUTCMilliseconds() / 1000).toFixed(3)).slice(2, 5)
); );
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}.${milliseconds}`;
}; };
export default () => { export default () => {
@@ -35,97 +26,129 @@ export default () => {
name: 'vite:mockserver', name: 'vite:mockserver',
configureServer: async (server) => { configureServer: async (server) => {
server.middlewares.use(async (req, res, next) => { server.middlewares.use(async (req, res, next) => {
// catch any file uploads // Handle file uploads
if (req.url.startsWith('/rest/uploadFile')) { if (req.url.startsWith('/rest/uploadFile')) {
// show progress const fileSize = parseInt(req.headers['content-length'] || '0', 10);
let progress = 0; let progress = 0;
const file_size = req.headers['content-length'];
console.log('File size: ' + file_size); // Track upload progress
req.on('data', async (chunk) => { req.on('data', (chunk) => {
progress += chunk.length; progress += chunk.length;
const percentage = (progress / file_size) * 100; if (fileSize > 0) {
console.log(`Progress: ${Math.round(percentage)}%`); const percentage = Math.round((progress / fileSize) * 100);
// await new Promise((resolve) => setTimeout(() => resolve(), 3000)); // slow it down console.log(`Upload progress: ${percentage}%`);
}
}); });
const form = formidable({});
let fields;
let files;
try { try {
[fields, files] = await form.parse(req); const form = formidable({
} catch (err) { maxFileSize: 50 * 1024 * 1024, // 50MB limit
console.error('Not json form content'); keepExtensions: true
res.writeHead(err.httpCode || 400, {
'Content-Type': 'text/plain'
}); });
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; return;
} }
// only process when we have a file const uploadedFile = files.file[0];
if (Object.keys(files).length > 0) { const fileName = uploadedFile.originalFilename;
const uploaded_file = files.file[0]; const fileExtension = fileName
const file_name = uploaded_file.originalFilename; .substring(fileName.lastIndexOf('.') + 1)
const file_extension = file_name.substring( .toLowerCase();
file_name.lastIndexOf('.') + 1
console.log(
`File uploaded: ${fileName} (${fileExtension}, ${fileSize} bytes)`
); );
console.log('Filename: ' + file_name); // Validate file extension
console.log('Extension: ' + file_extension); const validExtensions = new Set(['bin', 'json', 'md5']);
console.log('File size: ' + file_size); if (!validExtensions.has(fileExtension)) {
res.statusCode = 406;
res.end('Invalid file extension');
return;
}
if (file_extension === 'bin' || file_extension === 'json') { // Handle different file types
console.log('File uploaded successfully!'); if (fileExtension === 'md5') {
} else if (file_extension === 'md5') { res.setHeader('Content-Type', 'application/json');
console.log('MD5 hash generated successfully!');
res.end( res.end(
JSON.stringify({ JSON.stringify({
md5: 'ef4304fc4d9025a58dcf25d71c882d2c' md5: 'ef4304fc4d9025a58dcf25d71c882d2c'
}) })
); );
} else { } else {
res.statusCode = 406; console.log('File uploaded successfully!');
console.log('Invalid file extension!');
}
}
res.end(); 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')) { else if (req.url.startsWith('/es/log')) {
// Set SSE headers
res.writeHead(200, { res.writeHead(200, {
Connection: 'keep-alive', Connection: 'keep-alive',
'Cache-Control': 'no-cache', '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; let messageCount = 0;
const interval = setInterval(() => { const logLevels = [3, 4, 5, 6, 7, 8]; // Different log levels
let message = 'message #' + count; const logNames = ['system', 'ems', 'wifi', 'mqtt', 'ntp', 'api'];
if (count % 6 === 1) {
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 += 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(), const logData = {
l: 3 + (count % 6), t: formatDate(new Date()),
i: count, l: level,
n: 'system', i: messageCount,
n: name,
m: message m: message
}; };
count++;
res.write(`data: ${JSON.stringify(data)}\n\n`);
}, 1000);
// if client closes connection res.write(`data: ${JSON.stringify(logData)}\n\n`);
res.on('close', () => { messageCount++;
console.log('Closing ES connection'); };
// 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); clearInterval(interval);
if (!res.destroyed) {
res.end(); res.end();
}); }
};
res.on('close', cleanup);
res.on('error', cleanup);
} else { } 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", "name": "mock-api",
"version": "3.7.2", "version": "3.7.3",
"description": "mock api for EMS-ESP", "description": "mock api for EMS-ESP",
"author": "proddy, emsesp.org", "author": "proddy, emsesp.org",
"license": "MIT", "license": "MIT",
@@ -15,5 +15,5 @@
"itty-router": "^5.0.22", "itty-router": "^5.0.22",
"prettier": "^3.6.2" "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 DEV_VERSION_IS_UPGRADEABLE: boolean;
let STABLE_VERSION_IS_UPGRADEABLE: boolean; let STABLE_VERSION_IS_UPGRADEABLE: boolean;
let THIS_VERSION: string; let THIS_VERSION: string;
let version_test: number;
let LATEST_STABLE_VERSION = '3.7.2'; let LATEST_STABLE_VERSION = '3.7.2';
let LATEST_DEV_VERSION = '3.7.3-dev.6'; let LATEST_DEV_VERSION = '3.7.3-dev.6';
// scenarios for testing versioning // scenarios for testing versioning
version_test = 0; // on latest stable, or switch to dev let version_test = 0; // on latest stable, or switch to dev
// version_test = 1; // on latest dev, or switch back to stable // let 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 // let 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 // let 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 = 4; // downgrade to an older dev, or switch back to stable
switch (version_test as number) { switch (version_test as number) {
case 0: case 0:
@@ -4380,78 +4378,124 @@ router
function deviceData(id: number) { function deviceData(id: number) {
if (id == 1) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { if (id == 8) {
// test changing the selected flow temp on a Bosch Compress 7000i AW Heat Pump (Boiler/HP) // 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); 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) { 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) { 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) { 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) { 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) { function deviceEntities(id: number) {
if (id == 1) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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 // 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 // prepare dashboard data
@@ -4558,8 +4602,8 @@ router
} }
// add temperature sensor data. no command c // add temperature sensor data. no command c
let sensor_data: any[] = []; if (emsesp_sensordata.ts.length > 0) {
sensor_data = emsesp_sensordata.ts.map((item, index) => ({ const sensor_data = emsesp_sensordata.ts.map((item, index) => ({
id: DeviceTypeUniqueID.TEMPERATURESENSOR_UID * 100 + index, id: DeviceTypeUniqueID.TEMPERATURESENSOR_UID * 100 + index,
dv: { dv: {
id: '00' + item.n, id: '00' + item.n,
@@ -4572,15 +4616,16 @@ router
t: DeviceType.TEMPERATURESENSOR, t: DeviceType.TEMPERATURESENSOR,
nodes: sensor_data nodes: sensor_data
}; };
// only add to dashboard if we have values
if ((dashboard_object.nodes ?? []).length > 0) {
dashboard_nodes.push(dashboard_object); dashboard_nodes.push(dashboard_object);
} }
// add analog sensor data. no command c // add analog sensor data. no command c
// remove disabled sensors first (t = 0) // remove disabled sensors first (t = 0) and create data in one pass
sensor_data = emsesp_sensordata.as.filter((item) => item.t !== 0); const enabledAnalogSensors = emsesp_sensordata.as.filter(
sensor_data = sensor_data.map((item, index) => ({ (item) => item.t !== 0
);
if (enabledAnalogSensors.length > 0) {
const sensor_data = enabledAnalogSensors.map((item, index) => ({
id: DeviceTypeUniqueID.ANALOGSENSOR_UID * 100 + index, id: DeviceTypeUniqueID.ANALOGSENSOR_UID * 100 + index,
dv: { dv: {
id: '00' + item.n, id: '00' + item.n,
@@ -4593,15 +4638,14 @@ router
t: DeviceType.ANALOGSENSOR, t: DeviceType.ANALOGSENSOR,
nodes: sensor_data nodes: sensor_data
}; };
// only add to dashboard if we have values
if ((dashboard_object.nodes ?? []).length > 0) {
dashboard_nodes.push(dashboard_object); dashboard_nodes.push(dashboard_object);
} }
// add the scheduler data // add the scheduler data
// filter emsesp_schedule with only if it has a name // filter emsesp_schedule with only if it has a name and create data in one pass
let scheduler_data = emsesp_schedule.schedule.filter((item) => item.name); const namedSchedules = emsesp_schedule.schedule.filter((item) => item.name);
let scheduler_data2 = scheduler_data.map((item, index) => ({ if (namedSchedules.length > 0) {
const scheduler_data = namedSchedules.map((item, index) => ({
id: DeviceTypeUniqueID.SCHEDULER_UID * 100 + index, id: DeviceTypeUniqueID.SCHEDULER_UID * 100 + index,
dv: { dv: {
id: '00' + item.name, id: '00' + item.name,
@@ -4613,10 +4657,8 @@ router
dashboard_object = { dashboard_object = {
id: DeviceTypeUniqueID.SCHEDULER_UID, id: DeviceTypeUniqueID.SCHEDULER_UID,
t: DeviceType.SCHEDULER, 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); dashboard_nodes.push(dashboard_object);
} }
} else { } else {
@@ -4660,8 +4702,12 @@ router
}; };
// console.log('dashboardData: ', dashboardData); // console.log('dashboardData: ', dashboardData);
// Clear references to help with garbage collection
dashboard_nodes = [];
dashboard_object = {};
// return dashboard_data; // if not using msgpack // 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 // Customizations
@@ -4852,10 +4898,12 @@ router
} else { } else {
if (as.deleted) { if (as.deleted) {
emsesp_sensordata.as[objIndex].d = true; emsesp_sensordata.as[objIndex].d = true;
var filtered = emsesp_sensordata.as.filter(function (value, index, arr) { // Remove deleted items in-place to avoid creating new arrays
return !value.d; for (let i = emsesp_sensordata.as.length - 1; i >= 0; i--) {
}); if (emsesp_sensordata.as[i].d) {
emsesp_sensordata.as = filtered; emsesp_sensordata.as.splice(i, 1);
}
}
} else { } else {
emsesp_sensordata.as[objIndex].n = as.name; emsesp_sensordata.as[objIndex].n = as.name;
emsesp_sensordata.as[objIndex].f = as.factor; emsesp_sensordata.as[objIndex].f = as.factor;

View File

@@ -197,7 +197,7 @@ extra_scripts =
build_flags = build_flags =
-DARDUINOJSON_ENABLE_ARDUINO_STRING=1 -DARDUINOJSON_ENABLE_ARDUINO_STRING=1
-DEMSESP_STANDALONE -DEMSESP_TEST -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 -std=gnu++17 -Og -ggdb
build_unflags = -std=gnu++11 -std=gnu++14 build_unflags = -std=gnu++11 -std=gnu++14
build_type = debug build_type = debug

View File

@@ -1426,3 +1426,11 @@ Roomparams
Roomdata Roomdata
Roomschedule Roomschedule
dewtemp 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: if int(entity["modbus count"]) <= 0:
raise Exception('Entity "' + entity_dev_name + ' (' + entity_shortname + ')' + 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": # if entity["modbus count"] == "0":
# print("ignoring " + entity_dev_name + " - it has a register length of zero") # 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}, {163, DeviceType::WATER, "SM100, MS100", DeviceFlags::EMS_DEVICE_FLAG_SM100},
{164, DeviceType::WATER, "SM200, MS200", DeviceFlags::EMS_DEVICE_FLAG_SM100}, {164, DeviceType::WATER, "SM200, MS200", DeviceFlags::EMS_DEVICE_FLAG_SM100},
{248, DeviceType::MIXER, "HM210", DeviceFlags::EMS_DEVICE_FLAG_MMPLUS}, {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} // {157, DeviceType::THERMOSTAT, "RC120", DeviceFlags::EMS_DEVICE_FLAG_CR120}
#endif #endif

View File

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

View File

@@ -222,7 +222,7 @@ class EMSESP {
static void scan_devices(); static void scan_devices();
static void clear_all_devices(); static void clear_all_devices();
static std::deque<std::unique_ptr<EMSdevice>> emsdevices; static std::vector<std::unique_ptr<EMSdevice>> emsdevices;
// services // services
static Mqtt mqtt_; 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 // capitalizes one UTF-8 character in char array
// works with Latin1 (1 byte), Polish amd some other (2 bytes) characters // works with Latin1 (1 byte), Polish and other (2 bytes) characters
// TODO add special characters that occur in other supported languages // supports special characters for all 11 supported languages: EN, DE, NL, SV, PL, NO, FR, TR, IT, SK, CZ
#if defined(EMSESP_STANDALONE) #if defined(EMSESP_STANDALONE)
#pragma GCC diagnostic push #pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wtype-limits" #pragma GCC diagnostic ignored "-Wtype-limits"
@@ -803,23 +803,77 @@ void Helpers::CharToUpperUTF8(char * c) {
if ((p_v >= (char)0xA0) && (p_v <= (char)0xBE)) { if ((p_v >= (char)0xA0) && (p_v <= (char)0xBE)) {
*p -= 0x20; *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; break;
case (char)0xC4: case (char)0xC4:
switch (p_v) { switch (p_v) {
case (char)0x85: //ą (0xC4,0x85) -> Ą (0xC4,0x84) case (char)0x85: //ą (0xC4,0x85) -> Ą (0xC4,0x84) (Polish)
case (char)0x87: //ć (0xC4,0x87) -> Ć (0xC4,0x86) case (char)0x87: //ć (0xC4,0x87) -> Ć (0xC4,0x86) (Polish)
case (char)0x99: //ę (0xC4,0x99) -> Ę (0xC4,0x98) 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; *p -= 1;
break; break;
} }
break; break;
case (char)0xC5: case (char)0xC5:
switch (p_v) { switch (p_v) {
case (char)0x82: //ł (0xC5,0x82) -> Ł (0xC5,0x81) case (char)0x81: //ł (0xC5,0x81) -> Ł (0xC5,0x80) (Polish)
case (char)0x84: //ń (0xC5,0x84) -> Ń (0xC5,0x83) case (char)0x82: //ł (0xC5,0x82) -> Ł (0xC5,0x81) (Polish)
case (char)0x9B: //ś (0xC5,0x9B) -> Ś (0xC5,0x9A) case (char)0x83: //ń (0xC5,0x83) -> Ń (0xC5,0x82) (Polish)
case (char)0xBA: //ź (0xC5,0xBA) -> Ź (0xC5,0xB9) case (char)0x84: //ń (0xC5,0x84) -> Ń (0xC5,0x83) (Polish)
case (char)0xBC: //ż (0xC5,0xBC) -> Ż (0xC5,0xBB) 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; *p -= 1;
break; 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_(roomsensor), 199, 1), // roomsensor
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(heatup), 200, 1), // heatup 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_(mode), 0, 1), // mode
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwSetTemp), 1, 1), // settemp REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(modetype), 1, 1), // modetype
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwSetTempLow), 2, 1), // settemplow REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwSetTemp), 2, 1), // settemp
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwCircMode), 3, 1), // circmode REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwSetTempLow), 3, 1), // settemplow
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwChargeDuration), 4, 1), // chargeduration REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwCircMode), 4, 1), // circmode
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwCharge), 5, 1), // charge REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwChargeDuration), 5, 1), // chargeduration
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwExtra), 6, 1), // extra REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwCharge), 6, 1), // charge
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwDisinfecting), 7, 1), // disinfecting REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwExtra), 7, 1), // extra
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwDisinfectDay), 8, 1), // disinfectday REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwDisinfecting), 8, 1), // disinfecting
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwDisinfectTime), 9, 1), // disinfecttime REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwDisinfectDay), 9, 1), // disinfectday
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwDailyHeating), 10, 1), // dailyheating REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwDisinfectTime), 10, 1), // disinfecttime
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwDailyHeatTime), 11, 1), // dailyheattime REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwDailyHeating), 11, 1), // dailyheating
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwProgMode), 12, 1), // progmode REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwDailyHeatTime), 12, 1), // dailyheattime
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwCircProg), 13, 1), // circprog REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwProgMode), 13, 1), // progmode
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwDisinfectHour), 14, 1), // disinfecthour REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwCircProg), 14, 1), // circprog
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwMaxTemp), 15, 1), // maxtemp REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwDisinfectHour), 15, 1), // disinfecthour
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwOneTimeKey), 16, 1), // onetimekey REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwMaxTemp), 16, 1), // maxtemp
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(switchtime), 17, 8), // switchtime REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwOneTimeKey), 17, 1), // onetimekey
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwcircswitchtime), 25, 8), // circswitchtime REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(switchtime), 18, 8), // switchtime
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(holidays), 33, 13), // holidays REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwcircswitchtime), 26, 8), // circswitchtime
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(vacations), 46, 13), // vacations REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(holidays), 34, 13), // holidays
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwWhenModeOff), 59, 1), // whenmodeoff 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_(flowTempHc), 0, 1), // flowtemphc
REGISTER_MAPPING(dt::MIXER, TAG_TYPE_HC, FL_(valveStatus), 1, 1), // valvestatus REGISTER_MAPPING(dt::MIXER, TAG_TYPE_HC, FL_(valveStatus), 1, 1), // valvestatus
REGISTER_MAPPING(dt::MIXER, TAG_TYPE_HC, FL_(flowSetTemp), 2, 1), // flowsettemp 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_(flowTempHc), 1, 1), // flowtemphc
REGISTER_MAPPING(dt::SWITCH, TAG_TYPE_DEVICE_DATA, FL_(status), 2, 1), // status 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::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_(setFlowTemp), 0, 1), // setflowtemp
REGISTER_MAPPING(dt::ALERT, TAG_TYPE_DEVICE_DATA, FL_(setBurnPow), 1, 1), // setburnpow 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 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 // turn off hot water to send a shot of cold
void Shower::shower_alert_start() { void Shower::shower_alert_start() {
LOG_DEBUG("Shower Alert started"); 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; doing_cold_shot_ = true;
force_coldshot = false; force_coldshot = false;
alert_timer_start_ = uuid::get_uptime_sec(); // timer starts now alert_timer_start_ = uuid::get_uptime_sec(); // timer starts now
@@ -161,7 +161,7 @@ void Shower::shower_alert_start() {
void Shower::shower_alert_stop() { void Shower::shower_alert_stop() {
if (doing_cold_shot_) { if (doing_cold_shot_) {
LOG_DEBUG("Shower Alert stopped"); 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; doing_cold_shot_ = false;
force_coldshot = false; force_coldshot = false;
next_alert_ += shower_alert_trigger_; next_alert_ += shower_alert_trigger_;

View File

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

View File

@@ -135,7 +135,7 @@ void Connect::process_roomThermostat(std::shared_ptr<const Telegram> telegram) {
has_update(rc->dewtemp_, dt); 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) // 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) { void Connect::process_roomThermostatName(std::shared_ptr<const Telegram> telegram) {
auto rc = room_circuit(telegram->type_id - 0xB3D); 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"); test("boiler");
// device type, command, data // device type, command, data
Command::call(EMSdevice::DeviceType::BOILER, "tapactivated", "false", 9); Command::call(EMSdevice::DeviceType::BOILER, "tapactivated", "false", DeviceValueTAG::TAG_DHW1);
ok = true; ok = true;
} }

View File

@@ -318,7 +318,7 @@ const char * run_console_command(const char * command) {
} }
void console_test1() { 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")); TEST_ASSERT_EQUAL_STRING(expected_response, run_console_command("log"));
} }
@@ -329,7 +329,7 @@ void console_test2() {
void console_test3() { void console_test3() {
// test bad command // 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")); TEST_ASSERT_EQUAL_STRING(expected_response, run_console_command("call thermostat mode bad"));
} }