Home Assistant Unofficial Reference 2024.12.1
controller.py
Go to the documentation of this file.
1 """Interface to the SmartTub API."""
2 
3 import asyncio
4 from datetime import timedelta
5 import logging
6 
7 from aiohttp import client_exceptions
8 from smarttub import APIError, LoginFailed, SmartTub
9 from smarttub.api import Account
10 
11 from homeassistant.const import CONF_EMAIL, CONF_PASSWORD
12 from homeassistant.core import callback
13 from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
14 from homeassistant.helpers import device_registry as dr
15 from homeassistant.helpers.aiohttp_client import async_get_clientsession
16 from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
17 
18 from .const import (
19  ATTR_ERRORS,
20  ATTR_LIGHTS,
21  ATTR_PUMPS,
22  ATTR_REMINDERS,
23  ATTR_STATUS,
24  DOMAIN,
25  POLLING_TIMEOUT,
26  SCAN_INTERVAL,
27 )
28 from .helpers import get_spa_name
29 
30 _LOGGER = logging.getLogger(__name__)
31 
32 
34  """Interface between Home Assistant and the SmartTub API."""
35 
36  def __init__(self, hass):
37  """Initialize an interface to SmartTub."""
38  self._hass_hass = hass
39  self._account_account = None
40  self.spasspas = set()
41 
42  self.coordinatorcoordinator = None
43 
44  async def async_setup_entry(self, entry):
45  """Perform initial setup.
46 
47  Authenticate, query static state, set up polling, and otherwise make
48  ready for normal operations .
49  """
50 
51  try:
52  self._account_account = await self.loginlogin(
53  entry.data[CONF_EMAIL], entry.data[CONF_PASSWORD]
54  )
55  except LoginFailed as ex:
56  # credentials were changed or invalidated, we need new ones
57  raise ConfigEntryAuthFailed from ex
58  except (
59  TimeoutError,
60  client_exceptions.ClientOSError,
61  client_exceptions.ServerDisconnectedError,
62  client_exceptions.ContentTypeError,
63  ) as err:
64  raise ConfigEntryNotReady from err
65 
66  self.spasspas = await self._account_account.get_spas()
67 
68  self.coordinatorcoordinator = DataUpdateCoordinator(
69  self._hass_hass,
70  _LOGGER,
71  name=DOMAIN,
72  update_method=self.async_update_dataasync_update_data,
73  update_interval=timedelta(seconds=SCAN_INTERVAL),
74  )
75 
76  await self.coordinatorcoordinator.async_refresh()
77 
78  self.async_register_devicesasync_register_devices(entry)
79 
80  return True
81 
82  async def async_update_data(self):
83  """Query the API and return the new state."""
84 
85  data = {}
86  try:
87  async with asyncio.timeout(POLLING_TIMEOUT):
88  for spa in self.spasspas:
89  data[spa.id] = await self._get_spa_data_get_spa_data(spa)
90  except APIError as err:
91  raise UpdateFailed(err) from err
92 
93  return data
94 
95  async def _get_spa_data(self, spa):
96  full_status, reminders, errors = await asyncio.gather(
97  spa.get_status_full(),
98  spa.get_reminders(),
99  spa.get_errors(),
100  )
101  return {
102  ATTR_STATUS: full_status,
103  ATTR_PUMPS: {pump.id: pump for pump in full_status.pumps},
104  ATTR_LIGHTS: {light.zone: light for light in full_status.lights},
105  ATTR_REMINDERS: {reminder.id: reminder for reminder in reminders},
106  ATTR_ERRORS: errors,
107  }
108 
109  @callback
110  def async_register_devices(self, entry):
111  """Register devices with the device registry for all spas."""
112  device_registry = dr.async_get(self._hass_hass)
113  for spa in self.spasspas:
114  device_registry.async_get_or_create(
115  config_entry_id=entry.entry_id,
116  identifiers={(DOMAIN, spa.id)},
117  manufacturer=spa.brand,
118  name=get_spa_name(spa),
119  model=spa.model,
120  )
121 
122  async def login(self, email, password) -> Account:
123  """Retrieve the account corresponding to the specified email and password.
124 
125  Returns None if the credentials are invalid.
126  """
127 
128  api = SmartTub(async_get_clientsession(self._hass_hass))
129 
130  await api.login(email, password)
131  return await api.get_account()
str get_spa_name(smarttub.Spa spa)
Definition: helpers.py:6
aiohttp.ClientSession async_get_clientsession(HomeAssistant hass, bool verify_ssl=True, socket.AddressFamily family=socket.AF_UNSPEC, ssl_util.SSLCipherList ssl_cipher=ssl_util.SSLCipherList.PYTHON_DEFAULT)