1 """Update platform for ESPHome."""
3 from __future__
import annotations
8 from aioesphomeapi
import (
9 DeviceInfo
as ESPHomeDeviceInfo,
30 from .coordinator
import ESPHomeDashboardCoordinator
31 from .dashboard
import async_get_dashboard
32 from .domain_data
import DomainData
35 convert_api_error_ha_error,
36 esphome_state_property,
37 platform_async_setup_entry,
39 from .entry_data
import RuntimeEntryData
41 KEY_UPDATE_LOCK =
"esphome_update_lock"
43 NO_FEATURES = UpdateEntityFeature(0)
49 async_add_entities: AddEntitiesCallback,
51 """Set up ESPHome update based on a config entry."""
57 entity_type=ESPHomeUpdateEntity,
58 state_type=UpdateState,
63 entry_data = DomainData.get(hass).get_entry_data(entry)
64 assert entry_data.device_info
is not None
65 device_name = entry_data.device_info.name
66 unsubs: list[CALLBACK_TYPE] = []
69 def _async_setup_update_entity() -> None:
70 """Set up the update entity."""
72 assert dashboard
is not None
74 if not entry_data.available
or not dashboard.last_update_success:
78 if dashboard.data
is None or dashboard.data.get(device_name)
is None:
89 and dashboard.last_update_success
90 and dashboard.data
is not None
91 and dashboard.data.get(device_name)
93 _async_setup_update_entity()
97 entry_data.async_subscribe_device_updated(_async_setup_update_entity),
98 dashboard.async_add_listener(_async_setup_update_entity),
103 CoordinatorEntity[ESPHomeDashboardCoordinator], UpdateEntity
105 """Defines an ESPHome update entity."""
107 _attr_has_entity_name =
True
108 _attr_device_class = UpdateDeviceClass.FIRMWARE
109 _attr_title =
"ESPHome"
110 _attr_name =
"Firmware"
111 _attr_release_url =
"https://esphome.io/changelog/"
112 _attr_entity_registry_enabled_default =
False
115 self, entry_data: RuntimeEntryData, coordinator: ESPHomeDashboardCoordinator
117 """Initialize the update entity."""
118 super().
__init__(coordinator=coordinator)
119 assert entry_data.device_info
is not None
124 (dr.CONNECTION_NETWORK_MAC, entry_data.device_info.mac_address)
131 """Update the supported features."""
134 coordinator = self.coordinator
138 coordinator.last_update_success
139 and coordinator.supports_update
140 and not device_info.has_deep_sleep
146 device = coordinator.data.get(device_info.name)
147 assert device
is not None
152 """Handle updated data from the coordinator."""
158 """Return the device info."""
159 assert self.
_entry_data_entry_data.device_info
is not None
164 """Return if update is available.
166 During deep sleep the ESP will not be connectable (by design)
167 and thus, even when unavailable, we'll show it as available.
169 return super().available
and (
177 self, static_info: list[EntityInfo] |
None =
None
179 """Handle updated data from the device."""
184 """Handle entity added to Home Assistant."""
195 self, version: str |
None, backup: bool, **kwargs: Any
197 """Install an update."""
198 async
with self.
hasshasshass.data.setdefault(KEY_UPDATE_LOCK, asyncio.Lock()):
199 coordinator = self.coordinator
200 api = coordinator.api
201 device = coordinator.data.get(self.
_device_info_device_info.name)
202 assert device
is not None
204 if not await api.compile(device[
"configuration"]):
206 f
"Error compiling {device['configuration']}; "
207 "Try again in ESPHome dashboard for more information."
209 if not await api.upload(device[
"configuration"],
"OTA"):
211 f
"Error updating {device['configuration']} via OTA; "
212 "Try again in ESPHome dashboard for more information."
219 """A update implementation for esphome."""
221 _attr_supported_features = (
222 UpdateEntityFeature.INSTALL | UpdateEntityFeature.PROGRESS
227 """Set attrs from static info."""
231 UpdateDeviceClass, static_info.device_class
235 @esphome_state_property
237 """Return the installed version."""
238 return self.
_state_state.current_version
241 @esphome_state_property
243 """Return if the update is in progress."""
244 return self.
_state_state.in_progress
247 @esphome_state_property
249 """Return the latest version."""
250 return self.
_state_state.latest_version
253 @esphome_state_property
255 """Return the release summary."""
256 return self.
_state_state.release_summary
259 @esphome_state_property
261 """Return the release URL."""
262 return self.
_state_state.release_url
265 @esphome_state_property
267 """Return the title of the update."""
268 return self.
_state_state.title
271 @esphome_state_property
273 """Return if the update is in progress."""
274 if self.
_state_state.has_progress:
278 @convert_api_error_ha_error
280 """Command device to check for update."""
282 self.
_client_client.update_command(key=self.
_key_key, command=UpdateCommand.CHECK)
284 @convert_api_error_ha_error
286 self, version: str |
None, backup: bool, **kwargs: Any
288 """Command device to install update."""
289 self.
_client_client.update_command(key=self.
_key_key, command=UpdateCommand.INSTALL)
None __init__(self, RuntimeEntryData entry_data, ESPHomeDashboardCoordinator coordinator)
None async_install(self, str|None version, bool backup, **Any kwargs)
None async_added_to_hass(self)
None _handle_device_update(self, list[EntityInfo]|None static_info=None)
ESPHomeDeviceInfo _device_info(self)
None _handle_coordinator_update(self)
int|None update_percentage(self)
None async_install(self, str|None version, bool backup, **Any kwargs)
None _on_static_info_update(self, EntityInfo static_info)
str|None latest_version(self)
str|None release_url(self)
str|None release_summary(self)
None async_write_ha_state(self)
None async_on_remove(self, CALLBACK_TYPE func)
None async_request_refresh(self)
ESPHomeDashboardCoordinator|None async_get_dashboard(HomeAssistant hass)
None platform_async_setup_entry(HomeAssistant hass, ESPHomeConfigEntry entry, AddEntitiesCallback async_add_entities, *type[_InfoT] info_type, type[_EntityT] entity_type, type[_StateT] state_type)
None async_setup_entry(HomeAssistant hass, ConfigEntry entry, AddEntitiesCallback async_add_entities)