From a1f5e4f37aaddd86438313ad805daf25aa9c67d5 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Sat, 16 Nov 2024 05:22:06 +1300 Subject: [PATCH] Do not create ESPHome Dashboard update entity if no configuration found (#129751) --- homeassistant/components/esphome/update.py | 19 ++++++--- tests/components/esphome/test_update.py | 47 +++++++++++++++++----- 2 files changed, 50 insertions(+), 16 deletions(-) diff --git a/homeassistant/components/esphome/update.py b/homeassistant/components/esphome/update.py index 5e571399ecb..2b593051742 100644 --- a/homeassistant/components/esphome/update.py +++ b/homeassistant/components/esphome/update.py @@ -61,6 +61,8 @@ async def async_setup_entry( if (dashboard := async_get_dashboard(hass)) is None: return entry_data = DomainData.get(hass).get_entry_data(entry) + assert entry_data.device_info is not None + device_name = entry_data.device_info.name unsubs: list[CALLBACK_TYPE] = [] @callback @@ -72,13 +74,22 @@ async def async_setup_entry( if not entry_data.available or not dashboard.last_update_success: return + # Do not add Dashboard Entity if this device is not known to the ESPHome dashboard. + if dashboard.data is None or dashboard.data.get(device_name) is None: + return + for unsub in unsubs: unsub() unsubs.clear() async_add_entities([ESPHomeDashboardUpdateEntity(entry_data, dashboard)]) - if entry_data.available and dashboard.last_update_success: + if ( + entry_data.available + and dashboard.last_update_success + and dashboard.data is not None + and dashboard.data.get(device_name) + ): _async_setup_update_entity() return @@ -133,10 +144,8 @@ class ESPHomeDashboardUpdateEntity( self._attr_supported_features = NO_FEATURES self._attr_installed_version = device_info.esphome_version device = coordinator.data.get(device_info.name) - if device is None: - self._attr_latest_version = None - else: - self._attr_latest_version = device["current_version"] + assert device is not None + self._attr_latest_version = device["current_version"] @callback def _handle_coordinator_update(self) -> None: diff --git a/tests/components/esphome/test_update.py b/tests/components/esphome/test_update.py index 7593ab21838..5060471f5d2 100644 --- a/tests/components/esphome/test_update.py +++ b/tests/components/esphome/test_update.py @@ -31,7 +31,6 @@ from homeassistant.const import ( STATE_OFF, STATE_ON, STATE_UNAVAILABLE, - STATE_UNKNOWN, ) from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError @@ -83,11 +82,6 @@ def stub_reconnect(): "supported_features": 0, }, ), - ( - [], - STATE_UNKNOWN, # dashboard is available but device is unknown - {"supported_features": 0}, - ), ], ) async def test_update_entity( @@ -408,11 +402,7 @@ async def test_update_becomes_available_at_runtime( ) await hass.async_block_till_done() state = hass.states.get("update.test_firmware") - assert state is not None - features = state.attributes[ATTR_SUPPORTED_FEATURES] - # There are no devices on the dashboard so no - # way to tell the version so install is disabled - assert features is UpdateEntityFeature(0) + assert state is None # A device gets added to the dashboard mock_dashboard["configured"] = [ @@ -433,6 +423,41 @@ async def test_update_becomes_available_at_runtime( assert features is UpdateEntityFeature.INSTALL +async def test_update_entity_not_present_with_dashboard_but_unknown_device( + hass: HomeAssistant, + mock_client: APIClient, + mock_esphome_device: Callable[ + [APIClient, list[EntityInfo], list[UserService], list[EntityState]], + Awaitable[MockESPHomeDevice], + ], + mock_dashboard: dict[str, Any], +) -> None: + """Test ESPHome update entity does not get created if the device is unknown to the dashboard.""" + await mock_esphome_device( + mock_client=mock_client, + entity_info=[], + user_service=[], + states=[], + ) + + mock_dashboard["configured"] = [ + { + "name": "other-test", + "current_version": "2023.2.0-dev", + "configuration": "other-test.yaml", + } + ] + + state = hass.states.get("update.test_firmware") + assert state is None + + await async_get_dashboard(hass).async_refresh() + await hass.async_block_till_done() + + state = hass.states.get("update.none_firmware") + assert state is None + + async def test_generic_device_update_entity( hass: HomeAssistant, mock_client: APIClient,