Home Assistant Unofficial Reference 2024.12.1
controller.py
Go to the documentation of this file.
1 """Controller module."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Mapping
6 from functools import partial
7 import logging
8 import ssl
9 from typing import Any
10 
11 from deebot_client.api_client import ApiClient
12 from deebot_client.authentication import Authenticator, create_rest_config
13 from deebot_client.const import UNDEFINED, UndefinedType
14 from deebot_client.device import Device
15 from deebot_client.exceptions import DeebotError, InvalidAuthenticationError
16 from deebot_client.mqtt_client import MqttClient, create_mqtt_config
17 from deebot_client.util import md5
18 from deebot_client.util.continents import get_continent
19 from sucks import EcoVacsAPI, VacBot
20 
21 from homeassistant.const import CONF_COUNTRY, CONF_PASSWORD, CONF_USERNAME
22 from homeassistant.core import HomeAssistant
23 from homeassistant.exceptions import ConfigEntryError, ConfigEntryNotReady
24 from homeassistant.helpers import aiohttp_client
25 from homeassistant.util.ssl import get_default_no_verify_context
26 
27 from .const import (
28  CONF_OVERRIDE_MQTT_URL,
29  CONF_OVERRIDE_REST_URL,
30  CONF_VERIFY_MQTT_CERTIFICATE,
31 )
32 from .util import get_client_device_id
33 
34 _LOGGER = logging.getLogger(__name__)
35 
36 
38  """Ecovacs controller."""
39 
40  def __init__(self, hass: HomeAssistant, config: Mapping[str, Any]) -> None:
41  """Initialize controller."""
42  self._hass_hass = hass
43  self._devices: list[Device] = []
44  self._legacy_devices: list[VacBot] = []
45  rest_url = config.get(CONF_OVERRIDE_REST_URL)
46  self._device_id_device_id = get_client_device_id(hass, rest_url is not None)
47  country = config[CONF_COUNTRY]
48  self._continent_continent = get_continent(country)
49 
50  self._authenticator_authenticator = Authenticator(
51  create_rest_config(
52  aiohttp_client.async_get_clientsession(self._hass_hass),
53  device_id=self._device_id_device_id,
54  alpha_2_country=country,
55  override_rest_url=rest_url,
56  ),
57  config[CONF_USERNAME],
58  md5(config[CONF_PASSWORD]),
59  )
60  self._api_client_api_client = ApiClient(self._authenticator_authenticator)
61 
62  mqtt_url = config.get(CONF_OVERRIDE_MQTT_URL)
63  ssl_context: UndefinedType | ssl.SSLContext = UNDEFINED
64  if not config.get(CONF_VERIFY_MQTT_CERTIFICATE, True) and mqtt_url:
65  ssl_context = get_default_no_verify_context()
66 
67  self._mqtt_config_fn_mqtt_config_fn = partial(
68  create_mqtt_config,
69  device_id=self._device_id_device_id,
70  country=country,
71  override_mqtt_url=mqtt_url,
72  ssl_context=ssl_context,
73  )
74  self._mqtt_client_mqtt_client: MqttClient | None = None
75 
76  self._added_legacy_entities: set[str] = set()
77 
78  async def initialize(self) -> None:
79  """Init controller."""
80  try:
81  devices = await self._api_client_api_client.get_devices()
82  credentials = await self._authenticator_authenticator.authenticate()
83  for device_info in devices.mqtt:
84  device = Device(device_info, self._authenticator_authenticator)
85  mqtt = await self._get_mqtt_client_get_mqtt_client()
86  await device.initialize(mqtt)
87  self._devices.append(device)
88  for device_config in devices.xmpp:
89  bot = VacBot(
90  credentials.user_id,
91  EcoVacsAPI.REALM,
92  self._device_id_device_id[0:8],
93  credentials.token,
94  device_config,
95  self._continent_continent,
96  monitor=True,
97  )
98  self._legacy_devices.append(bot)
99  for device_config in devices.not_supported:
100  _LOGGER.warning(
101  (
102  'Device "%s" not supported. More information at '
103  "https://github.com/DeebotUniverse/client.py/issues/612: %s"
104  ),
105  device_config["deviceName"],
106  device_config,
107  )
108 
109  except InvalidAuthenticationError as ex:
110  raise ConfigEntryError("Invalid credentials") from ex
111  except DeebotError as ex:
112  raise ConfigEntryNotReady("Error during setup") from ex
113 
114  _LOGGER.debug("Controller initialize complete")
115 
116  async def teardown(self) -> None:
117  """Disconnect controller."""
118  for device in self._devices:
119  await device.teardown()
120  for legacy_device in self._legacy_devices:
121  await self._hass_hass.async_add_executor_job(legacy_device.disconnect)
122  if self._mqtt_client_mqtt_client is not None:
123  await self._mqtt_client_mqtt_client.disconnect()
124  await self._authenticator_authenticator.teardown()
125 
126  def add_legacy_entity(self, device: VacBot, component: str) -> None:
127  """Add legacy entity."""
128  self._added_legacy_entities.add(f"{device.vacuum['did']}_{component}")
129 
130  def legacy_entity_is_added(self, device: VacBot, component: str) -> bool:
131  """Check if legacy entity is added."""
132  return f"{device.vacuum['did']}_{component}" in self._added_legacy_entities
133 
134  async def _get_mqtt_client(self) -> MqttClient:
135  """Return validated MQTT client."""
136  if self._mqtt_client_mqtt_client is None:
137  config = await self._hass_hass.async_add_executor_job(self._mqtt_config_fn_mqtt_config_fn)
138  mqtt = MqttClient(config, self._authenticator_authenticator)
139  await mqtt.verify_config()
140  self._mqtt_client_mqtt_client = mqtt
141 
142  return self._mqtt_client_mqtt_client
143 
144  @property
145  def devices(self) -> list[Device]:
146  """Return devices."""
147  return self._devices
148 
149  @property
150  def legacy_devices(self) -> list[VacBot]:
151  """Return legacy devices."""
152  return self._legacy_devices
None add_legacy_entity(self, VacBot device, str component)
Definition: controller.py:126
None __init__(self, HomeAssistant hass, Mapping[str, Any] config)
Definition: controller.py:40
bool legacy_entity_is_added(self, VacBot device, str component)
Definition: controller.py:130
bool add(self, _T matcher)
Definition: match.py:185
str get_client_device_id(HomeAssistant hass, bool self_hosted)
Definition: util.py:23
def authenticate(HomeAssistant hass, host, port, servers)
Definition: config_flow.py:104
ssl.SSLContext get_default_no_verify_context()
Definition: ssl.py:123