Home Assistant Unofficial Reference 2024.12.1
coordinator.py
Go to the documentation of this file.
1 """DataUpdateCoordinator for the Verisure integration."""
2 
3 from __future__ import annotations
4 
5 from datetime import timedelta
6 from time import sleep
7 
8 from verisure import (
9  Error as VerisureError,
10  LoginError as VerisureLoginError,
11  Session as Verisure,
12 )
13 
14 from homeassistant.config_entries import ConfigEntry
15 from homeassistant.const import CONF_EMAIL, CONF_PASSWORD
16 from homeassistant.core import HomeAssistant
17 from homeassistant.exceptions import ConfigEntryAuthFailed
18 from homeassistant.helpers.storage import STORAGE_DIR
19 from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
20 from homeassistant.util import Throttle
21 
22 from .const import CONF_GIID, DEFAULT_SCAN_INTERVAL, DOMAIN, LOGGER
23 
24 
26  """A Verisure Data Update Coordinator."""
27 
28  def __init__(self, hass: HomeAssistant, entry: ConfigEntry) -> None:
29  """Initialize the Verisure hub."""
30  self.imageseriesimageseries: list[dict[str, str]] = []
31  self.entryentry = entry
32  self._overview_overview: list[dict] = []
33 
34  self.verisureverisure = Verisure(
35  username=entry.data[CONF_EMAIL],
36  password=entry.data[CONF_PASSWORD],
37  cookie_file_name=hass.config.path(
38  STORAGE_DIR, f"verisure_{entry.data[CONF_EMAIL]}"
39  ),
40  )
41 
42  super().__init__(
43  hass, LOGGER, name=DOMAIN, update_interval=DEFAULT_SCAN_INTERVAL
44  )
45 
46  async def async_login(self) -> bool:
47  """Login to Verisure."""
48  try:
49  await self.hasshass.async_add_executor_job(self.verisureverisure.login_cookie)
50  except VerisureLoginError as ex:
51  LOGGER.error("Credentials expired for Verisure, %s", ex)
52  raise ConfigEntryAuthFailed("Credentials expired for Verisure") from ex
53  except VerisureError as ex:
54  LOGGER.error("Could not log in to verisure, %s", ex)
55  return False
56 
57  await self.hasshass.async_add_executor_job(
58  self.verisureverisure.set_giid, self.entryentry.data[CONF_GIID]
59  )
60 
61  return True
62 
63  async def _async_update_data(self) -> dict:
64  """Fetch data from Verisure."""
65  try:
66  await self.hasshass.async_add_executor_job(self.verisureverisure.update_cookie)
67  except VerisureLoginError:
68  LOGGER.debug("Cookie expired, acquiring new cookies")
69  try:
70  await self.hasshass.async_add_executor_job(self.verisureverisure.login_cookie)
71  except VerisureLoginError as ex:
72  LOGGER.error("Credentials expired for Verisure, %s", ex)
73  raise ConfigEntryAuthFailed("Credentials expired for Verisure") from ex
74  except VerisureError as ex:
75  LOGGER.error("Could not log in to verisure, %s", ex)
76  raise ConfigEntryAuthFailed("Could not log in to verisure") from ex
77  except VerisureError as ex:
78  raise UpdateFailed("Unable to update cookie") from ex
79  try:
80  overview = await self.hasshass.async_add_executor_job(
81  self.verisureverisure.request,
82  self.verisureverisure.arm_state(),
83  self.verisureverisure.broadband(),
84  self.verisureverisure.cameras(),
85  self.verisureverisure.climate(),
86  self.verisureverisure.door_window(),
87  self.verisureverisure.smart_lock(),
88  self.verisureverisure.smartplugs(),
89  )
90  except VerisureError as err:
91  LOGGER.error("Could not read overview, %s", err)
92  raise UpdateFailed("Could not read overview") from err
93 
94  def unpack(overview: list, value: str) -> dict | list:
95  unpacked: dict | list | None = next(
96  (
97  item["data"]["installation"][value]
98  for item in overview
99  if value in item.get("data", {}).get("installation", {})
100  ),
101  None,
102  )
103  return unpacked or []
104 
105  # Store data in a way Home Assistant can easily consume it
106  self._overview_overview = overview
107  return {
108  "alarm": unpack(overview, "armState"),
109  "broadband": unpack(overview, "broadband"),
110  "cameras": {
111  device["device"]["deviceLabel"]: device
112  for device in unpack(overview, "cameras")
113  },
114  "climate": {
115  device["device"]["deviceLabel"]: device
116  for device in unpack(overview, "climates")
117  },
118  "door_window": {
119  device["device"]["deviceLabel"]: device
120  for device in unpack(overview, "doorWindows")
121  },
122  "locks": {
123  device["device"]["deviceLabel"]: device
124  for device in unpack(overview, "smartLocks")
125  },
126  "smart_plugs": {
127  device["device"]["deviceLabel"]: device
128  for device in unpack(overview, "smartplugs")
129  },
130  }
131 
132  @Throttle(timedelta(seconds=60))
133  def update_smartcam_imageseries(self) -> None:
134  """Update the image series."""
135  image_data = self.verisureverisure.request(self.verisureverisure.cameras_image_series())
136  self.imageseriesimageseries = [
137  content
138  for series in (
139  image_data.get("data", {})
140  .get("ContentProviderMediaSearch", {})
141  .get("mediaSeriesList", [])
142  )
143  for content in series.get("deviceMediaList", [])
144  if content.get("contentType") == "IMAGE_JPEG"
145  ]
146 
147  @Throttle(timedelta(seconds=30))
148  def smartcam_capture(self, device_id: str) -> None:
149  """Capture a new image from a smartcam."""
150  capture_request = self.verisureverisure.request(
151  self.verisureverisure.camera_get_request_id(device_id)
152  )
153  request_id = (
154  capture_request.get("data", {})
155  .get("ContentProviderCaptureImageRequest", {})
156  .get("requestId")
157  )
158  capture_status = None
159  attempts = 0
160  while capture_status != "AVAILABLE":
161  if attempts == 30:
162  break
163  if attempts > 1:
164  sleep(0.5)
165  attempts += 1
166  capture_data = self.verisureverisure.request(
167  self.verisureverisure.camera_capture(device_id, request_id)
168  )
169  capture_status = (
170  capture_data.get("data", {})
171  .get("installation", {})
172  .get("cameraContentProvider", {})
173  .get("captureImageRequestStatus", {})
174  .get("mediaRequestStatus")
175  )
None __init__(self, HomeAssistant hass, ConfigEntry entry)
Definition: coordinator.py:28
web.Response get(self, web.Request request, str config_key)
Definition: view.py:88