From 2e2e336921a0d3ae6d1cfc384997e53ce027c5d7 Mon Sep 17 00:00:00 2001 From: abmantis Date: Mon, 9 Dec 2024 22:37:24 +0000 Subject: [PATCH 1/9] Add support for rms_current_ph_b/c --- zha/application/platforms/sensor/__init__.py | 50 ++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/zha/application/platforms/sensor/__init__.py b/zha/application/platforms/sensor/__init__.py index f5341d98c..968078992 100644 --- a/zha/application/platforms/sensor/__init__.py +++ b/zha/application/platforms/sensor/__init__.py @@ -686,6 +686,56 @@ class ElectricalMeasurementRMSCurrent(PolledElectricalMeasurement): _div_mul_prefix = "ac_current" +@MULTI_MATCH(cluster_handler_names=CLUSTER_HANDLER_ELECTRICAL_MEASUREMENT) +class ElectricalMeasurementRMSCurrentPhB(ElectricalMeasurementRMSCurrent): + """RMS current measurement.""" + + _attribute_name = "rms_current_ph_b" + _unique_id_suffix = "rms_current_ph_b" + _attr_translation_key: str = "rms_current_ph_b" + + @classmethod + def create_platform_entity( + cls: type[Self], + unique_id: str, + cluster_handlers: list[ClusterHandler], + endpoint: Endpoint, + device: Device, + **kwargs: Any, + ) -> Self | None: + """Entity Factory.""" + if cluster_handlers[0].cluster.get(cls._attribute_name) is None: + return None + return super().create_platform_entity( + unique_id, cluster_handlers, endpoint, device, **kwargs + ) + + +@MULTI_MATCH(cluster_handler_names=CLUSTER_HANDLER_ELECTRICAL_MEASUREMENT) +class ElectricalMeasurementRMSCurrentPhC(ElectricalMeasurementRMSCurrent): + """RMS current measurement.""" + + _attribute_name: str = "rms_current_ph_c" + _unique_id_suffix: str = "rms_current_ph_c" + _attr_translation_key: str = "rms_current_ph_c" + + @classmethod + def create_platform_entity( + cls: type[Self], + unique_id: str, + cluster_handlers: list[ClusterHandler], + endpoint: Endpoint, + device: Device, + **kwargs: Any, + ) -> Self | None: + """Entity Factory.""" + if cluster_handlers[0].cluster.get(cls._attribute_name) is None: + return None + return super().create_platform_entity( + unique_id, cluster_handlers, endpoint, device, **kwargs + ) + + @MULTI_MATCH(cluster_handler_names=CLUSTER_HANDLER_ELECTRICAL_MEASUREMENT) class ElectricalMeasurementRMSVoltage(PolledElectricalMeasurement): """RMS Voltage measurement.""" From abfd57416d51de68b7b3dbf30b7b5f8b25f93af5 Mon Sep 17 00:00:00 2001 From: abmantis Date: Tue, 10 Dec 2024 18:59:41 +0000 Subject: [PATCH 2/9] Make max attribute name overrideable; Add tests --- tests/test_sensor.py | 46 ++++++++++++++++--- zha/application/platforms/sensor/__init__.py | 19 +++++++- zha/zigbee/cluster_handlers/homeautomation.py | 16 +++++++ 3 files changed, 72 insertions(+), 9 deletions(-) diff --git a/tests/test_sensor.py b/tests/test_sensor.py index 25bbb4a36..38c6c8950 100644 --- a/tests/test_sensor.py +++ b/tests/test_sensor.py @@ -3,6 +3,7 @@ import asyncio from collections.abc import Awaitable, Callable from datetime import UTC, datetime +from functools import partial import math from typing import Any, Optional from unittest.mock import AsyncMock, MagicMock @@ -298,22 +299,27 @@ async def async_test_em_power_factor( async def async_test_em_rms_current( - zha_gateway: Gateway, cluster: Cluster, entity: PlatformEntity + current_attrid: int, + current_max_attrid: int, + current_max_attr_name: str, + zha_gateway: Gateway, + cluster: Cluster, + entity: PlatformEntity, ) -> None: """Test electrical measurement RMS Current sensor.""" - await send_attributes_report(zha_gateway, cluster, {0: 1, 0x0508: 1234}) + await send_attributes_report(zha_gateway, cluster, {0: 1, current_attrid: 1234}) assert_state(entity, 1.2, "A") await send_attributes_report(zha_gateway, cluster, {"ac_current_divisor": 10}) - await send_attributes_report(zha_gateway, cluster, {0: 1, 0x0508: 236}) + await send_attributes_report(zha_gateway, cluster, {0: 1, current_attrid: 236}) assert_state(entity, 23.6, "A") - await send_attributes_report(zha_gateway, cluster, {0: 1, 0x0508: 1236}) + await send_attributes_report(zha_gateway, cluster, {0: 1, current_attrid: 1236}) assert_state(entity, 124, "A") - await send_attributes_report(zha_gateway, cluster, {0: 1, 0x050A: 88}) - assert entity.state["rms_current_max"] == 8.8 + await send_attributes_report(zha_gateway, cluster, {0: 1, current_max_attrid: 88}) + assert entity.state[current_max_attr_name] == 8.8 async def async_test_em_rms_voltage( @@ -515,10 +521,32 @@ async def async_test_change_source_timestamp( ( homeautomation.ElectricalMeasurement.cluster_id, sensor.ElectricalMeasurementRMSCurrent, - async_test_em_rms_current, + partial(async_test_em_rms_current, 0x0508, 0x050A, "rms_current_max"), {"ac_current_divisor": 1000, "ac_current_multiplier": 1}, {"active_power", "apparent_power", "rms_voltage"}, ), + ( + homeautomation.ElectricalMeasurement.cluster_id, + sensor.ElectricalMeasurementRMSCurrentPhB, + partial(async_test_em_rms_current, 0x0908, 0x090A, "rms_current_max_ph_b"), + { + "ac_current_divisor": 1000, + "ac_current_multiplier": 1, + "rms_current_ph_b": 0, + }, + {"active_power", "apparent_power", "rms_voltage"}, + ), + ( + homeautomation.ElectricalMeasurement.cluster_id, + sensor.ElectricalMeasurementRMSCurrentPhC, + partial(async_test_em_rms_current, 0x0A08, 0x0A0A, "rms_current_max_ph_c"), + { + "ac_current_divisor": 1000, + "ac_current_multiplier": 1, + "rms_current_ph_c": 0, + }, + {"active_power", "apparent_power", "rms_voltage"}, + ), ( homeautomation.ElectricalMeasurement.cluster_id, sensor.ElectricalMeasurementRMSVoltage, @@ -1122,7 +1150,11 @@ async def test_elec_measurement_skip_unsupported_attribute( "active_power_max", "apparent_power", "rms_current", + "rms_current_ph_b", + "rms_current_ph_c", "rms_current_max", + "rms_current_max_ph_b", + "rms_current_max_ph_c", "rms_voltage", "rms_voltage_max", "power_factor", diff --git a/zha/application/platforms/sensor/__init__.py b/zha/application/platforms/sensor/__init__.py index 968078992..398552284 100644 --- a/zha/application/platforms/sensor/__init__.py +++ b/zha/application/platforms/sensor/__init__.py @@ -617,9 +617,14 @@ def __init__( super().__init__(unique_id, cluster_handlers, endpoint, device, **kwargs) self._attr_extra_state_attribute_names: set[str] = { "measurement_type", - f"{self._attribute_name}_max", + self._max_attribute_name, } + @property + def _max_attribute_name(self) -> str: + """Return the max attribute name.""" + return f"{self._attribute_name}_max" + @property def state(self) -> dict[str, Any]: """Return the state for this sensor.""" @@ -627,7 +632,7 @@ def state(self) -> dict[str, Any]: if self._cluster_handler.measurement_type is not None: response["measurement_type"] = self._cluster_handler.measurement_type - max_attr_name = f"{self._attribute_name}_max" + max_attr_name = self._max_attribute_name if not hasattr(self._cluster_handler.cluster.AttributeDefs, max_attr_name): return response @@ -694,6 +699,11 @@ class ElectricalMeasurementRMSCurrentPhB(ElectricalMeasurementRMSCurrent): _unique_id_suffix = "rms_current_ph_b" _attr_translation_key: str = "rms_current_ph_b" + @property + def _max_attribute_name(self) -> str: + """Return the max attribute name.""" + return "rms_current_max_ph_b" + @classmethod def create_platform_entity( cls: type[Self], @@ -719,6 +729,11 @@ class ElectricalMeasurementRMSCurrentPhC(ElectricalMeasurementRMSCurrent): _unique_id_suffix: str = "rms_current_ph_c" _attr_translation_key: str = "rms_current_ph_c" + @property + def _max_attribute_name(self) -> str: + """Return the max attribute name.""" + return "rms_current_max_ph_c" + @classmethod def create_platform_entity( cls: type[Self], diff --git a/zha/zigbee/cluster_handlers/homeautomation.py b/zha/zigbee/cluster_handlers/homeautomation.py index 57067c9df..180ab286e 100644 --- a/zha/zigbee/cluster_handlers/homeautomation.py +++ b/zha/zigbee/cluster_handlers/homeautomation.py @@ -77,10 +77,26 @@ class MeasurementType(enum.IntFlag): attr=ElectricalMeasurement.AttributeDefs.rms_current.name, config=REPORT_CONFIG_OP, ), + AttrReportConfig( + attr=ElectricalMeasurement.AttributeDefs.rms_current_ph_b.name, + config=REPORT_CONFIG_OP, + ), + AttrReportConfig( + attr=ElectricalMeasurement.AttributeDefs.rms_current_ph_c.name, + config=REPORT_CONFIG_OP, + ), AttrReportConfig( attr=ElectricalMeasurement.AttributeDefs.rms_current_max.name, config=REPORT_CONFIG_DEFAULT, ), + AttrReportConfig( + attr=ElectricalMeasurement.AttributeDefs.rms_current_max_ph_b.name, + config=REPORT_CONFIG_DEFAULT, + ), + AttrReportConfig( + attr=ElectricalMeasurement.AttributeDefs.rms_current_max_ph_c.name, + config=REPORT_CONFIG_DEFAULT, + ), AttrReportConfig( attr=ElectricalMeasurement.AttributeDefs.rms_voltage.name, config=REPORT_CONFIG_OP, From a924e3bbcd8494ef379019888d19c1c079ac987a Mon Sep 17 00:00:00 2001 From: abmantis Date: Tue, 10 Dec 2024 20:22:42 +0000 Subject: [PATCH 3/9] Use attribute for conditional create_platform_entity --- zha/application/platforms/sensor/__init__.py | 60 ++++---------------- 1 file changed, 11 insertions(+), 49 deletions(-) diff --git a/zha/application/platforms/sensor/__init__.py b/zha/application/platforms/sensor/__init__.py index 398552284..809ee658b 100644 --- a/zha/application/platforms/sensor/__init__.py +++ b/zha/application/platforms/sensor/__init__.py @@ -157,6 +157,7 @@ class Sensor(PlatformEntity): _attr_native_unit_of_measurement: str | None = None _attr_device_class: SensorDeviceClass | None = None _attr_state_class: SensorStateClass | None = None + _skip_creation_if_none: bool = False @classmethod def create_platform_entity( @@ -183,6 +184,12 @@ def create_platform_entity( ) return None + if ( + cls._skip_creation_if_none + and cluster_handlers[0].cluster.get(cls._attribute_name) is None + ): + return None + return cls(unique_id, cluster_handlers, endpoint, device, **kwargs) def __init__( @@ -698,28 +705,13 @@ class ElectricalMeasurementRMSCurrentPhB(ElectricalMeasurementRMSCurrent): _attribute_name = "rms_current_ph_b" _unique_id_suffix = "rms_current_ph_b" _attr_translation_key: str = "rms_current_ph_b" + _skip_creation_if_none = True @property def _max_attribute_name(self) -> str: """Return the max attribute name.""" return "rms_current_max_ph_b" - @classmethod - def create_platform_entity( - cls: type[Self], - unique_id: str, - cluster_handlers: list[ClusterHandler], - endpoint: Endpoint, - device: Device, - **kwargs: Any, - ) -> Self | None: - """Entity Factory.""" - if cluster_handlers[0].cluster.get(cls._attribute_name) is None: - return None - return super().create_platform_entity( - unique_id, cluster_handlers, endpoint, device, **kwargs - ) - @MULTI_MATCH(cluster_handler_names=CLUSTER_HANDLER_ELECTRICAL_MEASUREMENT) class ElectricalMeasurementRMSCurrentPhC(ElectricalMeasurementRMSCurrent): @@ -728,28 +720,13 @@ class ElectricalMeasurementRMSCurrentPhC(ElectricalMeasurementRMSCurrent): _attribute_name: str = "rms_current_ph_c" _unique_id_suffix: str = "rms_current_ph_c" _attr_translation_key: str = "rms_current_ph_c" + _skip_creation_if_none = True @property def _max_attribute_name(self) -> str: """Return the max attribute name.""" return "rms_current_max_ph_c" - @classmethod - def create_platform_entity( - cls: type[Self], - unique_id: str, - cluster_handlers: list[ClusterHandler], - endpoint: Endpoint, - device: Device, - **kwargs: Any, - ) -> Self | None: - """Entity Factory.""" - if cluster_handlers[0].cluster.get(cls._attribute_name) is None: - return None - return super().create_platform_entity( - unique_id, cluster_handlers, endpoint, device, **kwargs - ) - @MULTI_MATCH(cluster_handler_names=CLUSTER_HANDLER_ELECTRICAL_MEASUREMENT) class ElectricalMeasurementRMSVoltage(PolledElectricalMeasurement): @@ -1162,29 +1139,14 @@ class SmartEnergySummationReceived(PolledSmartEnergySummation): _unique_id_suffix = "summation_received" _attr_translation_key: str = "summation_received" - @classmethod - def create_platform_entity( - cls: type[Self], - unique_id: str, - cluster_handlers: list[ClusterHandler], - endpoint: Endpoint, - device: Device, - **kwargs: Any, - ) -> Self | None: - """Entity Factory. - - This attribute only started to be initialized in HA 2024.2.0, + """ This attribute only started to be initialized in HA 2024.2.0, so the entity would be created on the first HA start after the upgrade for existing devices, as the initialization to see if an attribute is unsupported happens later in the background. To avoid creating unnecessary entities for existing devices, wait until the attribute was properly initialized once for now. """ - if cluster_handlers[0].cluster.get(cls._attribute_name) is None: - return None - return super().create_platform_entity( - unique_id, cluster_handlers, endpoint, device, **kwargs - ) + _skip_creation_if_none = True @MULTI_MATCH(cluster_handler_names=CLUSTER_HANDLER_PRESSURE) From 30bfcbc3b66bdc263070749180b42565c43cbc22 Mon Sep 17 00:00:00 2001 From: abmantis Date: Tue, 10 Dec 2024 20:29:36 +0000 Subject: [PATCH 4/9] Fix cluster handler config test --- tests/test_cluster_handlers.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/test_cluster_handlers.py b/tests/test_cluster_handlers.py index 237a46b16..7827be9ad 100644 --- a/tests/test_cluster_handlers.py +++ b/tests/test_cluster_handlers.py @@ -285,11 +285,17 @@ async def poll_control_device_mock(zha_gateway: Gateway) -> Device: zigpy.zcl.clusters.homeautomation.ElectricalMeasurement.cluster_id, 1, { + "ac_frequency", + "ac_frequency_max", "active_power", "active_power_max", "apparent_power", "rms_current", + "rms_current_ph_b", + "rms_current_ph_c", "rms_current_max", + "rms_current_max_b", + "rms_current_max_c", "rms_voltage", "rms_voltage_max", }, From a0296f564c4c0490d60ed1993c0cb3c6108cdb8b Mon Sep 17 00:00:00 2001 From: abmantis Date: Tue, 24 Dec 2024 16:03:03 +0000 Subject: [PATCH 5/9] Use ph B as base for C to reduce duplicated attrs --- zha/application/platforms/sensor/__init__.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/zha/application/platforms/sensor/__init__.py b/zha/application/platforms/sensor/__init__.py index 809ee658b..00edd8b9d 100644 --- a/zha/application/platforms/sensor/__init__.py +++ b/zha/application/platforms/sensor/__init__.py @@ -714,13 +714,12 @@ def _max_attribute_name(self) -> str: @MULTI_MATCH(cluster_handler_names=CLUSTER_HANDLER_ELECTRICAL_MEASUREMENT) -class ElectricalMeasurementRMSCurrentPhC(ElectricalMeasurementRMSCurrent): +class ElectricalMeasurementRMSCurrentPhC(ElectricalMeasurementRMSCurrentPhB): """RMS current measurement.""" _attribute_name: str = "rms_current_ph_c" _unique_id_suffix: str = "rms_current_ph_c" _attr_translation_key: str = "rms_current_ph_c" - _skip_creation_if_none = True @property def _max_attribute_name(self) -> str: From 3dd43d88ef91711c167b1db53be80bcbc5424992 Mon Sep 17 00:00:00 2001 From: abmantis Date: Tue, 24 Dec 2024 17:17:51 +0000 Subject: [PATCH 6/9] Add missing 3 phase electrical attributes --- zha/application/platforms/sensor/__init__.py | 96 +++++++++++++++++++- 1 file changed, 94 insertions(+), 2 deletions(-) diff --git a/zha/application/platforms/sensor/__init__.py b/zha/application/platforms/sensor/__init__.py index 00edd8b9d..54fa7d79a 100644 --- a/zha/application/platforms/sensor/__init__.py +++ b/zha/application/platforms/sensor/__init__.py @@ -188,6 +188,11 @@ def create_platform_entity( cls._skip_creation_if_none and cluster_handlers[0].cluster.get(cls._attribute_name) is None ): + _LOGGER.debug( + "%s has no value - skipping %s entity creation", + cls._attribute_name, + cls.__name__, + ) return None return cls(unique_id, cluster_handlers, endpoint, device, **kwargs) @@ -674,6 +679,36 @@ class PolledElectricalMeasurement(ElectricalMeasurement): _use_custom_polling: bool = True +@MULTI_MATCH(cluster_handler_names=CLUSTER_HANDLER_ELECTRICAL_MEASUREMENT) +class ElectricalMeasurementRMSActivePowerPhB(PolledElectricalMeasurement): + """RMS active power phase B measurement.""" + + _attribute_name = "active_power_ph_b" + _unique_id_suffix = "active_power_ph_b" + _attr_translation_key: str = "active_power_ph_b" + _use_custom_polling = False # Poll indirectly by ElectricalMeasurementSensor + _skip_creation_if_none = True + + @property + def _max_attribute_name(self) -> str: + """Return the max attribute name.""" + return "active_power_max_ph_b" + + +@MULTI_MATCH(cluster_handler_names=CLUSTER_HANDLER_ELECTRICAL_MEASUREMENT) +class ElectricalMeasurementRMSActivePowerPhC(ElectricalMeasurementRMSActivePowerPhB): + """RMS active power phase C measurement.""" + + _attribute_name = "active_power_ph_c" + _unique_id_suffix = "active_power_ph_c" + _attr_translation_key: str = "active_power_ph_c" + + @property + def _max_attribute_name(self) -> str: + """Return the max attribute name.""" + return "active_power_max_ph_c" + + @MULTI_MATCH(cluster_handler_names=CLUSTER_HANDLER_ELECTRICAL_MEASUREMENT) class ElectricalMeasurementApparentPower(PolledElectricalMeasurement): """Apparent power measurement.""" @@ -700,7 +735,7 @@ class ElectricalMeasurementRMSCurrent(PolledElectricalMeasurement): @MULTI_MATCH(cluster_handler_names=CLUSTER_HANDLER_ELECTRICAL_MEASUREMENT) class ElectricalMeasurementRMSCurrentPhB(ElectricalMeasurementRMSCurrent): - """RMS current measurement.""" + """RMS current phase B measurement.""" _attribute_name = "rms_current_ph_b" _unique_id_suffix = "rms_current_ph_b" @@ -715,7 +750,7 @@ def _max_attribute_name(self) -> str: @MULTI_MATCH(cluster_handler_names=CLUSTER_HANDLER_ELECTRICAL_MEASUREMENT) class ElectricalMeasurementRMSCurrentPhC(ElectricalMeasurementRMSCurrentPhB): - """RMS current measurement.""" + """RMS current phase C measurement.""" _attribute_name: str = "rms_current_ph_c" _unique_id_suffix: str = "rms_current_ph_c" @@ -739,6 +774,35 @@ class ElectricalMeasurementRMSVoltage(PolledElectricalMeasurement): _div_mul_prefix = "ac_voltage" +@MULTI_MATCH(cluster_handler_names=CLUSTER_HANDLER_ELECTRICAL_MEASUREMENT) +class ElectricalMeasurementRMSVoltagePhB(ElectricalMeasurementRMSVoltage): + """RMS voltage phase B measurement.""" + + _attribute_name = "rms_voltage_ph_b" + _unique_id_suffix = "rms_voltage_ph_b" + _attr_translation_key: str = "rms_voltage_ph_b" + _skip_creation_if_none = True + + @property + def _max_attribute_name(self) -> str: + """Return the max attribute name.""" + return "rms_voltage_max_ph_b" + + +@MULTI_MATCH(cluster_handler_names=CLUSTER_HANDLER_ELECTRICAL_MEASUREMENT) +class ElectricalMeasurementRMSVoltagePhC(ElectricalMeasurementRMSVoltagePhB): + """RMS voltage phase C measurement.""" + + _attribute_name = "rms_voltage_ph_c" + _unique_id_suffix = "rms_voltage_ph_c" + _attr_translation_key: str = "rms_voltage_ph_c" + + @property + def _max_attribute_name(self) -> str: + """Return the max attribute name.""" + return "rms_voltage_max_ph_c" + + @MULTI_MATCH(cluster_handler_names=CLUSTER_HANDLER_ELECTRICAL_MEASUREMENT) class ElectricalMeasurementFrequency(PolledElectricalMeasurement): """Frequency measurement.""" @@ -764,6 +828,34 @@ class ElectricalMeasurementPowerFactor(PolledElectricalMeasurement): _div_mul_prefix = None +@MULTI_MATCH(cluster_handler_names=CLUSTER_HANDLER_ELECTRICAL_MEASUREMENT) +class ElectricalMeasurementRMSPowerFactorPhB(ElectricalMeasurementPowerFactor): + """Power factor phase B measurement.""" + + _attribute_name = "power_factor_ph_b" + _unique_id_suffix = "power_factor_ph_b" + _attr_translation_key: str = "power_factor_ph_b" + + @property + def _max_attribute_name(self) -> str: + """Return the max attribute name.""" + return "power_factor_max_ph_b" + + +@MULTI_MATCH(cluster_handler_names=CLUSTER_HANDLER_ELECTRICAL_MEASUREMENT) +class ElectricalMeasurementRMSPowerFactorPhC(ElectricalMeasurementRMSPowerFactorPhB): + """Power factor phase C measurement.""" + + _attribute_name = "power_factor_ph_c" + _unique_id_suffix = "power_factor_ph_c" + _attr_translation_key: str = "power_factor_ph_c" + + @property + def _max_attribute_name(self) -> str: + """Return the max attribute name.""" + return "power_factor_max_ph_c" + + @MULTI_MATCH( generic_ids=CLUSTER_HANDLER_ST_HUMIDITY_CLUSTER, stop_on_match_group=CLUSTER_HANDLER_HUMIDITY, From 0c64f1b1964c06b2799ed132c27dc5c12834301d Mon Sep 17 00:00:00 2001 From: abmantis Date: Fri, 27 Dec 2024 23:32:02 +0000 Subject: [PATCH 7/9] Add missing attr --- zha/application/platforms/sensor/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/zha/application/platforms/sensor/__init__.py b/zha/application/platforms/sensor/__init__.py index 54fa7d79a..8f26b5ce7 100644 --- a/zha/application/platforms/sensor/__init__.py +++ b/zha/application/platforms/sensor/__init__.py @@ -835,6 +835,7 @@ class ElectricalMeasurementRMSPowerFactorPhB(ElectricalMeasurementPowerFactor): _attribute_name = "power_factor_ph_b" _unique_id_suffix = "power_factor_ph_b" _attr_translation_key: str = "power_factor_ph_b" + _skip_creation_if_none = True @property def _max_attribute_name(self) -> str: From 9a737c985c6ba1cd2c5717943e646b3921eadebb Mon Sep 17 00:00:00 2001 From: abmantis Date: Wed, 15 Jan 2025 23:24:58 +0000 Subject: [PATCH 8/9] Address review comments --- tests/test_cluster_handlers.py | 11 ---- tests/test_sensor.py | 9 --- zha/application/platforms/sensor/__init__.py | 10 ++- zha/zigbee/cluster_handlers/homeautomation.py | 64 +++---------------- 4 files changed, 16 insertions(+), 78 deletions(-) diff --git a/tests/test_cluster_handlers.py b/tests/test_cluster_handlers.py index cab79f51c..00fdc6fee 100644 --- a/tests/test_cluster_handlers.py +++ b/tests/test_cluster_handlers.py @@ -286,27 +286,16 @@ async def poll_control_device_mock(zha_gateway: Gateway) -> Device: 1, { "ac_frequency", - "ac_frequency_max", "active_power", "active_power_ph_b", "active_power_ph_c", - "active_power_max", "apparent_power", - "power_factor", - "power_factor_ph_b", - "power_factor_ph_c", "rms_current", "rms_current_ph_b", "rms_current_ph_c", - "rms_current_max", - "rms_current_max_b", - "rms_current_max_c", "rms_voltage", "rms_voltage_ph_b", "rms_voltage_ph_c", - "rms_voltage_max", - "rms_voltage_max_ph_b", - "rms_voltage_max_ph_c", }, ), ], diff --git a/tests/test_sensor.py b/tests/test_sensor.py index e53d7f659..c85381611 100644 --- a/tests/test_sensor.py +++ b/tests/test_sensor.py @@ -1111,26 +1111,17 @@ async def test_elec_measurement_sensor_polling(zha_gateway: Gateway) -> None: set(), { "active_power", - "active_power_max", "rms_current", - "rms_current_max", "rms_voltage", - "rms_voltage_max", }, { "active_power", }, - { - "active_power", - "active_power_max", - }, { "rms_current", - "rms_current_max", }, { "rms_voltage", - "rms_voltage_max", }, ), ) diff --git a/zha/application/platforms/sensor/__init__.py b/zha/application/platforms/sensor/__init__.py index b5214655f..7fe2c478f 100644 --- a/zha/application/platforms/sensor/__init__.py +++ b/zha/application/platforms/sensor/__init__.py @@ -707,12 +707,14 @@ class ElectricalMeasurementRMSActivePowerPhB(PolledElectricalMeasurement): @MULTI_MATCH(cluster_handler_names=CLUSTER_HANDLER_ELECTRICAL_MEASUREMENT) -class ElectricalMeasurementRMSActivePowerPhC(ElectricalMeasurementRMSActivePowerPhB): +class ElectricalMeasurementRMSActivePowerPhC(PolledElectricalMeasurement): """RMS active power phase C measurement.""" _attribute_name = "active_power_ph_c" _unique_id_suffix = "active_power_ph_c" _attr_translation_key: str = "active_power_ph_c" + _use_custom_polling = False # Poll indirectly by ElectricalMeasurementSensor + _skip_creation_if_no_attr_cache = True _attr_max_attribute_name = "active_power_max_ph_c" @@ -786,12 +788,13 @@ class ElectricalMeasurementRMSVoltagePhB(ElectricalMeasurementRMSVoltage): @MULTI_MATCH(cluster_handler_names=CLUSTER_HANDLER_ELECTRICAL_MEASUREMENT) -class ElectricalMeasurementRMSVoltagePhC(ElectricalMeasurementRMSVoltagePhB): +class ElectricalMeasurementRMSVoltagePhC(ElectricalMeasurementRMSVoltage): """RMS voltage phase C measurement.""" _attribute_name = "rms_voltage_ph_c" _unique_id_suffix = "rms_voltage_ph_c" _attr_translation_key: str = "rms_voltage_ph_c" + _skip_creation_if_no_attr_cache = True _attr_max_attribute_name = "rms_voltage_max_ph_c" @@ -832,12 +835,13 @@ class ElectricalMeasurementPowerFactorPhB(ElectricalMeasurementPowerFactor): @MULTI_MATCH(cluster_handler_names=CLUSTER_HANDLER_ELECTRICAL_MEASUREMENT) -class ElectricalMeasurementPowerFactorPhC(ElectricalMeasurementPowerFactorPhB): +class ElectricalMeasurementPowerFactorPhC(ElectricalMeasurementPowerFactor): """Power factor phase C measurement.""" _attribute_name = "power_factor_ph_c" _unique_id_suffix = "power_factor_ph_c" _attr_translation_key: str = "power_factor_ph_c" + _skip_creation_if_no_attr_cache = True _attr_max_attribute_name = "power_factor_max_ph_c" diff --git a/zha/zigbee/cluster_handlers/homeautomation.py b/zha/zigbee/cluster_handlers/homeautomation.py index a504c8f3a..b16b12883 100644 --- a/zha/zigbee/cluster_handlers/homeautomation.py +++ b/zha/zigbee/cluster_handlers/homeautomation.py @@ -16,7 +16,6 @@ from zha.zigbee.cluster_handlers import AttrReportConfig, ClusterHandler, registries from zha.zigbee.cluster_handlers.const import ( CLUSTER_HANDLER_ELECTRICAL_MEASUREMENT, - REPORT_CONFIG_DEFAULT, REPORT_CONFIG_OP, ) @@ -73,18 +72,6 @@ class MeasurementType(enum.IntFlag): attr=ElectricalMeasurement.AttributeDefs.active_power_ph_c.name, config=REPORT_CONFIG_OP, ), - AttrReportConfig( - attr=ElectricalMeasurement.AttributeDefs.active_power_max.name, - config=REPORT_CONFIG_DEFAULT, - ), - AttrReportConfig( - attr=ElectricalMeasurement.AttributeDefs.active_power_max_ph_b.name, - config=REPORT_CONFIG_DEFAULT, - ), - AttrReportConfig( - attr=ElectricalMeasurement.AttributeDefs.active_power_max_ph_c.name, - config=REPORT_CONFIG_DEFAULT, - ), AttrReportConfig( attr=ElectricalMeasurement.AttributeDefs.apparent_power.name, config=REPORT_CONFIG_OP, @@ -101,18 +88,6 @@ class MeasurementType(enum.IntFlag): attr=ElectricalMeasurement.AttributeDefs.rms_current_ph_c.name, config=REPORT_CONFIG_OP, ), - AttrReportConfig( - attr=ElectricalMeasurement.AttributeDefs.rms_current_max.name, - config=REPORT_CONFIG_DEFAULT, - ), - AttrReportConfig( - attr=ElectricalMeasurement.AttributeDefs.rms_current_max_ph_b.name, - config=REPORT_CONFIG_DEFAULT, - ), - AttrReportConfig( - attr=ElectricalMeasurement.AttributeDefs.rms_current_max_ph_c.name, - config=REPORT_CONFIG_DEFAULT, - ), AttrReportConfig( attr=ElectricalMeasurement.AttributeDefs.rms_voltage.name, config=REPORT_CONFIG_OP, @@ -125,51 +100,30 @@ class MeasurementType(enum.IntFlag): attr=ElectricalMeasurement.AttributeDefs.rms_voltage_ph_c.name, config=REPORT_CONFIG_OP, ), - AttrReportConfig( - attr=ElectricalMeasurement.AttributeDefs.rms_voltage_max.name, - config=REPORT_CONFIG_DEFAULT, - ), - AttrReportConfig( - attr=ElectricalMeasurement.AttributeDefs.rms_voltage_max_ph_b.name, - config=REPORT_CONFIG_DEFAULT, - ), - AttrReportConfig( - attr=ElectricalMeasurement.AttributeDefs.rms_voltage_max_ph_c.name, - config=REPORT_CONFIG_DEFAULT, - ), AttrReportConfig( attr=ElectricalMeasurement.AttributeDefs.ac_frequency.name, config=REPORT_CONFIG_OP, ), - AttrReportConfig( - attr=ElectricalMeasurement.AttributeDefs.ac_frequency_max.name, - config=REPORT_CONFIG_DEFAULT, - ), - AttrReportConfig( - attr=ElectricalMeasurement.AttributeDefs.power_factor.name, - config=REPORT_CONFIG_OP, - ), - AttrReportConfig( - attr=ElectricalMeasurement.AttributeDefs.power_factor_ph_b.name, - config=REPORT_CONFIG_OP, - ), - AttrReportConfig( - attr=ElectricalMeasurement.AttributeDefs.power_factor_ph_c.name, - config=REPORT_CONFIG_OP, - ), ) ZCL_INIT_ATTRS = { ElectricalMeasurement.AttributeDefs.ac_current_divisor.name: True, ElectricalMeasurement.AttributeDefs.ac_current_multiplier.name: True, + ElectricalMeasurement.AttributeDefs.ac_frequency_divisor.name: True, + ElectricalMeasurement.AttributeDefs.ac_frequency_max.name: False, + ElectricalMeasurement.AttributeDefs.ac_frequency_multiplier.name: True, ElectricalMeasurement.AttributeDefs.ac_power_divisor.name: True, ElectricalMeasurement.AttributeDefs.ac_power_multiplier.name: True, ElectricalMeasurement.AttributeDefs.ac_voltage_divisor.name: True, ElectricalMeasurement.AttributeDefs.ac_voltage_multiplier.name: True, - ElectricalMeasurement.AttributeDefs.ac_frequency_divisor.name: True, - ElectricalMeasurement.AttributeDefs.ac_frequency_multiplier.name: True, + ElectricalMeasurement.AttributeDefs.active_power_max.name: False, ElectricalMeasurement.AttributeDefs.measurement_type.name: True, ElectricalMeasurement.AttributeDefs.power_divisor.name: True, + ElectricalMeasurement.AttributeDefs.power_factor.name: False, + ElectricalMeasurement.AttributeDefs.power_factor_ph_b.name: False, + ElectricalMeasurement.AttributeDefs.power_factor_ph_c.name: False, ElectricalMeasurement.AttributeDefs.power_multiplier.name: True, + ElectricalMeasurement.AttributeDefs.rms_current_max.name: False, + ElectricalMeasurement.AttributeDefs.rms_voltage_max.name: False, } async def async_update(self): From 15ff910fa3ed43115b686a9c1725776024a20170 Mon Sep 17 00:00:00 2001 From: abmantis Date: Fri, 17 Jan 2025 23:52:53 +0000 Subject: [PATCH 9/9] Move attrs back to REPORT_CONFIG --- tests/test_cluster_handlers.py | 11 ++++ tests/test_sensor.py | 9 +++ zha/zigbee/cluster_handlers/homeautomation.py | 64 ++++++++++++++++--- 3 files changed, 75 insertions(+), 9 deletions(-) diff --git a/tests/test_cluster_handlers.py b/tests/test_cluster_handlers.py index 00fdc6fee..cab79f51c 100644 --- a/tests/test_cluster_handlers.py +++ b/tests/test_cluster_handlers.py @@ -286,16 +286,27 @@ async def poll_control_device_mock(zha_gateway: Gateway) -> Device: 1, { "ac_frequency", + "ac_frequency_max", "active_power", "active_power_ph_b", "active_power_ph_c", + "active_power_max", "apparent_power", + "power_factor", + "power_factor_ph_b", + "power_factor_ph_c", "rms_current", "rms_current_ph_b", "rms_current_ph_c", + "rms_current_max", + "rms_current_max_b", + "rms_current_max_c", "rms_voltage", "rms_voltage_ph_b", "rms_voltage_ph_c", + "rms_voltage_max", + "rms_voltage_max_ph_b", + "rms_voltage_max_ph_c", }, ), ], diff --git a/tests/test_sensor.py b/tests/test_sensor.py index c85381611..e53d7f659 100644 --- a/tests/test_sensor.py +++ b/tests/test_sensor.py @@ -1111,17 +1111,26 @@ async def test_elec_measurement_sensor_polling(zha_gateway: Gateway) -> None: set(), { "active_power", + "active_power_max", "rms_current", + "rms_current_max", "rms_voltage", + "rms_voltage_max", }, { "active_power", }, + { + "active_power", + "active_power_max", + }, { "rms_current", + "rms_current_max", }, { "rms_voltage", + "rms_voltage_max", }, ), ) diff --git a/zha/zigbee/cluster_handlers/homeautomation.py b/zha/zigbee/cluster_handlers/homeautomation.py index b16b12883..a504c8f3a 100644 --- a/zha/zigbee/cluster_handlers/homeautomation.py +++ b/zha/zigbee/cluster_handlers/homeautomation.py @@ -16,6 +16,7 @@ from zha.zigbee.cluster_handlers import AttrReportConfig, ClusterHandler, registries from zha.zigbee.cluster_handlers.const import ( CLUSTER_HANDLER_ELECTRICAL_MEASUREMENT, + REPORT_CONFIG_DEFAULT, REPORT_CONFIG_OP, ) @@ -72,6 +73,18 @@ class MeasurementType(enum.IntFlag): attr=ElectricalMeasurement.AttributeDefs.active_power_ph_c.name, config=REPORT_CONFIG_OP, ), + AttrReportConfig( + attr=ElectricalMeasurement.AttributeDefs.active_power_max.name, + config=REPORT_CONFIG_DEFAULT, + ), + AttrReportConfig( + attr=ElectricalMeasurement.AttributeDefs.active_power_max_ph_b.name, + config=REPORT_CONFIG_DEFAULT, + ), + AttrReportConfig( + attr=ElectricalMeasurement.AttributeDefs.active_power_max_ph_c.name, + config=REPORT_CONFIG_DEFAULT, + ), AttrReportConfig( attr=ElectricalMeasurement.AttributeDefs.apparent_power.name, config=REPORT_CONFIG_OP, @@ -88,6 +101,18 @@ class MeasurementType(enum.IntFlag): attr=ElectricalMeasurement.AttributeDefs.rms_current_ph_c.name, config=REPORT_CONFIG_OP, ), + AttrReportConfig( + attr=ElectricalMeasurement.AttributeDefs.rms_current_max.name, + config=REPORT_CONFIG_DEFAULT, + ), + AttrReportConfig( + attr=ElectricalMeasurement.AttributeDefs.rms_current_max_ph_b.name, + config=REPORT_CONFIG_DEFAULT, + ), + AttrReportConfig( + attr=ElectricalMeasurement.AttributeDefs.rms_current_max_ph_c.name, + config=REPORT_CONFIG_DEFAULT, + ), AttrReportConfig( attr=ElectricalMeasurement.AttributeDefs.rms_voltage.name, config=REPORT_CONFIG_OP, @@ -100,30 +125,51 @@ class MeasurementType(enum.IntFlag): attr=ElectricalMeasurement.AttributeDefs.rms_voltage_ph_c.name, config=REPORT_CONFIG_OP, ), + AttrReportConfig( + attr=ElectricalMeasurement.AttributeDefs.rms_voltage_max.name, + config=REPORT_CONFIG_DEFAULT, + ), + AttrReportConfig( + attr=ElectricalMeasurement.AttributeDefs.rms_voltage_max_ph_b.name, + config=REPORT_CONFIG_DEFAULT, + ), + AttrReportConfig( + attr=ElectricalMeasurement.AttributeDefs.rms_voltage_max_ph_c.name, + config=REPORT_CONFIG_DEFAULT, + ), AttrReportConfig( attr=ElectricalMeasurement.AttributeDefs.ac_frequency.name, config=REPORT_CONFIG_OP, ), + AttrReportConfig( + attr=ElectricalMeasurement.AttributeDefs.ac_frequency_max.name, + config=REPORT_CONFIG_DEFAULT, + ), + AttrReportConfig( + attr=ElectricalMeasurement.AttributeDefs.power_factor.name, + config=REPORT_CONFIG_OP, + ), + AttrReportConfig( + attr=ElectricalMeasurement.AttributeDefs.power_factor_ph_b.name, + config=REPORT_CONFIG_OP, + ), + AttrReportConfig( + attr=ElectricalMeasurement.AttributeDefs.power_factor_ph_c.name, + config=REPORT_CONFIG_OP, + ), ) ZCL_INIT_ATTRS = { ElectricalMeasurement.AttributeDefs.ac_current_divisor.name: True, ElectricalMeasurement.AttributeDefs.ac_current_multiplier.name: True, - ElectricalMeasurement.AttributeDefs.ac_frequency_divisor.name: True, - ElectricalMeasurement.AttributeDefs.ac_frequency_max.name: False, - ElectricalMeasurement.AttributeDefs.ac_frequency_multiplier.name: True, ElectricalMeasurement.AttributeDefs.ac_power_divisor.name: True, ElectricalMeasurement.AttributeDefs.ac_power_multiplier.name: True, ElectricalMeasurement.AttributeDefs.ac_voltage_divisor.name: True, ElectricalMeasurement.AttributeDefs.ac_voltage_multiplier.name: True, - ElectricalMeasurement.AttributeDefs.active_power_max.name: False, + ElectricalMeasurement.AttributeDefs.ac_frequency_divisor.name: True, + ElectricalMeasurement.AttributeDefs.ac_frequency_multiplier.name: True, ElectricalMeasurement.AttributeDefs.measurement_type.name: True, ElectricalMeasurement.AttributeDefs.power_divisor.name: True, - ElectricalMeasurement.AttributeDefs.power_factor.name: False, - ElectricalMeasurement.AttributeDefs.power_factor_ph_b.name: False, - ElectricalMeasurement.AttributeDefs.power_factor_ph_c.name: False, ElectricalMeasurement.AttributeDefs.power_multiplier.name: True, - ElectricalMeasurement.AttributeDefs.rms_current_max.name: False, - ElectricalMeasurement.AttributeDefs.rms_voltage_max.name: False, } async def async_update(self):