|
12 | 12 | PROFILE_ID, |
13 | 13 | ) |
14 | 14 | from zhaquirks.tuya.tuya_valve import ParksideTuyaValveManufCluster |
| 15 | +import zigpy |
15 | 16 | from zigpy.exceptions import ZigbeeException |
16 | 17 | from zigpy.profiles import zha |
17 | 18 | from zigpy.quirks import CustomCluster, CustomDevice, DeviceRegistry |
|
34 | 35 | update_attribute_cache, |
35 | 36 | ) |
36 | 37 | from zha.application import Platform |
| 38 | +from zha.application.const import ZCL_INIT_ATTRS |
37 | 39 | from zha.application.gateway import Gateway |
38 | 40 | from zha.application.platforms import EntityCategory, PlatformEntity |
39 | 41 | from zha.application.platforms.button import ( |
|
43 | 45 | ) |
44 | 46 | from zha.application.platforms.button.const import ButtonDeviceClass |
45 | 47 | from zha.exceptions import ZHAException |
| 48 | +from zha.zigbee.cluster_handlers.manufacturerspecific import OppleRemoteClusterHandler |
46 | 49 | from zha.zigbee.device import Device |
47 | 50 |
|
48 | 51 | ZIGPY_DEVICE = { |
@@ -354,3 +357,89 @@ async def test_quirks_write_attr_buttons_uid(zha_gateway: Gateway) -> None: |
354 | 357 | assert entity_btn_2.translation_key == "btn_2" |
355 | 358 | assert entity_btn_2._unique_id_suffix == "btn_2" |
356 | 359 | assert entity_btn_2._attribute_value == 2 |
| 360 | + |
| 361 | + |
| 362 | +class OppleCluster(CustomCluster, ManufacturerSpecificCluster): |
| 363 | + """Aqara manufacturer specific cluster.""" |
| 364 | + |
| 365 | + cluster_id = 0xFCC0 |
| 366 | + ep_attribute = "opple_cluster" |
| 367 | + |
| 368 | + class ServerCommandDefs(zcl_f.BaseCommandDefs): |
| 369 | + """Server command definitions.""" |
| 370 | + |
| 371 | + self_test: Final = zcl_f.ZCLCommandDef( |
| 372 | + id=0x00, schema={"identify_time": t.uint16_t} |
| 373 | + ) |
| 374 | + |
| 375 | + |
| 376 | +async def test_cluster_handler_quirks_unnecessary_claiming( |
| 377 | + zha_gateway: Gateway, |
| 378 | +) -> None: |
| 379 | + """Test quirks button doesn't claim cluster handlers unnecessarily.""" |
| 380 | + |
| 381 | + registry = DeviceRegistry() |
| 382 | + ( |
| 383 | + QuirkBuilder( |
| 384 | + "Fake_Manufacturer_sensor_2", "Fake_Model_sensor_2", registry=registry |
| 385 | + ) |
| 386 | + .replaces(OppleCluster) |
| 387 | + .command_button( |
| 388 | + OppleCluster.ServerCommandDefs.self_test.name, |
| 389 | + OppleCluster.cluster_id, |
| 390 | + command_kwargs={"identify_time": 5}, |
| 391 | + translation_key="self_test", |
| 392 | + fallback_name="Self test", |
| 393 | + ) |
| 394 | + .add_to_registry() |
| 395 | + ) |
| 396 | + |
| 397 | + zigpy_device = create_mock_zigpy_device( |
| 398 | + zha_gateway, |
| 399 | + { |
| 400 | + 1: { |
| 401 | + SIG_EP_INPUT: [ |
| 402 | + general.Basic.cluster_id, |
| 403 | + OppleCluster.cluster_id, |
| 404 | + ], |
| 405 | + SIG_EP_OUTPUT: [], |
| 406 | + SIG_EP_TYPE: zigpy.profiles.zha.DeviceType.OCCUPANCY_SENSOR, |
| 407 | + SIG_EP_PROFILE: zigpy.profiles.zha.PROFILE_ID, |
| 408 | + } |
| 409 | + }, |
| 410 | + manufacturer="Fake_Manufacturer_sensor_2", |
| 411 | + model="Fake_Model_sensor_2", |
| 412 | + ) |
| 413 | + zigpy_device = registry.get_device(zigpy_device) |
| 414 | + |
| 415 | + # Suppress normal endpoint probing, as this will claim the Opple cluster handler |
| 416 | + # already due to it being in the "CLUSTER_HANDLER_ONLY_CLUSTERS" registry. |
| 417 | + # We want to test the handler also gets claimed via quirks v2 attributes init. |
| 418 | + with patch("zha.application.discovery.EndpointProbe.discover_entities"): |
| 419 | + zha_device = await join_zigpy_device(zha_gateway, zigpy_device) |
| 420 | + assert isinstance(zha_device.device, CustomDeviceV2) |
| 421 | + |
| 422 | + # get cluster handler of OppleCluster |
| 423 | + opple_ch = zha_device.endpoints[1].all_cluster_handlers["1:0xfcc0"] |
| 424 | + assert isinstance(opple_ch, OppleRemoteClusterHandler) |
| 425 | + |
| 426 | + # make sure the cluster handler was not claimed, |
| 427 | + # as no reporting is configured and no attributes are to be read |
| 428 | + assert opple_ch not in zha_device.endpoints[1].claimed_cluster_handlers.values() |
| 429 | + |
| 430 | + # check that BIND is left at default of True, though ZHA will ignore it |
| 431 | + assert opple_ch.BIND is True |
| 432 | + |
| 433 | + # check ZCL_INIT_ATTRS is empty |
| 434 | + assert opple_ch.ZCL_INIT_ATTRS == {} |
| 435 | + |
| 436 | + # check that no ZCL_INIT_ATTRS instance variable was created |
| 437 | + assert opple_ch.__dict__.get(ZCL_INIT_ATTRS) is None |
| 438 | + assert opple_ch.ZCL_INIT_ATTRS is OppleRemoteClusterHandler.ZCL_INIT_ATTRS |
| 439 | + |
| 440 | + # double check we didn't modify the class variable |
| 441 | + assert OppleRemoteClusterHandler.ZCL_INIT_ATTRS == {} |
| 442 | + |
| 443 | + # check if REPORT_CONFIG is empty, both instance and class variable |
| 444 | + assert opple_ch.REPORT_CONFIG == () |
| 445 | + assert OppleRemoteClusterHandler.REPORT_CONFIG == () |
0 commit comments