From 7d54e7505edd8e6ef844ddd8be44654f392d952a Mon Sep 17 00:00:00 2001 From: Sopsy <721951+Sopsy@users.noreply.github.com> Date: Sun, 14 Sep 2025 23:58:33 +0300 Subject: [PATCH 01/16] Update Bosch RBSH-RTH0-ZB-EU quirk --- zhaquirks/bosch/rbsh_rth0_zb_eu.py | 72 +++++++++++++++++++----------- 1 file changed, 47 insertions(+), 25 deletions(-) diff --git a/zhaquirks/bosch/rbsh_rth0_zb_eu.py b/zhaquirks/bosch/rbsh_rth0_zb_eu.py index 0e63f393fd..7e41b39a05 100644 --- a/zhaquirks/bosch/rbsh_rth0_zb_eu.py +++ b/zhaquirks/bosch/rbsh_rth0_zb_eu.py @@ -2,13 +2,14 @@ from zigpy.quirks import CustomCluster from zigpy.quirks.v2 import QuirkBuilder -from zigpy.quirks.v2.homeassistant import EntityPlatform, EntityType +from zigpy.quirks.v2.homeassistant import EntityType, PERCENTAGE import zigpy.types as t from zigpy.zcl.clusters.hvac import ( - ControlSequenceOfOperation, Thermostat, UserInterface, + TemperatureDisplayMode, ) +from zigpy.quirks.v2.homeassistant.sensor import SensorStateClass from zigpy.zcl.foundation import ZCLAttributeDef """Bosch specific thermostat attribute ids.""" @@ -16,6 +17,9 @@ # Mode of operation with values BoschOperatingMode. OPERATING_MODE_ATTR_ID = 0x4007 +# Valve duty cycle: 0% - 100% +VALVE_DUTY_CYCLE_ATTR_ID = 0x4020 + # Window open switch (changes to a lower target temperature when on). WINDOW_OPEN_ATTR_ID = 0x4042 @@ -30,12 +34,9 @@ # Display brightness (0 - 10). SCREEN_BRIGHTNESS_ATTR_ID = 0x403B -# Control sequence of operation (heating/cooling) -CTRL_SEQUENCE_OF_OPERATION_ID = Thermostat.AttributeDefs.ctrl_sequence_of_oper.id - class BoschOperatingMode(t.enum8): - """Bosh operating mode attribute values.""" + """Bosch operating mode attribute values.""" Schedule = 0x00 Manual = 0x01 @@ -49,16 +50,15 @@ class State(t.enum8): On = 0x01 -class BoschControlSequenceOfOperation(t.enum8): - """Supported ControlSequenceOfOperation modes.""" - - Cooling = ControlSequenceOfOperation.Cooling_Only - Heating = ControlSequenceOfOperation.Heating_Only - - class BoschThermostatCluster(CustomCluster, Thermostat): """Bosch thermostat cluster.""" + # Works around an issue where ZHA thinks "Heating_Only" can't be changed + # 0x06 is "centralite specific", but works perfectly for this thermostat as well + _CONSTANT_ATTRIBUTES = { + Thermostat.AttributeDefs.ctrl_sequence_of_oper.id: 0x06 + } + class AttributeDefs(Thermostat.AttributeDefs): """Bosch thermostat manufacturer specific attributes.""" @@ -68,6 +68,13 @@ class AttributeDefs(Thermostat.AttributeDefs): is_manufacturer_specific=True, ) + valve_duty_cycle = ZCLAttributeDef( + id=VALVE_DUTY_CYCLE_ATTR_ID, + # Values range from 0-100 + type=t.uint8_t, + is_manufacturer_specific=True, + ) + window_open = ZCLAttributeDef( id=WINDOW_OPEN_ATTR_ID, type=State, @@ -80,6 +87,12 @@ class AttributeDefs(Thermostat.AttributeDefs): is_manufacturer_specific=True, ) + temperature_display_mode = ZCLAttributeDef( + id=0x0000, + type=TemperatureDisplayMode, + access="rw", + ) + class BoschUserInterfaceCluster(CustomCluster, UserInterface): """Bosch UserInterface cluster.""" @@ -107,17 +120,34 @@ class AttributeDefs(UserInterface.AttributeDefs): .applies_to("Bosch", "RBSH-RTH0-BAT-ZB-EU") .replaces(BoschThermostatCluster) .replaces(BoschUserInterfaceCluster) - # Operating mode - read-only: controlled automatically through Thermostat.system_mode (HAVC mode). + # Valve duty cycle, PWM controlled. + .sensor( + BoschThermostatCluster.AttributeDefs.valve_duty_cycle.name, + BoschThermostatCluster.cluster_id, + state_class=SensorStateClass.MEASUREMENT, + unit=PERCENTAGE, + translation_key="valve_duty_cycle", + fallback_name="Valve duty cycle", + ) + # Operating mode - On/Pause automatically from HVAC mode, Schedule/Manual configured here. .enum( BoschThermostatCluster.AttributeDefs.operating_mode.name, BoschOperatingMode, BoschThermostatCluster.cluster_id, - entity_platform=EntityPlatform.SENSOR, - entity_type=EntityType.DIAGNOSTIC, + entity_type=EntityType.CONFIG, translation_key="operating_mode", fallback_name="Operating mode", ) - # Fast heating/boost. + # Temperature display type. + .enum( + BoschUserInterfaceCluster.AttributeDefs.temperature_display_mode.name, + TemperatureDisplayMode, + BoschUserInterfaceCluster.cluster_id, + entity_type=EntityType.CONFIG, + translation_key="temperature_display_mode", + fallback_name="Temperature display mode", + ) + # Fast heating/boost - Only works with Heater type: Radiator. .switch( BoschThermostatCluster.AttributeDefs.boost_heating.name, BoschThermostatCluster.cluster_id, @@ -151,13 +181,5 @@ class AttributeDefs(UserInterface.AttributeDefs): translation_key="display_brightness", fallback_name="Display brightness", ) - # Heating vs Cooling. - .enum( - Thermostat.AttributeDefs.ctrl_sequence_of_oper.name, - BoschControlSequenceOfOperation, - BoschThermostatCluster.cluster_id, - translation_key="ctrl_sequence_of_oper", - fallback_name="Control sequence", - ) .add_to_registry() ) From bc62e438eec5fb7c5323ff3efad50736b4e2537d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 14 Sep 2025 21:14:10 +0000 Subject: [PATCH 02/16] Apply pre-commit auto fixes --- zhaquirks/bosch/rbsh_rth0_zb_eu.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/zhaquirks/bosch/rbsh_rth0_zb_eu.py b/zhaquirks/bosch/rbsh_rth0_zb_eu.py index 7e41b39a05..b70f726878 100644 --- a/zhaquirks/bosch/rbsh_rth0_zb_eu.py +++ b/zhaquirks/bosch/rbsh_rth0_zb_eu.py @@ -2,14 +2,10 @@ from zigpy.quirks import CustomCluster from zigpy.quirks.v2 import QuirkBuilder -from zigpy.quirks.v2.homeassistant import EntityType, PERCENTAGE -import zigpy.types as t -from zigpy.zcl.clusters.hvac import ( - Thermostat, - UserInterface, - TemperatureDisplayMode, -) +from zigpy.quirks.v2.homeassistant import PERCENTAGE, EntityType from zigpy.quirks.v2.homeassistant.sensor import SensorStateClass +import zigpy.types as t +from zigpy.zcl.clusters.hvac import TemperatureDisplayMode, Thermostat, UserInterface from zigpy.zcl.foundation import ZCLAttributeDef """Bosch specific thermostat attribute ids.""" @@ -55,9 +51,7 @@ class BoschThermostatCluster(CustomCluster, Thermostat): # Works around an issue where ZHA thinks "Heating_Only" can't be changed # 0x06 is "centralite specific", but works perfectly for this thermostat as well - _CONSTANT_ATTRIBUTES = { - Thermostat.AttributeDefs.ctrl_sequence_of_oper.id: 0x06 - } + _CONSTANT_ATTRIBUTES = {Thermostat.AttributeDefs.ctrl_sequence_of_oper.id: 0x06} class AttributeDefs(Thermostat.AttributeDefs): """Bosch thermostat manufacturer specific attributes.""" From 82b6e817bf7d601d79e76168b4c3f93c08b3266f Mon Sep 17 00:00:00 2001 From: Sopsy <721951+Sopsy@users.noreply.github.com> Date: Fri, 19 Sep 2025 01:35:41 +0300 Subject: [PATCH 03/16] Add outdoor temperature input --- zhaquirks/bosch/rbsh_rth0_zb_eu.py | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/zhaquirks/bosch/rbsh_rth0_zb_eu.py b/zhaquirks/bosch/rbsh_rth0_zb_eu.py index b70f726878..5f95a5c0f6 100644 --- a/zhaquirks/bosch/rbsh_rth0_zb_eu.py +++ b/zhaquirks/bosch/rbsh_rth0_zb_eu.py @@ -2,7 +2,8 @@ from zigpy.quirks import CustomCluster from zigpy.quirks.v2 import QuirkBuilder -from zigpy.quirks.v2.homeassistant import PERCENTAGE, EntityType +from zigpy.quirks.v2.homeassistant import PERCENTAGE, EntityType, UnitOfTemperature +from zigpy.quirks.v2.homeassistant.number import NumberDeviceClass from zigpy.quirks.v2.homeassistant.sensor import SensorStateClass import zigpy.types as t from zigpy.zcl.clusters.hvac import TemperatureDisplayMode, Thermostat, UserInterface @@ -22,6 +23,9 @@ # Boost heating preset mode. BOOST_HEATING_ATTR_ID = 0x4043 +# Outdoor temperature input +OUTDOOR_TEMP_INPUT_ATTR_ID = 0x4051 + """Bosch specific user interface attribute ids.""" # Display on-time (5s - 30s). @@ -86,6 +90,12 @@ class AttributeDefs(Thermostat.AttributeDefs): type=TemperatureDisplayMode, access="rw", ) + + outdoor_temperature_input = ZCLAttributeDef( + id=OUTDOOR_TEMP_INPUT_ATTR_ID, + type=t.int16s, + is_manufacturer_specific=True, + ) class BoschUserInterfaceCluster(CustomCluster, UserInterface): @@ -175,5 +185,19 @@ class AttributeDefs(UserInterface.AttributeDefs): translation_key="display_brightness", fallback_name="Display brightness", ) + # Input for displaying outdoor temperature in the corner of the screen. + .number( + BoschThermostatCluster.AttributeDefs.outdoor_temperature_input.name, + BoschThermostatCluster.cluster_id, + min_value=-32768, + max_value=32767, + step=1, + unit=UnitOfTemperature.CELSIUS, + multiplier=0.01, + entity_type=EntityType.CONFIG, + device_class=NumberDeviceClass.TEMPERATURE, + translation_key="outdoor_temperature_input", + fallback_name="Outdoor temperature input", + ) .add_to_registry() ) From 7c86595f1edaefb9219aaebb1a02137122abbe57 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 18 Sep 2025 22:36:27 +0000 Subject: [PATCH 04/16] Apply pre-commit auto fixes --- zhaquirks/bosch/rbsh_rth0_zb_eu.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zhaquirks/bosch/rbsh_rth0_zb_eu.py b/zhaquirks/bosch/rbsh_rth0_zb_eu.py index 5f95a5c0f6..02ddf88d52 100644 --- a/zhaquirks/bosch/rbsh_rth0_zb_eu.py +++ b/zhaquirks/bosch/rbsh_rth0_zb_eu.py @@ -90,7 +90,7 @@ class AttributeDefs(Thermostat.AttributeDefs): type=TemperatureDisplayMode, access="rw", ) - + outdoor_temperature_input = ZCLAttributeDef( id=OUTDOOR_TEMP_INPUT_ATTR_ID, type=t.int16s, From aee8ecbcddd472683d937871da8940eb61f387d8 Mon Sep 17 00:00:00 2001 From: Sopsy <721951+Sopsy@users.noreply.github.com> Date: Sat, 20 Sep 2025 12:24:09 +0300 Subject: [PATCH 05/16] Add more missing features --- zhaquirks/bosch/rbsh_rth0_zb_eu.py | 178 +++++++++++++++++++++++++++-- 1 file changed, 168 insertions(+), 10 deletions(-) diff --git a/zhaquirks/bosch/rbsh_rth0_zb_eu.py b/zhaquirks/bosch/rbsh_rth0_zb_eu.py index 02ddf88d52..4d4eb98c89 100644 --- a/zhaquirks/bosch/rbsh_rth0_zb_eu.py +++ b/zhaquirks/bosch/rbsh_rth0_zb_eu.py @@ -1,5 +1,23 @@ """Device handler for Bosch RBSH-RTH0-ZB-EU thermostat.""" +""" +There are some more undocumented values that have not been figured out what they do. +In Thermostat cluster: +0x4023: Valid values 0-7 +0x4024: Valid values 0-23 +0x4025: Valid values 0-100 +0x4050: Valid values 5-10 +0x405b: Valid values 0-255 +0x4063: Valid values 0-3 (turns on display when changed, probably the UFH/Boiler/Radiator setting, but the values are unknown) + +In UserInterface cluster: +0x4032: Valid values 0-15 +0x406a: Valid values 0-255 +0x406b: Valid values 0-255 +0x406c: Valid values 0-255 +0x406d: Valid values 0-255 +""" + from zigpy.quirks import CustomCluster from zigpy.quirks.v2 import QuirkBuilder from zigpy.quirks.v2.homeassistant import PERCENTAGE, EntityType, UnitOfTemperature @@ -14,20 +32,35 @@ # Mode of operation with values BoschOperatingMode. OPERATING_MODE_ATTR_ID = 0x4007 -# Valve duty cycle: 0% - 100% +# Valve duty cycle: 0% - 100%. VALVE_DUTY_CYCLE_ATTR_ID = 0x4020 +# Valve state (relay on/off). +VALVE_STATE_ATTR_ID = 0x4022 + # Window open switch (changes to a lower target temperature when on). WINDOW_OPEN_ATTR_ID = 0x4042 # Boost heating preset mode. BOOST_HEATING_ATTR_ID = 0x4043 -# Outdoor temperature input -OUTDOOR_TEMP_INPUT_ATTR_ID = 0x4051 +# Outdoor temperature (writing to this adds it to the corner of the screen). +OUTDOOR_TEMP_ATTR_ID = 0x4051 + +# External sensor (S1/S2 10K NTC) temperature. +EXTERNAL_SENSOR_TEMP_ATTR_ID = 0x4052 + +# Actuator type setting (NO/NC). +ACTUATOR_TYPE_ATTR_ID = 0x4060 + +# External sensor connection config. +SENSOR_CONNECTION_ATTR_ID = 0x4062 """Bosch specific user interface attribute ids.""" +# Valve status LED config. +VALVE_STATUS_LED_ATTR_ID = 0x4033 + # Display on-time (5s - 30s). SCREEN_TIMEOUT_ATTR_ID = 0x403A @@ -50,6 +83,29 @@ class State(t.enum8): On = 0x01 +class BoschActuatorType(t.enum8): + """Actuator type: Normally Open or Normally Closed.""" + + NormallyClosed = 0x00 + NormallyOpen = 0x01 + + +class BoschValveStatusLed(t.enum8): + """Valve status LED (dot next to heat/cool icon) functionality""" + + AlwaysOff = 0x00 + Normal = 0x01 + AlwaysOn = 0x02 + + +class BoschSensorConnection(t.enum8): + """Sensor connection setting (for external 10K NTC sensor on S1/S2)""" + + NotUsed = 0x00 + WithoutRegulation = 0xb0 + WithRegulation = 0xb1 + + class BoschThermostatCluster(CustomCluster, Thermostat): """Bosch thermostat cluster.""" @@ -91,12 +147,36 @@ class AttributeDefs(Thermostat.AttributeDefs): access="rw", ) - outdoor_temperature_input = ZCLAttributeDef( - id=OUTDOOR_TEMP_INPUT_ATTR_ID, + outdoor_temperature = ZCLAttributeDef( + id=OUTDOOR_TEMP_ATTR_ID, + type=t.int16s, + is_manufacturer_specific=True, + ) + + external_sensor_temperature = ZCLAttributeDef( + id=EXTERNAL_SENSOR_TEMP_ATTR_ID, type=t.int16s, is_manufacturer_specific=True, ) + valve_state = ZCLAttributeDef( + id=VALVE_STATE_ATTR_ID, + type=State, + is_manufacturer_specific=True, + ) + + actuator_type = ZCLAttributeDef( + id=ACTUATOR_TYPE_ATTR_ID, + type=BoschActuatorType, + is_manufacturer_specific=True, + ) + + sensor_connection = ZCLAttributeDef( + id=SENSOR_CONNECTION_ATTR_ID, + type=BoschSensorConnection, + is_manufacturer_specific=True, + ) + class BoschUserInterfaceCluster(CustomCluster, UserInterface): """Bosch UserInterface cluster.""" @@ -118,6 +198,12 @@ class AttributeDefs(UserInterface.AttributeDefs): is_manufacturer_specific=True, ) + valve_status_led = ZCLAttributeDef( + id=VALVE_STATUS_LED_ATTR_ID, + type=BoschValveStatusLed, + is_manufacturer_specific=True, + ) + ( QuirkBuilder("Bosch", "RBSH-RTH0-ZB-EU") @@ -133,6 +219,24 @@ class AttributeDefs(UserInterface.AttributeDefs): translation_key="valve_duty_cycle", fallback_name="Valve duty cycle", ) + # Valve state (open/closed). + .sensor( + BoschThermostatCluster.AttributeDefs.valve_state.name, + BoschThermostatCluster.cluster_id, + translation_key="valve_state", + fallback_name="Valve state", + ) + # External sensor temperature. + # You CAN write to this, but it does not make any sense. + .sensor( + BoschThermostatCluster.AttributeDefs.external_sensor_temperature.name, + BoschThermostatCluster.cluster_id, + unit=UnitOfTemperature.CELSIUS, + multiplier=0.01, + device_class=NumberDeviceClass.TEMPERATURE, + translation_key="external_sensor_temperature", + fallback_name="External sensor temperature", + ) # Operating mode - On/Pause automatically from HVAC mode, Schedule/Manual configured here. .enum( BoschThermostatCluster.AttributeDefs.operating_mode.name, @@ -142,7 +246,25 @@ class AttributeDefs(UserInterface.AttributeDefs): translation_key="operating_mode", fallback_name="Operating mode", ) - # Temperature display type. + # Actuator type config. + .enum( + BoschThermostatCluster.AttributeDefs.actuator_type.name, + BoschActuatorType, + BoschThermostatCluster.cluster_id, + entity_type=EntityType.CONFIG, + translation_key="actuator_type", + fallback_name="Actuator type", + ) + # External sensor config. + .enum( + BoschThermostatCluster.AttributeDefs.sensor_connection.name, + BoschSensorConnection, + BoschThermostatCluster.cluster_id, + entity_type=EntityType.CONFIG, + translation_key="sensor_connection", + fallback_name="Sensor connection", + ) + # Temperature display mode. .enum( BoschUserInterfaceCluster.AttributeDefs.temperature_display_mode.name, TemperatureDisplayMode, @@ -158,6 +280,33 @@ class AttributeDefs(UserInterface.AttributeDefs): translation_key="boost_heating", fallback_name="Boost heating", ) + # Cooling setpoint limits. + .number( + BoschThermostatCluster.AttributeDefs.min_cool_setpoint_limit.name, + BoschThermostatCluster.cluster_id, + min_value=-500, + max_value=3000, + step=0.5, + unit=UnitOfTemperature.CELSIUS, + multiplier=0.01, + entity_type=EntityType.CONFIG, + device_class=NumberDeviceClass.TEMPERATURE, + translation_key="min_cool_setpoint_limit", + fallback_name="Min cool setpoint limit", + ) + .number( + BoschThermostatCluster.AttributeDefs.max_cool_setpoint_limit.name, + BoschThermostatCluster.cluster_id, + min_value=-500, + max_value=3000, + step=0.5, + unit=UnitOfTemperature.CELSIUS, + multiplier=0.01, + entity_type=EntityType.CONFIG, + device_class=NumberDeviceClass.TEMPERATURE, + translation_key="max_cool_setpoint_limit", + fallback_name="Max cool setpoint limit", + ) # Window open switch: manually set or through an automation. .switch( BoschThermostatCluster.AttributeDefs.window_open.name, @@ -185,19 +334,28 @@ class AttributeDefs(UserInterface.AttributeDefs): translation_key="display_brightness", fallback_name="Display brightness", ) + # Valve status LED config. + .enum( + BoschUserInterfaceCluster.AttributeDefs.valve_status_led.name, + BoschValveStatusLed, + BoschUserInterfaceCluster.cluster_id, + entity_type=EntityType.CONFIG, + translation_key="valve_status_led", + fallback_name="Valve status LED", + ) # Input for displaying outdoor temperature in the corner of the screen. .number( - BoschThermostatCluster.AttributeDefs.outdoor_temperature_input.name, + BoschThermostatCluster.AttributeDefs.outdoor_temperature.name, BoschThermostatCluster.cluster_id, min_value=-32768, max_value=32767, - step=1, + step=0.1, unit=UnitOfTemperature.CELSIUS, multiplier=0.01, entity_type=EntityType.CONFIG, device_class=NumberDeviceClass.TEMPERATURE, - translation_key="outdoor_temperature_input", - fallback_name="Outdoor temperature input", + translation_key="outdoor_temperature", + fallback_name="Outdoor temperature", ) .add_to_registry() ) From 90759c1e6055f322b40827f95acfd13b8efcd13c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 20 Sep 2025 09:24:17 +0000 Subject: [PATCH 06/16] Apply pre-commit auto fixes --- zhaquirks/bosch/rbsh_rth0_zb_eu.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zhaquirks/bosch/rbsh_rth0_zb_eu.py b/zhaquirks/bosch/rbsh_rth0_zb_eu.py index 4d4eb98c89..42fa5aeffb 100644 --- a/zhaquirks/bosch/rbsh_rth0_zb_eu.py +++ b/zhaquirks/bosch/rbsh_rth0_zb_eu.py @@ -102,8 +102,8 @@ class BoschSensorConnection(t.enum8): """Sensor connection setting (for external 10K NTC sensor on S1/S2)""" NotUsed = 0x00 - WithoutRegulation = 0xb0 - WithRegulation = 0xb1 + WithoutRegulation = 0xB0 + WithRegulation = 0xB1 class BoschThermostatCluster(CustomCluster, Thermostat): From 58b5f9dd59167f56045abcc1c60154c0d692609c Mon Sep 17 00:00:00 2001 From: Sopsy <721951+Sopsy@users.noreply.github.com> Date: Sat, 20 Sep 2025 12:29:35 +0300 Subject: [PATCH 07/16] Ruff fixes --- zhaquirks/bosch/rbsh_rth0_zb_eu.py | 43 ++++++++++++++++-------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/zhaquirks/bosch/rbsh_rth0_zb_eu.py b/zhaquirks/bosch/rbsh_rth0_zb_eu.py index 42fa5aeffb..99a143d11b 100644 --- a/zhaquirks/bosch/rbsh_rth0_zb_eu.py +++ b/zhaquirks/bosch/rbsh_rth0_zb_eu.py @@ -1,23 +1,5 @@ """Device handler for Bosch RBSH-RTH0-ZB-EU thermostat.""" -""" -There are some more undocumented values that have not been figured out what they do. -In Thermostat cluster: -0x4023: Valid values 0-7 -0x4024: Valid values 0-23 -0x4025: Valid values 0-100 -0x4050: Valid values 5-10 -0x405b: Valid values 0-255 -0x4063: Valid values 0-3 (turns on display when changed, probably the UFH/Boiler/Radiator setting, but the values are unknown) - -In UserInterface cluster: -0x4032: Valid values 0-15 -0x406a: Valid values 0-255 -0x406b: Valid values 0-255 -0x406c: Valid values 0-255 -0x406d: Valid values 0-255 -""" - from zigpy.quirks import CustomCluster from zigpy.quirks.v2 import QuirkBuilder from zigpy.quirks.v2.homeassistant import PERCENTAGE, EntityType, UnitOfTemperature @@ -56,6 +38,17 @@ # External sensor connection config. SENSOR_CONNECTION_ATTR_ID = 0x4062 +""" +There are some more undocumented attributes that have not been figured out what they do. + +0x4023: Valid range 0-7 +0x4024: Valid range 0-23 +0x4025: Valid range 0-100 +0x4050: Valid range 5-10 +0x405b: Valid range 0-255 +0x4063: Valid range 0-3 (turns on display when changed, probably the UFH/Boiler/Radiator setting, but the values are unknown) +""" + """Bosch specific user interface attribute ids.""" # Valve status LED config. @@ -67,6 +60,16 @@ # Display brightness (0 - 10). SCREEN_BRIGHTNESS_ATTR_ID = 0x403B +""" +More undocumented and unknown attributes in the UserInterface cluster. + +0x4032: Valid range 0-15 +0x406a: Valid range 0-255 +0x406b: Valid range 0-255 +0x406c: Valid range 0-255 +0x406d: Valid range 0-255 +""" + class BoschOperatingMode(t.enum8): """Bosch operating mode attribute values.""" @@ -91,7 +94,7 @@ class BoschActuatorType(t.enum8): class BoschValveStatusLed(t.enum8): - """Valve status LED (dot next to heat/cool icon) functionality""" + """Valve status LED (dot next to heat/cool icon) functionality.""" AlwaysOff = 0x00 Normal = 0x01 @@ -99,7 +102,7 @@ class BoschValveStatusLed(t.enum8): class BoschSensorConnection(t.enum8): - """Sensor connection setting (for external 10K NTC sensor on S1/S2)""" + """Sensor connection setting (for external 10K NTC sensor on S1/S2).""" NotUsed = 0x00 WithoutRegulation = 0xB0 From 425038298493e4228280c263bed453bef85d5599 Mon Sep 17 00:00:00 2001 From: Sopsy <721951+Sopsy@users.noreply.github.com> Date: Sat, 20 Sep 2025 13:52:03 +0300 Subject: [PATCH 08/16] Added missing reporting --- zhaquirks/bosch/rbsh_rth0_zb_eu.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/zhaquirks/bosch/rbsh_rth0_zb_eu.py b/zhaquirks/bosch/rbsh_rth0_zb_eu.py index 99a143d11b..b9dcfa34f0 100644 --- a/zhaquirks/bosch/rbsh_rth0_zb_eu.py +++ b/zhaquirks/bosch/rbsh_rth0_zb_eu.py @@ -228,6 +228,9 @@ class AttributeDefs(UserInterface.AttributeDefs): BoschThermostatCluster.cluster_id, translation_key="valve_state", fallback_name="Valve state", + reporting_config=ReportingConfig( + min_interval=30, max_interval=900, reportable_change=1 + ), ) # External sensor temperature. # You CAN write to this, but it does not make any sense. @@ -239,6 +242,9 @@ class AttributeDefs(UserInterface.AttributeDefs): device_class=NumberDeviceClass.TEMPERATURE, translation_key="external_sensor_temperature", fallback_name="External sensor temperature", + reporting_config=ReportingConfig( + min_interval=30, max_interval=900, reportable_change=5 + ), ) # Operating mode - On/Pause automatically from HVAC mode, Schedule/Manual configured here. .enum( @@ -359,6 +365,9 @@ class AttributeDefs(UserInterface.AttributeDefs): device_class=NumberDeviceClass.TEMPERATURE, translation_key="outdoor_temperature", fallback_name="Outdoor temperature", + reporting_config=ReportingConfig( + min_interval=30, max_interval=900, reportable_change=5 + ), ) .add_to_registry() ) From 6db4d6278da52e3f38f5d7c0e7ca17bd080bf4d9 Mon Sep 17 00:00:00 2001 From: Sopsy <721951+Sopsy@users.noreply.github.com> Date: Sat, 20 Sep 2025 13:53:16 +0300 Subject: [PATCH 09/16] Added missing reporting --- zhaquirks/bosch/rbsh_rth0_zb_eu.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zhaquirks/bosch/rbsh_rth0_zb_eu.py b/zhaquirks/bosch/rbsh_rth0_zb_eu.py index b9dcfa34f0..3bf552d0da 100644 --- a/zhaquirks/bosch/rbsh_rth0_zb_eu.py +++ b/zhaquirks/bosch/rbsh_rth0_zb_eu.py @@ -1,7 +1,7 @@ """Device handler for Bosch RBSH-RTH0-ZB-EU thermostat.""" from zigpy.quirks import CustomCluster -from zigpy.quirks.v2 import QuirkBuilder +from zigpy.quirks.v2 import QuirkBuilder, ReportingConfig from zigpy.quirks.v2.homeassistant import PERCENTAGE, EntityType, UnitOfTemperature from zigpy.quirks.v2.homeassistant.number import NumberDeviceClass from zigpy.quirks.v2.homeassistant.sensor import SensorStateClass From d70f04a6ee498fcc417242545f6a9d1bc3e4da0c Mon Sep 17 00:00:00 2001 From: Sopsy <721951+Sopsy@users.noreply.github.com> Date: Sat, 20 Sep 2025 16:43:22 +0300 Subject: [PATCH 10/16] Slight optimizations and add access values --- zhaquirks/bosch/rbsh_rth0_zb_eu.py | 45 ++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/zhaquirks/bosch/rbsh_rth0_zb_eu.py b/zhaquirks/bosch/rbsh_rth0_zb_eu.py index 3bf552d0da..01b0d52f4a 100644 --- a/zhaquirks/bosch/rbsh_rth0_zb_eu.py +++ b/zhaquirks/bosch/rbsh_rth0_zb_eu.py @@ -5,6 +5,7 @@ from zigpy.quirks.v2.homeassistant import PERCENTAGE, EntityType, UnitOfTemperature from zigpy.quirks.v2.homeassistant.number import NumberDeviceClass from zigpy.quirks.v2.homeassistant.sensor import SensorStateClass +from zigpy.quirks.v2.homeassistant.binary_sensor import BinarySensorDeviceClass import zigpy.types as t from zigpy.zcl.clusters.hvac import TemperatureDisplayMode, Thermostat, UserInterface from zigpy.zcl.foundation import ZCLAttributeDef @@ -123,6 +124,7 @@ class AttributeDefs(Thermostat.AttributeDefs): id=OPERATING_MODE_ATTR_ID, type=BoschOperatingMode, is_manufacturer_specific=True, + access="rwp", ) valve_duty_cycle = ZCLAttributeDef( @@ -130,54 +132,56 @@ class AttributeDefs(Thermostat.AttributeDefs): # Values range from 0-100 type=t.uint8_t, is_manufacturer_specific=True, + access="rwp", + ) + + valve_state = ZCLAttributeDef( + id=VALVE_STATE_ATTR_ID, + type=State, + is_manufacturer_specific=True, + access="rwp", ) window_open = ZCLAttributeDef( id=WINDOW_OPEN_ATTR_ID, type=State, is_manufacturer_specific=True, + access="rwp", ) boost_heating = ZCLAttributeDef( id=BOOST_HEATING_ATTR_ID, type=State, is_manufacturer_specific=True, - ) - - temperature_display_mode = ZCLAttributeDef( - id=0x0000, - type=TemperatureDisplayMode, - access="rw", + access="rwp", ) outdoor_temperature = ZCLAttributeDef( id=OUTDOOR_TEMP_ATTR_ID, type=t.int16s, is_manufacturer_specific=True, + access="rwp", ) external_sensor_temperature = ZCLAttributeDef( id=EXTERNAL_SENSOR_TEMP_ATTR_ID, type=t.int16s, is_manufacturer_specific=True, - ) - - valve_state = ZCLAttributeDef( - id=VALVE_STATE_ATTR_ID, - type=State, - is_manufacturer_specific=True, + access="rwp", ) actuator_type = ZCLAttributeDef( id=ACTUATOR_TYPE_ATTR_ID, type=BoschActuatorType, is_manufacturer_specific=True, + access="rwp", ) sensor_connection = ZCLAttributeDef( id=SENSOR_CONNECTION_ATTR_ID, type=BoschSensorConnection, is_manufacturer_specific=True, + access="rwp", ) @@ -187,11 +191,18 @@ class BoschUserInterfaceCluster(CustomCluster, UserInterface): class AttributeDefs(UserInterface.AttributeDefs): """Bosch user interface manufacturer specific attributes.""" + temperature_display_mode = ZCLAttributeDef( + id=0x0000, + type=TemperatureDisplayMode, + access="rwp", + ) + display_on_time = ZCLAttributeDef( id=SCREEN_TIMEOUT_ATTR_ID, # Usable values range from 5-30 type=t.enum8, is_manufacturer_specific=True, + access="rwp", ) display_brightness = ZCLAttributeDef( @@ -199,12 +210,14 @@ class AttributeDefs(UserInterface.AttributeDefs): # Values range from 0-10 type=t.enum8, is_manufacturer_specific=True, + access="rwp", ) valve_status_led = ZCLAttributeDef( id=VALVE_STATUS_LED_ATTR_ID, type=BoschValveStatusLed, is_manufacturer_specific=True, + access="rwp", ) @@ -217,15 +230,17 @@ class AttributeDefs(UserInterface.AttributeDefs): .sensor( BoschThermostatCluster.AttributeDefs.valve_duty_cycle.name, BoschThermostatCluster.cluster_id, + entity_type=EntityType.DIAGNOSTIC, state_class=SensorStateClass.MEASUREMENT, unit=PERCENTAGE, translation_key="valve_duty_cycle", fallback_name="Valve duty cycle", ) # Valve state (open/closed). - .sensor( + .binary_sensor( BoschThermostatCluster.AttributeDefs.valve_state.name, BoschThermostatCluster.cluster_id, + device_class=BinarySensorDeviceClass.RUNNING, translation_key="valve_state", fallback_name="Valve state", reporting_config=ReportingConfig( @@ -243,7 +258,7 @@ class AttributeDefs(UserInterface.AttributeDefs): translation_key="external_sensor_temperature", fallback_name="External sensor temperature", reporting_config=ReportingConfig( - min_interval=30, max_interval=900, reportable_change=5 + min_interval=30, max_interval=900, reportable_change=25 ), ) # Operating mode - On/Pause automatically from HVAC mode, Schedule/Manual configured here. @@ -366,7 +381,7 @@ class AttributeDefs(UserInterface.AttributeDefs): translation_key="outdoor_temperature", fallback_name="Outdoor temperature", reporting_config=ReportingConfig( - min_interval=30, max_interval=900, reportable_change=5 + min_interval=30, max_interval=900, reportable_change=25 ), ) .add_to_registry() From 9b9a72de5f338cb32a6352f5f5e46ae61fa508fa Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 20 Sep 2025 13:43:46 +0000 Subject: [PATCH 11/16] Apply pre-commit auto fixes --- zhaquirks/bosch/rbsh_rth0_zb_eu.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zhaquirks/bosch/rbsh_rth0_zb_eu.py b/zhaquirks/bosch/rbsh_rth0_zb_eu.py index 01b0d52f4a..a0fc42fced 100644 --- a/zhaquirks/bosch/rbsh_rth0_zb_eu.py +++ b/zhaquirks/bosch/rbsh_rth0_zb_eu.py @@ -3,9 +3,9 @@ from zigpy.quirks import CustomCluster from zigpy.quirks.v2 import QuirkBuilder, ReportingConfig from zigpy.quirks.v2.homeassistant import PERCENTAGE, EntityType, UnitOfTemperature +from zigpy.quirks.v2.homeassistant.binary_sensor import BinarySensorDeviceClass from zigpy.quirks.v2.homeassistant.number import NumberDeviceClass from zigpy.quirks.v2.homeassistant.sensor import SensorStateClass -from zigpy.quirks.v2.homeassistant.binary_sensor import BinarySensorDeviceClass import zigpy.types as t from zigpy.zcl.clusters.hvac import TemperatureDisplayMode, Thermostat, UserInterface from zigpy.zcl.foundation import ZCLAttributeDef From 0bfee1bba3f980c8d57ca2fa6837df6f0d8c3174 Mon Sep 17 00:00:00 2001 From: Sopsy <721951+Sopsy@users.noreply.github.com> Date: Mon, 6 Oct 2025 13:36:48 +0300 Subject: [PATCH 12/16] Added heater type and error code, slight changes as per new research --- zhaquirks/bosch/rbsh_rth0_zb_eu.py | 133 ++++++++++++++++++++++------- 1 file changed, 102 insertions(+), 31 deletions(-) diff --git a/zhaquirks/bosch/rbsh_rth0_zb_eu.py b/zhaquirks/bosch/rbsh_rth0_zb_eu.py index a0fc42fced..5ade150a25 100644 --- a/zhaquirks/bosch/rbsh_rth0_zb_eu.py +++ b/zhaquirks/bosch/rbsh_rth0_zb_eu.py @@ -15,8 +15,8 @@ # Mode of operation with values BoschOperatingMode. OPERATING_MODE_ATTR_ID = 0x4007 -# Valve duty cycle: 0% - 100%. -VALVE_DUTY_CYCLE_ATTR_ID = 0x4020 +# Heating demand (valve duty cycle / PWM output): 0% - 100%. +HEATING_DEMAND_ATTR_ID = 0x4020 # Valve state (relay on/off). VALVE_STATE_ATTR_ID = 0x4022 @@ -31,7 +31,7 @@ OUTDOOR_TEMP_ATTR_ID = 0x4051 # External sensor (S1/S2 10K NTC) temperature. -EXTERNAL_SENSOR_TEMP_ATTR_ID = 0x4052 +EXTERNAL_TEMP_ATTR_ID = 0x4052 # Actuator type setting (NO/NC). ACTUATOR_TYPE_ATTR_ID = 0x4060 @@ -39,15 +39,21 @@ # External sensor connection config. SENSOR_CONNECTION_ATTR_ID = 0x4062 +# Heater type (UFH, boiler, radiator, central heating). +HEATER_TYPE_ATTR_ID = 0x4063 + +# Error codes (these do not match the E**-codes displayed on the screen). +ERROR_CODE_ATTR_ID = 0x5000 + """ There are some more undocumented attributes that have not been figured out what they do. -0x4023: Valid range 0-7 -0x4024: Valid range 0-23 -0x4025: Valid range 0-100 -0x4050: Valid range 5-10 -0x405b: Valid range 0-255 -0x4063: Valid range 0-3 (turns on display when changed, probably the UFH/Boiler/Radiator setting, but the values are unknown) +0x4023: R/W. Valid range 0-7. +0x4024: R/W. Valid range 0-23. +0x4025: R/W. Valid range 0-100. Changes depending on heater type (0x00: 0x03, 0x01: 0x01, 0x02: 0x02) +0x4050: R/W. Valid range 5-10. +0x405b: R/W. Valid range 0-255. +0x4061: Read-only. Changes depending on heater type (0x00: 0x14, 0x01: 0x06, 0x02: 0x0a) """ """Bosch specific user interface attribute ids.""" @@ -64,11 +70,11 @@ """ More undocumented and unknown attributes in the UserInterface cluster. -0x4032: Valid range 0-15 -0x406a: Valid range 0-255 -0x406b: Valid range 0-255 -0x406c: Valid range 0-255 -0x406d: Valid range 0-255 +0x4032: R/W. Valid range 0-15. +0x406a: R/W. Valid range 0-255. +0x406b: R/W. Valid range 0-255. +0x406c: R/W. Valid range 0-255. Changes depending on heater type (0x00: 0x00, 0x02: 0x05) +0x406d: R/W. Valid range 0-255. """ @@ -94,16 +100,31 @@ class BoschActuatorType(t.enum8): NormallyOpen = 0x01 +class BoschHeaterType(t.enum8): + """ + Heater type: + 1. Underfloor heating (230V) + 2. Boiler (potential free) + 3. Radiator (radio only) + 4. Central Heating (relay disconnected, controlled externally). + """ + + UnderfloorHeating = 0x00 + Boiler = 0x01 + Radiator = 0x02 + CentralHeating = 0x03 + + class BoschValveStatusLed(t.enum8): """Valve status LED (dot next to heat/cool icon) functionality.""" - AlwaysOff = 0x00 + Off = 0x00 Normal = 0x01 - AlwaysOn = 0x02 + On = 0x02 class BoschSensorConnection(t.enum8): - """Sensor connection setting (for external 10K NTC sensor on S1/S2).""" + """Sensor connection setting (for an external 10K NTC sensor on S1/S2).""" NotUsed = 0x00 WithoutRegulation = 0xB0 @@ -127,8 +148,8 @@ class AttributeDefs(Thermostat.AttributeDefs): access="rwp", ) - valve_duty_cycle = ZCLAttributeDef( - id=VALVE_DUTY_CYCLE_ATTR_ID, + heating_demand = ZCLAttributeDef( + id=HEATING_DEMAND_ATTR_ID, # Values range from 0-100 type=t.uint8_t, is_manufacturer_specific=True, @@ -163,13 +184,20 @@ class AttributeDefs(Thermostat.AttributeDefs): access="rwp", ) - external_sensor_temperature = ZCLAttributeDef( - id=EXTERNAL_SENSOR_TEMP_ATTR_ID, + external_temperature = ZCLAttributeDef( + id=EXTERNAL_TEMP_ATTR_ID, type=t.int16s, is_manufacturer_specific=True, access="rwp", ) + heater_type = ZCLAttributeDef( + id=HEATER_TYPE_ATTR_ID, + type=BoschHeaterType, + is_manufacturer_specific=True, + access="rwp", + ) + actuator_type = ZCLAttributeDef( id=ACTUATOR_TYPE_ATTR_ID, type=BoschActuatorType, @@ -184,6 +212,13 @@ class AttributeDefs(Thermostat.AttributeDefs): access="rwp", ) + error_code = ZCLAttributeDef( + id=ERROR_CODE_ATTR_ID, + type=t.bitmap8, + is_manufacturer_specific=True, + access="rwp", + ) + class BoschUserInterfaceCluster(CustomCluster, UserInterface): """Bosch UserInterface cluster.""" @@ -226,15 +261,18 @@ class AttributeDefs(UserInterface.AttributeDefs): .applies_to("Bosch", "RBSH-RTH0-BAT-ZB-EU") .replaces(BoschThermostatCluster) .replaces(BoschUserInterfaceCluster) - # Valve duty cycle, PWM controlled. + # Heating demand, either valve duty cycle or PWM output. .sensor( - BoschThermostatCluster.AttributeDefs.valve_duty_cycle.name, + BoschThermostatCluster.AttributeDefs.heating_demand.name, BoschThermostatCluster.cluster_id, entity_type=EntityType.DIAGNOSTIC, state_class=SensorStateClass.MEASUREMENT, unit=PERCENTAGE, - translation_key="valve_duty_cycle", - fallback_name="Valve duty cycle", + translation_key="heating_demand", + fallback_name="Heating demand", + reporting_config=ReportingConfig( + min_interval=1, max_interval=900, reportable_change=1 + ), ) # Valve state (open/closed). .binary_sensor( @@ -244,19 +282,32 @@ class AttributeDefs(UserInterface.AttributeDefs): translation_key="valve_state", fallback_name="Valve state", reporting_config=ReportingConfig( - min_interval=30, max_interval=900, reportable_change=1 + min_interval=1, max_interval=900, reportable_change=1 ), ) - # External sensor temperature. + # Local temperature. + .sensor( + BoschThermostatCluster.AttributeDefs.local_temperature.name, + BoschThermostatCluster.cluster_id, + unit=UnitOfTemperature.CELSIUS, + multiplier=0.01, + device_class=NumberDeviceClass.TEMPERATURE, + translation_key="local_temperature", + fallback_name="Local temperature", + reporting_config=ReportingConfig( + min_interval=30, max_interval=900, reportable_change=25 + ), + ) + # External temperature. # You CAN write to this, but it does not make any sense. .sensor( - BoschThermostatCluster.AttributeDefs.external_sensor_temperature.name, + BoschThermostatCluster.AttributeDefs.external_temperature.name, BoschThermostatCluster.cluster_id, unit=UnitOfTemperature.CELSIUS, multiplier=0.01, device_class=NumberDeviceClass.TEMPERATURE, - translation_key="external_sensor_temperature", - fallback_name="External sensor temperature", + translation_key="external_temperature", + fallback_name="External temperature", reporting_config=ReportingConfig( min_interval=30, max_interval=900, reportable_change=25 ), @@ -270,6 +321,15 @@ class AttributeDefs(UserInterface.AttributeDefs): translation_key="operating_mode", fallback_name="Operating mode", ) + # Heater type config. + .enum( + BoschThermostatCluster.AttributeDefs.heater_type.name, + BoschHeaterType, + BoschThermostatCluster.cluster_id, + entity_type=EntityType.CONFIG, + translation_key="heater_type", + fallback_name="Heater type", + ) # Actuator type config. .enum( BoschThermostatCluster.AttributeDefs.actuator_type.name, @@ -297,7 +357,7 @@ class AttributeDefs(UserInterface.AttributeDefs): translation_key="temperature_display_mode", fallback_name="Temperature display mode", ) - # Fast heating/boost - Only works with Heater type: Radiator. + # Fast heating/boost - Only works with Heater type: Radiator and when heating. .switch( BoschThermostatCluster.AttributeDefs.boost_heating.name, BoschThermostatCluster.cluster_id, @@ -384,5 +444,16 @@ class AttributeDefs(UserInterface.AttributeDefs): min_interval=30, max_interval=900, reportable_change=25 ), ) + # Error codes + .sensor( + BoschThermostatCluster.AttributeDefs.error_code.name, + BoschThermostatCluster.cluster_id, + entity_type=EntityType.DIAGNOSTIC, + translation_key="error_code", + fallback_name="Error code", + reporting_config=ReportingConfig( + min_interval=1, max_interval=900, reportable_change=1 + ), + ) .add_to_registry() ) From 8f2c2e8494239028e59e3a307997e37e17cf9d76 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 6 Oct 2025 10:37:15 +0000 Subject: [PATCH 13/16] Apply pre-commit auto fixes --- zhaquirks/bosch/rbsh_rth0_zb_eu.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/zhaquirks/bosch/rbsh_rth0_zb_eu.py b/zhaquirks/bosch/rbsh_rth0_zb_eu.py index 5ade150a25..3ded8f71d0 100644 --- a/zhaquirks/bosch/rbsh_rth0_zb_eu.py +++ b/zhaquirks/bosch/rbsh_rth0_zb_eu.py @@ -101,8 +101,7 @@ class BoschActuatorType(t.enum8): class BoschHeaterType(t.enum8): - """ - Heater type: + """Heater type: 1. Underfloor heating (230V) 2. Boiler (potential free) 3. Radiator (radio only) From 5b76db69978a2dc8e12def23956ed235e8ec4e6a Mon Sep 17 00:00:00 2001 From: Sopsy <721951+Sopsy@users.noreply.github.com> Date: Mon, 6 Oct 2025 13:40:19 +0300 Subject: [PATCH 14/16] Insert single blank line --- zhaquirks/bosch/rbsh_rth0_zb_eu.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/zhaquirks/bosch/rbsh_rth0_zb_eu.py b/zhaquirks/bosch/rbsh_rth0_zb_eu.py index 3ded8f71d0..762328e5c1 100644 --- a/zhaquirks/bosch/rbsh_rth0_zb_eu.py +++ b/zhaquirks/bosch/rbsh_rth0_zb_eu.py @@ -101,7 +101,9 @@ class BoschActuatorType(t.enum8): class BoschHeaterType(t.enum8): - """Heater type: + """ + Heater type: + 1. Underfloor heating (230V) 2. Boiler (potential free) 3. Radiator (radio only) From af522690a9373794935ef3e1006631284e493d94 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 6 Oct 2025 10:41:07 +0000 Subject: [PATCH 15/16] Apply pre-commit auto fixes --- zhaquirks/bosch/rbsh_rth0_zb_eu.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/zhaquirks/bosch/rbsh_rth0_zb_eu.py b/zhaquirks/bosch/rbsh_rth0_zb_eu.py index 762328e5c1..9c0c09ac35 100644 --- a/zhaquirks/bosch/rbsh_rth0_zb_eu.py +++ b/zhaquirks/bosch/rbsh_rth0_zb_eu.py @@ -101,8 +101,7 @@ class BoschActuatorType(t.enum8): class BoschHeaterType(t.enum8): - """ - Heater type: + """Heater type: 1. Underfloor heating (230V) 2. Boiler (potential free) From 61062a89ae2ccf7b711a6378d51647f149134f56 Mon Sep 17 00:00:00 2001 From: Sopsy <721951+Sopsy@users.noreply.github.com> Date: Mon, 6 Oct 2025 14:01:11 +0300 Subject: [PATCH 16/16] Now ruff should be happy. At least locally is. --- zhaquirks/bosch/rbsh_rth0_zb_eu.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/zhaquirks/bosch/rbsh_rth0_zb_eu.py b/zhaquirks/bosch/rbsh_rth0_zb_eu.py index 9c0c09ac35..8b8c92e041 100644 --- a/zhaquirks/bosch/rbsh_rth0_zb_eu.py +++ b/zhaquirks/bosch/rbsh_rth0_zb_eu.py @@ -101,13 +101,7 @@ class BoschActuatorType(t.enum8): class BoschHeaterType(t.enum8): - """Heater type: - - 1. Underfloor heating (230V) - 2. Boiler (potential free) - 3. Radiator (radio only) - 4. Central Heating (relay disconnected, controlled externally). - """ + """Heater type: Underfloor heating (230V), Boiler (potential free), Radiator (radio only), Central Heating (relay disconnected, controlled externally).""" UnderfloorHeating = 0x00 Boiler = 0x01 @@ -444,7 +438,7 @@ class AttributeDefs(UserInterface.AttributeDefs): min_interval=30, max_interval=900, reportable_change=25 ), ) - # Error codes + # Error codes. .sensor( BoschThermostatCluster.AttributeDefs.error_code.name, BoschThermostatCluster.cluster_id,