From ef69e6d54be8451bcbc64877e948642b6643748e Mon Sep 17 00:00:00 2001 From: Maciej Bieniek Date: Sun, 19 Oct 2025 00:57:48 +0200 Subject: [PATCH] Improve entity names for powered by Shelly devices (#154592) --- homeassistant/components/shelly/binary_sensor.py | 4 ++-- homeassistant/components/shelly/button.py | 10 ++++++++-- homeassistant/components/shelly/const.py | 2 ++ homeassistant/components/shelly/entity.py | 4 +++- homeassistant/components/shelly/number.py | 3 ++- homeassistant/components/shelly/select.py | 3 ++- homeassistant/components/shelly/sensor.py | 11 +++++++---- homeassistant/components/shelly/switch.py | 3 ++- homeassistant/components/shelly/text.py | 3 ++- homeassistant/components/shelly/utils.py | 11 +++++++---- tests/components/shelly/test_sensor.py | 3 ++- 11 files changed, 39 insertions(+), 18 deletions(-) diff --git a/homeassistant/components/shelly/binary_sensor.py b/homeassistant/components/shelly/binary_sensor.py index 78885d767bdb..67e71a712a37 100644 --- a/homeassistant/components/shelly/binary_sensor.py +++ b/homeassistant/components/shelly/binary_sensor.py @@ -18,7 +18,7 @@ from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.helpers.restore_state import RestoreEntity -from .const import CONF_SLEEP_PERIOD, MODEL_FRANKEVER_WATER_VALVE +from .const import CONF_SLEEP_PERIOD, MODEL_FRANKEVER_WATER_VALVE, ROLE_GENERIC from .coordinator import ShellyConfigEntry, ShellyRpcCoordinator from .entity import ( BlockEntityDescription, @@ -272,7 +272,7 @@ RPC_SENSORS: Final = { removal_condition=lambda config, _status, key: not is_view_for_platform( config, key, BINARY_SENSOR_PLATFORM ), - role="generic", + role=ROLE_GENERIC, ), "boolean_has_power": RpcBinarySensorDescription( key="boolean", diff --git a/homeassistant/components/shelly/button.py b/homeassistant/components/shelly/button.py index f5e0e315fd8a..e5bbc2104e63 100644 --- a/homeassistant/components/shelly/button.py +++ b/homeassistant/components/shelly/button.py @@ -23,7 +23,13 @@ from homeassistant.helpers import entity_registry as er from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.helpers.update_coordinator import CoordinatorEntity -from .const import DOMAIN, LOGGER, MODEL_FRANKEVER_WATER_VALVE, SHELLY_GAS_MODELS +from .const import ( + DOMAIN, + LOGGER, + MODEL_FRANKEVER_WATER_VALVE, + ROLE_GENERIC, + SHELLY_GAS_MODELS, +) from .coordinator import ShellyBlockCoordinator, ShellyConfigEntry, ShellyRpcCoordinator from .entity import ( RpcEntityDescription, @@ -351,7 +357,7 @@ class RpcVirtualButton(ShellyRpcAttributeEntity, ButtonEntity): RPC_BUTTONS = { "button_generic": RpcButtonDescription( key="button", - role="generic", + role=ROLE_GENERIC, ), "button_open": RpcButtonDescription( key="button", diff --git a/homeassistant/components/shelly/const.py b/homeassistant/components/shelly/const.py index ff38a24a15b2..935d16825358 100644 --- a/homeassistant/components/shelly/const.py +++ b/homeassistant/components/shelly/const.py @@ -328,3 +328,5 @@ MODEL_LINKEDGO_ST802_THERMOSTAT = "ST-802" MODEL_LINKEDGO_ST1820_THERMOSTAT = "ST1820" MODEL_TOP_EV_CHARGER_EVE01 = "EVE01" MODEL_FRANKEVER_IRRIGATION_CONTROLLER = "Irrigation" + +ROLE_GENERIC = "generic" diff --git a/homeassistant/components/shelly/entity.py b/homeassistant/components/shelly/entity.py index 07d64fa6eb07..f9eb51c2ac8d 100644 --- a/homeassistant/components/shelly/entity.py +++ b/homeassistant/components/shelly/entity.py @@ -560,7 +560,9 @@ class ShellyRpcAttributeEntity(ShellyRpcEntity, Entity): self.entity_description = description self._attr_unique_id = f"{super().unique_id}-{attribute}" - self._attr_name = get_rpc_entity_name(coordinator.device, key, description.name) + self._attr_name = get_rpc_entity_name( + coordinator.device, key, description.name, description.role + ) self._last_value = None id_key = key.split(":")[-1] self._id = int(id_key) if id_key.isnumeric() else None diff --git a/homeassistant/components/shelly/number.py b/homeassistant/components/shelly/number.py index a7531f47a6b7..29bd88a71949 100644 --- a/homeassistant/components/shelly/number.py +++ b/homeassistant/components/shelly/number.py @@ -32,6 +32,7 @@ from .const import ( MODEL_LINKEDGO_ST802_THERMOSTAT, MODEL_LINKEDGO_ST1820_THERMOSTAT, MODEL_TOP_EV_CHARGER_EVE01, + ROLE_GENERIC, VIRTUAL_NUMBER_MODE_MAP, ) from .coordinator import ShellyBlockCoordinator, ShellyConfigEntry, ShellyRpcCoordinator @@ -223,7 +224,7 @@ RPC_NUMBERS: Final = { step_fn=lambda config: config["meta"]["ui"].get("step"), unit=get_virtual_component_unit, method="number_set", - role="generic", + role=ROLE_GENERIC, ), "number_current_limit": RpcNumberDescription( key="number", diff --git a/homeassistant/components/shelly/select.py b/homeassistant/components/shelly/select.py index 165e9bc21a23..4274eac9fafb 100644 --- a/homeassistant/components/shelly/select.py +++ b/homeassistant/components/shelly/select.py @@ -15,6 +15,7 @@ from homeassistant.components.select import ( from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback +from .const import ROLE_GENERIC from .coordinator import ShellyConfigEntry, ShellyRpcCoordinator from .entity import ( RpcEntityDescription, @@ -44,7 +45,7 @@ RPC_SELECT_ENTITIES: Final = { removal_condition=lambda config, _status, key: not is_view_for_platform( config, key, SELECT_PLATFORM ), - role="generic", + role=ROLE_GENERIC, ), } diff --git a/homeassistant/components/shelly/sensor.py b/homeassistant/components/shelly/sensor.py index 64ef9847249b..ec1abd415ae6 100644 --- a/homeassistant/components/shelly/sensor.py +++ b/homeassistant/components/shelly/sensor.py @@ -41,7 +41,7 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.helpers.entity_registry import RegistryEntry from homeassistant.helpers.typing import StateType -from .const import CONF_SLEEP_PERIOD +from .const import CONF_SLEEP_PERIOD, ROLE_GENERIC from .coordinator import ShellyBlockCoordinator, ShellyConfigEntry, ShellyRpcCoordinator from .entity import ( BlockEntityDescription, @@ -1425,7 +1425,7 @@ RPC_SENSORS: Final = { removal_condition=lambda config, _, key: not is_view_for_platform( config, key, SENSOR_PLATFORM ), - role="generic", + role=ROLE_GENERIC, ), "number_generic": RpcSensorDescription( key="number", @@ -1434,7 +1434,7 @@ RPC_SENSORS: Final = { config, key, SENSOR_PLATFORM ), unit=get_virtual_component_unit, - role="generic", + role=ROLE_GENERIC, ), "enum_generic": RpcSensorDescription( key="enum", @@ -1444,7 +1444,7 @@ RPC_SENSORS: Final = { ), options_fn=lambda config: config["options"], device_class=SensorDeviceClass.ENUM, - role="generic", + role=ROLE_GENERIC, ), "valve_position": RpcSensorDescription( key="blutrv", @@ -1489,6 +1489,7 @@ RPC_SENSORS: Final = { "number_current_humidity": RpcSensorDescription( key="number", sub_key="value", + name="Humidity", native_unit_of_measurement=PERCENTAGE, suggested_display_precision=1, device_class=SensorDeviceClass.HUMIDITY, @@ -1498,6 +1499,7 @@ RPC_SENSORS: Final = { "number_current_temperature": RpcSensorDescription( key="number", sub_key="value", + name="Temperature", native_unit_of_measurement=UnitOfTemperature.CELSIUS, suggested_display_precision=1, device_class=SensorDeviceClass.TEMPERATURE, @@ -1507,6 +1509,7 @@ RPC_SENSORS: Final = { "number_flow_rate": RpcSensorDescription( key="number", sub_key="value", + name="Water flow rate", native_unit_of_measurement=UnitOfVolumeFlowRate.CUBIC_METERS_PER_MINUTE, device_class=SensorDeviceClass.VOLUME_FLOW_RATE, state_class=SensorStateClass.MEASUREMENT, diff --git a/homeassistant/components/shelly/switch.py b/homeassistant/components/shelly/switch.py index 61b8796cf8b3..2f6c76995f15 100644 --- a/homeassistant/components/shelly/switch.py +++ b/homeassistant/components/shelly/switch.py @@ -27,6 +27,7 @@ from .const import ( MODEL_LINKEDGO_ST1820_THERMOSTAT, MODEL_NEO_WATER_VALVE, MODEL_TOP_EV_CHARGER_EVE01, + ROLE_GENERIC, ) from .coordinator import ShellyBlockCoordinator, ShellyConfigEntry, ShellyRpcCoordinator from .entity import ( @@ -105,7 +106,7 @@ RPC_SWITCHES = { method_on="boolean_set", method_off="boolean_set", method_params_fn=lambda id, value: (id, value), - role="generic", + role=ROLE_GENERIC, ), "boolean_anti_freeze": RpcSwitchDescription( key="boolean", diff --git a/homeassistant/components/shelly/text.py b/homeassistant/components/shelly/text.py index 5643c34c7272..164445d569a6 100644 --- a/homeassistant/components/shelly/text.py +++ b/homeassistant/components/shelly/text.py @@ -15,6 +15,7 @@ from homeassistant.components.text import ( from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback +from .const import ROLE_GENERIC from .coordinator import ShellyConfigEntry from .entity import ( RpcEntityDescription, @@ -44,7 +45,7 @@ RPC_TEXT_ENTITIES: Final = { removal_condition=lambda config, _status, key: not is_view_for_platform( config, key, TEXT_PLATFORM ), - role="generic", + role=ROLE_GENERIC, ), } diff --git a/homeassistant/components/shelly/utils.py b/homeassistant/components/shelly/utils.py index 963494a30f38..36478a3dc620 100644 --- a/homeassistant/components/shelly/utils.py +++ b/homeassistant/components/shelly/utils.py @@ -66,6 +66,7 @@ from .const import ( GEN2_RELEASE_URL, LOGGER, MAX_SCRIPT_SIZE, + ROLE_GENERIC, RPC_INPUTS_EVENTS_TYPES, SHAIR_MAX_WORK_HOURS, SHBTN_INPUTS_EVENTS_TYPES, @@ -436,13 +437,15 @@ def get_rpc_sub_device_name( def get_rpc_entity_name( - device: RpcDevice, key: str, description: str | None = None + device: RpcDevice, key: str, name: str | None = None, role: str | None = None ) -> str | None: """Naming for RPC based switch and sensors.""" channel_name = get_rpc_channel_name(device, key) - if description: - return f"{channel_name} {description.lower()}" if channel_name else description + if name: + if role and role != ROLE_GENERIC: + return name + return f"{channel_name} {name.lower()}" if channel_name else name return channel_name @@ -483,7 +486,7 @@ def get_rpc_key_by_role(keys_dict: dict[str, Any], role: str) -> str | None: def get_rpc_role_by_key(keys_dict: dict[str, Any], key: str) -> str: """Return role by key for RPC device from a dict.""" - return cast(str, keys_dict[key].get("role", "generic")) + return cast(str, keys_dict[key].get("role", ROLE_GENERIC)) def id_from_key(key: str) -> int: diff --git a/tests/components/shelly/test_sensor.py b/tests/components/shelly/test_sensor.py index 6e6a626eb5e0..640d7439eb9f 100644 --- a/tests/components/shelly/test_sensor.py +++ b/tests/components/shelly/test_sensor.py @@ -1575,7 +1575,7 @@ async def test_rpc_device_virtual_number_sensor_with_device_class( entity_registry: EntityRegistry, ) -> None: """Test a virtual number sensor with device class for RPC device.""" - entity_id = f"{SENSOR_DOMAIN}.test_name_current_humidity" + entity_id = f"{SENSOR_DOMAIN}.test_name_humidity" config = deepcopy(mock_rpc_device.config) config["number:203"] = { "name": "Current humidity", @@ -1587,6 +1587,7 @@ async def test_rpc_device_virtual_number_sensor_with_device_class( monkeypatch.setattr(mock_rpc_device, "config", config) status = deepcopy(mock_rpc_device.status) + status.pop("humidity:0") status["number:203"] = {"value": 34} monkeypatch.setattr(mock_rpc_device, "status", status)