From 37390cfcebe8916f27007a10047a66b4a541c214 Mon Sep 17 00:00:00 2001 From: TheJulianJES Date: Wed, 22 Oct 2025 03:26:33 +0200 Subject: [PATCH 01/13] Add test to check for proper implementation --- tests/test_sensor.py | 75 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) diff --git a/tests/test_sensor.py b/tests/test_sensor.py index e4839fa5b..5a3fe2a8a 100644 --- a/tests/test_sensor.py +++ b/tests/test_sensor.py @@ -1598,7 +1598,7 @@ async def test_state_class( assert "Quirks provided an invalid state class: energy" in caplog.text -async def test_cluster_handler_quirks_attributes(zha_gateway: Gateway) -> None: +async def test_cluster_handler_quirks_attribute_reporting(zha_gateway: Gateway) -> None: """Test quirks sensor setting up ZCL_INIT_ATTRS and REPORT_CONFIG correctly.""" # Suppress normal endpoint probing, as this will claim the Opple cluster handler @@ -1615,6 +1615,9 @@ async def test_cluster_handler_quirks_attributes(zha_gateway: Gateway) -> None: # make sure the cluster handler was claimed due to reporting config, so ZHA binds it assert opple_ch in zha_device.endpoints[1].claimed_cluster_handlers.values() + # check that BIND is set to True, as reporting is configured + assert opple_ch.BIND is True + # check ZCL_INIT_ATTRS contains sensor attributes that are not in REPORT_CONFIG assert opple_ch.ZCL_INIT_ATTRS == { "energy": True, @@ -1645,6 +1648,76 @@ async def test_cluster_handler_quirks_attributes(zha_gateway: Gateway) -> None: assert OppleRemoteClusterHandler.REPORT_CONFIG == () +async def test_cluster_handler_quirks_attribute_reading(zha_gateway: Gateway) -> None: + """Test quirks sensor setting up ZCL_INIT_ATTRS, claiming cluster handler.""" + + registry = DeviceRegistry() + ( + QuirkBuilder( + "Fake_Manufacturer_sensor_2", "Fake_Model_sensor_2", registry=registry + ) + .replaces(OppleCluster) + .sensor( + "attribute_to_be_read", + OppleCluster.cluster_id, + translation_key="attribute_to_be_read", + fallback_name="Attribute to be read", + ) + .add_to_registry() + ) + + zigpy_device = create_mock_zigpy_device( + zha_gateway, + { + 1: { + SIG_EP_INPUT: [ + general.Basic.cluster_id, + OppleCluster.cluster_id, + ], + SIG_EP_OUTPUT: [], + SIG_EP_TYPE: zigpy.profiles.zha.DeviceType.OCCUPANCY_SENSOR, + SIG_EP_PROFILE: zigpy.profiles.zha.PROFILE_ID, + } + }, + manufacturer="Fake_Manufacturer_sensor_2", + model="Fake_Model_sensor_2", + ) + zigpy_device = registry.get_device(zigpy_device) + + # Suppress normal endpoint probing, as this will claim the Opple cluster handler + # already due to it being in the "CLUSTER_HANDLER_ONLY_CLUSTERS" registry. + # We want to test the handler also gets claimed via quirks v2 attributes init. + with patch("zha.application.discovery.EndpointProbe.discover_entities"): + zha_device = await join_zigpy_device(zha_gateway, zigpy_device) + assert isinstance(zha_device.device, CustomDeviceV2) + + # get cluster handler of OppleCluster + opple_ch = zha_device.endpoints[1].all_cluster_handlers["1:0xfcc0"] + assert isinstance(opple_ch, OppleRemoteClusterHandler) + + # make sure the cluster handler was claimed due to attributes to be initialized + # otherwise, ZHA won't configure the cluster handler, so attributes are not read + assert opple_ch in zha_device.endpoints[1].claimed_cluster_handlers.values() + + # check that BIND is set to False, as no reporting is configured + assert opple_ch.BIND is False + + # check ZCL_INIT_ATTRS contains sensor attributes that are not in REPORT_CONFIG + assert opple_ch.ZCL_INIT_ATTRS == { + "attribute_to_be_read": True, + } + # check that ZCL_INIT_ATTRS is an instance variable and not a class variable now + assert opple_ch.ZCL_INIT_ATTRS is opple_ch.__dict__[ZCL_INIT_ATTRS] + assert opple_ch.ZCL_INIT_ATTRS is not OppleRemoteClusterHandler.ZCL_INIT_ATTRS + + # double check we didn't modify the class variable + assert OppleRemoteClusterHandler.ZCL_INIT_ATTRS == {} + + # check if REPORT_CONFIG is empty, both instance and class variable + assert opple_ch.REPORT_CONFIG == () + assert OppleRemoteClusterHandler.REPORT_CONFIG == () + + async def test_device_counter_sensors(zha_gateway: Gateway) -> None: """Test coordinator counter sensor.""" From 0ebbce831b92017e742efb6a12c545ac6548318f Mon Sep 17 00:00:00 2001 From: TheJulianJES Date: Wed, 22 Oct 2025 03:27:14 +0200 Subject: [PATCH 02/13] WIP: first basic implementation --- zha/application/discovery.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/zha/application/discovery.py b/zha/application/discovery.py index f6f49d898..82d81a776 100644 --- a/zha/application/discovery.py +++ b/zha/application/discovery.py @@ -270,6 +270,9 @@ def discover_quirks_v2_entities(self, device: Device) -> Iterator[PlatformEntity ) # claim the cluster handler, so ZHA configures and binds it endpoint.claim_cluster_handlers([cluster_handler]) + # TODO: clean up BIND logic + # explicitly set BIND to True, as REPORT_CONFIG implies binding + cluster_handler.BIND = True # not in REPORT_CONFIG, add to ZCL_INIT_ATTRS if it not already in elif attr_name not in cluster_handler.ZCL_INIT_ATTRS: @@ -283,6 +286,13 @@ def discover_quirks_v2_entities(self, device: Device) -> Iterator[PlatformEntity cluster_handler.ZCL_INIT_ATTRS[attr_name] = ( entity_metadata.attribute_initialized_from_cache ) + # claim the cluster handler, so ZHA initializes the attribute + endpoint.claim_cluster_handlers([cluster_handler]) + # if REPORT_CONFIG is still empty, disable binding + if not cluster_handler.REPORT_CONFIG: + cluster_handler.BIND = ( + False # no need to bind if only initializing attribute + ) yield entity_class( cluster_handlers=[cluster_handler], From f98323dfea2ed9fb1e45e47f8d47f3d029d07fed Mon Sep 17 00:00:00 2001 From: TheJulianJES Date: Wed, 22 Oct 2025 03:58:01 +0200 Subject: [PATCH 03/13] Rework implementation --- zha/application/discovery.py | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/zha/application/discovery.py b/zha/application/discovery.py index 82d81a776..db277d460 100644 --- a/zha/application/discovery.py +++ b/zha/application/discovery.py @@ -232,6 +232,10 @@ def discover_quirks_v2_entities(self, device: Device) -> Iterator[PlatformEntity assert cluster_handler + # flags to determine if we need to claim/bind the cluster handler + attribute_initialization_found: bool = False + reporting_found: bool = False + for entity_metadata in entity_metadata_list: platform = Platform(entity_metadata.entity_platform.value) metadata_type = type(entity_metadata) @@ -268,11 +272,8 @@ def discover_quirks_v2_entities(self, device: Device) -> Iterator[PlatformEntity cluster_handler.REPORT_CONFIG += ( AttrReportConfig(attr=attr_name, config=astuple(rep_conf)), ) - # claim the cluster handler, so ZHA configures and binds it - endpoint.claim_cluster_handlers([cluster_handler]) - # TODO: clean up BIND logic - # explicitly set BIND to True, as REPORT_CONFIG implies binding - cluster_handler.BIND = True + # mark cluster handler for claiming and binding later + reporting_found = True # not in REPORT_CONFIG, add to ZCL_INIT_ATTRS if it not already in elif attr_name not in cluster_handler.ZCL_INIT_ATTRS: @@ -286,13 +287,8 @@ def discover_quirks_v2_entities(self, device: Device) -> Iterator[PlatformEntity cluster_handler.ZCL_INIT_ATTRS[attr_name] = ( entity_metadata.attribute_initialized_from_cache ) - # claim the cluster handler, so ZHA initializes the attribute - endpoint.claim_cluster_handlers([cluster_handler]) - # if REPORT_CONFIG is still empty, disable binding - if not cluster_handler.REPORT_CONFIG: - cluster_handler.BIND = ( - False # no need to bind if only initializing attribute - ) + # mark cluster handler for claiming later, but not binding + attribute_initialization_found = True yield entity_class( cluster_handlers=[cluster_handler], @@ -309,6 +305,19 @@ def discover_quirks_v2_entities(self, device: Device) -> Iterator[PlatformEntity [cluster_handler.name], ) + # if the cluster handler is unclaimed, claim it and set BIND accordingly, + # so ZHA configures the cluster handler: reporting + reads attributes + if (attribute_initialization_found or reporting_found) and ( + cluster_handler not in endpoint.claimed_cluster_handlers.values() + ): + endpoint.claim_cluster_handlers([cluster_handler]) + # BIND is True by default, so only set to False if no reporting found. + # We can safely do this, since quirks v2 entities are initialized last, + # so if the cluster handler wasn't claimed by EndpointProbe so far, + # only v2 entities need it. + if not reporting_found: + cluster_handler.BIND = False + @ignore_exceptions_during_iteration def discover_coordinator_device_entities( self, device: Device From 1a7d2433176ccce344693eea42c81a486cc1fca3 Mon Sep 17 00:00:00 2001 From: TheJulianJES Date: Wed, 22 Oct 2025 03:58:17 +0200 Subject: [PATCH 04/13] Change test to use existing attribute --- tests/test_sensor.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_sensor.py b/tests/test_sensor.py index 5a3fe2a8a..b5f939205 100644 --- a/tests/test_sensor.py +++ b/tests/test_sensor.py @@ -1658,10 +1658,10 @@ async def test_cluster_handler_quirks_attribute_reading(zha_gateway: Gateway) -> ) .replaces(OppleCluster) .sensor( - "attribute_to_be_read", + "last_feeding_size", OppleCluster.cluster_id, - translation_key="attribute_to_be_read", - fallback_name="Attribute to be read", + translation_key="last_feeding_size", + fallback_name="Last feeding size", ) .add_to_registry() ) @@ -1704,7 +1704,7 @@ async def test_cluster_handler_quirks_attribute_reading(zha_gateway: Gateway) -> # check ZCL_INIT_ATTRS contains sensor attributes that are not in REPORT_CONFIG assert opple_ch.ZCL_INIT_ATTRS == { - "attribute_to_be_read": True, + "last_feeding_size": True, } # check that ZCL_INIT_ATTRS is an instance variable and not a class variable now assert opple_ch.ZCL_INIT_ATTRS is opple_ch.__dict__[ZCL_INIT_ATTRS] From 19c92791285728cc50e639ddb6e27e2333763c22 Mon Sep 17 00:00:00 2001 From: TheJulianJES Date: Wed, 22 Oct 2025 03:59:06 +0200 Subject: [PATCH 05/13] Test command button doesn't claim cluster handler unnecessarily --- tests/test_button.py | 97 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/tests/test_button.py b/tests/test_button.py index b29dafeaa..b14ac2f01 100644 --- a/tests/test_button.py +++ b/tests/test_button.py @@ -12,6 +12,7 @@ PROFILE_ID, ) from zhaquirks.tuya.tuya_valve import ParksideTuyaValveManufCluster +import zigpy from zigpy.exceptions import ZigbeeException from zigpy.profiles import zha from zigpy.quirks import CustomCluster, CustomDevice, DeviceRegistry @@ -34,6 +35,7 @@ update_attribute_cache, ) from zha.application import Platform +from zha.application.const import ZCL_INIT_ATTRS from zha.application.gateway import Gateway from zha.application.platforms import EntityCategory, PlatformEntity from zha.application.platforms.button import ( @@ -43,6 +45,7 @@ ) from zha.application.platforms.button.const import ButtonDeviceClass from zha.exceptions import ZHAException +from zha.zigbee.cluster_handlers.manufacturerspecific import OppleRemoteClusterHandler from zha.zigbee.device import Device ZIGPY_DEVICE = { @@ -354,3 +357,97 @@ async def test_quirks_write_attr_buttons_uid(zha_gateway: Gateway) -> None: assert entity_btn_2.translation_key == "btn_2" assert entity_btn_2._unique_id_suffix == "btn_2" assert entity_btn_2._attribute_value == 2 + + +class OppleCluster(CustomCluster, ManufacturerSpecificCluster): + """Aqara manufacturer specific cluster.""" + + cluster_id = 0xFCC0 + ep_attribute = "opple_cluster" + + class ServerCommandDefs(zcl_f.BaseCommandDefs): + """Server command definitions.""" + + self_test: Final = zcl_f.ZCLCommandDef( + id=0x00, schema={"identify_time": t.uint16_t} + ) + + +async def test_cluster_handler_quirks_unnecessary_claiming( + zha_gateway: Gateway, +) -> None: + """Test quirks button doesn't claim cluster handlers unnecessarily.""" + + registry = DeviceRegistry() + ( + QuirkBuilder( + "Fake_Manufacturer_sensor_2", "Fake_Model_sensor_2", registry=registry + ) + .replaces(OppleCluster) + .command_button( + FakeManufacturerCluster.ServerCommandDefs.self_test.name, + OppleCluster.cluster_id, + command_args=(5,), + translation_key="self_test", + fallback_name="Self test", + ) + # TODO: we claim the cluster handler + read attributes for write_attr_button... + # .write_attr_button( + # "last_feeding_size", + # 20, + # OppleCluster.cluster_id, + # translation_key="last_feeding_size", + # fallback_name="Last Feeding Size", + # ) + .add_to_registry() + ) + + zigpy_device = create_mock_zigpy_device( + zha_gateway, + { + 1: { + SIG_EP_INPUT: [ + general.Basic.cluster_id, + OppleCluster.cluster_id, + ], + SIG_EP_OUTPUT: [], + SIG_EP_TYPE: zigpy.profiles.zha.DeviceType.OCCUPANCY_SENSOR, + SIG_EP_PROFILE: zigpy.profiles.zha.PROFILE_ID, + } + }, + manufacturer="Fake_Manufacturer_sensor_2", + model="Fake_Model_sensor_2", + ) + zigpy_device = registry.get_device(zigpy_device) + + # Suppress normal endpoint probing, as this will claim the Opple cluster handler + # already due to it being in the "CLUSTER_HANDLER_ONLY_CLUSTERS" registry. + # We want to test the handler also gets claimed via quirks v2 attributes init. + with patch("zha.application.discovery.EndpointProbe.discover_entities"): + zha_device = await join_zigpy_device(zha_gateway, zigpy_device) + assert isinstance(zha_device.device, CustomDeviceV2) + + # get cluster handler of OppleCluster + opple_ch = zha_device.endpoints[1].all_cluster_handlers["1:0xfcc0"] + assert isinstance(opple_ch, OppleRemoteClusterHandler) + + # make sure the cluster handler was not claimed, + # as no reporting is configured and no attributes are to be read + assert opple_ch not in zha_device.endpoints[1].claimed_cluster_handlers.values() + + # check that BIND is left at default of True, though ZHA will ignore it + assert opple_ch.BIND is True + + # check ZCL_INIT_ATTRS is empty + assert opple_ch.ZCL_INIT_ATTRS == {} + + # check that no ZCL_INIT_ATTRS instance variable was created + assert opple_ch.__dict__.get(ZCL_INIT_ATTRS) is None + assert opple_ch.ZCL_INIT_ATTRS is OppleRemoteClusterHandler.ZCL_INIT_ATTRS + + # double check we didn't modify the class variable + assert OppleRemoteClusterHandler.ZCL_INIT_ATTRS == {} + + # check if REPORT_CONFIG is empty, both instance and class variable + assert opple_ch.REPORT_CONFIG == () + assert OppleRemoteClusterHandler.REPORT_CONFIG == () From a9f81414eef4be2b9ce0dacdc982b7727a749aa7 Mon Sep 17 00:00:00 2001 From: TheJulianJES Date: Wed, 22 Oct 2025 03:59:54 +0200 Subject: [PATCH 06/13] Remove comment about write_attr_button causing claimage --- tests/test_button.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tests/test_button.py b/tests/test_button.py index b14ac2f01..fd9797138 100644 --- a/tests/test_button.py +++ b/tests/test_button.py @@ -391,14 +391,6 @@ async def test_cluster_handler_quirks_unnecessary_claiming( translation_key="self_test", fallback_name="Self test", ) - # TODO: we claim the cluster handler + read attributes for write_attr_button... - # .write_attr_button( - # "last_feeding_size", - # 20, - # OppleCluster.cluster_id, - # translation_key="last_feeding_size", - # fallback_name="Last Feeding Size", - # ) .add_to_registry() ) From fc53e7acaa4ba7c1af092c0346a9aa5a8fd45ae9 Mon Sep 17 00:00:00 2001 From: TheJulianJES Date: Wed, 22 Oct 2025 04:00:51 +0200 Subject: [PATCH 07/13] Temporarily add comment about "write attr button" claiming cluster handler --- zha/application/discovery.py | 1 + 1 file changed, 1 insertion(+) diff --git a/zha/application/discovery.py b/zha/application/discovery.py index db277d460..c37e787bd 100644 --- a/zha/application/discovery.py +++ b/zha/application/discovery.py @@ -258,6 +258,7 @@ def discover_quirks_v2_entities(self, device: Device) -> Iterator[PlatformEntity # process the entity metadata for ZCL_INIT_ATTRS and REPORT_CONFIG if attr_name := getattr(entity_metadata, "attribute_name", None): + # TODO: ignore "attribute write buttons"? currently, we claim ch # if the entity has a reporting config, add it to the cluster handler if rep_conf := getattr(entity_metadata, "reporting_config", None): # if attr is already in REPORT_CONFIG, remove it first From 49110519ce497c4215065782779609f59b93555d Mon Sep 17 00:00:00 2001 From: TheJulianJES Date: Wed, 22 Oct 2025 04:07:01 +0200 Subject: [PATCH 08/13] Change comment to emphasize that `BIND = True` by default already --- tests/test_sensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_sensor.py b/tests/test_sensor.py index b5f939205..b59a78307 100644 --- a/tests/test_sensor.py +++ b/tests/test_sensor.py @@ -1615,7 +1615,7 @@ async def test_cluster_handler_quirks_attribute_reporting(zha_gateway: Gateway) # make sure the cluster handler was claimed due to reporting config, so ZHA binds it assert opple_ch in zha_device.endpoints[1].claimed_cluster_handlers.values() - # check that BIND is set to True, as reporting is configured + # check that BIND is not set to False, as reporting is configured assert opple_ch.BIND is True # check ZCL_INIT_ATTRS contains sensor attributes that are not in REPORT_CONFIG From bcf7b3726bc24f92c1930a425291e63fb59a7ed4 Mon Sep 17 00:00:00 2001 From: TheJulianJES Date: Wed, 22 Oct 2025 17:59:03 +0200 Subject: [PATCH 09/13] Adjust three device tests to represent v2 entities initializing chs --- tests/data/devices/bosch-rbsh-mms-zb-eu.json | 20 +++++++++---------- ...weden-vallhorn-wireless-motion-sensor.json | 4 ++-- .../third-reality-inc-3rsp02028bz.json | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/tests/data/devices/bosch-rbsh-mms-zb-eu.json b/tests/data/devices/bosch-rbsh-mms-zb-eu.json index 7ac85d2b7..83d971375 100644 --- a/tests/data/devices/bosch-rbsh-mms-zb-eu.json +++ b/tests/data/devices/bosch-rbsh-mms-zb-eu.json @@ -836,7 +836,7 @@ }, "id": "1:0xfca0", "unique_id": "ab:cd:ef:12:75:e4:96:a5:1:0xfca0", - "status": "CREATED", + "status": "INITIALIZED", "value_attribute": null } ], @@ -882,7 +882,7 @@ }, "id": "1:0xfca0", "unique_id": "ab:cd:ef:12:75:e4:96:a5:1:0xfca0", - "status": "CREATED", + "status": "INITIALIZED", "value_attribute": null } ], @@ -928,7 +928,7 @@ }, "id": "1:0xfca0", "unique_id": "ab:cd:ef:12:75:e4:96:a5:1:0xfca0", - "status": "CREATED", + "status": "INITIALIZED", "value_attribute": null } ], @@ -974,7 +974,7 @@ }, "id": "1:0xfca0", "unique_id": "ab:cd:ef:12:75:e4:96:a5:1:0xfca0", - "status": "CREATED", + "status": "INITIALIZED", "value_attribute": null } ], @@ -1022,7 +1022,7 @@ }, "id": "1:0xfca0", "unique_id": "ab:cd:ef:12:75:e4:96:a5:1:0xfca0", - "status": "CREATED", + "status": "INITIALIZED", "value_attribute": null } ], @@ -1069,7 +1069,7 @@ }, "id": "1:0xfca0", "unique_id": "ab:cd:ef:12:75:e4:96:a5:1:0xfca0", - "status": "CREATED", + "status": "INITIALIZED", "value_attribute": null } ], @@ -1494,7 +1494,7 @@ }, "id": "1:0xfca0", "unique_id": "ab:cd:ef:12:75:e4:96:a5:1:0xfca0", - "status": "CREATED", + "status": "INITIALIZED", "value_attribute": null } ], @@ -1586,7 +1586,7 @@ }, "id": "1:0xfca0", "unique_id": "ab:cd:ef:12:75:e4:96:a5:1:0xfca0", - "status": "CREATED", + "status": "INITIALIZED", "value_attribute": null } ], @@ -1633,7 +1633,7 @@ }, "id": "2:0xfca0", "unique_id": "ab:cd:ef:12:75:e4:96:a5:2:0xfca0", - "status": "CREATED", + "status": "INITIALIZED", "value_attribute": null } ], @@ -1680,7 +1680,7 @@ }, "id": "3:0xfca0", "unique_id": "ab:cd:ef:12:75:e4:96:a5:3:0xfca0", - "status": "CREATED", + "status": "INITIALIZED", "value_attribute": null } ], diff --git a/tests/data/devices/ikea-of-sweden-vallhorn-wireless-motion-sensor.json b/tests/data/devices/ikea-of-sweden-vallhorn-wireless-motion-sensor.json index d0fb6c30b..49a6ef76e 100644 --- a/tests/data/devices/ikea-of-sweden-vallhorn-wireless-motion-sensor.json +++ b/tests/data/devices/ikea-of-sweden-vallhorn-wireless-motion-sensor.json @@ -463,7 +463,7 @@ }, "id": "1:0xfc81", "unique_id": "8c:6f:b9:ff:fe:26:3b:4a:1:0xfc81", - "status": "CREATED", + "status": "INITIALIZED", "value_attribute": null } ], @@ -693,7 +693,7 @@ }, "id": "1:0xfc81", "unique_id": "8c:6f:b9:ff:fe:26:3b:4a:1:0xfc81", - "status": "CREATED", + "status": "INITIALIZED", "value_attribute": null } ], diff --git a/tests/data/devices/third-reality-inc-3rsp02028bz.json b/tests/data/devices/third-reality-inc-3rsp02028bz.json index b7bb8c2c3..103b1a53c 100644 --- a/tests/data/devices/third-reality-inc-3rsp02028bz.json +++ b/tests/data/devices/third-reality-inc-3rsp02028bz.json @@ -424,7 +424,7 @@ }, "id": "1:0xff03", "unique_id": "28:2c:02:bf:ff:eb:4f:2f:1:0xff03", - "status": "CREATED", + "status": "INITIALIZED", "value_attribute": null } ], From 6c127608e951476e5fd41ea7417e29e0f34b13ee Mon Sep 17 00:00:00 2001 From: TheJulianJES Date: Tue, 28 Oct 2025 18:09:08 +0100 Subject: [PATCH 10/13] Change `command_kwargs` in new and existing test --- tests/test_button.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_button.py b/tests/test_button.py index fd9797138..827aa2ec6 100644 --- a/tests/test_button.py +++ b/tests/test_button.py @@ -221,7 +221,7 @@ async def custom_button_device(zha_gateway: Gateway): .command_button( FakeManufacturerCluster.ServerCommandDefs.self_test.name, FakeManufacturerCluster.cluster_id, - command_args=(5,), + command_kwargs={"identify_time": 5}, translation_key="self_test", fallback_name="Self test", ) @@ -387,7 +387,7 @@ async def test_cluster_handler_quirks_unnecessary_claiming( .command_button( FakeManufacturerCluster.ServerCommandDefs.self_test.name, OppleCluster.cluster_id, - command_args=(5,), + command_kwargs={"identify_time": 5}, translation_key="self_test", fallback_name="Self test", ) From 51682e9e648ee45a4189ce487d2e61b00efcab44 Mon Sep 17 00:00:00 2001 From: TheJulianJES Date: Tue, 28 Oct 2025 18:09:25 +0100 Subject: [PATCH 11/13] Change `FakeManufacturerCluster` to `OppleCluster` --- tests/test_button.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_button.py b/tests/test_button.py index 827aa2ec6..76a99ff51 100644 --- a/tests/test_button.py +++ b/tests/test_button.py @@ -385,7 +385,7 @@ async def test_cluster_handler_quirks_unnecessary_claiming( ) .replaces(OppleCluster) .command_button( - FakeManufacturerCluster.ServerCommandDefs.self_test.name, + OppleCluster.ServerCommandDefs.self_test.name, OppleCluster.cluster_id, command_kwargs={"identify_time": 5}, translation_key="self_test", From dfa5653527d5219404d8e4725d5f49317a523e70 Mon Sep 17 00:00:00 2001 From: TheJulianJES Date: Tue, 28 Oct 2025 18:17:46 +0100 Subject: [PATCH 12/13] Revert change in unrelated test, as more of it needs to be changed --- tests/test_button.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_button.py b/tests/test_button.py index 76a99ff51..b1d6e2bfc 100644 --- a/tests/test_button.py +++ b/tests/test_button.py @@ -221,7 +221,7 @@ async def custom_button_device(zha_gateway: Gateway): .command_button( FakeManufacturerCluster.ServerCommandDefs.self_test.name, FakeManufacturerCluster.cluster_id, - command_kwargs={"identify_time": 5}, + command_args=(5,), translation_key="self_test", fallback_name="Self test", ) @@ -268,7 +268,7 @@ async def test_quirks_command_button( assert len(cluster.request.mock_calls) == 1 assert cluster.request.call_args[0][0] is False assert cluster.request.call_args[0][1] == 0 - assert cluster.request.call_args[0][3] == 5 # duration in seconds + assert cluster.request.call_args[0][3] == 5 async def test_quirks_write_attr_button( From 3d4632249df3a170ad2a9f0e12e699463adabdc1 Mon Sep 17 00:00:00 2001 From: TheJulianJES Date: Tue, 28 Oct 2025 18:19:39 +0100 Subject: [PATCH 13/13] Add back removed comment in test --- tests/test_button.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_button.py b/tests/test_button.py index b1d6e2bfc..6de870147 100644 --- a/tests/test_button.py +++ b/tests/test_button.py @@ -268,7 +268,7 @@ async def test_quirks_command_button( assert len(cluster.request.mock_calls) == 1 assert cluster.request.call_args[0][0] is False assert cluster.request.call_args[0][1] == 0 - assert cluster.request.call_args[0][3] == 5 + assert cluster.request.call_args[0][3] == 5 # duration in seconds async def test_quirks_write_attr_button(