From a653d08e91d1ef7d31ba06c3298fc75b6bdb2e54 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 23 Jun 2025 17:49:47 +0200 Subject: [PATCH] cover --- homeassistant/components/esphome/manager.py | 2 +- tests/components/esphome/test_entity.py | 104 ++++++++++++++++++++ tests/components/esphome/test_manager.py | 43 ++++++++ 3 files changed, 148 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/esphome/manager.py b/homeassistant/components/esphome/manager.py index 89e552bd596..0da13ab973d 100644 --- a/homeassistant/components/esphome/manager.py +++ b/homeassistant/components/esphome/manager.py @@ -836,7 +836,7 @@ def _async_setup_device_registry( sub_device_entry = device_registry.async_get_or_create( config_entry_id=entry.entry_id, identifiers={(DOMAIN, f"{device_info.mac_address}_{sub_device.device_id}")}, - name=sub_device.name, + name=sub_device.name or device_entry.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 4be798ae336..6f1d5b2743c 100644 --- a/tests/components/esphome/test_entity.py +++ b/tests/components/esphome/test_entity.py @@ -797,3 +797,107 @@ async def test_entity_assignment_to_sub_device( assert hass.states.get("binary_sensor.test_main_sensor").state == STATE_ON assert hass.states.get("binary_sensor.test_motion").state == STATE_OFF assert hass.states.get("binary_sensor.test_door").state == STATE_ON + + # Check entity friendly names + # Main device entity should have: "{device_name} {entity_name}" + main_sensor_state = hass.states.get("binary_sensor.test_main_sensor") + assert main_sensor_state.attributes[ATTR_FRIENDLY_NAME] == "Test Main Sensor" + + # Sub device 1 entity should have: "Motion Sensor Motion" + motion_sensor_state = hass.states.get("binary_sensor.test_motion") + assert motion_sensor_state.attributes[ATTR_FRIENDLY_NAME] == "Motion Sensor Motion" + + # Sub device 2 entity should have: "Door Sensor Door" + door_sensor_state = hass.states.get("binary_sensor.test_door") + assert door_sensor_state.attributes[ATTR_FRIENDLY_NAME] == "Door Sensor Door" + + +async def test_entity_friendly_names_with_empty_device_names( + hass: HomeAssistant, + entity_registry: er.EntityRegistry, + device_registry: dr.DeviceRegistry, + mock_client: APIClient, + mock_esphome_device: MockESPHomeDeviceType, +) -> None: + """Test entity friendly names when sub-devices have empty names.""" + # Define sub devices with different name scenarios + sub_devices = [ + SubDeviceInfo(device_id=11111111, name="", area_id=0), # Empty name + SubDeviceInfo( + device_id=22222222, name="Kitchen Light", area_id=0 + ), # Valid name + ] + + device_info = { + "devices": sub_devices, + "friendly_name": "Main Device", + } + + # Entity on sub-device with empty name + entity_info = [ + BinarySensorInfo( + object_id="motion", + key=1, + name="Motion Detected", + device_id=11111111, + ), + # Entity on sub-device with valid name + BinarySensorInfo( + object_id="status", + key=2, + name="Status", + device_id=22222222, + ), + # Entity with empty name on sub-device with valid name + BinarySensorInfo( + object_id="sensor", + key=3, + name="", # Empty entity name + device_id=22222222, + ), + # Entity on main device + BinarySensorInfo( + object_id="main_status", + key=4, + name="Main Status", + device_id=0, + ), + ] + + states = [ + BinarySensorState(key=1, state=True, missing_state=False), + BinarySensorState(key=2, state=False, missing_state=False), + BinarySensorState(key=3, state=True, missing_state=False), + BinarySensorState(key=4, state=True, missing_state=False), + ] + + await mock_esphome_device( + mock_client=mock_client, + device_info=device_info, + entity_info=entity_info, + states=states, + ) + + # Check entity friendly name on sub-device with empty name + state_1 = hass.states.get("binary_sensor.test_motion") + assert state_1 is not None + # With has_entity_name, friendly name is "{device_name} {entity_name}" + # Since sub-device falls back to main device name: "Main Device Motion Detected" + assert state_1.attributes[ATTR_FRIENDLY_NAME] == "Main Device Motion Detected" + + # Check entity friendly name on sub-device with valid name + state_2 = hass.states.get("binary_sensor.test_status") + assert state_2 is not None + # Device has name "Kitchen Light", entity has name "Status" + assert state_2.attributes[ATTR_FRIENDLY_NAME] == "Kitchen Light Status" + + # Test entity with empty name on sub-device + state_3 = hass.states.get("binary_sensor.test") + assert state_3 is not None + # Entity has empty name, so friendly name is just the device name + assert state_3.attributes[ATTR_FRIENDLY_NAME] == "Kitchen Light" + + # Test entity on main device + state_4 = hass.states.get("binary_sensor.test_main_status") + assert state_4 is not None + assert state_4.attributes[ATTR_FRIENDLY_NAME] == "Main Device Main Status" diff --git a/tests/components/esphome/test_manager.py b/tests/components/esphome/test_manager.py index a95af3f1046..318ccde221f 100644 --- a/tests/components/esphome/test_manager.py +++ b/tests/components/esphome/test_manager.py @@ -1680,6 +1680,49 @@ async def test_sub_device_cleanup( ) +async def test_sub_device_with_empty_name( + hass: HomeAssistant, + mock_client: APIClient, + mock_esphome_device: MockESPHomeDeviceType, +) -> None: + """Test sub devices with empty names are handled correctly.""" + device_registry = dr.async_get(hass) + + # Define sub devices with empty names + sub_devices = [ + SubDeviceInfo(device_id=11111111, name="", area_id=0), # Empty name + SubDeviceInfo(device_id=22222222, name="Valid Name", area_id=0), + ] + + device_info = { + "devices": sub_devices, + } + + device = await mock_esphome_device( + mock_client=mock_client, + device_info=device_info, + ) + await hass.async_block_till_done() + + # Check sub device with empty name + sub_device_1 = device_registry.async_get_device( + identifiers={(DOMAIN, f"{device.device_info.mac_address}_11111111")} + ) + assert sub_device_1 is not None + # Empty sub-device names should fall back to main device name + main_device = device_registry.async_get_device( + connections={(dr.CONNECTION_NETWORK_MAC, device.device_info.mac_address)} + ) + assert sub_device_1.name == main_device.name + + # Check sub device with valid name + sub_device_2 = device_registry.async_get_device( + identifiers={(DOMAIN, f"{device.device_info.mac_address}_22222222")} + ) + assert sub_device_2 is not None + assert sub_device_2.name == "Valid Name" + + async def test_sub_device_references_main_device_area( hass: HomeAssistant, mock_client: APIClient,