@@ -2490,3 +2490,101 @@ class ServerCommandDefs(Basic.ServerCommandDefs):
24902490 (TestCluster .ServerCommandDefs .reset_fact_default , None ),
24912491 ]:
24922492 assert cluster ._get_effective_manufacturer_code (definition ) is expected
2493+
2494+
2495+ async def test_quirk_manufacturer_code_context_isolation (app_mock ) -> None :
2496+ """Test that manufacturer code context is properly handled in _update_attribute.
2497+
2498+ When a manufacturer-specific attribute is reported and the cluster has multiple
2499+ attributes sharing the same ID (with different manufacturer codes), the
2500+ _update_attribute call must use the correct manufacturer code. This tests that:
2501+ 1. The value is stored directly in the typed cache (not via legacy cache fallback)
2502+ 2. Other attributes updated by quirks don't inherit the manufacturer code context
2503+ """
2504+
2505+ class TestCluster (zcl .Cluster ):
2506+ cluster_id = 0xABCD
2507+ ep_attribute = "test_cluster"
2508+ _skip_registry = True
2509+
2510+ class AttributeDefs (zcl .foundation .BaseAttributeDefs ):
2511+ # Two attributes sharing the same ID with different manufacturer codes
2512+ manuf_attr = foundation .ZCLAttributeDef (
2513+ id = 0x0001 ,
2514+ type = t .uint8_t ,
2515+ manufacturer_code = 0x1234 ,
2516+ )
2517+ standard_attr = foundation .ZCLAttributeDef (
2518+ id = 0x0001 ,
2519+ type = t .uint8_t ,
2520+ manufacturer_code = None ,
2521+ )
2522+ # A different attribute that the quirk will also update
2523+ other_attr = foundation .ZCLAttributeDef (
2524+ id = 0x0002 ,
2525+ type = t .uint8_t ,
2526+ )
2527+
2528+ def _update_attribute (self , attrid , value ):
2529+ super ()._update_attribute (attrid , value )
2530+
2531+ # When updating the manufacturer-specific attribute, also update other_attr
2532+ if attrid == self .AttributeDefs .manuf_attr .id :
2533+ super ()._update_attribute (self .AttributeDefs .other_attr .id , 99 )
2534+
2535+ dev = add_initialized_device (app_mock , nwk = 0x1234 , ieee = make_ieee (1 ))
2536+ cluster = TestCluster (dev .endpoints [1 ])
2537+ dev .endpoints [1 ].add_input_cluster (TestCluster .cluster_id , cluster )
2538+
2539+ events = []
2540+ cluster .on_event (AttributeReportedEvent .event_type , events .append )
2541+ cluster .on_event (AttributeUpdatedEvent .event_type , events .append )
2542+
2543+ # The attribute is currently marked as unsupported
2544+ cluster .add_unsupported_attribute (TestCluster .AttributeDefs .manuf_attr )
2545+
2546+ # Report the manufacturer-specific attribute
2547+ await mock_attribute_report (
2548+ cluster , {TestCluster .AttributeDefs .manuf_attr : t .uint8_t (42 )}
2549+ )
2550+
2551+ # The legacy cache should not contain the attribute, as the typed cache was used
2552+ assert 0x0001 not in cluster ._attr_cache ._legacy_cache
2553+
2554+ # Verify that the manufacturer-specific attribute was stored correctly
2555+ assert cluster ._attr_cache .get_value (TestCluster .AttributeDefs .manuf_attr ) == 42
2556+
2557+ # Verify that the standard attribute (same ID, no manufacturer code) was NOT updated
2558+ with pytest .raises (KeyError ):
2559+ cluster ._attr_cache .get_value (TestCluster .AttributeDefs .standard_attr )
2560+
2561+ # Verify that other_attr was updated (by the quirk) without manufacturer code context
2562+ assert cluster ._attr_cache .get_value (TestCluster .AttributeDefs .other_attr ) == 99
2563+
2564+ # Verify the events have the correct manufacturer codes
2565+ assert len (events ) == 2
2566+
2567+ # First event: other_attr updated by quirk (should have no manufacturer code)
2568+ assert events [0 ] == AttributeUpdatedEvent (
2569+ device_ieee = str (dev .ieee ),
2570+ endpoint_id = 1 ,
2571+ cluster_type = zcl .ClusterType .Server ,
2572+ cluster_id = TestCluster .cluster_id ,
2573+ attribute_name = "other_attr" ,
2574+ attribute_id = TestCluster .AttributeDefs .other_attr .id ,
2575+ manufacturer_code = None ,
2576+ value = 99 ,
2577+ )
2578+
2579+ # Second event: manuf_attr reported (should have the manufacturer code)
2580+ assert events [1 ] == AttributeReportedEvent (
2581+ device_ieee = str (dev .ieee ),
2582+ endpoint_id = 1 ,
2583+ cluster_type = zcl .ClusterType .Server ,
2584+ cluster_id = TestCluster .cluster_id ,
2585+ attribute_name = "manuf_attr" ,
2586+ attribute_id = TestCluster .AttributeDefs .manuf_attr .id ,
2587+ manufacturer_code = 0x1234 ,
2588+ raw_value = 42 ,
2589+ value = 42 ,
2590+ )
0 commit comments