diff --git a/homeassistant/components/zha/entity.py b/homeassistant/components/zha/entity.py index fea90b5167fa86..e6d5a95912c7c0 100644 --- a/homeassistant/components/zha/entity.py +++ b/homeassistant/components/zha/entity.py @@ -72,6 +72,8 @@ def name(self) -> str | UndefinedType | None: original_name = super().name if original_name not in (UNDEFINED, None) or meta.fallback_name is None: + if meta.postfix is not None: + return f"{original_name} ({meta.postfix})" return original_name # This is to allow local development and to register niche devices, since diff --git a/tests/components/zha/snapshots/test_diagnostics.ambr b/tests/components/zha/snapshots/test_diagnostics.ambr index 4d90942fb97183..d47b242b3a358c 100644 --- a/tests/components/zha/snapshots/test_diagnostics.ambr +++ b/tests/components/zha/snapshots/test_diagnostics.ambr @@ -272,6 +272,7 @@ 'migrate_unique_ids': list([ ]), 'platform': 'alarm_control_panel', + 'postfix': None, 'primary': False, 'state_class': None, 'supported_features': 15, @@ -318,6 +319,7 @@ 'migrate_unique_ids': list([ ]), 'platform': 'binary_sensor', + 'postfix': None, 'primary': True, 'state_class': None, 'translation_key': 'ias_zone', diff --git a/tests/components/zha/test_entity.py b/tests/components/zha/test_entity.py index eac987a070e5e2..bdca682706a635 100644 --- a/tests/components/zha/test_entity.py +++ b/tests/components/zha/test_entity.py @@ -1,17 +1,37 @@ """Test ZHA entities.""" from collections.abc import Callable, Coroutine +from unittest.mock import patch +import pytest from zigpy.device import Device from zigpy.profiles import zha -from zigpy.zcl.clusters import general +import zigpy.types as t +from zigpy.zcl.clusters import general, measurement from homeassistant.components.zha.helpers import get_zha_gateway +from homeassistant.const import Platform from homeassistant.core import HomeAssistant from homeassistant.helpers import device_registry as dr from .conftest import SIG_EP_INPUT, SIG_EP_OUTPUT, SIG_EP_PROFILE, SIG_EP_TYPE +ENTITY_ID_NO_PREFIX = "{}.fakemanufacturer_fakemodel" +ENTITY_ID_PREFIX_NUM = "{}.fakemanufacturer_fakemodel_{}_{}" + + +@pytest.fixture(autouse=True) +def sensor_and_switch_platform_only(): + """Only set up the switch and sensor platforms to speed up tests.""" + with patch( + "homeassistant.components.zha.PLATFORMS", + ( + Platform.SENSOR, + Platform.SWITCH, + ), + ): + yield + async def test_device_registry_via_device( hass: HomeAssistant, @@ -48,3 +68,116 @@ async def test_device_registry_via_device( ) assert reg_device.via_device_id == reg_coordinator_device.id + + +@pytest.mark.parametrize( + ( + "cluster_id", + "entity_prefix", + "entity_suffix", + ), + [ + ( + measurement.TemperatureMeasurement.cluster_id, + "sensor", + "temperature", + ), + ( + measurement.PressureMeasurement.cluster_id, + "sensor", + "pressure", + ), + ( + general.OnOff.cluster_id, + "switch", + "switch", + ), + ], +) +async def test_entity_postfix( + hass: HomeAssistant, + setup_zha: Callable[..., Coroutine[None]], + zigpy_device_mock: Callable[..., Device], + cluster_id: t.uint16_t, + entity_prefix: str, + entity_suffix: str, +) -> None: + """Test postfix when a device has several entities of the same type.""" + n_endpoints = 2 + + await setup_zha() + gateway = get_zha_gateway(hass) + + endpoint_definition = { + SIG_EP_INPUT: [cluster_id, general.Basic.cluster_id], + SIG_EP_OUTPUT: [], + SIG_EP_TYPE: zha.DeviceType.ON_OFF_SWITCH, + } + + # Create a device with n_endpoints identical endpoints + zigpy_device = zigpy_device_mock( + dict.fromkeys(range(1, n_endpoints + 1), endpoint_definition), + ) + + gateway.get_or_create_device(zigpy_device) + await gateway.async_device_initialized(zigpy_device) + await hass.async_block_till_done(wait_background_tasks=True) + + for n in range(1, n_endpoints + 1): + state = hass.states.get( + ENTITY_ID_PREFIX_NUM.format(entity_prefix, entity_suffix, n) + ) + assert state is not None + assert state.name.endswith(f" ({n})") + + +@pytest.mark.parametrize( + ( + "cluster_id", + "entity_prefix", + ), + [ + ( + measurement.TemperatureMeasurement.cluster_id, + "sensor", + ), + ( + measurement.PressureMeasurement.cluster_id, + "sensor", + ), + ( + general.OnOff.cluster_id, + "switch", + ), + ], +) +async def test_single_entity_no_postfix( + hass: HomeAssistant, + setup_zha: Callable[..., Coroutine[None]], + zigpy_device_mock: Callable[..., Device], + cluster_id: t.uint16_t, + entity_prefix: str, +) -> None: + """Test that postfix is not in the name of singular entities.""" + + await setup_zha() + gateway = get_zha_gateway(hass) + + zigpy_device = zigpy_device_mock( + { + 1: { + SIG_EP_INPUT: [cluster_id, general.Basic.cluster_id], + SIG_EP_OUTPUT: [], + SIG_EP_TYPE: zha.DeviceType.ON_OFF_SWITCH, + }, + } + ) + + gateway.get_or_create_device(zigpy_device) + await gateway.async_device_initialized(zigpy_device) + await hass.async_block_till_done(wait_background_tasks=True) + + state = hass.states.get(ENTITY_ID_NO_PREFIX.format(entity_prefix)) + assert state is not None + # Name should not be "Entity ()", check for trailing parenthesis + assert not state.name.endswith(")")