diff --git a/homeassistant/components/ps4/media_player.py b/homeassistant/components/ps4/media_player.py index cf5520f339db..744bcf02f943 100644 --- a/homeassistant/components/ps4/media_player.py +++ b/homeassistant/components/ps4/media_player.py @@ -282,7 +282,12 @@ class PS4Device(MediaPlayerEntity): self._attr_media_content_type = media_type await self.hass.async_add_executor_job(self.update_list) - self.async_write_ha_state() + + # Entities are added with update_before_add, so the poll that + # started this fetch can run before the entity has an entity_id. + # The platform writes the state it collected once it adds us. + if self.entity_id: + self.async_write_ha_state() def update_list(self) -> None: """Update Game List, Correct data if different.""" diff --git a/tests/components/ps4/test_media_player.py b/tests/components/ps4/test_media_player.py index 39c1ae57dc66..f2aa7b2513e7 100644 --- a/tests/components/ps4/test_media_player.py +++ b/tests/components/ps4/test_media_player.py @@ -1,7 +1,7 @@ """Tests for the PS4 media player platform.""" from typing import Any -from unittest.mock import MagicMock, patch +from unittest.mock import AsyncMock, MagicMock, patch from pyps4_2ndscreen.credential import get_ddp_message from pyps4_2ndscreen.ddp import DEFAULT_UDP_PORT @@ -25,6 +25,7 @@ from homeassistant.components.ps4.const import ( GAMES_FILE, PS4_DATA, ) +from homeassistant.components.ps4.media_player import PS4Device from homeassistant.const import ( ATTR_COMMAND, ATTR_ENTITY_ID, @@ -603,3 +604,40 @@ async def test_title_data_fetched_from_poll( assert mock_state.state == STATE_PLAYING assert mock_attrs.get(ATTR_MEDIA_TITLE) == MOCK_TITLE_NAME assert mock_attrs.get(ATTR_MEDIA_CONTENT_TYPE) == MOCK_TITLE_TYPE + + +async def test_title_data_fetched_before_entity_added( + hass: HomeAssistant, patch_get_status: MagicMock +) -> None: + """Test fetching title data before the entity is added does not raise. + + Entities are added with update_before_add, so the first poll runs while the + entity has no entity_id yet, and the fetch it starts can finish before the + entity is added. + """ + patch_get_status.return_value = MOCK_STATUS_PLAYING + + mock_result = MagicMock() + mock_result.name = MOCK_TITLE_NAME + mock_result.cover_art = MOCK_TITLE_ART_URL + mock_result.game_type = "not_an_app" + + mock_entry = MockConfigEntry( + domain=ps4.DOMAIN, data=MOCK_DATA, version=VERSION, entry_id=MOCK_ENTRY_ID + ) + mock_entity = PS4Device( + mock_entry, + MOCK_NAME, + MOCK_HOST, + MOCK_REGION, + MagicMock(async_get_ps_store_data=AsyncMock(return_value=mock_result)), + MOCK_CREDS, + ) + mock_entity.hass = hass + assert mock_entity.entity_id is None + + await mock_entity.async_get_title_data(MOCK_TITLE_ID, MOCK_TITLE_NAME) + + # The data is kept, the platform writes it out when the entity is added + assert mock_entity.media_title == MOCK_TITLE_NAME + assert mock_entity.media_content_type == MediaType.GAME