From 59428a9582c35f15e65747e0db02eab9af2890cc Mon Sep 17 00:00:00 2001 From: yoda-jm Date: Wed, 12 Aug 2026 12:38:07 +0200 Subject: [PATCH 1/7] Convert the ZLinky_TIC quirk to v2 The four v1 subclasses matched firmware variants by exact cluster list: the base signature, plus PowerConfiguration on v12, Time and the Tuya cluster on v14, and a different device type on v15. Matching on manufacturer and model covers all of them, so ZLinkyTIC, ZLinkyTICFWV12, ZLinkyTICFWV14 and ZLinkyTICFWV15 collapse into a single QuirkBuilder. PowerConfiguration is added unconditionally, as the v1 replacement already did for every variant. The two ZLinky entries in the test_suspicious_cluster_moves allow list go away with the v1 classes. No functional change intended: same clusters replaced, same entities. --- tests/test_lixee.py | 29 ++++++++++ tests/test_quirks.py | 3 - zhaquirks/lixee/zlinky.py | 117 ++++---------------------------------- 3 files changed, 39 insertions(+), 110 deletions(-) create mode 100644 tests/test_lixee.py diff --git a/tests/test_lixee.py b/tests/test_lixee.py new file mode 100644 index 0000000000..c7136742a5 --- /dev/null +++ b/tests/test_lixee.py @@ -0,0 +1,29 @@ +"""Tests for LiXee ZLinky_TIC quirks.""" + +from zigpy.zcl.clusters.general import PowerConfiguration +from zigpy.zcl.clusters.smartenergy import Metering + +from zhaquirks.lixee import LIXEE, ZLINKY_MANUFACTURER_CLUSTER_ID +from zhaquirks.lixee.zlinky import ZLinkyTICManufacturerCluster, ZLinkyTICMetering + + +def test_zlinky_clusters_replaced(zigpy_device_from_v2_quirk) -> None: + """Test that the ZLinky_TIC quirk replaces the expected clusters.""" + device = zigpy_device_from_v2_quirk( + LIXEE, + "ZLinky_TIC", + cluster_ids={ + 1: { + Metering.cluster_id: None, + ZLINKY_MANUFACTURER_CLUSTER_ID: None, + } + }, + ) + endpoint = device.endpoints[1] + + assert isinstance(endpoint.smartenergy_metering, ZLinkyTICMetering) + assert isinstance( + endpoint.zlinky_manufacturer_specific, ZLinkyTICManufacturerCluster + ) + # Not all firmware variants report it, the quirk adds it unconditionally + assert PowerConfiguration.cluster_id in endpoint.in_clusters diff --git a/tests/test_quirks.py b/tests/test_quirks.py index 36f68f1ee3..4384378f8f 100644 --- a/tests/test_quirks.py +++ b/tests/test_quirks.py @@ -920,9 +920,6 @@ def check_for_duplicate_cluster_ids(clusters) -> None: zhaquirks.aduro.adurolightncc.AdurolightNCC, # add a bunch of output clusters (Zhongxing motion sensor): zhaquirks.zhongxing.motion.SN10ZW, - # remove Tuya clusters from input and output clusters (ZLinky): - zhaquirks.lixee.zlinky.ZLinkyTICFWV14, - zhaquirks.lixee.zlinky.ZLinkyTICFWV15, ) ], ) diff --git a/zhaquirks/lixee/zlinky.py b/zhaquirks/lixee/zlinky.py index f23fa1d29d..6c76ca5cac 100644 --- a/zhaquirks/lixee/zlinky.py +++ b/zhaquirks/lixee/zlinky.py @@ -1,34 +1,15 @@ """Quirk for ZLinky_TIC.""" -from copy import deepcopy from typing import Final -from zigpy.profiles import zgp, zha import zigpy.types as t -from zigpy.zcl.clusters.general import ( - Basic, - GreenPowerProxy, - Identify, - Ota, - PowerConfiguration, - Time, -) -from zigpy.zcl.clusters.homeautomation import ElectricalMeasurement, MeterIdentification +from zigpy.zcl.clusters.general import PowerConfiguration from zigpy.zcl.clusters.smartenergy import Metering from zigpy.zcl.foundation import BaseAttributeDefs, ZCLAttributeDef +from zhaquirks.builder import QuirkBuilder from zhaquirks.clusters import CustomCluster -from zhaquirks.const import ( - DEVICE_TYPE, - ENDPOINTS, - INPUT_CLUSTERS, - MODELS_INFO, - OUTPUT_CLUSTERS, - PROFILE_ID, -) -from zhaquirks.legacy import CustomDevice from zhaquirks.lixee import LIXEE, ZLINKY_MANUFACTURER_CLUSTER_ID -from zhaquirks.tuya import TuyaManufCluster class ZLinkyTICManufacturerCluster(CustomCluster): @@ -226,89 +207,11 @@ class ZLinkyTICMetering(CustomCluster, Metering): _CONSTANT_ATTRIBUTES = {MULTIPLIER: 1, DIVISOR: 1000} -class ZLinkyTIC(CustomDevice): - """ZLinky_TIC from LiXee.""" - - signature = { - MODELS_INFO: [(LIXEE, "ZLinky_TIC")], - ENDPOINTS: { - 1: { - PROFILE_ID: zha.PROFILE_ID, - DEVICE_TYPE: zha.DeviceType.METER_INTERFACE, - INPUT_CLUSTERS: [ - Basic.cluster_id, - Identify.cluster_id, - Metering.cluster_id, - MeterIdentification.cluster_id, - ElectricalMeasurement.cluster_id, - ZLinkyTICManufacturerCluster.cluster_id, - ], - OUTPUT_CLUSTERS: [Ota.cluster_id], - }, - 242: { - PROFILE_ID: zgp.PROFILE_ID, - DEVICE_TYPE: zgp.DeviceType.PROXY_BASIC, - INPUT_CLUSTERS: [GreenPowerProxy.cluster_id], - OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id], - }, - }, - } - replacement = { - ENDPOINTS: { - 1: { - PROFILE_ID: zha.PROFILE_ID, - DEVICE_TYPE: zha.DeviceType.METER_INTERFACE, - INPUT_CLUSTERS: [ - Basic.cluster_id, - PowerConfiguration.cluster_id, - Identify.cluster_id, - ZLinkyTICMetering, - MeterIdentification.cluster_id, - ElectricalMeasurement.cluster_id, - ZLinkyTICManufacturerCluster, - ], - OUTPUT_CLUSTERS: [Ota.cluster_id], - }, - 242: { - PROFILE_ID: zgp.PROFILE_ID, - DEVICE_TYPE: zgp.DeviceType.PROXY_BASIC, - INPUT_CLUSTERS: [GreenPowerProxy.cluster_id], - OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id], - }, - }, - } - - -class ZLinkyTICFWV12(ZLinkyTIC): - """ZLinky_TIC from LiXee with firmware v12.0 & v13.0.""" - - signature = deepcopy(ZLinkyTIC.signature) - - # Insert PowerConfiguration cluster in signature for devices with firmware v12.0 & v13.0 - signature[ENDPOINTS][1][INPUT_CLUSTERS].insert(1, PowerConfiguration.cluster_id) - - -class ZLinkyTICFWV14(ZLinkyTICFWV12): - """ZLinky_TIC from LiXee with firmware v14.0+.""" - - signature = deepcopy(ZLinkyTICFWV12.signature) - replacement = deepcopy(ZLinkyTICFWV12.replacement) - - # Insert Time configuration cluster in signature for devices with firmware v14.0+ - signature[ENDPOINTS][1][INPUT_CLUSTERS].insert(1, Time.cluster_id) - - # Insert Tuya cluster in signature for devices with firmware v14.0+ - signature[ENDPOINTS][1][INPUT_CLUSTERS].insert(7, TuyaManufCluster.cluster_id) - signature[ENDPOINTS][1][OUTPUT_CLUSTERS].insert(1, TuyaManufCluster.cluster_id) - - replacement[ENDPOINTS][1][INPUT_CLUSTERS].insert(1, Time.cluster_id) - - -class ZLinkyTICFWV15(ZLinkyTICFWV14): - """ZLinky_TIC from LiXee with firmware v15.0+.""" - - signature = deepcopy(ZLinkyTICFWV14.signature) - replacement = deepcopy(ZLinkyTICFWV14.replacement) - - signature[ENDPOINTS][1][DEVICE_TYPE] = zha.DeviceType.DIMMABLE_LIGHT - replacement[ENDPOINTS][1][DEVICE_TYPE] = zha.DeviceType.DIMMABLE_LIGHT +( + QuirkBuilder(LIXEE, "ZLinky_TIC") + # Not all firmware variants have a power configuration cluster + .adds(PowerConfiguration.cluster_id, endpoint_id=1) + .replaces(ZLinkyTICMetering, endpoint_id=1) + .replaces(ZLinkyTICManufacturerCluster, endpoint_id=1) + .add_to_registry() +) From cda18772a7a09029f928f965b29950e59786b0fb Mon Sep 17 00:00:00 2001 From: yoda-jm Date: Wed, 12 Aug 2026 13:02:14 +0200 Subject: [PATCH 2/7] Keep removing the Tuya cluster the ZLinky_TIC does not implement Firmware v14 and later report a Tuya cluster in both directions that the device does not implement. The v1 quirk dropped it by listing it in the FWV14 and FWV15 signatures but not in their replacements, which is what the two ZLinky entries in the test_suspicious_cluster_moves allow list were about. The v2 conversion lost that, so restore it explicitly. RemoveCluster pops with a default, so this is a no-op on firmware that does not report the cluster, and a test now pins the behaviour rather than an allow list entry. --- tests/test_lixee.py | 20 ++++++++++++++++++++ zhaquirks/lixee/zlinky.py | 9 +++++++++ 2 files changed, 29 insertions(+) diff --git a/tests/test_lixee.py b/tests/test_lixee.py index c7136742a5..0260ab7676 100644 --- a/tests/test_lixee.py +++ b/tests/test_lixee.py @@ -1,10 +1,12 @@ """Tests for LiXee ZLinky_TIC quirks.""" +from zigpy.zcl import ClusterType from zigpy.zcl.clusters.general import PowerConfiguration from zigpy.zcl.clusters.smartenergy import Metering from zhaquirks.lixee import LIXEE, ZLINKY_MANUFACTURER_CLUSTER_ID from zhaquirks.lixee.zlinky import ZLinkyTICManufacturerCluster, ZLinkyTICMetering +from zhaquirks.tuya import TuyaManufCluster def test_zlinky_clusters_replaced(zigpy_device_from_v2_quirk) -> None: @@ -27,3 +29,21 @@ def test_zlinky_clusters_replaced(zigpy_device_from_v2_quirk) -> None: ) # Not all firmware variants report it, the quirk adds it unconditionally assert PowerConfiguration.cluster_id in endpoint.in_clusters + + +def test_zlinky_tuya_cluster_removed(zigpy_device_from_v2_quirk) -> None: + """Test that the Tuya cluster firmware v14+ reports is removed.""" + device = zigpy_device_from_v2_quirk( + LIXEE, + "ZLinky_TIC", + cluster_ids={ + 1: { + ZLINKY_MANUFACTURER_CLUSTER_ID: None, + TuyaManufCluster.cluster_id: ClusterType.Server, + } + }, + ) + endpoint = device.endpoints[1] + + assert TuyaManufCluster.cluster_id not in endpoint.in_clusters + assert TuyaManufCluster.cluster_id not in endpoint.out_clusters diff --git a/zhaquirks/lixee/zlinky.py b/zhaquirks/lixee/zlinky.py index 6c76ca5cac..8cf3d6ee21 100644 --- a/zhaquirks/lixee/zlinky.py +++ b/zhaquirks/lixee/zlinky.py @@ -3,6 +3,7 @@ from typing import Final import zigpy.types as t +from zigpy.zcl import ClusterType from zigpy.zcl.clusters.general import PowerConfiguration from zigpy.zcl.clusters.smartenergy import Metering from zigpy.zcl.foundation import BaseAttributeDefs, ZCLAttributeDef @@ -10,6 +11,7 @@ from zhaquirks.builder import QuirkBuilder from zhaquirks.clusters import CustomCluster from zhaquirks.lixee import LIXEE, ZLINKY_MANUFACTURER_CLUSTER_ID +from zhaquirks.tuya import TuyaManufCluster class ZLinkyTICManufacturerCluster(CustomCluster): @@ -211,6 +213,13 @@ class ZLinkyTICMetering(CustomCluster, Metering): QuirkBuilder(LIXEE, "ZLinky_TIC") # Not all firmware variants have a power configuration cluster .adds(PowerConfiguration.cluster_id, endpoint_id=1) + # Firmware v14 and later report a Tuya cluster the device does not + # implement, as the v1 signatures for those variants recorded. Removing it + # keeps the v1 replacement behaviour; it is a no-op on older firmware. + .removes(TuyaManufCluster.cluster_id, endpoint_id=1) + .removes( + TuyaManufCluster.cluster_id, endpoint_id=1, cluster_type=ClusterType.Client + ) .replaces(ZLinkyTICMetering, endpoint_id=1) .replaces(ZLinkyTICManufacturerCluster, endpoint_id=1) .add_to_registry() From a013b9ab6ace9b6810098eb4bf73ac823a702286 Mon Sep 17 00:00:00 2001 From: yoda-jm Date: Wed, 12 Aug 2026 13:12:55 +0200 Subject: [PATCH 3/7] Record why the v1 subclasses collapse and the allow list entries go The repository squash-merges, so commit messages do not reach the branch history. Move the parts worth keeping into the code itself: what distinguished the four v1 firmware subclasses and why matching on manufacturer and model replaces them, and why the two ZLinky entries in the test_suspicious_cluster_moves allow list existed, now recorded in the docstring of the test that replaces them. Comments only, no behaviour change. --- tests/test_lixee.py | 7 ++++++- zhaquirks/lixee/zlinky.py | 8 +++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/tests/test_lixee.py b/tests/test_lixee.py index 0260ab7676..e1837fbdbc 100644 --- a/tests/test_lixee.py +++ b/tests/test_lixee.py @@ -32,7 +32,12 @@ def test_zlinky_clusters_replaced(zigpy_device_from_v2_quirk) -> None: def test_zlinky_tuya_cluster_removed(zigpy_device_from_v2_quirk) -> None: - """Test that the Tuya cluster firmware v14+ reports is removed.""" + """Test that the Tuya cluster firmware v14+ reports is removed. + + The v1 quirk dropped it implicitly, by listing it in the FWV14 and FWV15 + signatures but not in their replacements. That is what the two ZLinky + entries in test_suspicious_cluster_moves recorded; this test replaces them. + """ device = zigpy_device_from_v2_quirk( LIXEE, "ZLinky_TIC", diff --git a/zhaquirks/lixee/zlinky.py b/zhaquirks/lixee/zlinky.py index 8cf3d6ee21..eb0193b3af 100644 --- a/zhaquirks/lixee/zlinky.py +++ b/zhaquirks/lixee/zlinky.py @@ -209,9 +209,15 @@ class ZLinkyTICMetering(CustomCluster, Metering): _CONSTANT_ATTRIBUTES = {MULTIPLIER: 1, DIVISOR: 1000} +# The v1 quirk carried four subclasses matching firmware variants by exact +# cluster list: the base signature, plus PowerConfiguration on v12, Time and a +# Tuya cluster on v14, and a different device type on v15. Matching on +# manufacturer and model covers every variant, and v2 only states the +# differences, so clusters the device already reports are kept untouched. ( QuirkBuilder(LIXEE, "ZLinky_TIC") - # Not all firmware variants have a power configuration cluster + # Added for every variant, as the v1 replacements did: not all firmware + # versions report a power configuration cluster. .adds(PowerConfiguration.cluster_id, endpoint_id=1) # Firmware v14 and later report a Tuya cluster the device does not # implement, as the v1 signatures for those variants recorded. Removing it From 9091c455d8fd129a7af05a3695daa748597a185d Mon Sep 17 00:00:00 2001 From: yoda-jm Date: Wed, 12 Aug 2026 13:23:56 +0200 Subject: [PATCH 4/7] Exercise both Tuya cluster removal directions in the test The test seeded the Tuya cluster as a server cluster only, so the assertion on out_clusters passed whatever the client-side removal did: dropping that .removes() call left the suite green. Parametrizing over both cluster types makes each removal fail on its own. Also pass ClusterType.Server explicitly instead of None. The fixture annotates cluster_ids as dict[int, dict[int, ClusterType]] and None only worked by falling through to the server branch. --- tests/test_lixee.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/tests/test_lixee.py b/tests/test_lixee.py index e1837fbdbc..63ecacf777 100644 --- a/tests/test_lixee.py +++ b/tests/test_lixee.py @@ -1,5 +1,6 @@ """Tests for LiXee ZLinky_TIC quirks.""" +import pytest from zigpy.zcl import ClusterType from zigpy.zcl.clusters.general import PowerConfiguration from zigpy.zcl.clusters.smartenergy import Metering @@ -16,8 +17,8 @@ def test_zlinky_clusters_replaced(zigpy_device_from_v2_quirk) -> None: "ZLinky_TIC", cluster_ids={ 1: { - Metering.cluster_id: None, - ZLINKY_MANUFACTURER_CLUSTER_ID: None, + Metering.cluster_id: ClusterType.Server, + ZLINKY_MANUFACTURER_CLUSTER_ID: ClusterType.Server, } }, ) @@ -31,20 +32,25 @@ def test_zlinky_clusters_replaced(zigpy_device_from_v2_quirk) -> None: assert PowerConfiguration.cluster_id in endpoint.in_clusters -def test_zlinky_tuya_cluster_removed(zigpy_device_from_v2_quirk) -> None: +@pytest.mark.parametrize("cluster_type", [ClusterType.Server, ClusterType.Client]) +def test_zlinky_tuya_cluster_removed(zigpy_device_from_v2_quirk, cluster_type) -> None: """Test that the Tuya cluster firmware v14+ reports is removed. The v1 quirk dropped it implicitly, by listing it in the FWV14 and FWV15 signatures but not in their replacements. That is what the two ZLinky entries in test_suspicious_cluster_moves recorded; this test replaces them. + + Parametrized over both directions: the device reports the cluster as an + input and an output cluster, and each removal has to be exercised on its + own or the assertion for the other direction passes vacuously. """ device = zigpy_device_from_v2_quirk( LIXEE, "ZLinky_TIC", cluster_ids={ 1: { - ZLINKY_MANUFACTURER_CLUSTER_ID: None, - TuyaManufCluster.cluster_id: ClusterType.Server, + ZLINKY_MANUFACTURER_CLUSTER_ID: ClusterType.Server, + TuyaManufCluster.cluster_id: cluster_type, } }, ) From a47ff9ef51d6f217739949c7b42a6acdb25fd50e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Auzi?= Date: Sat, 7 Feb 2026 16:09:14 +0100 Subject: [PATCH 5/7] Add missing ZLinkyTICMetering attribute definitions --- zhaquirks/lixee/zlinky.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/zhaquirks/lixee/zlinky.py b/zhaquirks/lixee/zlinky.py index eb0193b3af..6ef0255f36 100644 --- a/zhaquirks/lixee/zlinky.py +++ b/zhaquirks/lixee/zlinky.py @@ -208,6 +208,22 @@ class ZLinkyTICMetering(CustomCluster, Metering): DIVISOR = 0x0302 _CONSTANT_ATTRIBUTES = {MULTIPLIER: 1, DIVISOR: 1000} + # The attribute comments below are in French to match the reference documentation, + # see https://github.com/fairecasoimeme/Zlinky_TIC/tree/v9.0#synth%C3%A8se-d%C3%A9veloppeur + # and https://github.com/fairecasoimeme/Zlinky_TIC/blob/v9.0/ZLinky/Source/LixeeCluster.h + class AttributeDefs(Metering.AttributeDefs): + """Attribute additions.""" + + # Standard mode: EAIT "Energie active injectée totale" (Production) / Int48 9 car + std_total_injected_active_energy: Final = ZCLAttributeDef( + id=0x0001, type=t.uint48_t, is_manufacturer_specific=True + ) + + # Standard mode: PTEC "Période tarifaire en cours" / String 4 car + hist_current_tarif_period: Final = ZCLAttributeDef( + id=0x0020, type=t.LimitedCharString(4), is_manufacturer_specific=True + ) + # The v1 quirk carried four subclasses matching firmware variants by exact # cluster list: the base signature, plus PowerConfiguration on v12, Time and a From 1dce24a376a0a38a0248016f2c59edcfad902644 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Auzi?= Date: Sat, 7 Feb 2026 17:37:28 +0100 Subject: [PATCH 6/7] Fix pytest errors --- zhaquirks/lixee/zlinky.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/zhaquirks/lixee/zlinky.py b/zhaquirks/lixee/zlinky.py index 6ef0255f36..e0f57f3cfd 100644 --- a/zhaquirks/lixee/zlinky.py +++ b/zhaquirks/lixee/zlinky.py @@ -215,12 +215,14 @@ class AttributeDefs(Metering.AttributeDefs): """Attribute additions.""" # Standard mode: EAIT "Energie active injectée totale" (Production) / Int48 9 car - std_total_injected_active_energy: Final = ZCLAttributeDef( + # Overwrite: current_summ_received + current_summ_received: Final = ZCLAttributeDef( id=0x0001, type=t.uint48_t, is_manufacturer_specific=True ) # Standard mode: PTEC "Période tarifaire en cours" / String 4 car - hist_current_tarif_period: Final = ZCLAttributeDef( + # Overwrite: active_register_tier_delivered + active_register_tier_delivered: Final = ZCLAttributeDef( id=0x0020, type=t.LimitedCharString(4), is_manufacturer_specific=True ) From cdcaa808f32c374e31667b6b083a9857d236644d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Auzi?= Date: Mon, 9 Feb 2026 07:45:35 +0100 Subject: [PATCH 7/7] Change is_manufacturer_specific=True into manufacture_code=0x1037 in ZLinkyTIC clusters AttributeDefs Rename ZLinkyTICMetering attributes: * current_summ_received (id=0x0001) => total_active_energy_injected * active_register_tier_delivered (id=0x0020) => current_rate_period --- zhaquirks/lixee/zlinky.py | 98 +++++++++++++++++++++------------------ 1 file changed, 53 insertions(+), 45 deletions(-) diff --git a/zhaquirks/lixee/zlinky.py b/zhaquirks/lixee/zlinky.py index e0f57f3cfd..9b823f68bc 100644 --- a/zhaquirks/lixee/zlinky.py +++ b/zhaquirks/lixee/zlinky.py @@ -5,7 +5,7 @@ import zigpy.types as t from zigpy.zcl import ClusterType from zigpy.zcl.clusters.general import PowerConfiguration -from zigpy.zcl.clusters.smartenergy import Metering +from zigpy.zcl.clusters.smartenergy import Metering, RegisteredTier from zigpy.zcl.foundation import BaseAttributeDefs, ZCLAttributeDef from zhaquirks.builder import QuirkBuilder @@ -30,172 +30,172 @@ class AttributeDefs(BaseAttributeDefs): # Historical mode: OPTARIF "Option tarifaire" / String 4 car # Standard mode: NGTF "Nom du calendrier tarifaire fournisseur" / String 16 car hist_tariff_option_or_std_supplier_price_schedule_name: Final = ZCLAttributeDef( - id=0x0000, type=t.LimitedCharString(16), is_manufacturer_specific=True + id=0x0000, type=t.LimitedCharString(16), manufacturer_code=0x1037 ) # Historical mode: DEMAIN "Couleur du lendemain" / String 4 car hist_tomorrow_color: Final = ZCLAttributeDef( - id=0x0001, type=t.LimitedCharString(4), is_manufacturer_specific=True + id=0x0001, type=t.LimitedCharString(4), manufacturer_code=0x1037 ) # Historical mode: HHPHC "Horaire Heure Pleines Heures Creuses" / Uint8 1 car hist_schedule_peak_hours_off_peak_hours: Final = ZCLAttributeDef( - id=0x0002, type=t.uint8_t, is_manufacturer_specific=True + id=0x0002, type=t.uint8_t, manufacturer_code=0x1037 ) # Historical mode: PPOT "Présence des potentiels" (Triphasé) / Uint8 2 car hist_potentials_presence: Final = ZCLAttributeDef( - id=0x0003, type=t.uint8_t, is_manufacturer_specific=True + id=0x0003, type=t.uint8_t, manufacturer_code=0x1037 ) # Historical mode: PEJP "Préavis début EJP(30min)" / Uint8 2 car hist_ejp_start_notice: Final = ZCLAttributeDef( - id=0x0004, type=t.uint8_t, is_manufacturer_specific=True + id=0x0004, type=t.uint8_t, manufacturer_code=0x1037 ) # Historical mode: ADPS "Avertissement de Dépassement De Puissance Souscrite" / Uint16 3 car hist_subscribed_power_exceeding_warning: Final = ZCLAttributeDef( - id=0x0005, type=t.uint16_t, is_manufacturer_specific=True + id=0x0005, type=t.uint16_t, manufacturer_code=0x1037 ) # Historical mode: ADIR1 "Avertissement de Dépassement D'intensité phase 1" / Uint16 3 car hist_current_exceeding_warning_phase_1: Final = ZCLAttributeDef( - id=0x0006, type=t.uint16_t, is_manufacturer_specific=True + id=0x0006, type=t.uint16_t, manufacturer_code=0x1037 ) # Historical mode: ADIR2 "Avertissement de Dépassement D'intensité phase 2" / Uint16 3 car hist_current_exceeding_warning_phase_2: Final = ZCLAttributeDef( - id=0x0007, type=t.uint16_t, is_manufacturer_specific=True + id=0x0007, type=t.uint16_t, manufacturer_code=0x1037 ) # Historical mode: ADIR3 "Avertissement de Dépassement D'intensité phase 3" / Uint16 3 car hist_current_exceeding_warning_phase_3: Final = ZCLAttributeDef( - id=0x0008, type=t.uint16_t, is_manufacturer_specific=True + id=0x0008, type=t.uint16_t, manufacturer_code=0x1037 ) # Historical mode: MOTDETAT "Etat du Linky (From V13)" / String 6 car linky_status: Final = ZCLAttributeDef( - id=0x0009, type=t.LimitedCharString(6), is_manufacturer_specific=True + id=0x0009, type=t.LimitedCharString(6), manufacturer_code=0x1037 ) # Historical and standard mode: "Tariff Period (From V15)" / String 16 car linky_tariff_period: Final = ZCLAttributeDef( - id=0x0010, type=t.LimitedCharString(16), is_manufacturer_specific=True + id=0x0010, type=t.LimitedCharString(16), manufacturer_code=0x1037 ) # Historical and Standard mode: "Linky acquisition time (From V7)"" / Uint8 1 car linky_acquisition_time: Final = ZCLAttributeDef( - id=0x0100, type=t.uint8_t, is_manufacturer_specific=True + id=0x0100, type=t.uint8_t, manufacturer_code=0x1037 ) # Standard mode: LTARF "Libellé tarif fournisseur en cours" / String 16 car std_current_supplier_price_description: Final = ZCLAttributeDef( - id=0x0200, type=t.LimitedCharString(16), is_manufacturer_specific=True + id=0x0200, type=t.LimitedCharString(16), manufacturer_code=0x1037 ) # Standard mode: NTARF "Numéro de l'index tarifaire en cours" / Uint8 2 car std_current_tariff_index_number: Final = ZCLAttributeDef( - id=0x0201, type=t.uint8_t, is_manufacturer_specific=True + id=0x0201, type=t.uint8_t, manufacturer_code=0x1037 ) # Standard mode: DATE "Date et heure courant" / String 10 car std_current_date_and_time: Final = ZCLAttributeDef( - id=0x0202, type=t.LimitedCharString(10), is_manufacturer_specific=True + id=0x0202, type=t.LimitedCharString(10), manufacturer_code=0x1037 ) # Standard mode: EASD01 "Energie active soutirée Distributeur, index 01" / Uint32 9 car std_active_energy_withdrawn_distributor_index_01: Final = ZCLAttributeDef( - id=0x0203, type=t.uint32_t, is_manufacturer_specific=True + id=0x0203, type=t.uint32_t, manufacturer_code=0x1037 ) # Standard mode: EASD02 "Energie active soutirée Distributeur, index 02" / Uint32 9 car std_active_energy_withdrawn_distributor_index_02: Final = ZCLAttributeDef( - id=0x0204, type=t.uint32_t, is_manufacturer_specific=True + id=0x0204, type=t.uint32_t, manufacturer_code=0x1037 ) # Standard mode: EASD03 "Energie active soutirée Distributeur, index 03" / Uint32 9 car std_active_energy_withdrawn_distributor_index_03: Final = ZCLAttributeDef( - id=0x0205, type=t.uint32_t, is_manufacturer_specific=True + id=0x0205, type=t.uint32_t, manufacturer_code=0x1037 ) # Standard mode: EASD04 "Energie active soutirée Distributeur, index 04" / Uint32 9 car std_active_energy_withdrawn_distributor_index_04: Final = ZCLAttributeDef( - id=0x0206, type=t.uint32_t, is_manufacturer_specific=True + id=0x0206, type=t.uint32_t, manufacturer_code=0x1037 ) # Standard mode: SINSTI "Puissance app. Instantanée injectée" (Production) / Uint16 5 car std_apparent_power_injected_instantaneous: Final = ZCLAttributeDef( - id=0x0207, type=t.uint16_t, is_manufacturer_specific=True + id=0x0207, type=t.uint16_t, manufacturer_code=0x1037 ) # Standard mode: SMAXIN "Puissance app max. injectée n" (Production) / Uint16 5 car std_apparent_power_injected_max: Final = ZCLAttributeDef( - id=0x0208, type=t.uint16_t, is_manufacturer_specific=True + id=0x0208, type=t.uint16_t, manufacturer_code=0x1037 ) # Standard mode: SMAXIN-1 "Puissance app max. injectée n-1" (Production) / Uint16 5 car std_apparent_power_injected_max_1: Final = ZCLAttributeDef( - id=0x0209, type=t.uint16_t, is_manufacturer_specific=True + id=0x0209, type=t.uint16_t, manufacturer_code=0x1037 ) # Standard mode: CCAIN "Point n de la courbe de charge active injectée" (Production) / Uint16 5 car std_injected_active_load_curve_point_n: Final = ZCLAttributeDef( - id=0x0210, type=t.uint16_t, is_manufacturer_specific=True + id=0x0210, type=t.uint16_t, manufacturer_code=0x1037 ) # Standard mode: CCAIN-1 "Point n-1 de la courbe de charge active injectée" (Production) / Uint16 5 car std_injected_active_load_curve_point_n_1: Final = ZCLAttributeDef( - id=0x0211, type=t.uint16_t, is_manufacturer_specific=True + id=0x0211, type=t.uint16_t, manufacturer_code=0x1037 ) # Standard mode: SMAXN-1 "Puissance app. max. soutirée n-1" (Monophasé) / Uint16 5 car # Standard mode: SMAXN1-1 "Puissance app. max. soutirée n-1 ph.1" (Triphasé) / Uint16 5 car std_apparent_power_withdrawn_max_phase_1_n_1: Final = ZCLAttributeDef( - id=0x0212, type=t.uint16_t, is_manufacturer_specific=True + id=0x0212, type=t.uint16_t, manufacturer_code=0x1037 ) # Standard mode: SMAXN2-1 "Puissance app. max. soutirée n-1 ph. 2" (Triphasé) / Uint16 5 car std_apparent_power_withdrawn_max_phase_2_n_1: Final = ZCLAttributeDef( - id=0x0213, type=t.uint16_t, is_manufacturer_specific=True + id=0x0213, type=t.uint16_t, manufacturer_code=0x1037 ) # Standard mode: SMAXN3-1 "Puissance app. max. soutirée n-1 ph. 3" (Triphasé) / Uint16 5 car std_apparent_power_withdrawn_max_phase_3_n_1: Final = ZCLAttributeDef( - id=0x0214, type=t.uint16_t, is_manufacturer_specific=True + id=0x0214, type=t.uint16_t, manufacturer_code=0x1037 ) # Standard mode: MSG1 "Message court" / String 32 car std_message_short: Final = ZCLAttributeDef( - id=0x0215, type=t.LimitedCharString(32), is_manufacturer_specific=True + id=0x0215, type=t.LimitedCharString(32), manufacturer_code=0x1037 ) # Standard mode: MSG2 "Message ultra court" / String 16 car std_message_ultra_short: Final = ZCLAttributeDef( - id=0x0216, type=t.LimitedCharString(16), is_manufacturer_specific=True + id=0x0216, type=t.LimitedCharString(16), manufacturer_code=0x1037 ) # Standard mode: STGE "Registre de Statuts" / String 8 car /* codespell:ignore */ std_status_register: Final = ZCLAttributeDef( - id=0x0217, type=t.LimitedCharString(8), is_manufacturer_specific=True + id=0x0217, type=t.LimitedCharString(8), manufacturer_code=0x1037 ) # Standard mode: DPM1 "Début Pointe Mobile 1" / Uint8 2 car std_mobile_peak_start_1: Final = ZCLAttributeDef( - id=0x0218, type=t.uint8_t, is_manufacturer_specific=True + id=0x0218, type=t.uint8_t, manufacturer_code=0x1037 ) # Standard mode: FPM1 "Fin Pointe Mobile 1" / Uint8 2 car std_mobile_peak_end_1: Final = ZCLAttributeDef( - id=0x0219, type=t.uint8_t, is_manufacturer_specific=True + id=0x0219, type=t.uint8_t, manufacturer_code=0x1037 ) # Standard mode: DPM2 "Début Pointe Mobile 2" / Uint8 2 car std_mobile_peak_start_2: Final = ZCLAttributeDef( - id=0x0220, type=t.uint8_t, is_manufacturer_specific=True + id=0x0220, type=t.uint8_t, manufacturer_code=0x1037 ) # Standard mode: FPM2 "Fin Pointe Mobile 2" / Uint8 2 car std_mobile_peak_end_2: Final = ZCLAttributeDef( - id=0x0221, type=t.uint8_t, is_manufacturer_specific=True + id=0x0221, type=t.uint8_t, manufacturer_code=0x1037 ) # Standard mode: DPM3 "Début Pointe Mobile 3" / Uint8 2 car std_mobile_peak_start_3: Final = ZCLAttributeDef( - id=0x0222, type=t.uint8_t, is_manufacturer_specific=True + id=0x0222, type=t.uint8_t, manufacturer_code=0x1037 ) # Standard mode: FPM3 "Fin Pointe Mobile 3" / Uint8 2 car std_mobile_peak_end_3: Final = ZCLAttributeDef( - id=0x0223, type=t.uint8_t, is_manufacturer_specific=True + id=0x0223, type=t.uint8_t, manufacturer_code=0x1037 ) # Standard mode: RELAIS "RELAIS" / Uint16 3 car std_relay: Final = ZCLAttributeDef( - id=0x0224, type=t.uint8_t, is_manufacturer_specific=True + id=0x0224, type=t.uint8_t, manufacturer_code=0x1037 ) # Standard mode: NJOURF "Numéro du jour en cours calendrier fournisseur" / Uint8 2 car std_supplier_calendar_current_day_number: Final = ZCLAttributeDef( - id=0x0225, type=t.uint8_t, is_manufacturer_specific=True + id=0x0225, type=t.uint8_t, manufacturer_code=0x1037 ) # Standard mode: NJOURF+1 "Numéro du prochain jour calendrier fournisseur" / Uint8 2 car std_supplier_calendar_next_day_number: Final = ZCLAttributeDef( - id=0x0226, type=t.uint8_t, is_manufacturer_specific=True + id=0x0226, type=t.uint8_t, manufacturer_code=0x1037 ) # Standard mode: PJOURF+1 "Profil du prochain jour calendrier fournisseur" / String 98 car std_supplier_calendar_next_day_profile: Final = ZCLAttributeDef( - id=0x0227, type=t.LimitedCharString(98), is_manufacturer_specific=True + id=0x0227, type=t.LimitedCharString(98), manufacturer_code=0x1037 ) # Standard mode: PPOINTE1 "Profil du prochain jour de pointe" / String 98 car std_next_peak_day_profile: Final = ZCLAttributeDef( - id=0x0228, type=t.LimitedCharString(98), is_manufacturer_specific=True + id=0x0228, type=t.LimitedCharString(98), manufacturer_code=0x1037 ) # Historical and Standard mode: - "Linky Mode (From V4)" / Uint8 1 car linky_mode: Final = ZCLAttributeDef( - id=0x0300, type=t.uint8_t, is_manufacturer_specific=True + id=0x0300, type=t.uint8_t, manufacturer_code=0x1037 ) @@ -217,13 +217,21 @@ class AttributeDefs(Metering.AttributeDefs): # Standard mode: EAIT "Energie active injectée totale" (Production) / Int48 9 car # Overwrite: current_summ_received current_summ_received: Final = ZCLAttributeDef( - id=0x0001, type=t.uint48_t, is_manufacturer_specific=True + id=0x0001, type=t.uint48_t, access="r", manufacturer_code=0x1037 + ) + # Rename according the new definition + total_active_energy_injected: Final = ZCLAttributeDef( + id=0x0001, type=t.uint48_t, access="r", manufacturer_code=0x1037 ) # Standard mode: PTEC "Période tarifaire en cours" / String 4 car # Overwrite: active_register_tier_delivered active_register_tier_delivered: Final = ZCLAttributeDef( - id=0x0020, type=t.LimitedCharString(4), is_manufacturer_specific=True + id=0x0020, type=RegisteredTier, access="r", manufacturer_code=0x1037 + ) + # Rename according the new definition + current_rate_period: Final = ZCLAttributeDef( + id=0x0020, type=t.LimitedCharString(4), access="r", manufacturer_code=0x1037 )