From 5348f724828c44aec12962c731ef1885962ee16f Mon Sep 17 00:00:00 2001 From: TheJulianJES Date: Tue, 11 Nov 2025 00:48:09 +0100 Subject: [PATCH 1/6] Expose Frient EMI LED `current_summation` write-only attribute --- zhaquirks/develco/emi_led.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/zhaquirks/develco/emi_led.py b/zhaquirks/develco/emi_led.py index 949f646c46..a55e00e814 100644 --- a/zhaquirks/develco/emi_led.py +++ b/zhaquirks/develco/emi_led.py @@ -25,6 +25,11 @@ class AttributeDefs(BaseAttributeDefs): type=t.uint16_t, is_manufacturer_specific=True, ) + current_summation: Final = ZCLAttributeDef( + id=0x0301, + type=t.uint48_t, + is_manufacturer_specific=True, + ) ( From 185242be5bf16600f7d304a36dfb2920ab7c20c4 Mon Sep 17 00:00:00 2001 From: TheJulianJES Date: Tue, 11 Nov 2025 00:48:25 +0100 Subject: [PATCH 2/6] Add write attribute button to reset summation delivered --- zhaquirks/develco/emi_led.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/zhaquirks/develco/emi_led.py b/zhaquirks/develco/emi_led.py index a55e00e814..dc86eb0281 100644 --- a/zhaquirks/develco/emi_led.py +++ b/zhaquirks/develco/emi_led.py @@ -48,5 +48,13 @@ class AttributeDefs(BaseAttributeDefs): translation_key="pulse_configuration", fallback_name="Pulse configuration", ) + .write_attr_button( + attribute_name=ManufacturerMetering.AttributeDefs.current_summation.name, + attribute_value=0, + cluster_id=ManufacturerMetering.cluster_id, + endpoint_id=2, + translation_key="reset_summation_delivered", + fallback_name="Reset summation delivered", + ) .add_to_registry() ) From 296618b82146c84476edafe1efa8d7badc787a31 Mon Sep 17 00:00:00 2001 From: TheJulianJES Date: Tue, 11 Nov 2025 01:19:16 +0100 Subject: [PATCH 3/6] Mark attribute as read-only --- zhaquirks/develco/emi_led.py | 1 + 1 file changed, 1 insertion(+) diff --git a/zhaquirks/develco/emi_led.py b/zhaquirks/develco/emi_led.py index dc86eb0281..c16b962ecf 100644 --- a/zhaquirks/develco/emi_led.py +++ b/zhaquirks/develco/emi_led.py @@ -28,6 +28,7 @@ class AttributeDefs(BaseAttributeDefs): current_summation: Final = ZCLAttributeDef( id=0x0301, type=t.uint48_t, + access="r", is_manufacturer_specific=True, ) From 489a5efe872fb0735a00b26f3b9366bc43ab13a9 Mon Sep 17 00:00:00 2001 From: TheJulianJES Date: Wed, 12 Nov 2025 05:34:04 +0100 Subject: [PATCH 4/6] Also fix divisor and multiplier for Frient EMI LED --- zhaquirks/develco/emi_led.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/zhaquirks/develco/emi_led.py b/zhaquirks/develco/emi_led.py index c16b962ecf..84c3bcbcaf 100644 --- a/zhaquirks/develco/emi_led.py +++ b/zhaquirks/develco/emi_led.py @@ -1,6 +1,6 @@ """Frient Electricity Meter Interface LED variant.""" -from typing import Final +from typing import Any, Final from zigpy.quirks import CustomCluster from zigpy.quirks.v2 import QuirkBuilder @@ -33,8 +33,29 @@ class AttributeDefs(BaseAttributeDefs): ) +class FrientMetering(CustomCluster, Metering): + """Frient EMI P1 Metering cluster definition.""" + + # fix device issue (conflicting with manufacturer specific interface mode attr) + _CONSTANT_ATTRIBUTES = { + Metering.AttributeDefs.divisor.id: 1000, + Metering.AttributeDefs.multiplier.id: 1, + } + + def _update_attribute(self, attrid: int | t.uint16_t, value: Any) -> None: + """Update attribute with value.""" + # prevent attribute_updated events for divisor and multiplier + if attrid in ( + Metering.AttributeDefs.divisor.id, + Metering.AttributeDefs.multiplier.id, + ): + return + super()._update_attribute(attrid, value) + + ( QuirkBuilder("frient A/S", "EMIZB-141") + .replaces(FrientMetering, endpoint_id=2) .replaces(ManufacturerMetering, endpoint_id=2) .device_class(ManufacturerDeviceV2) .number( From 84e2a4fad2a0bca663cb589cb28eaf24a5227ce1 Mon Sep 17 00:00:00 2001 From: TheJulianJES Date: Wed, 12 Nov 2025 05:35:31 +0100 Subject: [PATCH 5/6] WIP: Expand tests to include EMI LED --- tests/test_develco.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/tests/test_develco.py b/tests/test_develco.py index dd95d12130..f813e026cb 100644 --- a/tests/test_develco.py +++ b/tests/test_develco.py @@ -2,8 +2,9 @@ from unittest import mock +import pytest import zigpy.types as t -from zigpy.zcl import ClusterType, foundation +from zigpy.zcl import foundation from zigpy.zcl.clusters.smartenergy import Metering from tests.common import ClusterListener @@ -17,7 +18,7 @@ async def test_frient_emi(zigpy_device_from_v2_quirk): device = zigpy_device_from_v2_quirk( "frient A/S", "EMIZB-141", - cluster_ids={2: {Metering.cluster_id: ClusterType.Server}}, + endpoint_ids=[2], ) metering_cluster = device.endpoints[2].smartenergy_metering @@ -141,9 +142,16 @@ async def test_frient_emi(zigpy_device_from_v2_quirk): assert result == (foundation.Status.SUCCESS, "done") -async def test_mfg_cluster_events(zigpy_device_from_v2_quirk): +@pytest.mark.parametrize( + ("manufacturer", "model"), + [ + ("frient A/S", "EMIZB-132"), + ("frient A/S", "EMIZB-141"), + ], +) +async def test_mfg_cluster_events(zigpy_device_from_v2_quirk, manufacturer, model): """Test Frient EMI Norwegian HAN ignoring incorrect divisor attribute reports.""" - device = zigpy_device_from_v2_quirk("frient A/S", "EMIZB-132", endpoint_ids=[1, 2]) + device = zigpy_device_from_v2_quirk(manufacturer, model, endpoint_ids=[1, 2]) metering_cluster = device.endpoints[2].smartenergy_metering metering_listener = ClusterListener(metering_cluster) From f8695e495b0ab2b2b8ce9cad666f51ac5785995f Mon Sep 17 00:00:00 2001 From: TheJulianJES Date: Wed, 12 Nov 2025 05:48:43 +0100 Subject: [PATCH 6/6] Fix typo in docstring --- zhaquirks/develco/emi_led.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zhaquirks/develco/emi_led.py b/zhaquirks/develco/emi_led.py index 84c3bcbcaf..2c52a83442 100644 --- a/zhaquirks/develco/emi_led.py +++ b/zhaquirks/develco/emi_led.py @@ -34,7 +34,7 @@ class AttributeDefs(BaseAttributeDefs): class FrientMetering(CustomCluster, Metering): - """Frient EMI P1 Metering cluster definition.""" + """Frient EMI LED Metering cluster definition.""" # fix device issue (conflicting with manufacturer specific interface mode attr) _CONSTANT_ATTRIBUTES = {