From 58a8fd5a367bee77234576bfbe3eb5e9488c87f6 Mon Sep 17 00:00:00 2001 From: David Mulcahey Date: Tue, 6 Aug 2024 13:15:35 -0400 Subject: [PATCH 1/6] common base for analog cluster handlers --- zha/zigbee/cluster_handlers/general.py | 35 ++++++++++++++------------ 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/zha/zigbee/cluster_handlers/general.py b/zha/zigbee/cluster_handlers/general.py index 858046fd..2470fb49 100644 --- a/zha/zigbee/cluster_handlers/general.py +++ b/zha/zigbee/cluster_handlers/general.py @@ -84,9 +84,8 @@ class AlarmsClusterHandler(ClusterHandler): """Alarms cluster handler.""" -@registries.CLUSTER_HANDLER_REGISTRY.register(AnalogInput.cluster_id) -class AnalogInputClusterHandler(ClusterHandler): - """Analog Input cluster handler.""" +class AnalogClusterHandler(ClusterHandler): + """Common base for analog input and output cluster handlers.""" REPORT_CONFIG = ( AttrReportConfig( @@ -94,19 +93,6 @@ class AnalogInputClusterHandler(ClusterHandler): config=REPORT_CONFIG_DEFAULT, ), ) - - -@registries.BINDABLE_CLUSTERS.register(AnalogOutput.cluster_id) -@registries.CLUSTER_HANDLER_REGISTRY.register(AnalogOutput.cluster_id) -class AnalogOutputClusterHandler(ClusterHandler): - """Analog Output cluster handler.""" - - REPORT_CONFIG = ( - AttrReportConfig( - attr=AnalogOutput.AttributeDefs.present_value.name, - config=REPORT_CONFIG_DEFAULT, - ), - ) ZCL_INIT_ATTRS = { AnalogOutput.AttributeDefs.min_present_value.name: True, AnalogOutput.AttributeDefs.max_present_value.name: True, @@ -157,6 +143,23 @@ def application_type(self) -> int | None: """Return cached value of application_type.""" return self.cluster.get(AnalogOutput.AttributeDefs.application_type.name) + +@registries.CLUSTER_HANDLER_REGISTRY.register(AnalogInput.cluster_id) +class AnalogInputClusterHandler(AnalogClusterHandler): + """Analog Input cluster handler.""" + + async def async_update(self): + """Update cluster value attribute.""" + await self.get_attribute_value( + AnalogInput.AttributeDefs.present_value.name, from_cache=False + ) + + +@registries.BINDABLE_CLUSTERS.register(AnalogOutput.cluster_id) +@registries.CLUSTER_HANDLER_REGISTRY.register(AnalogOutput.cluster_id) +class AnalogOutputClusterHandler(AnalogClusterHandler): + """Analog Output cluster handler.""" + async def async_set_present_value(self, value: float) -> None: """Update present_value.""" await self.write_attributes_safe( From 79c6dbf589529e83af115b0b9a885647a9379a5a Mon Sep 17 00:00:00 2001 From: David Mulcahey Date: Tue, 6 Aug 2024 13:26:16 -0400 Subject: [PATCH 2/6] nope that wont work --- zha/zigbee/cluster_handlers/general.py | 102 ++++++++++++++++++++----- 1 file changed, 83 insertions(+), 19 deletions(-) diff --git a/zha/zigbee/cluster_handlers/general.py b/zha/zigbee/cluster_handlers/general.py index 2470fb49..33110c7f 100644 --- a/zha/zigbee/cluster_handlers/general.py +++ b/zha/zigbee/cluster_handlers/general.py @@ -84,8 +84,9 @@ class AlarmsClusterHandler(ClusterHandler): """Alarms cluster handler.""" -class AnalogClusterHandler(ClusterHandler): - """Common base for analog input and output cluster handlers.""" +@registries.CLUSTER_HANDLER_REGISTRY.register(AnalogInput.cluster_id) +class AnalogInputClusterHandler(ClusterHandler): + """Analog Input cluster handler.""" REPORT_CONFIG = ( AttrReportConfig( @@ -93,6 +94,86 @@ class AnalogClusterHandler(ClusterHandler): config=REPORT_CONFIG_DEFAULT, ), ) + ZCL_INIT_ATTRS = { + AnalogInput.AttributeDefs.description.name: True, + AnalogInput.AttributeDefs.max_present_value.name: True, + AnalogInput.AttributeDefs.min_present_value.name: True, + AnalogInput.AttributeDefs.out_of_service.name: True, + AnalogInput.AttributeDefs.reliability.name: True, + AnalogInput.AttributeDefs.resolution.name: True, + AnalogInput.AttributeDefs.status_flags.name: True, + AnalogInput.AttributeDefs.engineering_units.name: True, + AnalogInput.AttributeDefs.application_type.name: True, + } + + @property + def present_value(self) -> float | None: + """Return cached value of present_value.""" + return self.cluster.get(AnalogInput.AttributeDefs.present_value.name) + + @property + def description(self) -> str | None: + """Return cached value of description.""" + return self.cluster.get(AnalogInput.AttributeDefs.description.name) + + @property + def max_present_value(self) -> float | None: + """Return cached value of max_present_value.""" + return self.cluster.get(AnalogInput.AttributeDefs.max_present_value.name) + + @property + def min_present_value(self) -> float | None: + """Return cached value of min_present_value.""" + return self.cluster.get(AnalogInput.AttributeDefs.min_present_value.name) + + @property + def out_of_service(self) -> bool | None: + """Return cached value of out_of_service.""" + return self.cluster.get(AnalogInput.AttributeDefs.out_of_service.name) + + @property + def reliability(self) -> int | None: + """Return cached value of reliability.""" + return self.cluster.get(AnalogInput.AttributeDefs.reliability.name) + + @property + def resolution(self) -> float | None: + """Return cached value of resolution.""" + return self.cluster.get(AnalogInput.AttributeDefs.resolution.name) + + @property + def status_flags(self) -> int | None: + """Return cached value of status_flags.""" + return self.cluster.get(AnalogInput.AttributeDefs.status_flags.name) + + @property + def engineering_units(self) -> int | None: + """Return cached value of engineering_units.""" + return self.cluster.get(AnalogInput.AttributeDefs.engineering_units.name) + + @property + def application_type(self) -> int | None: + """Return cached value of application_type.""" + return self.cluster.get(AnalogInput.AttributeDefs.application_type.name) + + async def async_update(self): + """Update cluster value attribute.""" + await self.get_attribute_value( + AnalogInput.AttributeDefs.present_value.name, from_cache=False + ) + + +@registries.BINDABLE_CLUSTERS.register(AnalogOutput.cluster_id) +@registries.CLUSTER_HANDLER_REGISTRY.register(AnalogOutput.cluster_id) +class AnalogOutputClusterHandler(ClusterHandler): + """Analog Output cluster handler.""" + + REPORT_CONFIG = ( + AttrReportConfig( + attr=AnalogOutput.AttributeDefs.present_value.name, + config=REPORT_CONFIG_DEFAULT, + ), + ) ZCL_INIT_ATTRS = { AnalogOutput.AttributeDefs.min_present_value.name: True, AnalogOutput.AttributeDefs.max_present_value.name: True, @@ -143,23 +224,6 @@ def application_type(self) -> int | None: """Return cached value of application_type.""" return self.cluster.get(AnalogOutput.AttributeDefs.application_type.name) - -@registries.CLUSTER_HANDLER_REGISTRY.register(AnalogInput.cluster_id) -class AnalogInputClusterHandler(AnalogClusterHandler): - """Analog Input cluster handler.""" - - async def async_update(self): - """Update cluster value attribute.""" - await self.get_attribute_value( - AnalogInput.AttributeDefs.present_value.name, from_cache=False - ) - - -@registries.BINDABLE_CLUSTERS.register(AnalogOutput.cluster_id) -@registries.CLUSTER_HANDLER_REGISTRY.register(AnalogOutput.cluster_id) -class AnalogOutputClusterHandler(AnalogClusterHandler): - """Analog Output cluster handler.""" - async def async_set_present_value(self, value: float) -> None: """Update present_value.""" await self.write_attributes_safe( From 971c8c79ad408f93b57d21c00f05ddfd768a402d Mon Sep 17 00:00:00 2001 From: David Mulcahey Date: Tue, 6 Aug 2024 13:42:49 -0400 Subject: [PATCH 3/6] this is reportable --- zha/zigbee/cluster_handlers/general.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/zha/zigbee/cluster_handlers/general.py b/zha/zigbee/cluster_handlers/general.py index 33110c7f..711261c3 100644 --- a/zha/zigbee/cluster_handlers/general.py +++ b/zha/zigbee/cluster_handlers/general.py @@ -93,6 +93,10 @@ class AnalogInputClusterHandler(ClusterHandler): attr=AnalogInput.AttributeDefs.present_value.name, config=REPORT_CONFIG_DEFAULT, ), + AttrReportConfig( + attr=AnalogInput.AttributeDefs.status_flags.name, + config=REPORT_CONFIG_DEFAULT, + ), ) ZCL_INIT_ATTRS = { AnalogInput.AttributeDefs.description.name: True, @@ -101,7 +105,6 @@ class AnalogInputClusterHandler(ClusterHandler): AnalogInput.AttributeDefs.out_of_service.name: True, AnalogInput.AttributeDefs.reliability.name: True, AnalogInput.AttributeDefs.resolution.name: True, - AnalogInput.AttributeDefs.status_flags.name: True, AnalogInput.AttributeDefs.engineering_units.name: True, AnalogInput.AttributeDefs.application_type.name: True, } From ca94a65655b7e8e334083b12bf65a24a9c5fc454 Mon Sep 17 00:00:00 2001 From: David Mulcahey Date: Tue, 6 Aug 2024 13:43:05 -0400 Subject: [PATCH 4/6] initial swing at the entity --- zha/application/platforms/sensor/__init__.py | 36 ++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/zha/application/platforms/sensor/__init__.py b/zha/application/platforms/sensor/__init__.py index d46dcbb7..13c3bf8f 100644 --- a/zha/application/platforms/sensor/__init__.py +++ b/zha/application/platforms/sensor/__init__.py @@ -29,6 +29,7 @@ ) from zha.application.platforms.climate.const import HVACAction from zha.application.platforms.helpers import validate_device_class +from zha.application.platforms.number.const import UNITS from zha.application.platforms.sensor.const import SensorDeviceClass, SensorStateClass from zha.application.registries import PLATFORM_ENTITIES from zha.decorators import periodic @@ -488,6 +489,41 @@ class AnalogInput(Sensor): _attr_translation_key: str = "analog_input" +@MULTI_MATCH( + cluster_handler_names=CLUSTER_HANDLER_ANALOG_INPUT, + stop_on_match_group=CLUSTER_HANDLER_ANALOG_INPUT, +) +class AnalogInputSensor(Sensor): + """Sensor that displays analog input values.""" + + _attribute_name = "present_value" + _attr_translation_key: str = "analog_input" + _attr_entity_registry_enabled_default = False + _attr_extra_state_attribute_names: set[str] = { + "description", + "max_present_value", + "min_present_value", + "out_of_service", + "reliability", + "resolution", + "status_flags", + "application_type", + } + + def __init__( + self, + unique_id: str, + cluster_handlers: list[ClusterHandler], + endpoint: Endpoint, + device: Device, + **kwargs: Any, + ) -> None: + """Init this sensor.""" + super().__init__(unique_id, cluster_handlers, endpoint, device, **kwargs) + engineering_units = self._cluster_handler.engineering_units + self._attr_native_unit_of_measurement = UNITS.get(engineering_units) + + @MULTI_MATCH(cluster_handler_names=CLUSTER_HANDLER_POWER_CONFIGURATION) class Battery(Sensor): """Battery sensor of power configuration cluster.""" From e56f504385d08f8b5cd0f87fbed42dd314c39d7b Mon Sep 17 00:00:00 2001 From: David Mulcahey Date: Wed, 7 Aug 2024 07:55:06 -0400 Subject: [PATCH 5/6] 2 impls are not needed --- zha/application/platforms/sensor/__init__.py | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/zha/application/platforms/sensor/__init__.py b/zha/application/platforms/sensor/__init__.py index 13c3bf8f..78c7fec0 100644 --- a/zha/application/platforms/sensor/__init__.py +++ b/zha/application/platforms/sensor/__init__.py @@ -477,22 +477,7 @@ def formatter(self, value: int) -> str | None: return self._enum(value).name -@MULTI_MATCH( - cluster_handler_names=CLUSTER_HANDLER_ANALOG_INPUT, - manufacturers="Digi", - stop_on_match_group=CLUSTER_HANDLER_ANALOG_INPUT, -) -class AnalogInput(Sensor): - """Sensor that displays analog input values.""" - - _attribute_name = "present_value" - _attr_translation_key: str = "analog_input" - - -@MULTI_MATCH( - cluster_handler_names=CLUSTER_HANDLER_ANALOG_INPUT, - stop_on_match_group=CLUSTER_HANDLER_ANALOG_INPUT, -) +@MULTI_MATCH(cluster_handler_names=CLUSTER_HANDLER_ANALOG_INPUT) class AnalogInputSensor(Sensor): """Sensor that displays analog input values.""" From 0f7fea010f45561c324e15a9f17cb21fc0a6d0d1 Mon Sep 17 00:00:00 2001 From: David Mulcahey Date: Wed, 7 Aug 2024 08:22:08 -0400 Subject: [PATCH 6/6] these are config / diagnostic --- zha/application/platforms/sensor/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zha/application/platforms/sensor/__init__.py b/zha/application/platforms/sensor/__init__.py index 78c7fec0..c9d3cd6e 100644 --- a/zha/application/platforms/sensor/__init__.py +++ b/zha/application/platforms/sensor/__init__.py @@ -477,7 +477,7 @@ def formatter(self, value: int) -> str | None: return self._enum(value).name -@MULTI_MATCH(cluster_handler_names=CLUSTER_HANDLER_ANALOG_INPUT) +@CONFIG_DIAGNOSTIC_MATCH(cluster_handler_names=CLUSTER_HANDLER_ANALOG_INPUT) class AnalogInputSensor(Sensor): """Sensor that displays analog input values."""