diff --git a/homeassistant/components/shelly/button.py b/homeassistant/components/shelly/button.py index edc33c9a8a0..a505867b3e8 100644 --- a/homeassistant/components/shelly/button.py +++ b/homeassistant/components/shelly/button.py @@ -154,6 +154,7 @@ class ShellyButton( entity_description: ShellyButtonDescription[ ShellyRpcCoordinator | ShellyBlockCoordinator ] + _attr_has_entity_name = True def __init__( self, @@ -166,7 +167,6 @@ class ShellyButton( super().__init__(coordinator) self.entity_description = description - self._attr_name = f"{coordinator.device.name} {description.name}" self._attr_unique_id = f"{coordinator.mac}_{description.key}" self._attr_device_info = DeviceInfo( connections={(CONNECTION_NETWORK_MAC, coordinator.mac)} diff --git a/homeassistant/components/shelly/climate.py b/homeassistant/components/shelly/climate.py index a9712e62d25..d77a491661c 100644 --- a/homeassistant/components/shelly/climate.py +++ b/homeassistant/components/shelly/climate.py @@ -130,6 +130,7 @@ class BlockSleepingClimate( ) _attr_target_temperature_step = SHTRV_01_TEMPERATURE_SETTINGS["step"] _attr_temperature_unit = UnitOfTemperature.CELSIUS + _attr_has_entity_name = True def __init__( self, @@ -173,11 +174,6 @@ class BlockSleepingClimate( """Set unique id of entity.""" return self._unique_id - @property - def name(self) -> str: - """Name of entity.""" - return self.coordinator.name - @property def target_temperature(self) -> float | None: """Set target temperature.""" @@ -354,7 +350,7 @@ class BlockSleepingClimate( severity=ir.IssueSeverity.ERROR, translation_key="device_not_calibrated", translation_placeholders={ - "device_name": self.name, + "device_name": self.coordinator.name, "ip_address": self.coordinator.device.ip_address, }, ) diff --git a/homeassistant/components/shelly/entity.py b/homeassistant/components/shelly/entity.py index 1dc7573b738..ac06624c750 100644 --- a/homeassistant/components/shelly/entity.py +++ b/homeassistant/components/shelly/entity.py @@ -321,6 +321,8 @@ class RestEntityDescription(EntityDescription): class ShellyBlockEntity(CoordinatorEntity[ShellyBlockCoordinator]): """Helper class to represent a block entity.""" + _attr_has_entity_name = True + def __init__(self, coordinator: ShellyBlockCoordinator, block: Block) -> None: """Initialize Shelly entity.""" super().__init__(coordinator) @@ -359,6 +361,8 @@ class ShellyBlockEntity(CoordinatorEntity[ShellyBlockCoordinator]): class ShellyRpcEntity(CoordinatorEntity[ShellyRpcCoordinator]): """Helper class to represent a rpc entity.""" + _attr_has_entity_name = True + def __init__(self, coordinator: ShellyRpcCoordinator, key: str) -> None: """Initialize Shelly entity.""" super().__init__(coordinator) @@ -462,6 +466,7 @@ class ShellyRestAttributeEntity(CoordinatorEntity[ShellyBlockCoordinator]): """Class to load info from REST.""" entity_description: RestEntityDescription + _attr_has_entity_name = True def __init__( self, diff --git a/homeassistant/components/shelly/logbook.py b/homeassistant/components/shelly/logbook.py index d55ffe0fd28..b8f0c8e1744 100644 --- a/homeassistant/components/shelly/logbook.py +++ b/homeassistant/components/shelly/logbook.py @@ -42,7 +42,8 @@ def async_describe_events( rpc_coordinator = get_rpc_coordinator_by_device_id(hass, device_id) if rpc_coordinator and rpc_coordinator.device.initialized: key = f"input:{channel-1}" - input_name = get_rpc_entity_name(rpc_coordinator.device, key) + if iname := get_rpc_entity_name(rpc_coordinator.device, key): + input_name = iname elif click_type in BLOCK_INPUTS_EVENTS_TYPES: block_coordinator = get_block_coordinator_by_device_id(hass, device_id) diff --git a/homeassistant/components/shelly/utils.py b/homeassistant/components/shelly/utils.py index a66b77ed94b..1faa36ce118 100644 --- a/homeassistant/components/shelly/utils.py +++ b/homeassistant/components/shelly/utils.py @@ -72,26 +72,26 @@ def get_block_entity_name( device: BlockDevice, block: Block | None, description: str | None = None, -) -> str: +) -> str | None: """Naming for block based switch and sensors.""" channel_name = get_block_channel_name(device, block) + if description and channel_name: + return f"{channel_name} {uncapitalize(description)}" if description: - return f"{channel_name} {description.lower()}" + return description return channel_name -def get_block_channel_name(device: BlockDevice, block: Block | None) -> str: +def get_block_channel_name(device: BlockDevice, block: Block | None) -> str | None: """Get name based on device and channel name.""" - entity_name = device.name - if ( not block or block.type == "device" or get_number_of_channels(device, block) == 1 ): - return entity_name + return None assert block.channel @@ -108,7 +108,7 @@ def get_block_channel_name(device: BlockDevice, block: Block | None) -> str: else: base = ord("1") - return f"{entity_name} channel {chr(int(block.channel)+base)}" + return f"Channel {chr(int(block.channel)+base)}" def is_block_momentary_input( @@ -285,32 +285,32 @@ def get_model_name(info: dict[str, Any]) -> str: return cast(str, MODEL_NAMES.get(info["type"], info["type"])) -def get_rpc_channel_name(device: RpcDevice, key: str) -> str: +def get_rpc_channel_name(device: RpcDevice, key: str) -> str | None: """Get name based on device and channel name.""" key = key.replace("emdata", "em") if device.config.get("switch:0"): key = key.replace("input", "switch") - device_name = device.name entity_name: str | None = None if key in device.config: - entity_name = device.config[key].get("name", device_name) + entity_name = device.config[key].get("name") if entity_name is None: if key.startswith(("input:", "light:", "switch:")): - return f"{device_name} {key.replace(':', '_')}" - return device_name + return key.replace(":", " ").capitalize() return entity_name def get_rpc_entity_name( device: RpcDevice, key: str, description: str | None = None -) -> str: +) -> str | None: """Naming for RPC based switch and sensors.""" channel_name = get_rpc_channel_name(device, key) + if description and channel_name: + return f"{channel_name} {uncapitalize(description)}" if description: - return f"{channel_name} {description.lower()}" + return description return channel_name @@ -405,3 +405,8 @@ def mac_address_from_name(name: str) -> str | None: """Convert a name to a mac address.""" mac = name.partition(".")[0].partition("-")[-1] return mac.upper() if len(mac) == 12 else None + + +def uncapitalize(description: str) -> str: + """Uncapitalize the first letter of a description.""" + return description[:1].lower() + description[1:] diff --git a/tests/components/shelly/test_binary_sensor.py b/tests/components/shelly/test_binary_sensor.py index c067f5dffc9..ebc5089f884 100644 --- a/tests/components/shelly/test_binary_sensor.py +++ b/tests/components/shelly/test_binary_sensor.py @@ -165,7 +165,7 @@ async def test_rpc_binary_sensor( hass: HomeAssistant, mock_rpc_device, monkeypatch ) -> None: """Test RPC binary sensor.""" - entity_id = f"{BINARY_SENSOR_DOMAIN}.test_cover_0_overpowering" + entity_id = f"{BINARY_SENSOR_DOMAIN}.test_name_test_cover_0_overpowering" await init_integration(hass, 2) assert hass.states.get(entity_id).state == STATE_OFF diff --git a/tests/components/shelly/test_coordinator.py b/tests/components/shelly/test_coordinator.py index 8536c3d72e6..eb546ce5835 100644 --- a/tests/components/shelly/test_coordinator.py +++ b/tests/components/shelly/test_coordinator.py @@ -353,7 +353,7 @@ async def test_rpc_reload_on_cfg_change( ) await hass.async_block_till_done() - assert hass.states.get("switch.test_switch_0") is not None + assert hass.states.get("switch.test_name_test_switch_0") is not None # Wait for debouncer async_fire_time_changed( @@ -361,7 +361,7 @@ async def test_rpc_reload_on_cfg_change( ) await hass.async_block_till_done() - assert hass.states.get("switch.test_switch_0") is None + assert hass.states.get("switch.test_name_test_switch_0") is None async def test_rpc_reload_with_invalid_auth( @@ -588,7 +588,7 @@ async def test_rpc_reconnect_error( """Test RPC reconnect error.""" await init_integration(hass, 2) - assert hass.states.get("switch.test_switch_0").state == STATE_ON + assert hass.states.get("switch.test_name_test_switch_0").state == STATE_ON monkeypatch.setattr(mock_rpc_device, "connected", False) monkeypatch.setattr( @@ -605,7 +605,7 @@ async def test_rpc_reconnect_error( ) await hass.async_block_till_done() - assert hass.states.get("switch.test_switch_0").state == STATE_UNAVAILABLE + assert hass.states.get("switch.test_name_test_switch_0").state == STATE_UNAVAILABLE async def test_rpc_polling_connection_error( diff --git a/tests/components/shelly/test_cover.py b/tests/components/shelly/test_cover.py index 08c0c76d35e..56740981fc5 100644 --- a/tests/components/shelly/test_cover.py +++ b/tests/components/shelly/test_cover.py @@ -97,10 +97,10 @@ async def test_rpc_device_services( await hass.services.async_call( COVER_DOMAIN, SERVICE_SET_COVER_POSITION, - {ATTR_ENTITY_ID: "cover.test_cover_0", ATTR_POSITION: 50}, + {ATTR_ENTITY_ID: "cover.test_name_test_cover_0", ATTR_POSITION: 50}, blocking=True, ) - state = hass.states.get("cover.test_cover_0") + state = hass.states.get("cover.test_name_test_cover_0") assert state.attributes[ATTR_CURRENT_POSITION] == 50 mutate_rpc_device_status( @@ -109,11 +109,11 @@ async def test_rpc_device_services( await hass.services.async_call( COVER_DOMAIN, SERVICE_OPEN_COVER, - {ATTR_ENTITY_ID: "cover.test_cover_0"}, + {ATTR_ENTITY_ID: "cover.test_name_test_cover_0"}, blocking=True, ) mock_rpc_device.mock_update() - assert hass.states.get("cover.test_cover_0").state == STATE_OPENING + assert hass.states.get("cover.test_name_test_cover_0").state == STATE_OPENING mutate_rpc_device_status( monkeypatch, mock_rpc_device, "cover:0", "state", "closing" @@ -121,21 +121,21 @@ async def test_rpc_device_services( await hass.services.async_call( COVER_DOMAIN, SERVICE_CLOSE_COVER, - {ATTR_ENTITY_ID: "cover.test_cover_0"}, + {ATTR_ENTITY_ID: "cover.test_name_test_cover_0"}, blocking=True, ) mock_rpc_device.mock_update() - assert hass.states.get("cover.test_cover_0").state == STATE_CLOSING + assert hass.states.get("cover.test_name_test_cover_0").state == STATE_CLOSING mutate_rpc_device_status(monkeypatch, mock_rpc_device, "cover:0", "state", "closed") await hass.services.async_call( COVER_DOMAIN, SERVICE_STOP_COVER, - {ATTR_ENTITY_ID: "cover.test_cover_0"}, + {ATTR_ENTITY_ID: "cover.test_name_test_cover_0"}, blocking=True, ) mock_rpc_device.mock_update() - assert hass.states.get("cover.test_cover_0").state == STATE_CLOSED + assert hass.states.get("cover.test_name_test_cover_0").state == STATE_CLOSED async def test_rpc_device_no_cover_keys( @@ -144,7 +144,7 @@ async def test_rpc_device_no_cover_keys( """Test RPC device without cover keys.""" monkeypatch.delitem(mock_rpc_device.status, "cover:0") await init_integration(hass, 2) - assert hass.states.get("cover.test_cover_0") is None + assert hass.states.get("cover.test_name_test_cover_0") is None async def test_rpc_device_update( @@ -153,11 +153,11 @@ async def test_rpc_device_update( """Test RPC device update.""" mutate_rpc_device_status(monkeypatch, mock_rpc_device, "cover:0", "state", "closed") await init_integration(hass, 2) - assert hass.states.get("cover.test_cover_0").state == STATE_CLOSED + assert hass.states.get("cover.test_name_test_cover_0").state == STATE_CLOSED mutate_rpc_device_status(monkeypatch, mock_rpc_device, "cover:0", "state", "open") mock_rpc_device.mock_update() - assert hass.states.get("cover.test_cover_0").state == STATE_OPEN + assert hass.states.get("cover.test_name_test_cover_0").state == STATE_OPEN async def test_rpc_device_no_position_control( @@ -168,4 +168,4 @@ async def test_rpc_device_no_position_control( monkeypatch, mock_rpc_device, "cover:0", "pos_control", False ) await init_integration(hass, 2) - assert hass.states.get("cover.test_cover_0").state == STATE_OPEN + assert hass.states.get("cover.test_name_test_cover_0").state == STATE_OPEN diff --git a/tests/components/shelly/test_init.py b/tests/components/shelly/test_init.py index be6e319c8ac..a62dfda82f9 100644 --- a/tests/components/shelly/test_init.py +++ b/tests/components/shelly/test_init.py @@ -175,7 +175,7 @@ async def test_sleeping_rpc_device_online_new_firmware( ("gen", "entity_id"), [ (1, "switch.test_name_channel_1"), - (2, "switch.test_switch_0"), + (2, "switch.test_name_test_switch_0"), ], ) async def test_entry_unload( @@ -198,7 +198,7 @@ async def test_entry_unload( ("gen", "entity_id"), [ (1, "switch.test_name_channel_1"), - (2, "switch.test_switch_0"), + (2, "switch.test_name_test_switch_0"), ], ) async def test_entry_unload_device_not_ready( @@ -226,7 +226,7 @@ async def test_entry_unload_not_connected( entry = await init_integration( hass, 2, options={CONF_BLE_SCANNER_MODE: BLEScannerMode.ACTIVE} ) - entity_id = "switch.test_switch_0" + entity_id = "switch.test_name_test_switch_0" assert entry.state is ConfigEntryState.LOADED assert hass.states.get(entity_id).state is STATE_ON @@ -252,7 +252,7 @@ async def test_entry_unload_not_connected_but_we_think_we_are( entry = await init_integration( hass, 2, options={CONF_BLE_SCANNER_MODE: BLEScannerMode.ACTIVE} ) - entity_id = "switch.test_switch_0" + entity_id = "switch.test_name_test_switch_0" assert entry.state is ConfigEntryState.LOADED assert hass.states.get(entity_id).state is STATE_ON diff --git a/tests/components/shelly/test_light.py b/tests/components/shelly/test_light.py index 69d0fccf421..ab631516ec2 100644 --- a/tests/components/shelly/test_light.py +++ b/tests/components/shelly/test_light.py @@ -385,25 +385,25 @@ async def test_rpc_device_switch_type_lights_mode( await hass.services.async_call( LIGHT_DOMAIN, SERVICE_TURN_ON, - {ATTR_ENTITY_ID: "light.test_switch_0"}, + {ATTR_ENTITY_ID: "light.test_name_test_switch_0"}, blocking=True, ) - assert hass.states.get("light.test_switch_0").state == STATE_ON + assert hass.states.get("light.test_name_test_switch_0").state == STATE_ON mutate_rpc_device_status(monkeypatch, mock_rpc_device, "switch:0", "output", False) await hass.services.async_call( LIGHT_DOMAIN, SERVICE_TURN_OFF, - {ATTR_ENTITY_ID: "light.test_switch_0"}, + {ATTR_ENTITY_ID: "light.test_name_test_switch_0"}, blocking=True, ) mock_rpc_device.mock_update() - assert hass.states.get("light.test_switch_0").state == STATE_OFF + assert hass.states.get("light.test_name_test_switch_0").state == STATE_OFF async def test_rpc_light(hass: HomeAssistant, mock_rpc_device, monkeypatch) -> None: """Test RPC light.""" - entity_id = f"{LIGHT_DOMAIN}.test_light_0" + entity_id = f"{LIGHT_DOMAIN}.test_name_test_light_0" monkeypatch.delitem(mock_rpc_device.status, "switch:0") await init_integration(hass, 2) diff --git a/tests/components/shelly/test_sensor.py b/tests/components/shelly/test_sensor.py index d87460fb17d..fe79b1d010a 100644 --- a/tests/components/shelly/test_sensor.py +++ b/tests/components/shelly/test_sensor.py @@ -262,7 +262,7 @@ async def test_block_sensor_unknown_value( async def test_rpc_sensor(hass: HomeAssistant, mock_rpc_device, monkeypatch) -> None: """Test RPC sensor.""" - entity_id = f"{SENSOR_DOMAIN}.test_cover_0_power" + entity_id = f"{SENSOR_DOMAIN}.test_name_test_cover_0_power" await init_integration(hass, 2) assert hass.states.get(entity_id).state == "85.3" diff --git a/tests/components/shelly/test_switch.py b/tests/components/shelly/test_switch.py index 7a709e0cc2e..a93d752f9e2 100644 --- a/tests/components/shelly/test_switch.py +++ b/tests/components/shelly/test_switch.py @@ -149,20 +149,20 @@ async def test_rpc_device_services( await hass.services.async_call( SWITCH_DOMAIN, SERVICE_TURN_ON, - {ATTR_ENTITY_ID: "switch.test_switch_0"}, + {ATTR_ENTITY_ID: "switch.test_name_test_switch_0"}, blocking=True, ) - assert hass.states.get("switch.test_switch_0").state == STATE_ON + assert hass.states.get("switch.test_name_test_switch_0").state == STATE_ON monkeypatch.setitem(mock_rpc_device.status["switch:0"], "output", False) await hass.services.async_call( SWITCH_DOMAIN, SERVICE_TURN_OFF, - {ATTR_ENTITY_ID: "switch.test_switch_0"}, + {ATTR_ENTITY_ID: "switch.test_name_test_switch_0"}, blocking=True, ) mock_rpc_device.mock_update() - assert hass.states.get("switch.test_switch_0").state == STATE_OFF + assert hass.states.get("switch.test_name_test_switch_0").state == STATE_OFF async def test_rpc_device_switch_type_lights_mode( @@ -173,7 +173,7 @@ async def test_rpc_device_switch_type_lights_mode( mock_rpc_device.config["sys"]["ui_data"], "consumption_types", ["lights"] ) await init_integration(hass, 2) - assert hass.states.get("switch.test_switch_0") is None + assert hass.states.get("switch.test_name_test_switch_0") is None @pytest.mark.parametrize("exc", [DeviceConnectionError, RpcCallError(-1, "error")]) @@ -188,7 +188,7 @@ async def test_rpc_set_state_errors( await hass.services.async_call( SWITCH_DOMAIN, SERVICE_TURN_OFF, - {ATTR_ENTITY_ID: "switch.test_switch_0"}, + {ATTR_ENTITY_ID: "switch.test_name_test_switch_0"}, blocking=True, ) @@ -209,7 +209,7 @@ async def test_rpc_auth_error( await hass.services.async_call( SWITCH_DOMAIN, SERVICE_TURN_OFF, - {ATTR_ENTITY_ID: "switch.test_switch_0"}, + {ATTR_ENTITY_ID: "switch.test_name_test_switch_0"}, blocking=True, ) await hass.async_block_till_done() diff --git a/tests/components/shelly/test_utils.py b/tests/components/shelly/test_utils.py index 1bf660deb2a..53fc77ed6ef 100644 --- a/tests/components/shelly/test_utils.py +++ b/tests/components/shelly/test_utils.py @@ -58,7 +58,7 @@ async def test_block_get_block_channel_name(mock_block_device, monkeypatch) -> N mock_block_device, mock_block_device.blocks[DEVICE_BLOCK_ID], ) - == "Test name channel 1" + == "Channel 1" ) monkeypatch.setitem(mock_block_device.settings["device"], "type", "SHEM-3") @@ -68,7 +68,7 @@ async def test_block_get_block_channel_name(mock_block_device, monkeypatch) -> N mock_block_device, mock_block_device.blocks[DEVICE_BLOCK_ID], ) - == "Test name channel A" + == "Channel A" ) monkeypatch.setitem( @@ -207,7 +207,7 @@ async def test_get_block_input_triggers(mock_block_device, monkeypatch) -> None: async def test_get_rpc_channel_name(mock_rpc_device) -> None: """Test get RPC channel name.""" assert get_rpc_channel_name(mock_rpc_device, "input:0") == "test switch_0" - assert get_rpc_channel_name(mock_rpc_device, "input:3") == "Test name switch_3" + assert get_rpc_channel_name(mock_rpc_device, "input:3") == "Switch 3" async def test_get_rpc_input_triggers(mock_rpc_device, monkeypatch) -> None: