diff --git a/homeassistant/components/esphome/manifest.json b/homeassistant/components/esphome/manifest.json index 3e92f5a515c..b07e0f3a476 100644 --- a/homeassistant/components/esphome/manifest.json +++ b/homeassistant/components/esphome/manifest.json @@ -3,7 +3,7 @@ "name": "ESPHome", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/esphome", - "requirements": ["aioesphomeapi==13.1.0", "esphome-dashboard-api==1.1"], + "requirements": ["aioesphomeapi==13.1.0", "esphome-dashboard-api==1.2.1"], "zeroconf": ["_esphomelib._tcp.local."], "dhcp": [{ "registered_devices": true }], "codeowners": ["@OttoWinter", "@jesserockz"], diff --git a/homeassistant/components/esphome/update.py b/homeassistant/components/esphome/update.py index aae2ab46f04..0f4836d0c66 100644 --- a/homeassistant/components/esphome/update.py +++ b/homeassistant/components/esphome/update.py @@ -1,7 +1,9 @@ """Update platform for ESPHome.""" from __future__ import annotations -from typing import cast +import asyncio +import logging +from typing import Any, cast from aioesphomeapi import DeviceInfo as ESPHomeDeviceInfo @@ -22,6 +24,8 @@ from .dashboard import ESPHomeDashboard, async_get_dashboard from .domain_data import DomainData from .entry_data import RuntimeEntryData +KEY_UPDATE_LOCK = "esphome_update_lock" + async def async_setup_entry( hass: HomeAssistant, @@ -64,7 +68,7 @@ class ESPHomeUpdateEntity(CoordinatorEntity[ESPHomeDashboard], UpdateEntity): _attr_has_entity_name = True _attr_device_class = UpdateDeviceClass.FIRMWARE - _attr_supported_features = UpdateEntityFeature.SPECIFIC_VERSION + _attr_supported_features = UpdateEntityFeature.INSTALL _attr_title = "ESPHome" _attr_name = "Firmware" @@ -106,3 +110,22 @@ class ESPHomeUpdateEntity(CoordinatorEntity[ESPHomeDashboard], UpdateEntity): def release_url(self) -> str | None: """URL to the full release notes of the latest version available.""" return "https://esphome.io/changelog/" + + async def async_install( + self, version: str | None, backup: bool, **kwargs: Any + ) -> None: + """Install an update.""" + async with self.hass.data.setdefault(KEY_UPDATE_LOCK, asyncio.Lock()): + device = self.coordinator.data.get(self._device_info.name) + assert device is not None + if not await self.coordinator.api.compile(device["configuration"]): + logging.getLogger(__name__).error( + "Error compiling %s. Try again in ESPHome dashboard for error", + device["configuration"], + ) + if not await self.coordinator.api.upload(device["configuration"], "OTA"): + logging.getLogger(__name__).error( + "Error OTA updating %s. Try again in ESPHome dashboard for error", + device["configuration"], + ) + await self.coordinator.async_request_refresh() diff --git a/requirements_all.txt b/requirements_all.txt index a36231aa96e..3753f1fd1a2 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -676,7 +676,7 @@ epson-projector==0.5.0 epsonprinter==0.0.9 # homeassistant.components.esphome -esphome-dashboard-api==1.1 +esphome-dashboard-api==1.2.1 # homeassistant.components.netgear_lte eternalegypt==0.0.12 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index bf09058933e..9c66ac244a1 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -526,7 +526,7 @@ ephem==4.1.2 epson-projector==0.5.0 # homeassistant.components.esphome -esphome-dashboard-api==1.1 +esphome-dashboard-api==1.2.1 # homeassistant.components.eufylife_ble eufylife_ble_client==0.1.7 diff --git a/tests/components/esphome/test_update.py b/tests/components/esphome/test_update.py index 054ea92c9da..a263f4ab0cd 100644 --- a/tests/components/esphome/test_update.py +++ b/tests/components/esphome/test_update.py @@ -17,12 +17,23 @@ def stub_reconnect(): "devices_payload,expected_state,expected_attributes", [ ( - [{"name": "test", "current_version": "1.2.3"}], + [ + { + "name": "test", + "current_version": "1.2.3", + "configuration": "test.yaml", + } + ], "on", {"latest_version": "1.2.3", "installed_version": "1.0.0"}, ), ( - [{"name": "test", "current_version": "1.0.0"}], + [ + { + "name": "test", + "current_version": "1.0.0", + }, + ], "off", {"latest_version": "1.0.0", "installed_version": "1.0.0"}, ), @@ -61,3 +72,24 @@ async def test_update_entity( assert state.state == expected_state for key, expected_value in expected_attributes.items(): assert state.attributes.get(key) == expected_value + + if expected_state != "on": + return + + with patch( + "esphome_dashboard_api.ESPHomeDashboardAPI.compile", return_value=True + ) as mock_compile, patch( + "esphome_dashboard_api.ESPHomeDashboardAPI.upload", return_value=True + ) as mock_upload: + await hass.services.async_call( + "update", + "install", + {"entity_id": "update.none_firmware"}, + blocking=True, + ) + + assert len(mock_compile.mock_calls) == 1 + assert mock_compile.mock_calls[0][1][0] == "test.yaml" + + assert len(mock_upload.mock_calls) == 1 + assert mock_upload.mock_calls[0][1][0] == "test.yaml"