From 81a8daee980f5bb08c6dbbb2a8d43b5fc039ae15 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Tue, 2 Jun 2026 14:57:09 -0400 Subject: [PATCH 1/6] Fix tests polluting the real quirks device registry --- tests/test_switch.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/tests/test_switch.py b/tests/test_switch.py index bf37af7cb..e3005cdcf 100644 --- a/tests/test_switch.py +++ b/tests/test_switch.py @@ -14,7 +14,7 @@ ) from zigpy.exceptions import ZigbeeException from zigpy.profiles import zha -from zigpy.quirks import DEVICE_REGISTRY, CustomCluster, CustomDevice +from zigpy.quirks import CustomCluster, CustomDevice, DeviceRegistry from zigpy.quirks.v2 import CustomDeviceV2, QuirkBuilder import zigpy.types as t from zigpy.typing import UNDEFINED @@ -495,8 +495,9 @@ async def test_switch_configurable_custom_on_off_values(zha_gateway: Gateway) -> model="model", ) + registry = DeviceRegistry() ( - QuirkBuilder(zigpy_dev.manufacturer, zigpy_dev.model) + QuirkBuilder(zigpy_dev.manufacturer, zigpy_dev.model, registry=registry) .adds(WindowDetectionFunctionQuirk.TuyaManufCluster) .switch( "window_detection_function", @@ -509,7 +510,7 @@ async def test_switch_configurable_custom_on_off_values(zha_gateway: Gateway) -> .add_to_registry() ) - zigpy_device_ = DEVICE_REGISTRY.get_device(zigpy_dev) + zigpy_device_ = registry.get_device(zigpy_dev) assert isinstance(zigpy_device_, CustomDeviceV2) cluster = zigpy_device_.endpoints[1].tuya_manufacturer @@ -575,8 +576,9 @@ async def test_switch_configurable_custom_on_off_values_force_inverted( model="model2", ) + registry = DeviceRegistry() ( - QuirkBuilder(zigpy_dev.manufacturer, zigpy_dev.model) + QuirkBuilder(zigpy_dev.manufacturer, zigpy_dev.model, registry=registry) .adds(WindowDetectionFunctionQuirk.TuyaManufCluster) .switch( "window_detection_function", @@ -590,7 +592,7 @@ async def test_switch_configurable_custom_on_off_values_force_inverted( .add_to_registry() ) - zigpy_device_ = DEVICE_REGISTRY.get_device(zigpy_dev) + zigpy_device_ = registry.get_device(zigpy_dev) assert isinstance(zigpy_device_, CustomDeviceV2) cluster = zigpy_device_.endpoints[1].tuya_manufacturer @@ -656,8 +658,9 @@ async def test_switch_configurable_custom_on_off_values_inverter_attribute( model="model3", ) + registry = DeviceRegistry() ( - QuirkBuilder(zigpy_dev.manufacturer, zigpy_dev.model) + QuirkBuilder(zigpy_dev.manufacturer, zigpy_dev.model, registry=registry) .adds(WindowDetectionFunctionQuirk.TuyaManufCluster) .switch( "window_detection_function", @@ -671,7 +674,7 @@ async def test_switch_configurable_custom_on_off_values_inverter_attribute( .add_to_registry() ) - zigpy_device_ = DEVICE_REGISTRY.get_device(zigpy_dev) + zigpy_device_ = registry.get_device(zigpy_dev) assert isinstance(zigpy_device_, CustomDeviceV2) cluster = zigpy_device_.endpoints[1].tuya_manufacturer From c9decc9a161d3679f3af784afce6df5da97aa6be Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Tue, 2 Jun 2026 16:57:51 -0400 Subject: [PATCH 2/6] Inline `SINOPE_MODELS` --- zha/application/platforms/select.py | 34 +++++++++++++++++------------ 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/zha/application/platforms/select.py b/zha/application/platforms/select.py index 5b747391d..c3a03c798 100644 --- a/zha/application/platforms/select.py +++ b/zha/application/platforms/select.py @@ -1221,18 +1221,6 @@ class SinopeLightLedColors(types.enum32): Blue = 0xFFFF00 -SINOPE_MODELS = frozenset( - { - "DM2500ZB", - "DM2500ZB-G2", - "DM2550ZB", - "DM2550ZB-G2", - "SW2500ZB", - "SW2500ZB-G2", - } -) - - @register_entity(SINOPE_MANUFACTURER_CLUSTER) class SinopeLightLEDOffColorSelect(ZCLEnumSelectEntity): """Representation of the marker LED Off-state color of Sinope light switches.""" @@ -1245,7 +1233,16 @@ class SinopeLightLEDOffColorSelect(ZCLEnumSelectEntity): _cluster_match = ClusterMatch( server_clusters=frozenset({SINOPE_MANUFACTURER_CLUSTER}), - models=SINOPE_MODELS, + models=frozenset( + { + "DM2500ZB", + "DM2500ZB-G2", + "DM2550ZB", + "DM2550ZB-G2", + "SW2500ZB", + "SW2500ZB-G2", + } + ), ) @@ -1261,7 +1258,16 @@ class SinopeLightLEDOnColorSelect(ZCLEnumSelectEntity): _cluster_match = ClusterMatch( server_clusters=frozenset({SINOPE_MANUFACTURER_CLUSTER}), - models=SINOPE_MODELS, + models=frozenset( + { + "DM2500ZB", + "DM2500ZB-G2", + "DM2550ZB", + "DM2550ZB-G2", + "SW2500ZB", + "SW2500ZB-G2", + } + ), ) From 93955d2c2324c6a4dd9fa44b9fcf70424298b434 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Tue, 2 Jun 2026 17:55:14 -0400 Subject: [PATCH 3/6] Inline climate constants --- zha/application/platforms/climate/__init__.py | 60 +++++++++++-------- zha/application/platforms/climate/const.py | 17 ------ 2 files changed, 34 insertions(+), 43 deletions(-) diff --git a/zha/application/platforms/climate/__init__.py b/zha/application/platforms/climate/__init__.py index 6e5d9a27a..0824eca03 100644 --- a/zha/application/platforms/climate/__init__.py +++ b/zha/application/platforms/climate/__init__.py @@ -37,14 +37,6 @@ register_entity, ) from zha.application.platforms.climate.const import ( - ATTR_OCCP_COOL_SETPT, - ATTR_OCCP_HEAT_SETPT, - ATTR_OCCUPANCY, - ATTR_PI_COOLING_DEMAND, - ATTR_PI_HEATING_DEMAND, - ATTR_SYS_MODE, - ATTR_UNOCCP_COOL_SETPT, - ATTR_UNOCCP_HEAT_SETPT, FAN_AUTO, FAN_ON, HVAC_MODE_2_SYSTEM, @@ -208,14 +200,14 @@ class Thermostat(BaseThermostat): _attr_translation_key: str = "thermostat" _enable_turn_on_off_backwards_compatibility = False _attr_extra_state_attribute_names: set[str] = { - ATTR_SYS_MODE, - ATTR_OCCUPANCY, - ATTR_OCCP_COOL_SETPT, - ATTR_OCCP_HEAT_SETPT, - ATTR_PI_HEATING_DEMAND, - ATTR_PI_COOLING_DEMAND, - ATTR_UNOCCP_COOL_SETPT, - ATTR_UNOCCP_HEAT_SETPT, + ThermostatCluster.AttributeDefs.system_mode.name, + ThermostatCluster.AttributeDefs.occupancy.name, + ThermostatCluster.AttributeDefs.occupied_cooling_setpoint.name, + ThermostatCluster.AttributeDefs.occupied_heating_setpoint.name, + ThermostatCluster.AttributeDefs.pi_heating_demand.name, + ThermostatCluster.AttributeDefs.pi_cooling_demand.name, + ThermostatCluster.AttributeDefs.unoccupied_cooling_setpoint.name, + ThermostatCluster.AttributeDefs.unoccupied_heating_setpoint.name, } _cluster_match = ClusterMatch( @@ -564,18 +556,30 @@ def state(self) -> dict[str, Any]: response = super().state - response[ATTR_SYS_MODE] = ( + response[ThermostatCluster.AttributeDefs.system_mode.name] = ( f"[{self._system_mode}]/{system_mode}" if self.hvac_mode is not None else None ) - response[ATTR_OCCUPANCY] = self._occupancy - response[ATTR_OCCP_COOL_SETPT] = self._occupied_cooling_setpoint - response[ATTR_OCCP_HEAT_SETPT] = self._occupied_heating_setpoint - response[ATTR_PI_HEATING_DEMAND] = self._pi_heating_demand - response[ATTR_PI_COOLING_DEMAND] = self._pi_cooling_demand - response[ATTR_UNOCCP_COOL_SETPT] = self._unoccupied_cooling_setpoint - response[ATTR_UNOCCP_HEAT_SETPT] = self._unoccupied_heating_setpoint + response[ThermostatCluster.AttributeDefs.occupancy.name] = self._occupancy + response[ThermostatCluster.AttributeDefs.occupied_cooling_setpoint.name] = ( + self._occupied_cooling_setpoint + ) + response[ThermostatCluster.AttributeDefs.occupied_heating_setpoint.name] = ( + self._occupied_heating_setpoint + ) + response[ThermostatCluster.AttributeDefs.pi_heating_demand.name] = ( + self._pi_heating_demand + ) + response[ThermostatCluster.AttributeDefs.pi_cooling_demand.name] = ( + self._pi_cooling_demand + ) + response[ThermostatCluster.AttributeDefs.unoccupied_cooling_setpoint.name] = ( + self._unoccupied_cooling_setpoint + ) + response[ThermostatCluster.AttributeDefs.unoccupied_heating_setpoint.name] = ( + self._unoccupied_heating_setpoint + ) return response @property @@ -779,7 +783,11 @@ async def _handle_attribute_updated( ) -> None: """Handle attribute update from device.""" if ( - event.attribute_name in (ATTR_OCCP_COOL_SETPT, ATTR_OCCP_HEAT_SETPT) + event.attribute_name + in ( + ThermostatCluster.AttributeDefs.occupied_cooling_setpoint.name, + ThermostatCluster.AttributeDefs.occupied_heating_setpoint.name, + ) and self.preset_mode == Preset.AWAY and await self._async_get_occupancy() is True ): @@ -1104,7 +1112,7 @@ def state(self) -> dict[str, Any]: response = super().state - response[ATTR_SYS_MODE] = ( + response[ThermostatCluster.AttributeDefs.system_mode.name] = ( f"[{self._system_mode}]/{system_mode}" if self.hvac_mode is not None else None diff --git a/zha/application/platforms/climate/const.py b/zha/application/platforms/climate/const.py index 17fe4d946..d5f6447da 100644 --- a/zha/application/platforms/climate/const.py +++ b/zha/application/platforms/climate/const.py @@ -5,23 +5,6 @@ from zigpy.zcl.clusters.hvac import ControlSequenceOfOperation, RunningMode, SystemMode -ATTR_SYS_MODE: Final[str] = "system_mode" -ATTR_FAN_MODE: Final[str] = "fan_mode" -ATTR_RUNNING_MODE: Final[str] = "running_mode" -ATTR_SETPT_CHANGE_SRC: Final[str] = "setpoint_change_source" -ATTR_SETPT_CHANGE_AMT: Final[str] = "setpoint_change_amount" -ATTR_OCCUPANCY: Final[str] = "occupancy" -ATTR_PI_COOLING_DEMAND: Final[str] = "pi_cooling_demand" -ATTR_PI_HEATING_DEMAND: Final[str] = "pi_heating_demand" -ATTR_OCCP_COOL_SETPT: Final[str] = "occupied_cooling_setpoint" -ATTR_OCCP_HEAT_SETPT: Final[str] = "occupied_heating_setpoint" -ATTR_UNOCCP_HEAT_SETPT: Final[str] = "unoccupied_heating_setpoint" -ATTR_UNOCCP_COOL_SETPT: Final[str] = "unoccupied_cooling_setpoint" -ATTR_HVAC_MODE: Final[str] = "hvac_mode" -ATTR_TARGET_TEMP_HIGH: Final[str] = "target_temp_high" -ATTR_TARGET_TEMP_LOW: Final[str] = "target_temp_low" -ATTR_TEMPERATURE: Final[str] = "temperature" - PRECISION_TENTHS: Final[float] = 0.1 # Possible fan state From a751a3d69d6f73eea71519ce5ffbb85d25cc423a Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Tue, 2 Jun 2026 17:58:49 -0400 Subject: [PATCH 4/6] Drop reporting config for unused entity attributes --- zha/application/platforms/device_tracker.py | 6 -- zha/application/platforms/sensor/__init__.py | 72 -------------------- 2 files changed, 78 deletions(-) diff --git a/zha/application/platforms/device_tracker.py b/zha/application/platforms/device_tracker.py index 0328195e0..19de161cf 100644 --- a/zha/application/platforms/device_tracker.py +++ b/zha/application/platforms/device_tracker.py @@ -100,12 +100,6 @@ class DeviceScannerEntity(BaseDeviceTracker): PowerConfiguration.cluster_id: ClusterConfig( bind=True, attributes={ - PowerConfiguration.AttributeDefs.battery_voltage: AttrConfig( - read_on_startup=False, - reporting=ReportingConfig( - min_interval=3600, max_interval=10800, reportable_change=1 - ), - ), PowerConfiguration.AttributeDefs.battery_percentage_remaining: AttrConfig( read_on_startup=False, reporting=ReportingConfig( diff --git a/zha/application/platforms/sensor/__init__.py b/zha/application/platforms/sensor/__init__.py index b6f415184..598a3a767 100644 --- a/zha/application/platforms/sensor/__init__.py +++ b/zha/application/platforms/sensor/__init__.py @@ -2955,36 +2955,6 @@ class ThermostatHVACAction(Sensor): Thermostat.cluster_id: ClusterConfig( bind=True, attributes={ - Thermostat.AttributeDefs.local_temperature: AttrConfig( - read_on_startup=True, - reporting=ReportingConfig( - min_interval=30, max_interval=900, reportable_change=25 - ), - ), - Thermostat.AttributeDefs.occupied_cooling_setpoint: AttrConfig( - read_on_startup=True, - reporting=ReportingConfig( - min_interval=30, max_interval=900, reportable_change=25 - ), - ), - Thermostat.AttributeDefs.occupied_heating_setpoint: AttrConfig( - read_on_startup=True, - reporting=ReportingConfig( - min_interval=30, max_interval=900, reportable_change=25 - ), - ), - Thermostat.AttributeDefs.unoccupied_cooling_setpoint: AttrConfig( - read_on_startup=True, - reporting=ReportingConfig( - min_interval=30, max_interval=900, reportable_change=25 - ), - ), - Thermostat.AttributeDefs.unoccupied_heating_setpoint: AttrConfig( - read_on_startup=True, - reporting=ReportingConfig( - min_interval=30, max_interval=900, reportable_change=25 - ), - ), Thermostat.AttributeDefs.running_mode: AttrConfig( read_on_startup=True, reporting=ReportingConfig( @@ -3003,12 +2973,6 @@ class ThermostatHVACAction(Sensor): min_interval=30, max_interval=900, reportable_change=1 ), ), - Thermostat.AttributeDefs.occupancy: AttrConfig( - read_on_startup=True, - reporting=ReportingConfig( - min_interval=30, max_interval=900, reportable_change=1 - ), - ), Thermostat.AttributeDefs.pi_cooling_demand: AttrConfig( read_on_startup=True, reporting=ReportingConfig( @@ -3021,42 +2985,6 @@ class ThermostatHVACAction(Sensor): min_interval=30, max_interval=900, reportable_change=5 ), ), - Thermostat.AttributeDefs.abs_min_heat_setpoint_limit: AttrConfig( - read_on_startup=False, - ), - Thermostat.AttributeDefs.abs_max_heat_setpoint_limit: AttrConfig( - read_on_startup=False, - ), - Thermostat.AttributeDefs.abs_min_cool_setpoint_limit: AttrConfig( - read_on_startup=False, - ), - Thermostat.AttributeDefs.abs_max_cool_setpoint_limit: AttrConfig( - read_on_startup=False, - ), - Thermostat.AttributeDefs.ctrl_sequence_of_oper: AttrConfig( - read_on_startup=True, - ), - Thermostat.AttributeDefs.max_cool_setpoint_limit: AttrConfig( - read_on_startup=False, - ), - Thermostat.AttributeDefs.max_heat_setpoint_limit: AttrConfig( - read_on_startup=False, - ), - Thermostat.AttributeDefs.min_cool_setpoint_limit: AttrConfig( - read_on_startup=False, - ), - Thermostat.AttributeDefs.min_heat_setpoint_limit: AttrConfig( - read_on_startup=False, - ), - Thermostat.AttributeDefs.local_temperature_calibration: AttrConfig( - read_on_startup=False, - ), - Thermostat.AttributeDefs.setpoint_change_source: AttrConfig( - read_on_startup=False, - ), - Thermostat.AttributeDefs.setpoint_change_source_timestamp: AttrConfig( - read_on_startup=False, - ), }, ), } From 4e33256aa49a53545d9cad9bc3dcab1ef3db3826 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Tue, 2 Jun 2026 18:01:06 -0400 Subject: [PATCH 5/6] Fold redundant init virtual entities into self-contained entities --- .../platforms/binary_sensor/__init__.py | 101 +++- zha/application/platforms/fan/__init__.py | 44 -- zha/application/platforms/number/__init__.py | 485 +++++++++++++----- zha/application/platforms/select.py | 354 +++++++------ zha/application/platforms/sensor/__init__.py | 257 +++++++--- zha/application/platforms/switch.py | 256 +++++++++ zha/application/platforms/virtual.py | 449 ++++------------ 7 files changed, 1136 insertions(+), 810 deletions(-) diff --git a/zha/application/platforms/binary_sensor/__init__.py b/zha/application/platforms/binary_sensor/__init__.py index 663c7daf5..b519ba7bd 100644 --- a/zha/application/platforms/binary_sensor/__init__.py +++ b/zha/application/platforms/binary_sensor/__init__.py @@ -208,24 +208,6 @@ class Accelerometer(BinarySensor): min_interval=1, max_interval=900, reportable_change=1 ), ), - "x_axis": AttrConfig( - read_on_startup=False, - reporting=ReportingConfig( - min_interval=1, max_interval=900, reportable_change=1 - ), - ), - "y_axis": AttrConfig( - read_on_startup=False, - reporting=ReportingConfig( - min_interval=1, max_interval=900, reportable_change=1 - ), - ), - "z_axis": AttrConfig( - read_on_startup=False, - reporting=ReportingConfig( - min_interval=1, max_interval=900, reportable_change=1 - ), - ), }, ), } @@ -254,12 +236,6 @@ class Occupancy(BinarySensor): min_interval=0, max_interval=900, reportable_change=1 ), ), - OccupancySensing.AttributeDefs.pir_o_to_u_delay: AttrConfig( - read_on_startup=False, - ), - OccupancySensing.AttributeDefs.pir_u_to_o_delay: AttrConfig( - read_on_startup=False, - ), }, ), } @@ -523,6 +499,20 @@ class ReplaceFilter(BinarySensor): server_clusters=frozenset({IKEA_AIR_PURIFIER_CLUSTER}), ) + _server_cluster_config = { + IKEA_AIR_PURIFIER_CLUSTER: ClusterConfig( + bind=True, + attributes={ + "replace_filter": AttrConfig( + read_on_startup=False, + reporting=ReportingConfig( + min_interval=0, max_interval=900, reportable_change=1 + ), + ), + }, + ), + } + @register_entity(AQARA_OPPLE_CLUSTER) class AqaraPetFeederErrorDetected(BinarySensor): @@ -538,6 +528,15 @@ class AqaraPetFeederErrorDetected(BinarySensor): models=frozenset({"aqara.feeder.acn001"}), ) + _server_cluster_config = { + AQARA_OPPLE_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "error_detected": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(AQARA_OPPLE_CLUSTER) class XiaomiPlugConsumerConnected(BinarySensor): @@ -554,6 +553,15 @@ class XiaomiPlugConsumerConnected(BinarySensor): models=frozenset({"lumi.plug.mmeu01", "lumi.plug.maeu01"}), ) + _server_cluster_config = { + AQARA_OPPLE_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "consumer_connected": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(AQARA_OPPLE_CLUSTER) class AqaraThermostatWindowOpen(BinarySensor): @@ -569,6 +577,15 @@ class AqaraThermostatWindowOpen(BinarySensor): models=frozenset({"lumi.airrtc.agl001"}), ) + _server_cluster_config = { + AQARA_OPPLE_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "window_open": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(AQARA_OPPLE_CLUSTER) class AqaraThermostatValveAlarm(BinarySensor): @@ -585,6 +602,15 @@ class AqaraThermostatValveAlarm(BinarySensor): models=frozenset({"lumi.airrtc.agl001"}), ) + _server_cluster_config = { + AQARA_OPPLE_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "valve_alarm": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(AQARA_OPPLE_CLUSTER) class AqaraThermostatCalibrated(BinarySensor): @@ -601,6 +627,15 @@ class AqaraThermostatCalibrated(BinarySensor): models=frozenset({"lumi.airrtc.agl001"}), ) + _server_cluster_config = { + AQARA_OPPLE_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "calibrated": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(AQARA_OPPLE_CLUSTER) class AqaraThermostatExternalSensor(BinarySensor): @@ -617,6 +652,15 @@ class AqaraThermostatExternalSensor(BinarySensor): models=frozenset({"lumi.airrtc.agl001"}), ) + _server_cluster_config = { + AQARA_OPPLE_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "sensor": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(AQARA_OPPLE_CLUSTER) class AqaraLinkageAlarmState(BinarySensor): @@ -649,6 +693,15 @@ class AqaraE1CurtainMotorOpenedByHandBinarySensor(BinarySensor): models=frozenset({"lumi.curtain.agl001"}), ) + _server_cluster_config = { + AQARA_OPPLE_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "hand_open": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(Thermostat.cluster_id) class DanfossMountingModeActive(BinarySensor): diff --git a/zha/application/platforms/fan/__init__.py b/zha/application/platforms/fan/__init__.py index 5f60e4663..e5fbaf584 100644 --- a/zha/application/platforms/fan/__init__.py +++ b/zha/application/platforms/fan/__init__.py @@ -410,48 +410,10 @@ class IkeaFan(BaseFan, PlatformEntity): models=frozenset({"STARKVIND Air purifier", "STARKVIND Air purifier table"}), ) - # Aggregated reporting/bind config for the whole STARKVIND manufacturer - # cluster; sibling ChildLock/DisableLed switches don't need their own. _server_cluster_config = { IKEA_AIR_PURIFIER_CLUSTER: ClusterConfig( bind=True, attributes={ - "filter_run_time": AttrConfig( - read_on_startup=False, - reporting=ReportingConfig( - min_interval=30, max_interval=900, reportable_change=1 - ), - ), - "replace_filter": AttrConfig( - read_on_startup=False, - reporting=ReportingConfig( - min_interval=0, max_interval=900, reportable_change=1 - ), - ), - "filter_life_time": AttrConfig( - read_on_startup=False, - reporting=ReportingConfig( - min_interval=30, max_interval=900, reportable_change=1 - ), - ), - "disable_led": AttrConfig( - read_on_startup=False, - reporting=ReportingConfig( - min_interval=0, max_interval=900, reportable_change=1 - ), - ), - "air_quality_25pm": AttrConfig( - read_on_startup=False, - reporting=ReportingConfig( - min_interval=0, max_interval=900, reportable_change=1 - ), - ), - "child_lock": AttrConfig( - read_on_startup=False, - reporting=ReportingConfig( - min_interval=0, max_interval=900, reportable_change=1 - ), - ), "fan_mode": AttrConfig( read_on_startup=False, reporting=ReportingConfig( @@ -464,12 +426,6 @@ class IkeaFan(BaseFan, PlatformEntity): min_interval=0, max_interval=900, reportable_change=1 ), ), - "device_run_time": AttrConfig( - read_on_startup=False, - reporting=ReportingConfig( - min_interval=30, max_interval=900, reportable_change=1 - ), - ), }, ), } diff --git a/zha/application/platforms/number/__init__.py b/zha/application/platforms/number/__init__.py index 0f437e077..74b90a14f 100644 --- a/zha/application/platforms/number/__init__.py +++ b/zha/application/platforms/number/__init__.py @@ -378,6 +378,15 @@ class AqaraMotionDetectionInterval(NumberConfigurationEntity): models=frozenset({"lumi.motion.ac02", "lumi.motion.agl04"}), ) + _server_cluster_config = { + AQARA_OPPLE_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "detection_interval": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(LevelControl.cluster_id) class OnOffTransitionTimeConfigurationEntity(NumberConfigurationEntity): @@ -396,31 +405,10 @@ class OnOffTransitionTimeConfigurationEntity(NumberConfigurationEntity): _server_cluster_config = { LevelControl.cluster_id: ClusterConfig( - bind=True, + bind=False, attributes={ - LevelControl.AttributeDefs.current_level: AttrConfig( - read_on_startup=True, - reporting=ReportingConfig( - min_interval=1, max_interval=900, reportable_change=1 - ), - ), LevelControl.AttributeDefs.on_off_transition_time: AttrConfig( - read_on_startup=False, - ), - LevelControl.AttributeDefs.on_level: AttrConfig( - read_on_startup=False, - ), - LevelControl.AttributeDefs.on_transition_time: AttrConfig( - read_on_startup=False, - ), - LevelControl.AttributeDefs.off_transition_time: AttrConfig( - read_on_startup=False, - ), - LevelControl.AttributeDefs.default_move_rate: AttrConfig( - read_on_startup=False, - ), - LevelControl.AttributeDefs.start_up_current_level: AttrConfig( - read_on_startup=False, + read_on_startup=False ), }, ), @@ -442,6 +430,15 @@ class OnLevelConfigurationEntity(NumberConfigurationEntity): server_clusters=frozenset({LevelControl.cluster_id}), ) + _server_cluster_config = { + LevelControl.cluster_id: ClusterConfig( + bind=False, + attributes={ + LevelControl.AttributeDefs.on_level: AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(LevelControl.cluster_id) class OnTransitionTimeConfigurationEntity(NumberConfigurationEntity): @@ -458,6 +455,17 @@ class OnTransitionTimeConfigurationEntity(NumberConfigurationEntity): server_clusters=frozenset({LevelControl.cluster_id}), ) + _server_cluster_config = { + LevelControl.cluster_id: ClusterConfig( + bind=False, + attributes={ + LevelControl.AttributeDefs.on_transition_time: AttrConfig( + read_on_startup=False + ), + }, + ), + } + @register_entity(LevelControl.cluster_id) class OffTransitionTimeConfigurationEntity(NumberConfigurationEntity): @@ -474,6 +482,17 @@ class OffTransitionTimeConfigurationEntity(NumberConfigurationEntity): server_clusters=frozenset({LevelControl.cluster_id}), ) + _server_cluster_config = { + LevelControl.cluster_id: ClusterConfig( + bind=False, + attributes={ + LevelControl.AttributeDefs.off_transition_time: AttrConfig( + read_on_startup=False + ), + }, + ), + } + @register_entity(LevelControl.cluster_id) class DefaultMoveRateConfigurationEntity(NumberConfigurationEntity): @@ -491,6 +510,17 @@ class DefaultMoveRateConfigurationEntity(NumberConfigurationEntity): server_clusters=frozenset({LevelControl.cluster_id}), ) + _server_cluster_config = { + LevelControl.cluster_id: ClusterConfig( + bind=False, + attributes={ + LevelControl.AttributeDefs.default_move_rate: AttrConfig( + read_on_startup=False + ), + }, + ), + } + @register_entity(LevelControl.cluster_id) class StartUpCurrentLevelConfigurationEntity(NumberConfigurationEntity): @@ -507,6 +537,17 @@ class StartUpCurrentLevelConfigurationEntity(NumberConfigurationEntity): server_clusters=frozenset({LevelControl.cluster_id}), ) + _server_cluster_config = { + LevelControl.cluster_id: ClusterConfig( + bind=False, + attributes={ + LevelControl.AttributeDefs.start_up_current_level: AttrConfig( + read_on_startup=False + ), + }, + ), + } + @register_entity(Color.cluster_id) class StartUpColorTemperatureConfigurationEntity(NumberConfigurationEntity): @@ -654,19 +695,10 @@ class PIROccupiedToUnoccupiedDelayConfigurationEntity(NumberConfigurationEntity) _server_cluster_config = { OccupancySensing.cluster_id: ClusterConfig( - bind=True, + bind=False, attributes={ - OccupancySensing.AttributeDefs.occupancy: AttrConfig( - read_on_startup=True, - reporting=ReportingConfig( - min_interval=0, max_interval=900, reportable_change=1 - ), - ), OccupancySensing.AttributeDefs.pir_o_to_u_delay: AttrConfig( - read_on_startup=False, - ), - OccupancySensing.AttributeDefs.pir_u_to_o_delay: AttrConfig( - read_on_startup=False, + read_on_startup=False ), }, ), @@ -690,6 +722,17 @@ class PIRUnoccupiedToOccupiedDelayConfigurationEntity(NumberConfigurationEntity) server_clusters=frozenset({OccupancySensing.cluster_id}), ) + _server_cluster_config = { + OccupancySensing.cluster_id: ClusterConfig( + bind=False, + attributes={ + OccupancySensing.AttributeDefs.pir_u_to_o_delay: AttrConfig( + read_on_startup=False + ), + }, + ), + } + @register_entity(OccupancySensing.cluster_id) class SonoffPresenceSenorTimeout(NumberConfigurationEntity): @@ -747,6 +790,20 @@ class FilterLifeTime(NumberConfigurationEntity): server_clusters=frozenset({IKEA_AIR_PURIFIER_CLUSTER}), ) + _server_cluster_config = { + IKEA_AIR_PURIFIER_CLUSTER: ClusterConfig( + bind=True, + attributes={ + "filter_life_time": AttrConfig( + read_on_startup=False, + reporting=ReportingConfig( + min_interval=30, max_interval=900, reportable_change=1 + ), + ), + }, + ), + } + @register_entity(Basic.cluster_id) class TiRouterTransmitPower(NumberConfigurationEntity): @@ -792,6 +849,15 @@ class InovelliRemoteDimmingUpSpeed(NumberConfigurationEntity): server_clusters=frozenset({INOVELLI_CLUSTER}), ) + _server_cluster_config = { + INOVELLI_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "dimming_speed_up_remote": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(INOVELLI_CLUSTER) class InovelliButtonDelay(NumberConfigurationEntity): @@ -810,6 +876,15 @@ class InovelliButtonDelay(NumberConfigurationEntity): server_clusters=frozenset({INOVELLI_CLUSTER}), ) + _server_cluster_config = { + INOVELLI_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "button_delay": AttrConfig(read_on_startup=True), + }, + ), + } + @register_entity(INOVELLI_CLUSTER) class InovelliLocalDimmingUpSpeed(NumberConfigurationEntity): @@ -828,6 +903,15 @@ class InovelliLocalDimmingUpSpeed(NumberConfigurationEntity): server_clusters=frozenset({INOVELLI_CLUSTER}), ) + _server_cluster_config = { + INOVELLI_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "dimming_speed_up_local": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(INOVELLI_CLUSTER) class InovelliLocalRampRateOffToOn(NumberConfigurationEntity): @@ -846,6 +930,15 @@ class InovelliLocalRampRateOffToOn(NumberConfigurationEntity): server_clusters=frozenset({INOVELLI_CLUSTER}), ) + _server_cluster_config = { + INOVELLI_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "ramp_rate_off_to_on_local": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(INOVELLI_CLUSTER) class InovelliRemoteDimmingSpeedOffToOn(NumberConfigurationEntity): @@ -864,6 +957,15 @@ class InovelliRemoteDimmingSpeedOffToOn(NumberConfigurationEntity): server_clusters=frozenset({INOVELLI_CLUSTER}), ) + _server_cluster_config = { + INOVELLI_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "ramp_rate_off_to_on_remote": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(INOVELLI_CLUSTER) class InovelliRemoteDimmingDownSpeed(NumberConfigurationEntity): @@ -882,6 +984,15 @@ class InovelliRemoteDimmingDownSpeed(NumberConfigurationEntity): server_clusters=frozenset({INOVELLI_CLUSTER}), ) + _server_cluster_config = { + INOVELLI_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "dimming_speed_down_remote": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(INOVELLI_CLUSTER) class InovelliLocalDimmingDownSpeed(NumberConfigurationEntity): @@ -900,6 +1011,15 @@ class InovelliLocalDimmingDownSpeed(NumberConfigurationEntity): server_clusters=frozenset({INOVELLI_CLUSTER}), ) + _server_cluster_config = { + INOVELLI_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "dimming_speed_down_local": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(INOVELLI_CLUSTER) class InovelliLocalRampRateOnToOff(NumberConfigurationEntity): @@ -918,6 +1038,15 @@ class InovelliLocalRampRateOnToOff(NumberConfigurationEntity): server_clusters=frozenset({INOVELLI_CLUSTER}), ) + _server_cluster_config = { + INOVELLI_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "ramp_rate_on_to_off_local": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(INOVELLI_CLUSTER) class InovelliRemoteDimmingSpeedOnToOff(NumberConfigurationEntity): @@ -936,6 +1065,15 @@ class InovelliRemoteDimmingSpeedOnToOff(NumberConfigurationEntity): server_clusters=frozenset({INOVELLI_CLUSTER}), ) + _server_cluster_config = { + INOVELLI_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "ramp_rate_on_to_off_remote": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(INOVELLI_CLUSTER) class InovelliMinimumLoadDimmingLevel(NumberConfigurationEntity): @@ -954,6 +1092,15 @@ class InovelliMinimumLoadDimmingLevel(NumberConfigurationEntity): server_clusters=frozenset({INOVELLI_CLUSTER}), ) + _server_cluster_config = { + INOVELLI_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "minimum_level": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(INOVELLI_CLUSTER) class InovelliMaximumLoadDimmingLevel(NumberConfigurationEntity): @@ -972,6 +1119,15 @@ class InovelliMaximumLoadDimmingLevel(NumberConfigurationEntity): server_clusters=frozenset({INOVELLI_CLUSTER}), ) + _server_cluster_config = { + INOVELLI_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "maximum_level": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(INOVELLI_CLUSTER) class InovelliAutoShutoffTimer(NumberConfigurationEntity): @@ -990,6 +1146,15 @@ class InovelliAutoShutoffTimer(NumberConfigurationEntity): server_clusters=frozenset({INOVELLI_CLUSTER}), ) + _server_cluster_config = { + INOVELLI_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "auto_off_timer": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(INOVELLI_CLUSTER) class InovelliLocalDefaultLevel(NumberConfigurationEntity): @@ -1008,6 +1173,15 @@ class InovelliLocalDefaultLevel(NumberConfigurationEntity): server_clusters=frozenset({INOVELLI_CLUSTER}), ) + _server_cluster_config = { + INOVELLI_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "default_level_local": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(INOVELLI_CLUSTER) class InovelliRemoteDefaultLevel(NumberConfigurationEntity): @@ -1026,6 +1200,15 @@ class InovelliRemoteDefaultLevel(NumberConfigurationEntity): server_clusters=frozenset({INOVELLI_CLUSTER}), ) + _server_cluster_config = { + INOVELLI_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "default_level_remote": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(INOVELLI_CLUSTER) class InovelliStartupDefaultLevel(NumberConfigurationEntity): @@ -1044,6 +1227,15 @@ class InovelliStartupDefaultLevel(NumberConfigurationEntity): server_clusters=frozenset({INOVELLI_CLUSTER}), ) + _server_cluster_config = { + INOVELLI_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "state_after_power_restored": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(INOVELLI_CLUSTER) class InovelliQuickStartTime(NumberConfigurationEntity): @@ -1063,6 +1255,15 @@ class InovelliQuickStartTime(NumberConfigurationEntity): models=frozenset({"VZM35-SN"}), ) + _server_cluster_config = { + INOVELLI_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "quick_start_time": AttrConfig(read_on_startup=True), + }, + ), + } + @register_entity(INOVELLI_CLUSTER) class InovelliLoadLevelIndicatorTimeout(NumberConfigurationEntity): @@ -1081,6 +1282,15 @@ class InovelliLoadLevelIndicatorTimeout(NumberConfigurationEntity): server_clusters=frozenset({INOVELLI_CLUSTER}), ) + _server_cluster_config = { + INOVELLI_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "load_level_indicator_timeout": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(INOVELLI_CLUSTER) class InovelliDefaultAllLEDOnColor(NumberConfigurationEntity): @@ -1099,6 +1309,15 @@ class InovelliDefaultAllLEDOnColor(NumberConfigurationEntity): server_clusters=frozenset({INOVELLI_CLUSTER}), ) + _server_cluster_config = { + INOVELLI_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "led_color_when_on": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(INOVELLI_CLUSTER) class InovelliDefaultAllLEDOffColor(NumberConfigurationEntity): @@ -1117,6 +1336,15 @@ class InovelliDefaultAllLEDOffColor(NumberConfigurationEntity): server_clusters=frozenset({INOVELLI_CLUSTER}), ) + _server_cluster_config = { + INOVELLI_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "led_color_when_off": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(INOVELLI_CLUSTER) class InovelliDefaultAllLEDOnIntensity(NumberConfigurationEntity): @@ -1135,6 +1363,15 @@ class InovelliDefaultAllLEDOnIntensity(NumberConfigurationEntity): server_clusters=frozenset({INOVELLI_CLUSTER}), ) + _server_cluster_config = { + INOVELLI_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "led_intensity_when_on": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(INOVELLI_CLUSTER) class InovelliDefaultAllLEDOffIntensity(NumberConfigurationEntity): @@ -1153,6 +1390,15 @@ class InovelliDefaultAllLEDOffIntensity(NumberConfigurationEntity): server_clusters=frozenset({INOVELLI_CLUSTER}), ) + _server_cluster_config = { + INOVELLI_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "led_intensity_when_off": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(INOVELLI_CLUSTER) class InovelliDoubleTapUpLevel(NumberConfigurationEntity): @@ -1171,6 +1417,15 @@ class InovelliDoubleTapUpLevel(NumberConfigurationEntity): server_clusters=frozenset({INOVELLI_CLUSTER}), ) + _server_cluster_config = { + INOVELLI_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "double_tap_up_level": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(INOVELLI_CLUSTER) class InovelliDoubleTapDownLevel(NumberConfigurationEntity): @@ -1189,6 +1444,15 @@ class InovelliDoubleTapDownLevel(NumberConfigurationEntity): server_clusters=frozenset({INOVELLI_CLUSTER}), ) + _server_cluster_config = { + INOVELLI_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "double_tap_down_level": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(AQARA_OPPLE_CLUSTER) class AqaraPetFeederServingSize(NumberConfigurationEntity): @@ -1209,6 +1473,15 @@ class AqaraPetFeederServingSize(NumberConfigurationEntity): models=frozenset({"aqara.feeder.acn001"}), ) + _server_cluster_config = { + AQARA_OPPLE_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "serving_size": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(AQARA_OPPLE_CLUSTER) class AqaraPetFeederPortionWeight(NumberConfigurationEntity): @@ -1230,6 +1503,15 @@ class AqaraPetFeederPortionWeight(NumberConfigurationEntity): models=frozenset({"aqara.feeder.acn001"}), ) + _server_cluster_config = { + AQARA_OPPLE_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "portion_weight": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(AQARA_OPPLE_CLUSTER) class AqaraThermostatAwayTemp(NumberConfigurationEntity): @@ -1252,6 +1534,15 @@ class AqaraThermostatAwayTemp(NumberConfigurationEntity): models=frozenset({"lumi.airrtc.agl001"}), ) + _server_cluster_config = { + AQARA_OPPLE_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "away_preset_temperature": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(Thermostat.cluster_id) class ThermostatLocalTempCalibration(NumberConfigurationEntity): @@ -1278,108 +1569,9 @@ class ThermostatLocalTempCalibration(NumberConfigurationEntity): Thermostat.cluster_id: ClusterConfig( bind=True, attributes={ - Thermostat.AttributeDefs.local_temperature: AttrConfig( - read_on_startup=True, - reporting=ReportingConfig( - min_interval=30, max_interval=900, reportable_change=25 - ), - ), - Thermostat.AttributeDefs.occupied_cooling_setpoint: AttrConfig( - read_on_startup=True, - reporting=ReportingConfig( - min_interval=30, max_interval=900, reportable_change=25 - ), - ), - Thermostat.AttributeDefs.occupied_heating_setpoint: AttrConfig( - read_on_startup=True, - reporting=ReportingConfig( - min_interval=30, max_interval=900, reportable_change=25 - ), - ), - Thermostat.AttributeDefs.unoccupied_cooling_setpoint: AttrConfig( - read_on_startup=True, - reporting=ReportingConfig( - min_interval=30, max_interval=900, reportable_change=25 - ), - ), - Thermostat.AttributeDefs.unoccupied_heating_setpoint: AttrConfig( - read_on_startup=True, - reporting=ReportingConfig( - min_interval=30, max_interval=900, reportable_change=25 - ), - ), - Thermostat.AttributeDefs.running_mode: AttrConfig( - read_on_startup=True, - reporting=ReportingConfig( - min_interval=30, max_interval=900, reportable_change=1 - ), - ), - Thermostat.AttributeDefs.running_state: AttrConfig( - read_on_startup=True, - reporting=ReportingConfig( - min_interval=30, max_interval=900, reportable_change=1 - ), - ), - Thermostat.AttributeDefs.system_mode: AttrConfig( - read_on_startup=True, - reporting=ReportingConfig( - min_interval=30, max_interval=900, reportable_change=1 - ), - ), - Thermostat.AttributeDefs.occupancy: AttrConfig( - read_on_startup=True, - reporting=ReportingConfig( - min_interval=30, max_interval=900, reportable_change=1 - ), - ), - Thermostat.AttributeDefs.pi_cooling_demand: AttrConfig( - read_on_startup=True, - reporting=ReportingConfig( - min_interval=30, max_interval=900, reportable_change=5 - ), - ), - Thermostat.AttributeDefs.pi_heating_demand: AttrConfig( - read_on_startup=True, - reporting=ReportingConfig( - min_interval=30, max_interval=900, reportable_change=5 - ), - ), - Thermostat.AttributeDefs.abs_min_heat_setpoint_limit: AttrConfig( - read_on_startup=False, - ), - Thermostat.AttributeDefs.abs_max_heat_setpoint_limit: AttrConfig( - read_on_startup=False, - ), - Thermostat.AttributeDefs.abs_min_cool_setpoint_limit: AttrConfig( - read_on_startup=False, - ), - Thermostat.AttributeDefs.abs_max_cool_setpoint_limit: AttrConfig( - read_on_startup=False, - ), - Thermostat.AttributeDefs.ctrl_sequence_of_oper: AttrConfig( - read_on_startup=True, - ), - Thermostat.AttributeDefs.max_cool_setpoint_limit: AttrConfig( - read_on_startup=False, - ), - Thermostat.AttributeDefs.max_heat_setpoint_limit: AttrConfig( - read_on_startup=False, - ), - Thermostat.AttributeDefs.min_cool_setpoint_limit: AttrConfig( - read_on_startup=False, - ), - Thermostat.AttributeDefs.min_heat_setpoint_limit: AttrConfig( - read_on_startup=False, - ), Thermostat.AttributeDefs.local_temperature_calibration: AttrConfig( read_on_startup=False, ), - Thermostat.AttributeDefs.setpoint_change_source: AttrConfig( - read_on_startup=False, - ), - Thermostat.AttributeDefs.setpoint_change_source_timestamp: AttrConfig( - read_on_startup=False, - ), }, ), } @@ -1601,6 +1793,15 @@ class SinopeDimmerOnLevelConfigurationEntity(NumberConfigurationEntity): models=frozenset({"DM2500ZB", "DM2500ZB-G2", "DM2550ZB", "DM2550ZB-G2"}), ) + _server_cluster_config = { + SINOPE_MANUFACTURER_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "on_intensity": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(SINOPE_MANUFACTURER_CLUSTER) class SinopeLightLEDOnIntensityConfigurationEntity(NumberConfigurationEntity): @@ -1628,6 +1829,15 @@ class SinopeLightLEDOnIntensityConfigurationEntity(NumberConfigurationEntity): ), ) + _server_cluster_config = { + SINOPE_MANUFACTURER_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "on_led_intensity": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(SINOPE_MANUFACTURER_CLUSTER) class SinopeLightLEDOffIntensityConfigurationEntity(NumberConfigurationEntity): @@ -1654,3 +1864,12 @@ class SinopeLightLEDOffIntensityConfigurationEntity(NumberConfigurationEntity): } ), ) + + _server_cluster_config = { + SINOPE_MANUFACTURER_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "off_led_intensity": AttrConfig(read_on_startup=False), + }, + ), + } diff --git a/zha/application/platforms/select.py b/zha/application/platforms/select.py index c3a03c798..da5538241 100644 --- a/zha/application/platforms/select.py +++ b/zha/application/platforms/select.py @@ -26,7 +26,6 @@ AttributeReportedEvent, AttributeUpdatedEvent, AttributeWrittenEvent, - ReportingConfig, ) from zigpy.zcl.clusters.general import LevelControl, OnOff from zigpy.zcl.clusters.hvac import Thermostat, UserInterface @@ -354,12 +353,6 @@ class StartupOnOffSelectEntity(ZCLEnumSelectEntity): OnOff.cluster_id: ClusterConfig( bind=True, attributes={ - OnOff.AttributeDefs.on_off: AttrConfig( - read_on_startup=True, - reporting=ReportingConfig( - min_interval=0, max_interval=900, reportable_change=1 - ), - ), OnOff.AttributeDefs.start_up_on_off: AttrConfig( read_on_startup=False, ), @@ -393,6 +386,7 @@ class TuyaPowerOnStateSelectEntity(ZCLEnumSelectEntity): _server_cluster_config = { OnOff.cluster_id: ClusterConfig( + bind=False, attributes={ "power_on_state": AttrConfig(read_on_startup=False), }, @@ -415,6 +409,15 @@ class TuyaManufacturerPowerOnStateSelectEntity(ZCLEnumSelectEntity): exposed_features=frozenset({TUYA_PLUG_MANUFACTURER}), ) + _server_cluster_config = { + TUYA_MANUFACTURER_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "power_on_state": AttrConfig(read_on_startup=False), + }, + ), + } + class TuyaBacklightMode(types.enum8): """Tuya switch backlight mode enum.""" @@ -441,6 +444,7 @@ class TuyaBacklightModeSelectEntity(ZCLEnumSelectEntity): _server_cluster_config = { OnOff.cluster_id: ClusterConfig( + bind=False, attributes={ "backlight_mode": AttrConfig(read_on_startup=False), }, @@ -472,6 +476,15 @@ class MoesBacklightModeSelectEntity(ZCLEnumSelectEntity): exposed_features=frozenset({TUYA_PLUG_MANUFACTURER}), ) + _server_cluster_config = { + TUYA_MANUFACTURER_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "backlight_mode": AttrConfig(read_on_startup=False), + }, + ), + } + class AqaraMotionSensitivities(types.enum8): """Aqara motion sensitivities.""" @@ -496,6 +509,15 @@ class AqaraMotionSensitivity(ZCLEnumSelectEntity): models=frozenset({"lumi.motion.ac01", "lumi.motion.ac02", "lumi.motion.agl04"}), ) + _server_cluster_config = { + AQARA_OPPLE_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "motion_sensitivity": AttrConfig(read_on_startup=False), + }, + ), + } + class HueV1MotionSensitivities(types.enum8): """Hue v1 motion sensitivities.""" @@ -521,26 +543,6 @@ class HueV1MotionSensitivity(ZCLEnumSelectEntity): models=frozenset({"SML001"}), ) - _server_cluster_config = { - OccupancySensing.cluster_id: ClusterConfig( - bind=True, - attributes={ - OccupancySensing.AttributeDefs.occupancy: AttrConfig( - read_on_startup=True, - reporting=ReportingConfig( - min_interval=0, max_interval=900, reportable_change=1 - ), - ), - OccupancySensing.AttributeDefs.pir_o_to_u_delay: AttrConfig( - read_on_startup=False, - ), - OccupancySensing.AttributeDefs.pir_u_to_o_delay: AttrConfig( - read_on_startup=False, - ), - }, - ), - } - class HueV2MotionSensitivities(types.enum8): """Hue v2 motion sensitivities.""" @@ -591,6 +593,15 @@ class AqaraMonitoringMode(ZCLEnumSelectEntity): models=frozenset({"lumi.motion.ac01"}), ) + _server_cluster_config = { + AQARA_OPPLE_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "monitoring_mode": AttrConfig(read_on_startup=False), + }, + ), + } + class AqaraApproachDistances(types.enum8): """Aqara approach distances.""" @@ -615,6 +626,15 @@ class AqaraApproachDistance(ZCLEnumSelectEntity): models=frozenset({"lumi.motion.ac01"}), ) + _server_cluster_config = { + AQARA_OPPLE_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "approach_distance": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(MagnetAC01OppleCluster.cluster_id) class AqaraMagnetAC01DetectionDistance(ZCLEnumSelectEntity): @@ -631,6 +651,15 @@ class AqaraMagnetAC01DetectionDistance(ZCLEnumSelectEntity): models=frozenset({"lumi.magnet.ac01"}), ) + _server_cluster_config = { + MagnetAC01OppleCluster.cluster_id: ClusterConfig( + bind=False, + attributes={ + "detection_distance": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(T2RelayOppleCluster.cluster_id) class AqaraT2RelaySwitchMode(ZCLEnumSelectEntity): @@ -647,6 +676,15 @@ class AqaraT2RelaySwitchMode(ZCLEnumSelectEntity): models=frozenset({"lumi.switch.acn047"}), ) + _server_cluster_config = { + T2RelayOppleCluster.cluster_id: ClusterConfig( + bind=False, + attributes={ + "switch_mode": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(T2RelayOppleCluster.cluster_id) class AqaraT2RelaySwitchType(ZCLEnumSelectEntity): @@ -663,6 +701,15 @@ class AqaraT2RelaySwitchType(ZCLEnumSelectEntity): models=frozenset({"lumi.switch.acn047"}), ) + _server_cluster_config = { + T2RelayOppleCluster.cluster_id: ClusterConfig( + bind=False, + attributes={ + "switch_type": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(T2RelayOppleCluster.cluster_id) class AqaraT2RelayStartupOnOff(ZCLEnumSelectEntity): @@ -679,6 +726,15 @@ class AqaraT2RelayStartupOnOff(ZCLEnumSelectEntity): models=frozenset({"lumi.switch.acn047"}), ) + _server_cluster_config = { + T2RelayOppleCluster.cluster_id: ClusterConfig( + bind=False, + attributes={ + "startup_on_off": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(T2RelayOppleCluster.cluster_id) class AqaraT2RelayDecoupledMode(ZCLEnumSelectEntity): @@ -695,6 +751,15 @@ class AqaraT2RelayDecoupledMode(ZCLEnumSelectEntity): models=frozenset({"lumi.switch.acn047"}), ) + _server_cluster_config = { + T2RelayOppleCluster.cluster_id: ClusterConfig( + bind=False, + attributes={ + "decoupled_mode": AttrConfig(read_on_startup=False), + }, + ), + } + class InovelliOutputMode(types.enum1): """Inovelli output mode.""" @@ -717,6 +782,15 @@ class InovelliOutputModeEntity(ZCLEnumSelectEntity): server_clusters=frozenset({INOVELLI_CLUSTER}), ) + _server_cluster_config = { + INOVELLI_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "output_mode": AttrConfig(read_on_startup=True), + }, + ), + } + class InovelliSwitchType(types.enum8): """Inovelli switch mode.""" @@ -742,6 +816,15 @@ class InovelliSwitchTypeEntity(ZCLEnumSelectEntity): models=frozenset({"VZM31-SN"}), ) + _server_cluster_config = { + INOVELLI_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "switch_type": AttrConfig(read_on_startup=True), + }, + ), + } + class InovelliFanSwitchType(types.enum1): """Inovelli fan switch mode.""" @@ -765,6 +848,15 @@ class InovelliFanSwitchTypeEntity(ZCLEnumSelectEntity): models=frozenset({"VZM35-SN"}), ) + _server_cluster_config = { + INOVELLI_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "switch_type": AttrConfig(read_on_startup=True), + }, + ), + } + class InovelliLedScalingMode(types.enum1): """Inovelli led mode.""" @@ -787,6 +879,15 @@ class InovelliLedScalingModeEntity(ZCLEnumSelectEntity): server_clusters=frozenset({INOVELLI_CLUSTER}), ) + _server_cluster_config = { + INOVELLI_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "led_scaling_mode": AttrConfig(read_on_startup=False), + }, + ), + } + class InovelliFanLedScalingMode(types.enum8): """Inovelli fan led mode.""" @@ -819,6 +920,15 @@ class InovelliFanLedScalingModeEntity(ZCLEnumSelectEntity): models=frozenset({"VZM35-SN"}), ) + _server_cluster_config = { + INOVELLI_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "smart_fan_led_display_levels": AttrConfig(read_on_startup=False), + }, + ), + } + class InovelliNonNeutralOutput(types.enum1): """Inovelli non neutral output selection.""" @@ -841,6 +951,15 @@ class InovelliNonNeutralOutputEntity(ZCLEnumSelectEntity): server_clusters=frozenset({INOVELLI_CLUSTER}), ) + _server_cluster_config = { + INOVELLI_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "increased_non_neutral_output": AttrConfig(read_on_startup=False), + }, + ), + } + class InovelliDimmingMode(types.enum1): """Inovelli dimming mode selection.""" @@ -864,6 +983,15 @@ class InovelliDimmingModeEntity(ZCLEnumSelectEntity): models=frozenset({"VZM31-SN", "VZM36"}), ) + _server_cluster_config = { + INOVELLI_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "leading_or_trailing_edge": AttrConfig(read_on_startup=False), + }, + ), + } + class AqaraFeedingMode(types.enum8): """Feeding mode.""" @@ -887,6 +1015,15 @@ class AqaraPetFeederMode(ZCLEnumSelectEntity): models=frozenset({"aqara.feeder.acn001"}), ) + _server_cluster_config = { + AQARA_OPPLE_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "feeding_mode": AttrConfig(read_on_startup=False), + }, + ), + } + class AqaraThermostatPresetMode(types.enum8): """Thermostat preset mode.""" @@ -911,6 +1048,15 @@ class AqaraThermostatPreset(ZCLEnumSelectEntity): models=frozenset({"lumi.airrtc.agl001"}), ) + _server_cluster_config = { + AQARA_OPPLE_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "preset": AttrConfig(read_on_startup=False), + }, + ), + } + class SonoffPresenceDetectionSensitivityEnum(types.enum8): """Enum for detection sensitivity select entity.""" @@ -991,116 +1137,6 @@ class DanfossExerciseDayOfTheWeek(ZCLEnumSelectEntity): exposed_features=frozenset({DANFOSS_ALLY_THERMOSTAT}), ) - _server_cluster_config = { - Thermostat.cluster_id: ClusterConfig( - bind=True, - attributes={ - Thermostat.AttributeDefs.local_temperature: AttrConfig( - read_on_startup=True, - reporting=ReportingConfig( - min_interval=30, max_interval=900, reportable_change=25 - ), - ), - Thermostat.AttributeDefs.occupied_cooling_setpoint: AttrConfig( - read_on_startup=True, - reporting=ReportingConfig( - min_interval=30, max_interval=900, reportable_change=25 - ), - ), - Thermostat.AttributeDefs.occupied_heating_setpoint: AttrConfig( - read_on_startup=True, - reporting=ReportingConfig( - min_interval=30, max_interval=900, reportable_change=25 - ), - ), - Thermostat.AttributeDefs.unoccupied_cooling_setpoint: AttrConfig( - read_on_startup=True, - reporting=ReportingConfig( - min_interval=30, max_interval=900, reportable_change=25 - ), - ), - Thermostat.AttributeDefs.unoccupied_heating_setpoint: AttrConfig( - read_on_startup=True, - reporting=ReportingConfig( - min_interval=30, max_interval=900, reportable_change=25 - ), - ), - Thermostat.AttributeDefs.running_mode: AttrConfig( - read_on_startup=True, - reporting=ReportingConfig( - min_interval=30, max_interval=900, reportable_change=1 - ), - ), - Thermostat.AttributeDefs.running_state: AttrConfig( - read_on_startup=True, - reporting=ReportingConfig( - min_interval=30, max_interval=900, reportable_change=1 - ), - ), - Thermostat.AttributeDefs.system_mode: AttrConfig( - read_on_startup=True, - reporting=ReportingConfig( - min_interval=30, max_interval=900, reportable_change=1 - ), - ), - Thermostat.AttributeDefs.occupancy: AttrConfig( - read_on_startup=True, - reporting=ReportingConfig( - min_interval=30, max_interval=900, reportable_change=1 - ), - ), - Thermostat.AttributeDefs.pi_cooling_demand: AttrConfig( - read_on_startup=True, - reporting=ReportingConfig( - min_interval=30, max_interval=900, reportable_change=5 - ), - ), - Thermostat.AttributeDefs.pi_heating_demand: AttrConfig( - read_on_startup=True, - reporting=ReportingConfig( - min_interval=30, max_interval=900, reportable_change=5 - ), - ), - Thermostat.AttributeDefs.abs_min_heat_setpoint_limit: AttrConfig( - read_on_startup=False, - ), - Thermostat.AttributeDefs.abs_max_heat_setpoint_limit: AttrConfig( - read_on_startup=False, - ), - Thermostat.AttributeDefs.abs_min_cool_setpoint_limit: AttrConfig( - read_on_startup=False, - ), - Thermostat.AttributeDefs.abs_max_cool_setpoint_limit: AttrConfig( - read_on_startup=False, - ), - Thermostat.AttributeDefs.ctrl_sequence_of_oper: AttrConfig( - read_on_startup=True, - ), - Thermostat.AttributeDefs.max_cool_setpoint_limit: AttrConfig( - read_on_startup=False, - ), - Thermostat.AttributeDefs.max_heat_setpoint_limit: AttrConfig( - read_on_startup=False, - ), - Thermostat.AttributeDefs.min_cool_setpoint_limit: AttrConfig( - read_on_startup=False, - ), - Thermostat.AttributeDefs.min_heat_setpoint_limit: AttrConfig( - read_on_startup=False, - ), - Thermostat.AttributeDefs.local_temperature_calibration: AttrConfig( - read_on_startup=False, - ), - Thermostat.AttributeDefs.setpoint_change_source: AttrConfig( - read_on_startup=False, - ), - Thermostat.AttributeDefs.setpoint_change_source_timestamp: AttrConfig( - read_on_startup=False, - ), - }, - ), - } - class DanfossOrientationEnum(types.enum8): """Vertical or Horizontal.""" @@ -1245,6 +1281,15 @@ class SinopeLightLEDOffColorSelect(ZCLEnumSelectEntity): ), ) + _server_cluster_config = { + SINOPE_MANUFACTURER_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "off_led_color": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(SINOPE_MANUFACTURER_CLUSTER) class SinopeLightLEDOnColorSelect(ZCLEnumSelectEntity): @@ -1270,6 +1315,15 @@ class SinopeLightLEDOnColorSelect(ZCLEnumSelectEntity): ), ) + _server_cluster_config = { + SINOPE_MANUFACTURER_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "on_led_color": AttrConfig(read_on_startup=False), + }, + ), + } + class BegaColorTemperatureChannel(types.enum8): """BEGA switchable white color temperature channel enum.""" @@ -1295,32 +1349,8 @@ class BegaColorTemperatureChannelSelect(ZCLEnumSelectEntity): _server_cluster_config = { LevelControl.cluster_id: ClusterConfig( - bind=True, + bind=False, attributes={ - LevelControl.AttributeDefs.current_level: AttrConfig( - read_on_startup=True, - reporting=ReportingConfig( - min_interval=1, max_interval=900, reportable_change=1 - ), - ), - LevelControl.AttributeDefs.on_off_transition_time: AttrConfig( - read_on_startup=False, - ), - LevelControl.AttributeDefs.on_level: AttrConfig( - read_on_startup=False, - ), - LevelControl.AttributeDefs.on_transition_time: AttrConfig( - read_on_startup=False, - ), - LevelControl.AttributeDefs.off_transition_time: AttrConfig( - read_on_startup=False, - ), - LevelControl.AttributeDefs.default_move_rate: AttrConfig( - read_on_startup=False, - ), - LevelControl.AttributeDefs.start_up_current_level: AttrConfig( - read_on_startup=False, - ), "switchable_white": AttrConfig(read_on_startup=False), "switchable_color_temperature_1": AttrConfig(read_on_startup=False), "switchable_color_temperature_2": AttrConfig(read_on_startup=False), diff --git a/zha/application/platforms/sensor/__init__.py b/zha/application/platforms/sensor/__init__.py index 598a3a767..2b72c7210 100644 --- a/zha/application/platforms/sensor/__init__.py +++ b/zha/application/platforms/sensor/__init__.py @@ -2106,6 +2106,49 @@ class ExposedFeatureMeteringPoller(MeteringPoller): ) +# Read-only Metering attributes consulted for scaling/formatting/decoding by every +# metering entity, regardless of which measurement attribute it exposes. +_METERING_CONFIG_ATTRS: dict[foundation.ZCLAttributeDef | str, AttrConfig] = { + Metering.AttributeDefs.demand_formatting: AttrConfig(read_on_startup=False), + Metering.AttributeDefs.divisor: AttrConfig(read_on_startup=False), + Metering.AttributeDefs.metering_device_type: AttrConfig(read_on_startup=False), + Metering.AttributeDefs.multiplier: AttrConfig(read_on_startup=False), + Metering.AttributeDefs.summation_formatting: AttrConfig(read_on_startup=False), + Metering.AttributeDefs.unit_of_measure: AttrConfig(read_on_startup=False), +} + + +def _metering_cluster_config( + measurement_attr: foundation.ZCLAttributeDef, + *, + min_interval: int, + reportable_change: int, +) -> dict[int, ClusterConfig]: + """Build a self-contained Metering config reporting a single measurement attr.""" + return { + Metering.cluster_id: ClusterConfig( + bind=True, + attributes={ + measurement_attr: AttrConfig( + read_on_startup=True, + reporting=ReportingConfig( + min_interval=min_interval, + max_interval=900, + reportable_change=reportable_change, + ), + ), + Metering.AttributeDefs.status: AttrConfig( + read_on_startup=True, + reporting=ReportingConfig( + min_interval=1, max_interval=900, reportable_change=1 + ), + ), + **_METERING_CONFIG_ATTRS, + }, + ), + } + + @register_entity(Metering.cluster_id) class SmartEnergyMetering(Sensor): """Metering sensor.""" @@ -2126,91 +2169,11 @@ class SmartEnergyMetering(Sensor): server_clusters=frozenset({Metering.cluster_id}), ) - _server_cluster_config = { - Metering.cluster_id: ClusterConfig( - bind=True, - attributes={ - Metering.AttributeDefs.instantaneous_demand: AttrConfig( - read_on_startup=True, - reporting=ReportingConfig( - min_interval=5, max_interval=900, reportable_change=1 - ), - ), - Metering.AttributeDefs.current_summ_delivered: AttrConfig( - read_on_startup=True, - reporting=ReportingConfig( - min_interval=30, max_interval=900, reportable_change=1 - ), - ), - Metering.AttributeDefs.current_tier1_summ_delivered: AttrConfig( - read_on_startup=True, - reporting=ReportingConfig( - min_interval=30, max_interval=900, reportable_change=1 - ), - ), - Metering.AttributeDefs.current_tier2_summ_delivered: AttrConfig( - read_on_startup=True, - reporting=ReportingConfig( - min_interval=30, max_interval=900, reportable_change=1 - ), - ), - Metering.AttributeDefs.current_tier3_summ_delivered: AttrConfig( - read_on_startup=True, - reporting=ReportingConfig( - min_interval=30, max_interval=900, reportable_change=1 - ), - ), - Metering.AttributeDefs.current_tier4_summ_delivered: AttrConfig( - read_on_startup=True, - reporting=ReportingConfig( - min_interval=30, max_interval=900, reportable_change=1 - ), - ), - Metering.AttributeDefs.current_tier5_summ_delivered: AttrConfig( - read_on_startup=True, - reporting=ReportingConfig( - min_interval=30, max_interval=900, reportable_change=1 - ), - ), - Metering.AttributeDefs.current_tier6_summ_delivered: AttrConfig( - read_on_startup=True, - reporting=ReportingConfig( - min_interval=30, max_interval=900, reportable_change=1 - ), - ), - Metering.AttributeDefs.current_summ_received: AttrConfig( - read_on_startup=True, - reporting=ReportingConfig( - min_interval=30, max_interval=900, reportable_change=1 - ), - ), - Metering.AttributeDefs.status: AttrConfig( - read_on_startup=True, - reporting=ReportingConfig( - min_interval=1, max_interval=900, reportable_change=1 - ), - ), - Metering.AttributeDefs.demand_formatting: AttrConfig( - read_on_startup=False, - ), - Metering.AttributeDefs.divisor: AttrConfig( - read_on_startup=False, - ), - Metering.AttributeDefs.metering_device_type: AttrConfig( - read_on_startup=False, - ), - Metering.AttributeDefs.multiplier: AttrConfig( - read_on_startup=False, - ), - Metering.AttributeDefs.summation_formatting: AttrConfig( - read_on_startup=False, - ), - Metering.AttributeDefs.unit_of_measure: AttrConfig( - read_on_startup=False, - ), - }, - ), - } + _server_cluster_config = _metering_cluster_config( + Metering.AttributeDefs.instantaneous_demand, + min_interval=5, + reportable_change=1, + ) _ENTITY_DESCRIPTION_MAP = { 0x00: SmartEnergyMeteringEntityDescription( @@ -2389,6 +2352,12 @@ class SmartEnergySummation(SmartEnergyMetering): server_clusters=frozenset({Metering.cluster_id}), ) + _server_cluster_config = _metering_cluster_config( + Metering.AttributeDefs.current_summ_delivered, + min_interval=30, + reportable_change=1, + ) + _ENTITY_DESCRIPTION_MAP = { 0x00: SmartEnergySummationEntityDescription( native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, @@ -2470,6 +2439,11 @@ class Tier1SmartEnergySummation(SmartEnergySummation): """Tier 1 Smart Energy Metering summation sensor.""" _attribute_name = "current_tier1_summ_delivered" + _server_cluster_config = _metering_cluster_config( + Metering.AttributeDefs.current_tier1_summ_delivered, + min_interval=30, + reportable_change=1, + ) _unique_id_suffix = "tier1_summation_delivered" _attr_translation_key: str = "tier1_summation_delivered" @@ -2484,6 +2458,11 @@ class Tier2SmartEnergySummation(SmartEnergySummation): """Tier 2 Smart Energy Metering summation sensor.""" _attribute_name = "current_tier2_summ_delivered" + _server_cluster_config = _metering_cluster_config( + Metering.AttributeDefs.current_tier2_summ_delivered, + min_interval=30, + reportable_change=1, + ) _unique_id_suffix = "tier2_summation_delivered" _attr_translation_key: str = "tier2_summation_delivered" @@ -2498,6 +2477,11 @@ class Tier3SmartEnergySummation(SmartEnergySummation): """Tier 3 Smart Energy Metering summation sensor.""" _attribute_name = "current_tier3_summ_delivered" + _server_cluster_config = _metering_cluster_config( + Metering.AttributeDefs.current_tier3_summ_delivered, + min_interval=30, + reportable_change=1, + ) _unique_id_suffix = "tier3_summation_delivered" _attr_translation_key: str = "tier3_summation_delivered" @@ -2512,6 +2496,11 @@ class Tier4SmartEnergySummation(SmartEnergySummation): """Tier 4 Smart Energy Metering summation sensor.""" _attribute_name = "current_tier4_summ_delivered" + _server_cluster_config = _metering_cluster_config( + Metering.AttributeDefs.current_tier4_summ_delivered, + min_interval=30, + reportable_change=1, + ) _unique_id_suffix = "tier4_summation_delivered" _attr_translation_key: str = "tier4_summation_delivered" @@ -2526,6 +2515,11 @@ class Tier5SmartEnergySummation(SmartEnergySummation): """Tier 5 Smart Energy Metering summation sensor.""" _attribute_name = "current_tier5_summ_delivered" + _server_cluster_config = _metering_cluster_config( + Metering.AttributeDefs.current_tier5_summ_delivered, + min_interval=30, + reportable_change=1, + ) _unique_id_suffix = "tier5_summation_delivered" _attr_translation_key: str = "tier5_summation_delivered" @@ -2540,6 +2534,11 @@ class Tier6SmartEnergySummation(SmartEnergySummation): """Tier 6 Smart Energy Metering summation sensor.""" _attribute_name = "current_tier6_summ_delivered" + _server_cluster_config = _metering_cluster_config( + Metering.AttributeDefs.current_tier6_summ_delivered, + min_interval=30, + reportable_change=1, + ) _unique_id_suffix = "tier6_summation_delivered" _attr_translation_key: str = "tier6_summation_delivered" @@ -2554,6 +2553,11 @@ class SmartEnergySummationReceived(SmartEnergySummation): """Smart Energy Metering summation received sensor.""" _attribute_name = "current_summ_received" + _server_cluster_config = _metering_cluster_config( + Metering.AttributeDefs.current_summ_received, + min_interval=30, + reportable_change=1, + ) _unique_id_suffix = "summation_received" _attr_translation_key: str = "summation_received" @@ -2711,6 +2715,15 @@ class InovelliInternalTemperature(Sensor): server_clusters=frozenset({INOVELLI_CLUSTER}), ) + _server_cluster_config = { + INOVELLI_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "internal_temp_monitor": AttrConfig(read_on_startup=False), + }, + ), + } + class InovelliOverheatedState(types.enum8): """Inovelli overheat protection state.""" @@ -2734,6 +2747,15 @@ class InovelliOverheated(EnumSensor): server_clusters=frozenset({INOVELLI_CLUSTER}), ) + _server_cluster_config = { + INOVELLI_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "overheated": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(CarbonDioxideConcentrationCluster.cluster_id) class CarbonDioxideConcentration(Sensor): @@ -3258,6 +3280,20 @@ class IkeaDeviceRunTime(Sensor): server_clusters=frozenset({IKEA_AIR_PURIFIER_CLUSTER}), ) + _server_cluster_config = { + IKEA_AIR_PURIFIER_CLUSTER: ClusterConfig( + bind=True, + attributes={ + "device_run_time": AttrConfig( + read_on_startup=False, + reporting=ReportingConfig( + min_interval=30, max_interval=900, reportable_change=1 + ), + ), + }, + ), + } + @register_entity(IKEA_AIR_PURIFIER_CLUSTER) class IkeaFilterRunTime(Sensor): @@ -3275,6 +3311,20 @@ class IkeaFilterRunTime(Sensor): server_clusters=frozenset({IKEA_AIR_PURIFIER_CLUSTER}), ) + _server_cluster_config = { + IKEA_AIR_PURIFIER_CLUSTER: ClusterConfig( + bind=True, + attributes={ + "filter_run_time": AttrConfig( + read_on_startup=False, + reporting=ReportingConfig( + min_interval=30, max_interval=900, reportable_change=1 + ), + ), + }, + ), + } + class AqaraFeedingSource(types.enum8): """Aqara pet feeder feeding source.""" @@ -3329,6 +3379,15 @@ class AqaraPetFeederPortionsDispensed(Sensor): models=frozenset({"aqara.feeder.acn001"}), ) + _server_cluster_config = { + AQARA_OPPLE_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "portions_dispensed": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(AQARA_OPPLE_CLUSTER) class AqaraPetFeederWeightDispensed(Sensor): @@ -3346,6 +3405,15 @@ class AqaraPetFeederWeightDispensed(Sensor): models=frozenset({"aqara.feeder.acn001"}), ) + _server_cluster_config = { + AQARA_OPPLE_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "weight_dispensed": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(AQARA_OPPLE_CLUSTER) class AqaraSmokeDensityDbm(Sensor): @@ -3387,6 +3455,15 @@ class SonoffPresenceSenorIlluminationStatus(EnumSensor): models=frozenset({"SNZB-06P"}), ) + _server_cluster_config = { + SONOFF_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "last_illumination_state": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(Thermostat.cluster_id) class PiHeatingDemand(Sensor): @@ -3505,6 +3582,7 @@ class AqaraCurtainMotorPowerSourceSensor(EnumSensor): _server_cluster_config = { Basic.cluster_id: ClusterConfig( + bind=False, attributes={ Basic.AttributeDefs.power_source: AttrConfig( read_on_startup=False, @@ -3539,6 +3617,15 @@ class AqaraCurtainHookStateSensor(EnumSensor): models=frozenset({"lumi.curtain.agl001"}), ) + _server_cluster_config = { + AQARA_OPPLE_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "hooks_state": AttrConfig(read_on_startup=False), + }, + ), + } + class BitMapSensor(Sensor): """A sensor with only state attributes. diff --git a/zha/application/platforms/switch.py b/zha/application/platforms/switch.py index e3d1899e8..582eb7c4c 100644 --- a/zha/application/platforms/switch.py +++ b/zha/application/platforms/switch.py @@ -591,6 +591,15 @@ class P1MotionTriggerIndicatorSwitch(ConfigurableAttributeSwitch): models=frozenset({"lumi.motion.ac02"}), ) + _server_cluster_config = { + AQARA_OPPLE_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "trigger_indicator": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(AQARA_OPPLE_CLUSTER) class XiaomiPlugPowerOutageMemorySwitch(ConfigurableAttributeSwitch): @@ -606,6 +615,15 @@ class XiaomiPlugPowerOutageMemorySwitch(ConfigurableAttributeSwitch): models=frozenset({"lumi.plug.mmeu01", "lumi.plug.maeu01"}), ) + _server_cluster_config = { + AQARA_OPPLE_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "power_outage_memory": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(Basic.cluster_id) class HueMotionTriggerIndicatorSwitch(ConfigurableAttributeSwitch): @@ -624,6 +642,7 @@ class HueMotionTriggerIndicatorSwitch(ConfigurableAttributeSwitch): _server_cluster_config = { Basic.cluster_id: ClusterConfig( + bind=False, attributes={ "trigger_indicator": AttrConfig(read_on_startup=False), }, @@ -645,6 +664,20 @@ class ChildLock(ConfigurableAttributeSwitch): models=frozenset({"STARKVIND Air purifier", "STARKVIND Air purifier table"}), ) + _server_cluster_config = { + IKEA_AIR_PURIFIER_CLUSTER: ClusterConfig( + bind=True, + attributes={ + "child_lock": AttrConfig( + read_on_startup=False, + reporting=ReportingConfig( + min_interval=0, max_interval=900, reportable_change=1 + ), + ), + }, + ), + } + @register_entity(IKEA_AIR_PURIFIER_CLUSTER) class DisableLed(ConfigurableAttributeSwitch): @@ -660,6 +693,20 @@ class DisableLed(ConfigurableAttributeSwitch): models=frozenset({"STARKVIND Air purifier", "STARKVIND Air purifier table"}), ) + _server_cluster_config = { + IKEA_AIR_PURIFIER_CLUSTER: ClusterConfig( + bind=True, + attributes={ + "disable_led": AttrConfig( + read_on_startup=False, + reporting=ReportingConfig( + min_interval=0, max_interval=900, reportable_change=1 + ), + ), + }, + ), + } + @register_entity(INOVELLI_CLUSTER) class InovelliInvertSwitch(ConfigurableAttributeSwitch): @@ -675,6 +722,15 @@ class InovelliInvertSwitch(ConfigurableAttributeSwitch): server_clusters=frozenset({INOVELLI_CLUSTER}), ) + _server_cluster_config = { + INOVELLI_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "invert_switch": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(INOVELLI_CLUSTER) class InovelliSmartBulbMode(ConfigurableAttributeSwitch): @@ -690,6 +746,15 @@ class InovelliSmartBulbMode(ConfigurableAttributeSwitch): server_clusters=frozenset({INOVELLI_CLUSTER}), ) + _server_cluster_config = { + INOVELLI_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "smart_bulb_mode": AttrConfig(read_on_startup=True), + }, + ), + } + @register_entity(INOVELLI_CLUSTER) class InovelliSmartFanMode(ConfigurableAttributeSwitch): @@ -706,6 +771,15 @@ class InovelliSmartFanMode(ConfigurableAttributeSwitch): models=frozenset({"VZM35-SN"}), ) + _server_cluster_config = { + INOVELLI_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "smart_fan_mode": AttrConfig(read_on_startup=True), + }, + ), + } + @register_entity(INOVELLI_CLUSTER) class InovelliDoubleTapUpEnabled(ConfigurableAttributeSwitch): @@ -721,6 +795,15 @@ class InovelliDoubleTapUpEnabled(ConfigurableAttributeSwitch): server_clusters=frozenset({INOVELLI_CLUSTER}), ) + _server_cluster_config = { + INOVELLI_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "double_tap_up_enabled": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(INOVELLI_CLUSTER) class InovelliDoubleTapDownEnabled(ConfigurableAttributeSwitch): @@ -736,6 +819,15 @@ class InovelliDoubleTapDownEnabled(ConfigurableAttributeSwitch): server_clusters=frozenset({INOVELLI_CLUSTER}), ) + _server_cluster_config = { + INOVELLI_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "double_tap_down_enabled": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(INOVELLI_CLUSTER) class InovelliAuxSwitchScenes(ConfigurableAttributeSwitch): @@ -751,6 +843,15 @@ class InovelliAuxSwitchScenes(ConfigurableAttributeSwitch): server_clusters=frozenset({INOVELLI_CLUSTER}), ) + _server_cluster_config = { + INOVELLI_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "aux_switch_scenes": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(INOVELLI_CLUSTER) class InovelliBindingOffToOnSyncLevel(ConfigurableAttributeSwitch): @@ -766,6 +867,15 @@ class InovelliBindingOffToOnSyncLevel(ConfigurableAttributeSwitch): server_clusters=frozenset({INOVELLI_CLUSTER}), ) + _server_cluster_config = { + INOVELLI_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "binding_off_to_on_sync_level": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(INOVELLI_CLUSTER) class InovelliLocalProtection(ConfigurableAttributeSwitch): @@ -781,6 +891,15 @@ class InovelliLocalProtection(ConfigurableAttributeSwitch): server_clusters=frozenset({INOVELLI_CLUSTER}), ) + _server_cluster_config = { + INOVELLI_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "local_protection": AttrConfig(read_on_startup=True), + }, + ), + } + @register_entity(INOVELLI_CLUSTER) class InovelliOnOffLEDMode(ConfigurableAttributeSwitch): @@ -796,6 +915,15 @@ class InovelliOnOffLEDMode(ConfigurableAttributeSwitch): server_clusters=frozenset({INOVELLI_CLUSTER}), ) + _server_cluster_config = { + INOVELLI_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "on_off_led_mode": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(INOVELLI_CLUSTER) class InovelliFirmwareProgressLED(ConfigurableAttributeSwitch): @@ -811,6 +939,15 @@ class InovelliFirmwareProgressLED(ConfigurableAttributeSwitch): server_clusters=frozenset({INOVELLI_CLUSTER}), ) + _server_cluster_config = { + INOVELLI_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "firmware_progress_led": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(INOVELLI_CLUSTER) class InovelliRelayClickInOnOffMode(ConfigurableAttributeSwitch): @@ -826,6 +963,15 @@ class InovelliRelayClickInOnOffMode(ConfigurableAttributeSwitch): server_clusters=frozenset({INOVELLI_CLUSTER}), ) + _server_cluster_config = { + INOVELLI_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "relay_click_in_on_off_mode": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(INOVELLI_CLUSTER) class InovelliDisableDoubleTapClearNotificationsMode(ConfigurableAttributeSwitch): @@ -841,6 +987,17 @@ class InovelliDisableDoubleTapClearNotificationsMode(ConfigurableAttributeSwitch server_clusters=frozenset({INOVELLI_CLUSTER}), ) + _server_cluster_config = { + INOVELLI_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "disable_clear_notifications_double_tap": AttrConfig( + read_on_startup=False + ), + }, + ), + } + @register_entity(AQARA_OPPLE_CLUSTER) class AqaraPetFeederLEDIndicator(ConfigurableAttributeSwitch): @@ -857,6 +1014,15 @@ class AqaraPetFeederLEDIndicator(ConfigurableAttributeSwitch): models=frozenset({"aqara.feeder.acn001"}), ) + _server_cluster_config = { + AQARA_OPPLE_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "disable_led_indicator": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(AQARA_OPPLE_CLUSTER) class AqaraPetFeederChildLock(ConfigurableAttributeSwitch): @@ -872,6 +1038,15 @@ class AqaraPetFeederChildLock(ConfigurableAttributeSwitch): models=frozenset({"aqara.feeder.acn001"}), ) + _server_cluster_config = { + AQARA_OPPLE_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "child_lock": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(OnOff.cluster_id) class TuyaChildLockSwitch(ConfigurableAttributeSwitch): @@ -911,6 +1086,15 @@ class AqaraThermostatWindowDetection(ConfigurableAttributeSwitch): models=frozenset({"lumi.airrtc.agl001"}), ) + _server_cluster_config = { + AQARA_OPPLE_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "window_detection": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(AQARA_OPPLE_CLUSTER) class AqaraThermostatValveDetection(ConfigurableAttributeSwitch): @@ -926,6 +1110,15 @@ class AqaraThermostatValveDetection(ConfigurableAttributeSwitch): models=frozenset({"lumi.airrtc.agl001"}), ) + _server_cluster_config = { + AQARA_OPPLE_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "valve_detection": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(AQARA_OPPLE_CLUSTER) class AqaraThermostatChildLock(ConfigurableAttributeSwitch): @@ -941,6 +1134,15 @@ class AqaraThermostatChildLock(ConfigurableAttributeSwitch): models=frozenset({"lumi.airrtc.agl001"}), ) + _server_cluster_config = { + AQARA_OPPLE_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "child_lock": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(AQARA_OPPLE_CLUSTER) class AqaraHeartbeatIndicator(ConfigurableAttributeSwitch): @@ -956,6 +1158,15 @@ class AqaraHeartbeatIndicator(ConfigurableAttributeSwitch): models=frozenset({"lumi.sensor_smoke.acn03"}), ) + _server_cluster_config = { + AQARA_OPPLE_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "heartbeat_indicator": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(AQARA_OPPLE_CLUSTER) class AqaraLinkageAlarm(ConfigurableAttributeSwitch): @@ -971,6 +1182,15 @@ class AqaraLinkageAlarm(ConfigurableAttributeSwitch): models=frozenset({"lumi.sensor_smoke.acn03"}), ) + _server_cluster_config = { + AQARA_OPPLE_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "linkage_alarm": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(AQARA_OPPLE_CLUSTER) class AqaraBuzzerManualMute(ConfigurableAttributeSwitch): @@ -986,6 +1206,15 @@ class AqaraBuzzerManualMute(ConfigurableAttributeSwitch): models=frozenset({"lumi.sensor_smoke.acn03"}), ) + _server_cluster_config = { + AQARA_OPPLE_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "buzzer_manual_mute": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(AQARA_OPPLE_CLUSTER) class AqaraBuzzerManualAlarm(ConfigurableAttributeSwitch): @@ -1001,6 +1230,15 @@ class AqaraBuzzerManualAlarm(ConfigurableAttributeSwitch): models=frozenset({"lumi.sensor_smoke.acn03"}), ) + _server_cluster_config = { + AQARA_OPPLE_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "buzzer_manual_alarm": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(WindowCovering.cluster_id) class WindowCoveringInversionSwitch(ConfigurableAttributeSwitch): @@ -1110,6 +1348,15 @@ class AqaraE1CurtainMotorHooksLockedSwitch(ConfigurableAttributeSwitch): models=frozenset({"lumi.curtain.agl001"}), ) + _server_cluster_config = { + AQARA_OPPLE_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "hooks_lock": AttrConfig(read_on_startup=False), + }, + ), + } + @register_entity(Thermostat.cluster_id) class DanfossExternalOpenWindowDetected(ConfigurableAttributeSwitch): @@ -1302,3 +1549,12 @@ class SinopeLightDoubleTapFullSwitch(ConfigurableAttributeSwitch): server_clusters=frozenset({SINOPE_MANUFACTURER_CLUSTER}), models=frozenset({"DM2500ZB", "DM2500ZB-G2", "DM2550ZB", "DM2550ZB-G2"}), ) + + _server_cluster_config = { + SINOPE_MANUFACTURER_CLUSTER: ClusterConfig( + bind=False, + attributes={ + "double_up_full": AttrConfig(read_on_startup=False), + }, + ), + } diff --git a/zha/application/platforms/virtual.py b/zha/application/platforms/virtual.py index 05c462014..fb36ea96e 100644 --- a/zha/application/platforms/virtual.py +++ b/zha/application/platforms/virtual.py @@ -12,7 +12,6 @@ import asyncio from typing import TYPE_CHECKING, Any -from zhaquirks.quirk_ids import TUYA_PLUG_MANUFACTURER import zigpy.exceptions import zigpy.types as t import zigpy.zcl @@ -41,6 +40,7 @@ ) from zha.application.platforms.const import ( AQARA_OPPLE_CLUSTER, + IKEA_AIR_PURIFIER_CLUSTER, IKEA_REMOTE_CLUSTER, IKEA_SHORTCUT_V1_CLUSTER, INOVELLI_CLUSTER, @@ -528,7 +528,29 @@ class SmartThingsAccelerationEvent(VirtualEntity): ) _server_cluster_config = { - SMARTTHINGS_ACCELERATION_CLUSTER: ClusterConfig(bind=False), + SMARTTHINGS_ACCELERATION_CLUSTER: ClusterConfig( + bind=True, + attributes={ + "x_axis": AttrConfig( + read_on_startup=False, + reporting=ReportingConfig( + min_interval=1, max_interval=900, reportable_change=1 + ), + ), + "y_axis": AttrConfig( + read_on_startup=False, + reporting=ReportingConfig( + min_interval=1, max_interval=900, reportable_change=1 + ), + ), + "z_axis": AttrConfig( + read_on_startup=False, + reporting=ReportingConfig( + min_interval=1, max_interval=900, reportable_change=1 + ), + ), + }, + ), } def handle_attribute_updated( @@ -549,6 +571,46 @@ def handle_attribute_updated( ) +@register_entity(IKEA_AIR_PURIFIER_CLUSTER) +class StarkvindAirQualityReporting(VirtualEntity): + """Drives `air_quality_25pm` reporting on the STARKVIND manufacturer cluster. + + The PM2.5 value is surfaced by a standard PM25 sensor on a separate, quirk- + added cluster; the quirk pushes `air_quality_25pm` updates from this cluster + into it via a bus. There is therefore no real entity on this cluster to own + the reporting, so this virtual entity binds and configures it. + + TODO: This is reporting plumbing for the STARKVIND quirk + (`zhaquirks/ikea/starkvind.py`): the quirk's IKEA cluster only forwards + `air_quality_25pm` into the PM25 cluster bus when the device actually sends + a report, so the reporting must be configured here. It is unintuitive for + this to live in ZHA, decoupled from the quirk that depends on it — it should + be moved into the quirk itself (e.g. a v2 quirk `reporting_config`) so the + device that needs it owns it, and this virtual entity can then be removed. + """ + + _unique_id_suffix = "starkvind_air_quality_reporting" + + _cluster_match = ClusterMatch( + server_clusters=frozenset({IKEA_AIR_PURIFIER_CLUSTER}), + models=frozenset({"STARKVIND Air purifier", "STARKVIND Air purifier table"}), + ) + + _server_cluster_config = { + IKEA_AIR_PURIFIER_CLUSTER: ClusterConfig( + bind=True, + attributes={ + "air_quality_25pm": AttrConfig( + read_on_startup=False, + reporting=ReportingConfig( + min_interval=0, max_interval=900, reportable_change=1 + ), + ), + }, + ), + } + + @register_entity(Identify.cluster_id) class IdentifyTriggerEffectEvent(VirtualEntity): """Emits a zha_event when the device sends `trigger_effect`.""" @@ -604,43 +666,6 @@ class AqaraOppleBind(VirtualEntity): } -@register_entity(AQARA_OPPLE_CLUSTER) -class AqaraMotionAc02Init(_AqaraOppleInitBase): - """Aqara P1 motion sensor attribute init.""" - - _cluster_match = ClusterMatch( - server_clusters=frozenset({AQARA_OPPLE_CLUSTER}), - models=frozenset({"lumi.motion.ac02"}), - ) - _server_cluster_config = { - AQARA_OPPLE_CLUSTER: ClusterConfig( - attributes={ - "detection_interval": AttrConfig(read_on_startup=False), - "motion_sensitivity": AttrConfig(read_on_startup=False), - "trigger_indicator": AttrConfig(read_on_startup=False), - }, - ), - } - - -@register_entity(AQARA_OPPLE_CLUSTER) -class AqaraMotionAgl04Init(_AqaraOppleInitBase): - """Aqara high-precision motion sensor attribute init.""" - - _cluster_match = ClusterMatch( - server_clusters=frozenset({AQARA_OPPLE_CLUSTER}), - models=frozenset({"lumi.motion.agl04"}), - ) - _server_cluster_config = { - AQARA_OPPLE_CLUSTER: ClusterConfig( - attributes={ - "detection_interval": AttrConfig(read_on_startup=False), - "motion_sensitivity": AttrConfig(read_on_startup=False), - }, - ), - } - - @register_entity(AQARA_OPPLE_CLUSTER) class AqaraMotionAc01Init(_AqaraOppleInitBase): """Aqara FP1 presence sensor attribute init.""" @@ -651,53 +676,9 @@ class AqaraMotionAc01Init(_AqaraOppleInitBase): ) _server_cluster_config = { AQARA_OPPLE_CLUSTER: ClusterConfig( + bind=False, attributes={ "presence": AttrConfig(read_on_startup=False), - "monitoring_mode": AttrConfig(read_on_startup=False), - "motion_sensitivity": AttrConfig(read_on_startup=False), - "approach_distance": AttrConfig(read_on_startup=False), - }, - ), - } - - -@register_entity(AQARA_OPPLE_CLUSTER) -class AqaraPlugInit(_AqaraOppleInitBase): - """Aqara EU plug attribute init.""" - - _cluster_match = ClusterMatch( - server_clusters=frozenset({AQARA_OPPLE_CLUSTER}), - models=frozenset({"lumi.plug.mmeu01", "lumi.plug.maeu01"}), - ) - _server_cluster_config = { - AQARA_OPPLE_CLUSTER: ClusterConfig( - attributes={ - "power_outage_memory": AttrConfig(read_on_startup=False), - "consumer_connected": AttrConfig(read_on_startup=False), - }, - ), - } - - -@register_entity(AQARA_OPPLE_CLUSTER) -class AqaraFeederInit(_AqaraOppleInitBase): - """Aqara pet feeder attribute init.""" - - _cluster_match = ClusterMatch( - server_clusters=frozenset({AQARA_OPPLE_CLUSTER}), - models=frozenset({"aqara.feeder.acn001"}), - ) - _server_cluster_config = { - AQARA_OPPLE_CLUSTER: ClusterConfig( - attributes={ - "portions_dispensed": AttrConfig(read_on_startup=False), - "weight_dispensed": AttrConfig(read_on_startup=False), - "error_detected": AttrConfig(read_on_startup=False), - "disable_led_indicator": AttrConfig(read_on_startup=False), - "child_lock": AttrConfig(read_on_startup=False), - "feeding_mode": AttrConfig(read_on_startup=False), - "serving_size": AttrConfig(read_on_startup=False), - "portion_weight": AttrConfig(read_on_startup=False), }, ), } @@ -713,18 +694,10 @@ class AqaraThermostatAgl001Init(_AqaraOppleInitBase): ) _server_cluster_config = { AQARA_OPPLE_CLUSTER: ClusterConfig( + bind=False, attributes={ "system_mode": AttrConfig(read_on_startup=False), - "preset": AttrConfig(read_on_startup=False), - "window_detection": AttrConfig(read_on_startup=False), - "valve_detection": AttrConfig(read_on_startup=False), - "valve_alarm": AttrConfig(read_on_startup=False), - "child_lock": AttrConfig(read_on_startup=False), - "away_preset_temperature": AttrConfig(read_on_startup=False), - "window_open": AttrConfig(read_on_startup=False), - "calibrated": AttrConfig(read_on_startup=False), "schedule": AttrConfig(read_on_startup=False), - "sensor": AttrConfig(read_on_startup=False), }, ), } @@ -740,50 +713,10 @@ class AqaraSmokeAcn03Init(_AqaraOppleInitBase): ) _server_cluster_config = { AQARA_OPPLE_CLUSTER: ClusterConfig( + bind=False, attributes={ - "buzzer_manual_mute": AttrConfig(read_on_startup=False), "smoke_density": AttrConfig(read_on_startup=False), - "heartbeat_indicator": AttrConfig(read_on_startup=False), - "buzzer_manual_alarm": AttrConfig(read_on_startup=False), "buzzer": AttrConfig(read_on_startup=False), - "linkage_alarm": AttrConfig(read_on_startup=False), - }, - ), - } - - -@register_entity(AQARA_OPPLE_CLUSTER) -class AqaraMagnetAc01Init(_AqaraOppleInitBase): - """Aqara P1 door sensor attribute init.""" - - _cluster_match = ClusterMatch( - server_clusters=frozenset({AQARA_OPPLE_CLUSTER}), - models=frozenset({"lumi.magnet.ac01"}), - ) - _server_cluster_config = { - AQARA_OPPLE_CLUSTER: ClusterConfig( - attributes={ - "detection_distance": AttrConfig(read_on_startup=False), - }, - ), - } - - -@register_entity(AQARA_OPPLE_CLUSTER) -class AqaraSwitchAcn047Init(_AqaraOppleInitBase): - """Aqara H1M wall switch attribute init.""" - - _cluster_match = ClusterMatch( - server_clusters=frozenset({AQARA_OPPLE_CLUSTER}), - models=frozenset({"lumi.switch.acn047"}), - ) - _server_cluster_config = { - AQARA_OPPLE_CLUSTER: ClusterConfig( - attributes={ - "switch_mode": AttrConfig(read_on_startup=False), - "switch_type": AttrConfig(read_on_startup=False), - "startup_on_off": AttrConfig(read_on_startup=False), - "decoupled_mode": AttrConfig(read_on_startup=False), }, ), } @@ -799,50 +732,14 @@ class AqaraCurtainAgl001Init(_AqaraOppleInitBase): ) _server_cluster_config = { AQARA_OPPLE_CLUSTER: ClusterConfig( + bind=False, attributes={ - "hooks_state": AttrConfig(read_on_startup=False), - "hooks_lock": AttrConfig(read_on_startup=False), "positions_stored": AttrConfig(read_on_startup=False), "light_level": AttrConfig(read_on_startup=False), - "hand_open": AttrConfig(read_on_startup=False), - }, - ), - } - - -@register_entity(AQARA_OPPLE_CLUSTER) -class AqaraMotionDetectionIntervalSync(_AqaraOppleInitBase): - """Propagate the Aqara motion sensor's `detection_interval` to `ias_zone.reset_s`. - - Runs after attribute init so `detection_interval` is in the cache. - """ - - _unique_id_suffix = "aqara_motion_detection_interval_sync" - - _cluster_match = ClusterMatch( - server_clusters=frozenset({AQARA_OPPLE_CLUSTER}), - models=frozenset({"lumi.motion.ac02", "lumi.motion.agl04"}), - ) - - _server_cluster_config = { - AQARA_OPPLE_CLUSTER: ClusterConfig( - attributes={ - "detection_interval": AttrConfig(read_on_startup=False), }, ), } - async def async_initialize_cluster(self, cluster: zigpy.zcl.Cluster) -> None: - """Mirror detection_interval into the sibling IAS Zone handler.""" - interval = cluster.get("detection_interval", cluster.get(0x0102)) - if interval is None: - return - ias_zone = getattr(cluster.endpoint, "ias_zone", None) - if ias_zone is None: - return - self.debug("Loaded detection interval at startup: %s", interval) - ias_zone.reset_s = int(interval) - # === Other manufacturer-specific clusters === @@ -861,25 +758,6 @@ class SonoffManufacturerBind(VirtualEntity): } -@register_entity(SONOFF_CLUSTER) -class SonoffPresenceSensorInit(VirtualEntity): - """Sonoff SNZB-06P presence sensor attribute init.""" - - _unique_id_suffix = "sonoff_presence_sensor_init" - - _cluster_match = ClusterMatch( - server_clusters=frozenset({SONOFF_CLUSTER}), - models=frozenset({"SNZB-06P"}), - ) - _server_cluster_config = { - SONOFF_CLUSTER: ClusterConfig( - attributes={ - "last_illumination_state": AttrConfig(read_on_startup=False), - }, - ), - } - - @register_entity(TUYA_MANUFACTURER_CLUSTER) class TuyaManufacturerBind(VirtualEntity): """Bind the Tuya manufacturer cluster on every device that exposes it.""" @@ -894,26 +772,6 @@ class TuyaManufacturerBind(VirtualEntity): } -@register_entity(TUYA_MANUFACTURER_CLUSTER) -class TuyaPlugManufacturerInit(VirtualEntity): - """Tuya plug manufacturer-cluster attribute init.""" - - _unique_id_suffix = "tuya_plug_manufacturer_init" - - _cluster_match = ClusterMatch( - server_clusters=frozenset({TUYA_MANUFACTURER_CLUSTER}), - exposed_features=frozenset({TUYA_PLUG_MANUFACTURER}), - ) - _server_cluster_config = { - TUYA_MANUFACTURER_CLUSTER: ClusterConfig( - attributes={ - "backlight_mode": AttrConfig(read_on_startup=False), - "power_on_state": AttrConfig(read_on_startup=False), - }, - ), - } - - @register_entity(SINOPE_MANUFACTURER_CLUSTER) class SinopeManufacturerBind(VirtualEntity): """Bind the Sinope manufacturer cluster on every device that exposes it.""" @@ -951,11 +809,6 @@ class SinopeSwitchInit(VirtualEntity): SINOPE_MANUFACTURER_CLUSTER: ClusterConfig( bind=True, attributes={ - "double_up_full": AttrConfig(read_on_startup=False), - "on_led_color": AttrConfig(read_on_startup=False), - "off_led_color": AttrConfig(read_on_startup=False), - "off_led_intensity": AttrConfig(read_on_startup=False), - "on_led_intensity": AttrConfig(read_on_startup=False), "action_report": AttrConfig( read_on_startup=False, reporting=ReportingConfig( @@ -967,25 +820,6 @@ class SinopeSwitchInit(VirtualEntity): } -@register_entity(SINOPE_MANUFACTURER_CLUSTER) -class SinopeDimmerInit(VirtualEntity): - """Extra Sinope dimmer attribute (DM2500/DM2550 only).""" - - _unique_id_suffix = "sinope_dimmer_init" - - _cluster_match = ClusterMatch( - server_clusters=frozenset({SINOPE_MANUFACTURER_CLUSTER}), - models=frozenset({"DM2500ZB", "DM2500ZB-G2", "DM2550ZB", "DM2550ZB-G2"}), - ) - _server_cluster_config = { - SINOPE_MANUFACTURER_CLUSTER: ClusterConfig( - attributes={ - "on_intensity": AttrConfig(read_on_startup=False), - }, - ), - } - - @register_entity(IKEA_REMOTE_CLUSTER) class IkeaRemoteClientBind(VirtualEntity): """Bind the IKEA remote client cluster on every device that exposes it. @@ -1075,46 +909,12 @@ class InovelliVzm30Init(VirtualEntity): ) _server_cluster_config = { INOVELLI_CLUSTER: ClusterConfig( + bind=False, attributes={ - name: AttrConfig(read_on_startup=fresh) - for name, fresh in { - "dimming_speed_up_remote": False, - "dimming_speed_up_local": False, - "ramp_rate_off_to_on_remote": False, - "ramp_rate_off_to_on_local": False, - "dimming_speed_down_remote": False, - "dimming_speed_down_local": False, - "ramp_rate_on_to_off_remote": False, - "ramp_rate_on_to_off_local": False, - "minimum_level": False, - "maximum_level": False, - "invert_switch": False, - "auto_off_timer": False, - "default_level_local": False, - "default_level_remote": False, - "state_after_power_restored": False, - "load_level_indicator_timeout": False, - "active_power_reports": False, - "periodic_power_and_energy_reports": False, - "active_energy_reports": False, - "power_type": True, - "switch_type": True, - "internal_temp_monitor": False, - "overheated": False, - "button_delay": True, - "smart_bulb_mode": True, - "led_color_when_on": False, - "led_color_when_off": False, - "led_intensity_when_on": False, - "led_intensity_when_off": False, - "led_scaling_mode": False, - "aux_switch_scenes": False, - "binding_off_to_on_sync_level": False, - "local_protection": True, - "output_mode": True, - "firmware_progress_led": False, - "disable_clear_notifications_double_tap": False, - }.items() + "active_power_reports": AttrConfig(read_on_startup=False), + "periodic_power_and_energy_reports": AttrConfig(read_on_startup=False), + "active_energy_reports": AttrConfig(read_on_startup=False), + "power_type": AttrConfig(read_on_startup=True), }, ), } @@ -1132,56 +932,13 @@ class InovelliVzm31Init(VirtualEntity): ) _server_cluster_config = { INOVELLI_CLUSTER: ClusterConfig( + bind=False, attributes={ - name: AttrConfig(read_on_startup=fresh) - for name, fresh in { - "dimming_speed_up_remote": False, - "dimming_speed_up_local": False, - "ramp_rate_off_to_on_remote": False, - "ramp_rate_off_to_on_local": False, - "dimming_speed_down_remote": False, - "dimming_speed_down_local": False, - "ramp_rate_on_to_off_remote": False, - "ramp_rate_on_to_off_local": False, - "minimum_level": False, - "maximum_level": False, - "invert_switch": False, - "auto_off_timer": False, - "default_level_local": False, - "default_level_remote": False, - "state_after_power_restored": False, - "load_level_indicator_timeout": False, - "active_power_reports": False, - "periodic_power_and_energy_reports": False, - "active_energy_reports": False, - "power_type": True, - "switch_type": True, - "quick_start_time": False, - "quick_start_level": False, - "increased_non_neutral_output": False, - "leading_or_trailing_edge": False, - "internal_temp_monitor": False, - "overheated": False, - "button_delay": True, - "smart_bulb_mode": True, - "double_tap_up_enabled": False, - "double_tap_down_enabled": False, - "double_tap_up_level": False, - "double_tap_down_level": False, - "led_color_when_on": False, - "led_color_when_off": False, - "led_intensity_when_on": False, - "led_intensity_when_off": False, - "led_scaling_mode": False, - "aux_switch_scenes": False, - "binding_off_to_on_sync_level": False, - "local_protection": True, - "output_mode": True, - "on_off_led_mode": False, - "firmware_progress_led": False, - "relay_click_in_on_off_mode": False, - "disable_clear_notifications_double_tap": False, - }.items() + "active_power_reports": AttrConfig(read_on_startup=False), + "periodic_power_and_energy_reports": AttrConfig(read_on_startup=False), + "active_energy_reports": AttrConfig(read_on_startup=False), + "power_type": AttrConfig(read_on_startup=True), + "quick_start_level": AttrConfig(read_on_startup=False), }, ), } @@ -1199,47 +956,15 @@ class InovelliVzm35Init(VirtualEntity): ) _server_cluster_config = { INOVELLI_CLUSTER: ClusterConfig( + bind=False, attributes={ - name: AttrConfig(read_on_startup=fresh) - for name, fresh in { - "dimming_speed_up_remote": False, - "dimming_speed_up_local": False, - "ramp_rate_off_to_on_local": False, - "ramp_rate_off_to_on_remote": False, - "dimming_speed_down_remote": False, - "dimming_speed_down_local": False, - "ramp_rate_on_to_off_local": False, - "ramp_rate_on_to_off_remote": False, - "minimum_level": False, - "maximum_level": False, - "invert_switch": False, - "auto_off_timer": False, - "default_level_local": False, - "default_level_remote": False, - "state_after_power_restored": False, - "load_level_indicator_timeout": False, - "power_type": True, - "switch_type": True, - "non_neutral_aux_med_gear_learn_value": False, - "non_neutral_aux_low_gear_learn_value": False, - "quick_start_time": True, - "button_delay": True, - "smart_fan_mode": True, - "double_tap_up_enabled": False, - "double_tap_down_enabled": False, - "double_tap_up_level": False, - "double_tap_down_level": False, - "led_color_when_on": False, - "led_color_when_off": False, - "led_intensity_when_on": False, - "led_intensity_when_off": False, - "aux_switch_scenes": False, - "local_protection": True, - "output_mode": True, - "on_off_led_mode": False, - "firmware_progress_led": False, - "smart_fan_led_display_levels": False, - }.items() + "power_type": AttrConfig(read_on_startup=True), + "non_neutral_aux_med_gear_learn_value": AttrConfig( + read_on_startup=False + ), + "non_neutral_aux_low_gear_learn_value": AttrConfig( + read_on_startup=False + ), }, ), } From 62095b89fa00e2f358c25227d75a0fa44a4116b3 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Tue, 2 Jun 2026 18:01:31 -0400 Subject: [PATCH 6/6] Require self-contained binding --- .../platforms/binary_sensor/__init__.py | 14 ++--- zha/application/platforms/number/__init__.py | 60 +++++++++---------- zha/application/platforms/select.py | 42 ++++++------- zha/application/platforms/sensor/__init__.py | 12 ++-- zha/application/platforms/switch.py | 50 ++++++++-------- zha/application/platforms/update.py | 2 + zha/application/platforms/virtual.py | 14 ++--- 7 files changed, 98 insertions(+), 96 deletions(-) diff --git a/zha/application/platforms/binary_sensor/__init__.py b/zha/application/platforms/binary_sensor/__init__.py index b519ba7bd..bada84ca1 100644 --- a/zha/application/platforms/binary_sensor/__init__.py +++ b/zha/application/platforms/binary_sensor/__init__.py @@ -530,7 +530,7 @@ class AqaraPetFeederErrorDetected(BinarySensor): _server_cluster_config = { AQARA_OPPLE_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "error_detected": AttrConfig(read_on_startup=False), }, @@ -555,7 +555,7 @@ class XiaomiPlugConsumerConnected(BinarySensor): _server_cluster_config = { AQARA_OPPLE_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "consumer_connected": AttrConfig(read_on_startup=False), }, @@ -579,7 +579,7 @@ class AqaraThermostatWindowOpen(BinarySensor): _server_cluster_config = { AQARA_OPPLE_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "window_open": AttrConfig(read_on_startup=False), }, @@ -604,7 +604,7 @@ class AqaraThermostatValveAlarm(BinarySensor): _server_cluster_config = { AQARA_OPPLE_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "valve_alarm": AttrConfig(read_on_startup=False), }, @@ -629,7 +629,7 @@ class AqaraThermostatCalibrated(BinarySensor): _server_cluster_config = { AQARA_OPPLE_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "calibrated": AttrConfig(read_on_startup=False), }, @@ -654,7 +654,7 @@ class AqaraThermostatExternalSensor(BinarySensor): _server_cluster_config = { AQARA_OPPLE_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "sensor": AttrConfig(read_on_startup=False), }, @@ -695,7 +695,7 @@ class AqaraE1CurtainMotorOpenedByHandBinarySensor(BinarySensor): _server_cluster_config = { AQARA_OPPLE_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "hand_open": AttrConfig(read_on_startup=False), }, diff --git a/zha/application/platforms/number/__init__.py b/zha/application/platforms/number/__init__.py index 74b90a14f..45e777448 100644 --- a/zha/application/platforms/number/__init__.py +++ b/zha/application/platforms/number/__init__.py @@ -380,7 +380,7 @@ class AqaraMotionDetectionInterval(NumberConfigurationEntity): _server_cluster_config = { AQARA_OPPLE_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "detection_interval": AttrConfig(read_on_startup=False), }, @@ -851,7 +851,7 @@ class InovelliRemoteDimmingUpSpeed(NumberConfigurationEntity): _server_cluster_config = { INOVELLI_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "dimming_speed_up_remote": AttrConfig(read_on_startup=False), }, @@ -878,7 +878,7 @@ class InovelliButtonDelay(NumberConfigurationEntity): _server_cluster_config = { INOVELLI_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "button_delay": AttrConfig(read_on_startup=True), }, @@ -905,7 +905,7 @@ class InovelliLocalDimmingUpSpeed(NumberConfigurationEntity): _server_cluster_config = { INOVELLI_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "dimming_speed_up_local": AttrConfig(read_on_startup=False), }, @@ -932,7 +932,7 @@ class InovelliLocalRampRateOffToOn(NumberConfigurationEntity): _server_cluster_config = { INOVELLI_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "ramp_rate_off_to_on_local": AttrConfig(read_on_startup=False), }, @@ -959,7 +959,7 @@ class InovelliRemoteDimmingSpeedOffToOn(NumberConfigurationEntity): _server_cluster_config = { INOVELLI_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "ramp_rate_off_to_on_remote": AttrConfig(read_on_startup=False), }, @@ -986,7 +986,7 @@ class InovelliRemoteDimmingDownSpeed(NumberConfigurationEntity): _server_cluster_config = { INOVELLI_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "dimming_speed_down_remote": AttrConfig(read_on_startup=False), }, @@ -1013,7 +1013,7 @@ class InovelliLocalDimmingDownSpeed(NumberConfigurationEntity): _server_cluster_config = { INOVELLI_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "dimming_speed_down_local": AttrConfig(read_on_startup=False), }, @@ -1040,7 +1040,7 @@ class InovelliLocalRampRateOnToOff(NumberConfigurationEntity): _server_cluster_config = { INOVELLI_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "ramp_rate_on_to_off_local": AttrConfig(read_on_startup=False), }, @@ -1067,7 +1067,7 @@ class InovelliRemoteDimmingSpeedOnToOff(NumberConfigurationEntity): _server_cluster_config = { INOVELLI_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "ramp_rate_on_to_off_remote": AttrConfig(read_on_startup=False), }, @@ -1094,7 +1094,7 @@ class InovelliMinimumLoadDimmingLevel(NumberConfigurationEntity): _server_cluster_config = { INOVELLI_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "minimum_level": AttrConfig(read_on_startup=False), }, @@ -1121,7 +1121,7 @@ class InovelliMaximumLoadDimmingLevel(NumberConfigurationEntity): _server_cluster_config = { INOVELLI_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "maximum_level": AttrConfig(read_on_startup=False), }, @@ -1148,7 +1148,7 @@ class InovelliAutoShutoffTimer(NumberConfigurationEntity): _server_cluster_config = { INOVELLI_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "auto_off_timer": AttrConfig(read_on_startup=False), }, @@ -1175,7 +1175,7 @@ class InovelliLocalDefaultLevel(NumberConfigurationEntity): _server_cluster_config = { INOVELLI_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "default_level_local": AttrConfig(read_on_startup=False), }, @@ -1202,7 +1202,7 @@ class InovelliRemoteDefaultLevel(NumberConfigurationEntity): _server_cluster_config = { INOVELLI_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "default_level_remote": AttrConfig(read_on_startup=False), }, @@ -1229,7 +1229,7 @@ class InovelliStartupDefaultLevel(NumberConfigurationEntity): _server_cluster_config = { INOVELLI_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "state_after_power_restored": AttrConfig(read_on_startup=False), }, @@ -1257,7 +1257,7 @@ class InovelliQuickStartTime(NumberConfigurationEntity): _server_cluster_config = { INOVELLI_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "quick_start_time": AttrConfig(read_on_startup=True), }, @@ -1284,7 +1284,7 @@ class InovelliLoadLevelIndicatorTimeout(NumberConfigurationEntity): _server_cluster_config = { INOVELLI_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "load_level_indicator_timeout": AttrConfig(read_on_startup=False), }, @@ -1311,7 +1311,7 @@ class InovelliDefaultAllLEDOnColor(NumberConfigurationEntity): _server_cluster_config = { INOVELLI_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "led_color_when_on": AttrConfig(read_on_startup=False), }, @@ -1338,7 +1338,7 @@ class InovelliDefaultAllLEDOffColor(NumberConfigurationEntity): _server_cluster_config = { INOVELLI_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "led_color_when_off": AttrConfig(read_on_startup=False), }, @@ -1365,7 +1365,7 @@ class InovelliDefaultAllLEDOnIntensity(NumberConfigurationEntity): _server_cluster_config = { INOVELLI_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "led_intensity_when_on": AttrConfig(read_on_startup=False), }, @@ -1392,7 +1392,7 @@ class InovelliDefaultAllLEDOffIntensity(NumberConfigurationEntity): _server_cluster_config = { INOVELLI_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "led_intensity_when_off": AttrConfig(read_on_startup=False), }, @@ -1419,7 +1419,7 @@ class InovelliDoubleTapUpLevel(NumberConfigurationEntity): _server_cluster_config = { INOVELLI_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "double_tap_up_level": AttrConfig(read_on_startup=False), }, @@ -1446,7 +1446,7 @@ class InovelliDoubleTapDownLevel(NumberConfigurationEntity): _server_cluster_config = { INOVELLI_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "double_tap_down_level": AttrConfig(read_on_startup=False), }, @@ -1475,7 +1475,7 @@ class AqaraPetFeederServingSize(NumberConfigurationEntity): _server_cluster_config = { AQARA_OPPLE_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "serving_size": AttrConfig(read_on_startup=False), }, @@ -1505,7 +1505,7 @@ class AqaraPetFeederPortionWeight(NumberConfigurationEntity): _server_cluster_config = { AQARA_OPPLE_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "portion_weight": AttrConfig(read_on_startup=False), }, @@ -1536,7 +1536,7 @@ class AqaraThermostatAwayTemp(NumberConfigurationEntity): _server_cluster_config = { AQARA_OPPLE_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "away_preset_temperature": AttrConfig(read_on_startup=False), }, @@ -1795,7 +1795,7 @@ class SinopeDimmerOnLevelConfigurationEntity(NumberConfigurationEntity): _server_cluster_config = { SINOPE_MANUFACTURER_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "on_intensity": AttrConfig(read_on_startup=False), }, @@ -1831,7 +1831,7 @@ class SinopeLightLEDOnIntensityConfigurationEntity(NumberConfigurationEntity): _server_cluster_config = { SINOPE_MANUFACTURER_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "on_led_intensity": AttrConfig(read_on_startup=False), }, @@ -1867,7 +1867,7 @@ class SinopeLightLEDOffIntensityConfigurationEntity(NumberConfigurationEntity): _server_cluster_config = { SINOPE_MANUFACTURER_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "off_led_intensity": AttrConfig(read_on_startup=False), }, diff --git a/zha/application/platforms/select.py b/zha/application/platforms/select.py index da5538241..ec6d35cac 100644 --- a/zha/application/platforms/select.py +++ b/zha/application/platforms/select.py @@ -411,7 +411,7 @@ class TuyaManufacturerPowerOnStateSelectEntity(ZCLEnumSelectEntity): _server_cluster_config = { TUYA_MANUFACTURER_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "power_on_state": AttrConfig(read_on_startup=False), }, @@ -478,7 +478,7 @@ class MoesBacklightModeSelectEntity(ZCLEnumSelectEntity): _server_cluster_config = { TUYA_MANUFACTURER_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "backlight_mode": AttrConfig(read_on_startup=False), }, @@ -511,7 +511,7 @@ class AqaraMotionSensitivity(ZCLEnumSelectEntity): _server_cluster_config = { AQARA_OPPLE_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "motion_sensitivity": AttrConfig(read_on_startup=False), }, @@ -595,7 +595,7 @@ class AqaraMonitoringMode(ZCLEnumSelectEntity): _server_cluster_config = { AQARA_OPPLE_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "monitoring_mode": AttrConfig(read_on_startup=False), }, @@ -628,7 +628,7 @@ class AqaraApproachDistance(ZCLEnumSelectEntity): _server_cluster_config = { AQARA_OPPLE_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "approach_distance": AttrConfig(read_on_startup=False), }, @@ -653,7 +653,7 @@ class AqaraMagnetAC01DetectionDistance(ZCLEnumSelectEntity): _server_cluster_config = { MagnetAC01OppleCluster.cluster_id: ClusterConfig( - bind=False, + bind=True, attributes={ "detection_distance": AttrConfig(read_on_startup=False), }, @@ -678,7 +678,7 @@ class AqaraT2RelaySwitchMode(ZCLEnumSelectEntity): _server_cluster_config = { T2RelayOppleCluster.cluster_id: ClusterConfig( - bind=False, + bind=True, attributes={ "switch_mode": AttrConfig(read_on_startup=False), }, @@ -703,7 +703,7 @@ class AqaraT2RelaySwitchType(ZCLEnumSelectEntity): _server_cluster_config = { T2RelayOppleCluster.cluster_id: ClusterConfig( - bind=False, + bind=True, attributes={ "switch_type": AttrConfig(read_on_startup=False), }, @@ -728,7 +728,7 @@ class AqaraT2RelayStartupOnOff(ZCLEnumSelectEntity): _server_cluster_config = { T2RelayOppleCluster.cluster_id: ClusterConfig( - bind=False, + bind=True, attributes={ "startup_on_off": AttrConfig(read_on_startup=False), }, @@ -753,7 +753,7 @@ class AqaraT2RelayDecoupledMode(ZCLEnumSelectEntity): _server_cluster_config = { T2RelayOppleCluster.cluster_id: ClusterConfig( - bind=False, + bind=True, attributes={ "decoupled_mode": AttrConfig(read_on_startup=False), }, @@ -784,7 +784,7 @@ class InovelliOutputModeEntity(ZCLEnumSelectEntity): _server_cluster_config = { INOVELLI_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "output_mode": AttrConfig(read_on_startup=True), }, @@ -818,7 +818,7 @@ class InovelliSwitchTypeEntity(ZCLEnumSelectEntity): _server_cluster_config = { INOVELLI_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "switch_type": AttrConfig(read_on_startup=True), }, @@ -850,7 +850,7 @@ class InovelliFanSwitchTypeEntity(ZCLEnumSelectEntity): _server_cluster_config = { INOVELLI_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "switch_type": AttrConfig(read_on_startup=True), }, @@ -881,7 +881,7 @@ class InovelliLedScalingModeEntity(ZCLEnumSelectEntity): _server_cluster_config = { INOVELLI_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "led_scaling_mode": AttrConfig(read_on_startup=False), }, @@ -922,7 +922,7 @@ class InovelliFanLedScalingModeEntity(ZCLEnumSelectEntity): _server_cluster_config = { INOVELLI_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "smart_fan_led_display_levels": AttrConfig(read_on_startup=False), }, @@ -953,7 +953,7 @@ class InovelliNonNeutralOutputEntity(ZCLEnumSelectEntity): _server_cluster_config = { INOVELLI_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "increased_non_neutral_output": AttrConfig(read_on_startup=False), }, @@ -985,7 +985,7 @@ class InovelliDimmingModeEntity(ZCLEnumSelectEntity): _server_cluster_config = { INOVELLI_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "leading_or_trailing_edge": AttrConfig(read_on_startup=False), }, @@ -1017,7 +1017,7 @@ class AqaraPetFeederMode(ZCLEnumSelectEntity): _server_cluster_config = { AQARA_OPPLE_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "feeding_mode": AttrConfig(read_on_startup=False), }, @@ -1050,7 +1050,7 @@ class AqaraThermostatPreset(ZCLEnumSelectEntity): _server_cluster_config = { AQARA_OPPLE_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "preset": AttrConfig(read_on_startup=False), }, @@ -1283,7 +1283,7 @@ class SinopeLightLEDOffColorSelect(ZCLEnumSelectEntity): _server_cluster_config = { SINOPE_MANUFACTURER_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "off_led_color": AttrConfig(read_on_startup=False), }, @@ -1317,7 +1317,7 @@ class SinopeLightLEDOnColorSelect(ZCLEnumSelectEntity): _server_cluster_config = { SINOPE_MANUFACTURER_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "on_led_color": AttrConfig(read_on_startup=False), }, diff --git a/zha/application/platforms/sensor/__init__.py b/zha/application/platforms/sensor/__init__.py index 2b72c7210..f900c063c 100644 --- a/zha/application/platforms/sensor/__init__.py +++ b/zha/application/platforms/sensor/__init__.py @@ -2717,7 +2717,7 @@ class InovelliInternalTemperature(Sensor): _server_cluster_config = { INOVELLI_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "internal_temp_monitor": AttrConfig(read_on_startup=False), }, @@ -2749,7 +2749,7 @@ class InovelliOverheated(EnumSensor): _server_cluster_config = { INOVELLI_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "overheated": AttrConfig(read_on_startup=False), }, @@ -3381,7 +3381,7 @@ class AqaraPetFeederPortionsDispensed(Sensor): _server_cluster_config = { AQARA_OPPLE_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "portions_dispensed": AttrConfig(read_on_startup=False), }, @@ -3407,7 +3407,7 @@ class AqaraPetFeederWeightDispensed(Sensor): _server_cluster_config = { AQARA_OPPLE_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "weight_dispensed": AttrConfig(read_on_startup=False), }, @@ -3457,7 +3457,7 @@ class SonoffPresenceSenorIlluminationStatus(EnumSensor): _server_cluster_config = { SONOFF_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "last_illumination_state": AttrConfig(read_on_startup=False), }, @@ -3619,7 +3619,7 @@ class AqaraCurtainHookStateSensor(EnumSensor): _server_cluster_config = { AQARA_OPPLE_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "hooks_state": AttrConfig(read_on_startup=False), }, diff --git a/zha/application/platforms/switch.py b/zha/application/platforms/switch.py index 582eb7c4c..ae254dd81 100644 --- a/zha/application/platforms/switch.py +++ b/zha/application/platforms/switch.py @@ -593,7 +593,7 @@ class P1MotionTriggerIndicatorSwitch(ConfigurableAttributeSwitch): _server_cluster_config = { AQARA_OPPLE_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "trigger_indicator": AttrConfig(read_on_startup=False), }, @@ -617,7 +617,7 @@ class XiaomiPlugPowerOutageMemorySwitch(ConfigurableAttributeSwitch): _server_cluster_config = { AQARA_OPPLE_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "power_outage_memory": AttrConfig(read_on_startup=False), }, @@ -724,7 +724,7 @@ class InovelliInvertSwitch(ConfigurableAttributeSwitch): _server_cluster_config = { INOVELLI_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "invert_switch": AttrConfig(read_on_startup=False), }, @@ -748,7 +748,7 @@ class InovelliSmartBulbMode(ConfigurableAttributeSwitch): _server_cluster_config = { INOVELLI_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "smart_bulb_mode": AttrConfig(read_on_startup=True), }, @@ -773,7 +773,7 @@ class InovelliSmartFanMode(ConfigurableAttributeSwitch): _server_cluster_config = { INOVELLI_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "smart_fan_mode": AttrConfig(read_on_startup=True), }, @@ -797,7 +797,7 @@ class InovelliDoubleTapUpEnabled(ConfigurableAttributeSwitch): _server_cluster_config = { INOVELLI_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "double_tap_up_enabled": AttrConfig(read_on_startup=False), }, @@ -821,7 +821,7 @@ class InovelliDoubleTapDownEnabled(ConfigurableAttributeSwitch): _server_cluster_config = { INOVELLI_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "double_tap_down_enabled": AttrConfig(read_on_startup=False), }, @@ -845,7 +845,7 @@ class InovelliAuxSwitchScenes(ConfigurableAttributeSwitch): _server_cluster_config = { INOVELLI_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "aux_switch_scenes": AttrConfig(read_on_startup=False), }, @@ -869,7 +869,7 @@ class InovelliBindingOffToOnSyncLevel(ConfigurableAttributeSwitch): _server_cluster_config = { INOVELLI_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "binding_off_to_on_sync_level": AttrConfig(read_on_startup=False), }, @@ -893,7 +893,7 @@ class InovelliLocalProtection(ConfigurableAttributeSwitch): _server_cluster_config = { INOVELLI_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "local_protection": AttrConfig(read_on_startup=True), }, @@ -917,7 +917,7 @@ class InovelliOnOffLEDMode(ConfigurableAttributeSwitch): _server_cluster_config = { INOVELLI_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "on_off_led_mode": AttrConfig(read_on_startup=False), }, @@ -941,7 +941,7 @@ class InovelliFirmwareProgressLED(ConfigurableAttributeSwitch): _server_cluster_config = { INOVELLI_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "firmware_progress_led": AttrConfig(read_on_startup=False), }, @@ -965,7 +965,7 @@ class InovelliRelayClickInOnOffMode(ConfigurableAttributeSwitch): _server_cluster_config = { INOVELLI_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "relay_click_in_on_off_mode": AttrConfig(read_on_startup=False), }, @@ -989,7 +989,7 @@ class InovelliDisableDoubleTapClearNotificationsMode(ConfigurableAttributeSwitch _server_cluster_config = { INOVELLI_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "disable_clear_notifications_double_tap": AttrConfig( read_on_startup=False @@ -1016,7 +1016,7 @@ class AqaraPetFeederLEDIndicator(ConfigurableAttributeSwitch): _server_cluster_config = { AQARA_OPPLE_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "disable_led_indicator": AttrConfig(read_on_startup=False), }, @@ -1040,7 +1040,7 @@ class AqaraPetFeederChildLock(ConfigurableAttributeSwitch): _server_cluster_config = { AQARA_OPPLE_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "child_lock": AttrConfig(read_on_startup=False), }, @@ -1088,7 +1088,7 @@ class AqaraThermostatWindowDetection(ConfigurableAttributeSwitch): _server_cluster_config = { AQARA_OPPLE_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "window_detection": AttrConfig(read_on_startup=False), }, @@ -1112,7 +1112,7 @@ class AqaraThermostatValveDetection(ConfigurableAttributeSwitch): _server_cluster_config = { AQARA_OPPLE_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "valve_detection": AttrConfig(read_on_startup=False), }, @@ -1136,7 +1136,7 @@ class AqaraThermostatChildLock(ConfigurableAttributeSwitch): _server_cluster_config = { AQARA_OPPLE_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "child_lock": AttrConfig(read_on_startup=False), }, @@ -1160,7 +1160,7 @@ class AqaraHeartbeatIndicator(ConfigurableAttributeSwitch): _server_cluster_config = { AQARA_OPPLE_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "heartbeat_indicator": AttrConfig(read_on_startup=False), }, @@ -1184,7 +1184,7 @@ class AqaraLinkageAlarm(ConfigurableAttributeSwitch): _server_cluster_config = { AQARA_OPPLE_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "linkage_alarm": AttrConfig(read_on_startup=False), }, @@ -1208,7 +1208,7 @@ class AqaraBuzzerManualMute(ConfigurableAttributeSwitch): _server_cluster_config = { AQARA_OPPLE_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "buzzer_manual_mute": AttrConfig(read_on_startup=False), }, @@ -1232,7 +1232,7 @@ class AqaraBuzzerManualAlarm(ConfigurableAttributeSwitch): _server_cluster_config = { AQARA_OPPLE_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "buzzer_manual_alarm": AttrConfig(read_on_startup=False), }, @@ -1350,7 +1350,7 @@ class AqaraE1CurtainMotorHooksLockedSwitch(ConfigurableAttributeSwitch): _server_cluster_config = { AQARA_OPPLE_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "hooks_lock": AttrConfig(read_on_startup=False), }, @@ -1552,7 +1552,7 @@ class SinopeLightDoubleTapFullSwitch(ConfigurableAttributeSwitch): _server_cluster_config = { SINOPE_MANUFACTURER_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "double_up_full": AttrConfig(read_on_startup=False), }, diff --git a/zha/application/platforms/update.py b/zha/application/platforms/update.py index cc7ce0a92..603bb9ee4 100644 --- a/zha/application/platforms/update.py +++ b/zha/application/platforms/update.py @@ -300,6 +300,7 @@ class FirmwareUpdateEntity(BaseFirmwareUpdateEntity): _client_cluster_config = { Ota.cluster_id: ClusterConfig( + bind=False, attributes={ Ota.AttributeDefs.current_file_version: AttrConfig( read_on_startup=False, @@ -368,6 +369,7 @@ class FirmwareUpdateServerEntity(FirmwareUpdateEntity): _server_cluster_config = { Ota.cluster_id: ClusterConfig( + bind=False, attributes={ Ota.AttributeDefs.current_file_version: AttrConfig( read_on_startup=False, diff --git a/zha/application/platforms/virtual.py b/zha/application/platforms/virtual.py index fb36ea96e..93d0d7b3d 100644 --- a/zha/application/platforms/virtual.py +++ b/zha/application/platforms/virtual.py @@ -676,7 +676,7 @@ class AqaraMotionAc01Init(_AqaraOppleInitBase): ) _server_cluster_config = { AQARA_OPPLE_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "presence": AttrConfig(read_on_startup=False), }, @@ -694,7 +694,7 @@ class AqaraThermostatAgl001Init(_AqaraOppleInitBase): ) _server_cluster_config = { AQARA_OPPLE_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "system_mode": AttrConfig(read_on_startup=False), "schedule": AttrConfig(read_on_startup=False), @@ -713,7 +713,7 @@ class AqaraSmokeAcn03Init(_AqaraOppleInitBase): ) _server_cluster_config = { AQARA_OPPLE_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "smoke_density": AttrConfig(read_on_startup=False), "buzzer": AttrConfig(read_on_startup=False), @@ -732,7 +732,7 @@ class AqaraCurtainAgl001Init(_AqaraOppleInitBase): ) _server_cluster_config = { AQARA_OPPLE_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "positions_stored": AttrConfig(read_on_startup=False), "light_level": AttrConfig(read_on_startup=False), @@ -909,7 +909,7 @@ class InovelliVzm30Init(VirtualEntity): ) _server_cluster_config = { INOVELLI_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "active_power_reports": AttrConfig(read_on_startup=False), "periodic_power_and_energy_reports": AttrConfig(read_on_startup=False), @@ -932,7 +932,7 @@ class InovelliVzm31Init(VirtualEntity): ) _server_cluster_config = { INOVELLI_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "active_power_reports": AttrConfig(read_on_startup=False), "periodic_power_and_energy_reports": AttrConfig(read_on_startup=False), @@ -956,7 +956,7 @@ class InovelliVzm35Init(VirtualEntity): ) _server_cluster_config = { INOVELLI_CLUSTER: ClusterConfig( - bind=False, + bind=True, attributes={ "power_type": AttrConfig(read_on_startup=True), "non_neutral_aux_med_gear_learn_value": AttrConfig(