diff --git a/homeassistant/components/esphome/entity.py b/homeassistant/components/esphome/entity.py index b28decc7c70..313785fd2df 100644 --- a/homeassistant/components/esphome/entity.py +++ b/homeassistant/components/esphome/entity.py @@ -198,6 +198,7 @@ class EsphomeEntity(Entity, Generic[_InfoT, _StateT]): """Define a base esphome entity.""" _attr_should_poll = False + _attr_has_entity_name = True _static_info: _InfoT _state: _StateT _has_state: bool @@ -223,24 +224,6 @@ class EsphomeEntity(Entity, Generic[_InfoT, _StateT]): self._attr_device_info = DeviceInfo( connections={(dr.CONNECTION_NETWORK_MAC, device_info.mac_address)} ) - # - # If `friendly_name` is set, we use the Friendly naming rules, if - # `friendly_name` is not set we make an exception to the naming rules for - # backwards compatibility and use the Legacy naming rules. - # - # Friendly naming - # - Friendly name is prepended to entity names - # - Device Name is prepended to entity ids - # - Entity id is constructed from device name and object id - # - # Legacy naming - # - Device name is not prepended to entity names - # - Device name is not prepended to entity ids - # - Entity id is constructed from entity name - # - if not device_info.friendly_name: - return - self._attr_has_entity_name = True self.entity_id = f"{domain}.{device_info.name}_{entity_info.object_id}" async def async_added_to_hass(self) -> None: diff --git a/homeassistant/components/esphome/manager.py b/homeassistant/components/esphome/manager.py index 62963178a8e..c173a3ada63 100644 --- a/homeassistant/components/esphome/manager.py +++ b/homeassistant/components/esphome/manager.py @@ -520,6 +520,15 @@ class ESPHomeManager: if device_info.name: reconnect_logic.name = device_info.name + if not device_info.friendly_name: + _LOGGER.info( + "No `friendly_name` set in the `esphome:` section of the " + "YAML config for device '%s' (MAC: %s); It's recommended " + "to add one for easier identification and better alignment " + "with Home Assistant naming conventions", + device_info.name, + device_mac, + ) self.device_id = _async_setup_device_registry(hass, entry, entry_data) entry_data.async_update_device_state() @@ -756,7 +765,7 @@ def _async_setup_device_registry( config_entry_id=entry.entry_id, configuration_url=configuration_url, connections={(dr.CONNECTION_NETWORK_MAC, device_info.mac_address)}, - name=entry_data.friendly_name, + name=entry_data.friendly_name or entry_data.name, manufacturer=manufacturer, model=model, sw_version=sw_version, diff --git a/tests/components/esphome/test_entity.py b/tests/components/esphome/test_entity.py index 977ec50ab30..5c82337e71b 100644 --- a/tests/components/esphome/test_entity.py +++ b/tests/components/esphome/test_entity.py @@ -500,6 +500,6 @@ async def test_esphome_device_without_friendly_name( states=states, device_info={"friendly_name": None}, ) - state = hass.states.get("binary_sensor.my_binary_sensor") + state = hass.states.get("binary_sensor.test_mybinary_sensor") assert state is not None assert state.state == STATE_ON diff --git a/tests/components/esphome/test_manager.py b/tests/components/esphome/test_manager.py index c897377f719..a4cef909fcc 100644 --- a/tests/components/esphome/test_manager.py +++ b/tests/components/esphome/test_manager.py @@ -1577,3 +1577,51 @@ async def test_entry_missing_bluetooth_mac_address( ) await hass.async_block_till_done() assert entry.data[CONF_BLUETOOTH_MAC_ADDRESS] == "AA:BB:CC:DD:EE:FC" + + +async def test_device_adds_friendly_name( + hass: HomeAssistant, + mock_client: APIClient, + mock_esphome_device: Callable[ + [APIClient, list[EntityInfo], list[UserService], list[EntityState]], + Awaitable[MockESPHomeDevice], + ], + caplog: pytest.LogCaptureFixture, +) -> None: + """Test a device with user services that change arguments.""" + entity_info = [] + states = [] + device: MockESPHomeDevice = await mock_esphome_device( + mock_client=mock_client, + entity_info=entity_info, + user_service=[], + device_info={"name": "nofriendlyname", "friendly_name": ""}, + states=states, + ) + await hass.async_block_till_done() + dev_reg = dr.async_get(hass) + dev = dev_reg.async_get_device( + connections={(dr.CONNECTION_NETWORK_MAC, device.entry.unique_id)} + ) + assert dev.name == "Nofriendlyname" + assert ( + "No `friendly_name` set in the `esphome:` section of " + "the YAML config for device 'nofriendlyname'" + ) in caplog.text + caplog.clear() + + await device.mock_disconnect(True) + await hass.async_block_till_done() + device.device_info = DeviceInfo( + **{**device.device_info.to_dict(), "friendly_name": "I have a friendly name"} + ) + mock_client.device_info = AsyncMock(return_value=device.device_info) + await device.mock_connect() + await hass.async_block_till_done() + dev = dev_reg.async_get_device( + connections={(dr.CONNECTION_NETWORK_MAC, device.entry.unique_id)} + ) + assert dev.name == "I have a friendly name" + assert ( + "No `friendly_name` set in the `esphome:` section of the YAML config for device" + ) not in caplog.text