Skip to content

Commit 0428d4f

Browse files
authored
Ensure list_entities_services always fills missing object_ids (#1468)
1 parent 09e6b27 commit 0428d4f

File tree

2 files changed

+52
-5
lines changed

2 files changed

+52
-5
lines changed

aioesphomeapi/client.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,9 @@ async def device_info(self) -> DeviceInfo:
302302
async def list_entities_services(
303303
self,
304304
) -> tuple[list[EntityInfo], list[UserService]]:
305+
# Ensure we have device_info for computing missing object_ids
306+
if (device_info := self._cached_device_info) is None:
307+
device_info = await self.device_info()
305308
msgs = await self._get_connection().send_messages_await_response_complex(
306309
(ListEntitiesRequest(),),
307310
lambda msg: type(msg) is not ListEntitiesDoneResponse,
@@ -319,9 +322,8 @@ async def list_entities_services(
319322
continue
320323
if cls := response_types[msg_type]:
321324
entities.append(cls.from_pb(msg))
322-
# Fill in missing object_id values if we have cached device_info
323-
if self._cached_device_info is not None:
324-
entities = fill_missing_object_ids(entities, self._cached_device_info)
325+
# Fill in missing object_id values using cached device_info
326+
entities = fill_missing_object_ids(entities, device_info)
325327
return entities, services
326328

327329
async def device_info_and_list_entities(

tests/test_client.py

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,8 @@ async def test_connect_while_already_connected(auth_client: APIClient) -> None:
355355
[
356356
(
357357
[ListEntitiesBinarySensorResponse(), ListEntitiesDoneResponse()],
358-
([BinarySensorInfo()], []),
358+
# Empty-name entity gets object_id from device name
359+
([BinarySensorInfo(object_id="test-device")], []),
359360
),
360361
(
361362
[ListEntitiesServicesResponse(), ListEntitiesDoneResponse()],
@@ -366,13 +367,54 @@ async def test_connect_while_already_connected(auth_client: APIClient) -> None:
366367
async def test_list_entities(
367368
auth_client: APIClient, input: dict[str, Any], output: dict[str, Any]
368369
) -> None:
370+
# list_entities_services now automatically fetches device_info if not cached
371+
patch_response_simple(
372+
auth_client,
373+
DeviceInfoResponse(name="test-device", mac_address="AA:BB:CC:DD:EE:FF"),
374+
)
369375
patch_response_complex(auth_client, input)
370376
resp = await auth_client.list_entities_services()
371377
assert resp == output
372378

373379

380+
async def test_list_entities_auto_fetches_device_info(auth_client: APIClient) -> None:
381+
"""Test list_entities_services automatically fetches device_info when not cached."""
382+
# Verify _cached_device_info is initially None
383+
assert auth_client._cached_device_info is None
384+
385+
# Patch device_info response (will be called automatically)
386+
patch_response_simple(
387+
auth_client,
388+
DeviceInfoResponse(name="auto-device", mac_address="AA:BB:CC:DD:EE:FF"),
389+
)
390+
391+
# Patch list_entities response
392+
patch_response_complex(
393+
auth_client,
394+
[
395+
ListEntitiesBinarySensorResponse(name="My Sensor"),
396+
ListEntitiesSensorResponse(name=""), # Empty name, should use device name
397+
ListEntitiesDoneResponse(),
398+
],
399+
)
400+
401+
# Call list_entities_services WITHOUT calling device_info first
402+
entities, _services = await auth_client.list_entities_services()
403+
404+
# Verify device_info was fetched and cached
405+
assert auth_client._cached_device_info is not None
406+
assert auth_client._cached_device_info.name == "auto-device"
407+
408+
# Verify object_id was filled in correctly
409+
assert len(entities) == 2
410+
# Named entity gets object_id from its name
411+
assert entities[0].object_id == "my_sensor"
412+
# Empty-name entity gets object_id from device name
413+
assert entities[1].object_id == "auto-device"
414+
415+
374416
async def test_list_entities_with_cached_device_info(auth_client: APIClient) -> None:
375-
"""Test list_entities_services fills object_id when device_info was called first."""
417+
"""Test list_entities_services uses cached device_info without re-fetching."""
376418
# First call device_info() to populate the cache
377419
patch_response_simple(
378420
auth_client,
@@ -381,6 +423,9 @@ async def test_list_entities_with_cached_device_info(auth_client: APIClient) ->
381423
device_info = await auth_client.device_info()
382424
assert device_info.name == "my-device"
383425

426+
# Clear the simple response patch - list_entities_services should NOT call device_info again
427+
auth_client._connection.send_message_await_response = None
428+
384429
# Now call list_entities_services - it should use cached device_info
385430
# to fill in missing object_id values
386431
patch_response_complex(

0 commit comments

Comments
 (0)