Skip to content

Commit 687e88b

Browse files
authored
Allow quirks v2 to specify primary entities (#468)
* Allow quirks v2 to specify primary entities * Add a unit test
1 parent a0fd5ac commit 687e88b

File tree

3 files changed

+56
-4
lines changed

3 files changed

+56
-4
lines changed

tests/test_device.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from zigpy.quirks.v2 import DeviceAlertLevel, DeviceAlertMetadata, QuirkBuilder
1414
import zigpy.types
1515
from zigpy.zcl.clusters import general
16+
from zigpy.zcl.clusters.general import PowerConfiguration
1617
from zigpy.zcl.foundation import Status, WriteAttributesResponse
1718
import zigpy.zdo.types as zdo_t
1819

@@ -895,6 +896,35 @@ async def test_primary_entity_computation(
895896
]
896897

897898

899+
async def test_quirks_v2_primary_entity(zha_gateway: Gateway) -> None:
900+
"""Test quirks v2 primary entity."""
901+
registry = DeviceRegistry()
902+
903+
(
904+
QuirkBuilder("CentraLite", "3405-L", registry=registry)
905+
.sensor(
906+
attribute_name=PowerConfiguration.AttributeDefs.battery_quantity.id,
907+
cluster_id=PowerConfiguration.cluster_id,
908+
translation_key="battery_quantity",
909+
fallback_name="Battery quantity",
910+
primary=True,
911+
)
912+
.add_to_registry()
913+
)
914+
915+
zigpy_dev = registry.get_device(
916+
await zigpy_device_from_json(
917+
zha_gateway.application_controller,
918+
"tests/data/devices/centralite-3405-l.json",
919+
)
920+
)
921+
922+
zha_device = await join_zigpy_device(zha_gateway, zigpy_dev)
923+
924+
(primary,) = [e for e in zha_device.platform_entities.values() if e.primary]
925+
assert primary.translation_key == "battery_quantity"
926+
927+
898928
async def test_quirks_v2_prevent_default_entities(zha_gateway: Gateway) -> None:
899929
"""Test quirks v2 can prevent creating default entities."""
900930
registry = DeviceRegistry()

zha/application/platforms/__init__.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ class BaseEntity(LogMixin, EventBase):
123123
_attr_state_class: str | None = None
124124
_attr_enabled: bool = True
125125
_attr_always_supported: bool = False
126-
_attr_primary: bool = False
126+
_attr_primary: bool | None = None
127127

128128
# When two entities both want to be primary, the one with the higher weight will be
129129
# chosen. If there is a tie, both lose.
@@ -173,10 +173,13 @@ def enabled(self, value: bool) -> None:
173173
@property
174174
def primary(self) -> bool:
175175
"""Return if the entity is the primary device control."""
176+
if self._attr_primary is None:
177+
return False
178+
176179
return self._attr_primary
177180

178181
@primary.setter
179-
def primary(self, value: bool) -> None:
182+
def primary(self, value: bool | None) -> None:
180183
"""Set the entity as the primary device control."""
181184
self._attr_primary = value
182185

@@ -404,6 +407,9 @@ def _init_from_quirks_metadata(self, entity_metadata: EntityMetadata) -> None:
404407
else:
405408
self._attr_entity_category = None
406409

410+
if entity_metadata.primary is not None:
411+
self._attr_primary = entity_metadata.primary
412+
407413
@cached_property
408414
def identifiers(self) -> PlatformEntityIdentifiers:
409415
"""Return a dict with the information necessary to identify this entity."""

zha/zigbee/device.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1207,11 +1207,27 @@ def log(self, level: int, msg: str, *args: Any, **kwargs: Any) -> None:
12071207
def _compute_primary_entity(self) -> None:
12081208
"""Compute the primary entity for this device."""
12091209

1210-
# Only consider non-counter entities
1210+
# First, check if any entity is explicitly primary
1211+
explicitly_primary = [
1212+
entity for entity in self._platform_entities.values() if entity.primary
1213+
]
1214+
1215+
if len(explicitly_primary) == 1:
1216+
self.debug(
1217+
"Device has a single explicitly primary entity,"
1218+
" not performing weight matching"
1219+
)
1220+
return
1221+
1222+
# It should not be possible for there to be more than one
1223+
assert not explicitly_primary
1224+
1225+
# For weight matching, only consider non-counter entities and entities which are
1226+
# not explicitly marked as not primary
12111227
candidates = [
12121228
e
12131229
for e in self._platform_entities.values()
1214-
if e.enabled and hasattr(e, "info_object")
1230+
if e.enabled and hasattr(e, "info_object") and e._attr_primary is not False
12151231
]
12161232
candidates.sort(reverse=True, key=lambda e: e.primary_weight)
12171233

0 commit comments

Comments
 (0)