Home Assistant Unofficial Reference 2024.12.1
__init__.py
Go to the documentation of this file.
1 """The elmax-cloud integration."""
2 
3 from __future__ import annotations
4 
5 from datetime import timedelta
6 import logging
7 
8 from elmax_api.exceptions import ElmaxBadLoginError
9 from elmax_api.http import Elmax, ElmaxLocal, GenericElmax
10 from elmax_api.model.panel import PanelEntry
11 
12 from homeassistant.config_entries import ConfigEntry
13 from homeassistant.const import EVENT_HOMEASSISTANT_STOP
14 from homeassistant.core import Event, HomeAssistant
15 from homeassistant.exceptions import ConfigEntryAuthFailed
16 
17 from .common import DirectPanel, build_direct_ssl_context, get_direct_api_url
18 from .const import (
19  CONF_ELMAX_MODE,
20  CONF_ELMAX_MODE_CLOUD,
21  CONF_ELMAX_MODE_DIRECT,
22  CONF_ELMAX_MODE_DIRECT_HOST,
23  CONF_ELMAX_MODE_DIRECT_PORT,
24  CONF_ELMAX_MODE_DIRECT_SSL,
25  CONF_ELMAX_MODE_DIRECT_SSL_CERT,
26  CONF_ELMAX_PANEL_ID,
27  CONF_ELMAX_PANEL_PIN,
28  CONF_ELMAX_PASSWORD,
29  CONF_ELMAX_USERNAME,
30  DOMAIN,
31  ELMAX_PLATFORMS,
32  POLLING_SECONDS,
33 )
34 from .coordinator import ElmaxCoordinator
35 
36 _LOGGER = logging.getLogger(__name__)
37 
38 
40  entry: ConfigEntry,
41 ) -> tuple[GenericElmax, PanelEntry]:
42  # Connection mode was not present in initial version, default to cloud if not set
43  mode = entry.data.get(CONF_ELMAX_MODE, CONF_ELMAX_MODE_CLOUD)
44  if mode == CONF_ELMAX_MODE_DIRECT:
45  client_api_url = get_direct_api_url(
46  host=entry.data[CONF_ELMAX_MODE_DIRECT_HOST],
47  port=entry.data[CONF_ELMAX_MODE_DIRECT_PORT],
48  use_ssl=entry.data[CONF_ELMAX_MODE_DIRECT_SSL],
49  )
50  custom_ssl_context = None
51  custom_ssl_cert = entry.data.get(CONF_ELMAX_MODE_DIRECT_SSL_CERT)
52  if custom_ssl_cert:
53  custom_ssl_context = build_direct_ssl_context(cadata=custom_ssl_cert)
54 
55  client = ElmaxLocal(
56  panel_api_url=client_api_url,
57  panel_code=entry.data[CONF_ELMAX_PANEL_PIN],
58  ssl_context=custom_ssl_context,
59  )
60  panel = DirectPanel(panel_uri=client_api_url)
61  else:
62  client = Elmax(
63  username=entry.data[CONF_ELMAX_USERNAME],
64  password=entry.data[CONF_ELMAX_PASSWORD],
65  )
66  client.set_current_panel(
67  entry.data[CONF_ELMAX_PANEL_ID], entry.data[CONF_ELMAX_PANEL_PIN]
68  )
69  # Make sure the panel is online and assigned to the current user
70  panel = await _check_cloud_panel_status(client, entry.data[CONF_ELMAX_PANEL_ID])
71 
72  return client, panel
73 
74 
75 async def _check_cloud_panel_status(client: Elmax, panel_id: str) -> PanelEntry:
76  """Perform integrity checks against the cloud for panel-user association."""
77  # Retrieve the panel online status first
78  panels = await client.list_control_panels()
79  panel = next((panel for panel in panels if panel.hash == panel_id), None)
80 
81  # If the panel is no longer available within the ones associated to that client, raise
82  # a config error as the user must reconfigure it in order to make it work again
83  if not panel:
85  f"Panel ID {panel_id} is no longer linked to this user account"
86  )
87  return panel
88 
89 
90 async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
91  """Set up elmax-cloud from a config entry."""
92  try:
93  client, panel = await _load_elmax_panel_client(entry)
94  except ElmaxBadLoginError as err:
95  raise ConfigEntryAuthFailed from err
96 
97  # Create the API client object and attempt a login, so that we immediately know
98  # if there is something wrong with user credentials
99  coordinator = ElmaxCoordinator(
100  hass=hass,
101  logger=_LOGGER,
102  elmax_api_client=client,
103  panel=panel,
104  name=f"Elmax Cloud {entry.entry_id}",
105  update_interval=timedelta(seconds=POLLING_SECONDS),
106  )
107 
108  async def _async_on_hass_stop(_: Event) -> None:
109  """Close connection when hass stops."""
110  await coordinator.async_shutdown()
111 
112  entry.async_on_unload(
113  hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, _async_on_hass_stop)
114  )
115 
116  # Issue a first refresh, so that we trigger a re-auth flow if necessary
117  await coordinator.async_config_entry_first_refresh()
118 
119  # Store a global reference to the coordinator for later use
120  hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator
121 
122  entry.async_on_unload(entry.add_update_listener(async_reload_entry))
123 
124  # Perform platform initialization.
125  await hass.config_entries.async_forward_entry_setups(entry, ELMAX_PLATFORMS)
126  return True
127 
128 
129 async def async_reload_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
130  """Handle an options update."""
131  await hass.config_entries.async_reload(entry.entry_id)
132 
133 
134 async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
135  """Unload a config entry."""
136  unload_ok = await hass.config_entries.async_unload_platforms(entry, ELMAX_PLATFORMS)
137  if unload_ok:
138  hass.data[DOMAIN].pop(entry.entry_id)
139 
140  return unload_ok
ssl.SSLContext build_direct_ssl_context(str cadata)
Definition: common.py:19
str get_direct_api_url(str host, int port, bool use_ssl)
Definition: common.py:13
None async_reload_entry(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:129
tuple[GenericElmax, PanelEntry] _load_elmax_panel_client(ConfigEntry entry)
Definition: __init__.py:41
PanelEntry _check_cloud_panel_status(Elmax client, str panel_id)
Definition: __init__.py:75
bool async_unload_entry(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:134
bool async_setup_entry(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:90