Home Assistant Unofficial Reference 2024.12.1
__init__.py
Go to the documentation of this file.
1 """The Husqvarna Automower integration."""
2 
3 import logging
4 
5 from aioautomower.session import AutomowerSession
6 from aiohttp import ClientResponseError
7 
8 from homeassistant.config_entries import ConfigEntry
9 from homeassistant.const import Platform
10 from homeassistant.core import HomeAssistant
11 from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
12 from homeassistant.helpers import (
13  aiohttp_client,
14  config_entry_oauth2_flow,
15  device_registry as dr,
16  entity_registry as er,
17 )
18 from homeassistant.util import dt as dt_util
19 
20 from . import api
21 from .const import DOMAIN
22 from .coordinator import AutomowerDataUpdateCoordinator
23 
24 _LOGGER = logging.getLogger(__name__)
25 
26 PLATFORMS: list[Platform] = [
27  Platform.BINARY_SENSOR,
28  Platform.BUTTON,
29  Platform.CALENDAR,
30  Platform.DEVICE_TRACKER,
31  Platform.LAWN_MOWER,
32  Platform.NUMBER,
33  Platform.SELECT,
34  Platform.SENSOR,
35  Platform.SWITCH,
36 ]
37 
38 type AutomowerConfigEntry = ConfigEntry[AutomowerDataUpdateCoordinator]
39 
40 
41 async def async_setup_entry(hass: HomeAssistant, entry: AutomowerConfigEntry) -> bool:
42  """Set up this integration using UI."""
43  implementation = (
44  await config_entry_oauth2_flow.async_get_config_entry_implementation(
45  hass, entry
46  )
47  )
48  session = config_entry_oauth2_flow.OAuth2Session(hass, entry, implementation)
49  api_api = api.AsyncConfigEntryAuth(
50  aiohttp_client.async_get_clientsession(hass),
51  session,
52  )
53  time_zone_str = str(dt_util.DEFAULT_TIME_ZONE)
54  automower_api = AutomowerSession(
55  api_api,
56  await dt_util.async_get_time_zone(time_zone_str),
57  )
58  try:
59  await api_api.async_get_access_token()
60  except ClientResponseError as err:
61  if 400 <= err.status < 500:
62  raise ConfigEntryAuthFailed from err
63  raise ConfigEntryNotReady from err
64 
65  coordinator = AutomowerDataUpdateCoordinator(hass, automower_api, entry)
66  await coordinator.async_config_entry_first_refresh()
67  available_devices = list(coordinator.data)
68  cleanup_removed_devices(hass, coordinator.config_entry, available_devices)
69  entry.runtime_data = coordinator
70 
71  entry.async_create_background_task(
72  hass,
73  coordinator.client_listen(hass, entry, automower_api),
74  "websocket_task",
75  )
76 
77  if "amc:api" not in entry.data["token"]["scope"]:
78  # We raise ConfigEntryAuthFailed here because the websocket can't be used
79  # without the scope. So only polling would be possible.
80  raise ConfigEntryAuthFailed
81 
82  await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
83  return True
84 
85 
86 async def async_unload_entry(hass: HomeAssistant, entry: AutomowerConfigEntry) -> bool:
87  """Handle unload of an entry."""
88  return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
89 
90 
92  hass: HomeAssistant, config_entry: ConfigEntry, available_devices: list[str]
93 ) -> None:
94  """Cleanup entity and device registry from removed devices."""
95  device_reg = dr.async_get(hass)
96  identifiers = {(DOMAIN, mower_id) for mower_id in available_devices}
97  for device in dr.async_entries_for_config_entry(device_reg, config_entry.entry_id):
98  if not set(device.identifiers) & identifiers:
99  _LOGGER.debug("Removing obsolete device entry %s", device.name)
100  device_reg.async_update_device(
101  device.id, remove_config_entry_id=config_entry.entry_id
102  )
103 
104 
106  hass: HomeAssistant,
107  config_entry: ConfigEntry,
108  removed_work_areas: set[int],
109  mower_id: str,
110 ) -> None:
111  """Remove all unused work area entities for the specified mower."""
112  entity_reg = er.async_get(hass)
113  for entity_entry in er.async_entries_for_config_entry(
114  entity_reg, config_entry.entry_id
115  ):
116  for work_area_id in removed_work_areas:
117  if entity_entry.unique_id.startswith(f"{mower_id}_{work_area_id}_"):
118  _LOGGER.info("Deleting: %s", entity_entry.entity_id)
119  entity_reg.async_remove(entity_entry.entity_id)
bool async_unload_entry(HomeAssistant hass, AutomowerConfigEntry entry)
Definition: __init__.py:86
None remove_work_area_entities(HomeAssistant hass, ConfigEntry config_entry, set[int] removed_work_areas, str mower_id)
Definition: __init__.py:110
bool async_setup_entry(HomeAssistant hass, AutomowerConfigEntry entry)
Definition: __init__.py:41
None cleanup_removed_devices(HomeAssistant hass, ConfigEntry config_entry, list[str] available_devices)
Definition: __init__.py:93