Home Assistant Unofficial Reference 2024.12.1
coordinator.py
Go to the documentation of this file.
1 """Provides the coordinator for a LOQED lock."""
2 
3 import asyncio
4 import logging
5 from typing import TypedDict
6 
7 from aiohttp.web import Request
8 from loqedAPI import loqed
9 
10 from homeassistant.components import cloud, webhook
11 from homeassistant.config_entries import ConfigEntry
12 from homeassistant.const import CONF_NAME, CONF_WEBHOOK_ID
13 from homeassistant.core import HomeAssistant
14 from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
15 
16 from .const import CONF_CLOUDHOOK_URL, DOMAIN
17 
18 _LOGGER = logging.getLogger(__name__)
19 
20 
21 class BatteryMessage(TypedDict):
22  """Properties in a battery update message."""
23 
24  mac_wifi: str
25  mac_ble: str
26  battery_type: str
27  battery_percentage: int
28 
29 
30 class StateReachedMessage(TypedDict):
31  """Properties in a battery update message."""
32 
33  requested_state: str
34  requested_state_numeric: int
35  event_type: str
36  key_local_id: int
37  mac_wifi: str
38  mac_ble: str
39 
40 
41 class TransitionMessage(TypedDict):
42  """Properties in a battery update message."""
43 
44  go_to_state: str
45  go_to_state_numeric: int
46  event_type: str
47  key_local_id: int
48  mac_wifi: str
49  mac_ble: str
50 
51 
52 class StatusMessage(TypedDict):
53  """Properties returned by the status endpoint of the bridhge."""
54 
55  battery_percentage: int
56  battery_type: str
57  battery_type_numeric: int
58  battery_voltage: float
59  bolt_state: str
60  bolt_state_numeric: int
61  bridge_mac_wifi: str
62  bridge_mac_ble: str
63  lock_online: int
64  webhooks_number: int
65  ip_address: str
66  up_timestamp: int
67  wifi_strength: int
68  ble_strength: int
69 
70 
72  """Data update coordinator for the loqed platform."""
73 
74  def __init__(
75  self,
76  hass: HomeAssistant,
77  api: loqed.LoqedAPI,
78  lock: loqed.Lock,
79  entry: ConfigEntry,
80  ) -> None:
81  """Initialize the Loqed Data Update coordinator."""
82  super().__init__(hass, _LOGGER, name="Loqed sensors")
83  self._api_api = api
84  self._entry_entry = entry
85  self.locklock = lock
86  self.device_namedevice_name = self._entry_entry.data[CONF_NAME]
87 
88  async def _async_update_data(self) -> StatusMessage:
89  """Fetch data from API endpoint."""
90  async with asyncio.timeout(10):
91  return await self._api_api.async_get_lock_details()
92 
93  async def _handle_webhook(
94  self, hass: HomeAssistant, webhook_id: str, request: Request
95  ) -> None:
96  """Handle incoming Loqed messages."""
97  _LOGGER.debug("Callback received: %s", request.headers)
98  received_ts = request.headers["TIMESTAMP"]
99  received_hash = request.headers["HASH"]
100  body = await request.text()
101 
102  _LOGGER.debug("Callback body: %s", body)
103 
104  event_data = await self.locklock.receiveWebhook(body, received_hash, received_ts)
105  if "error" in event_data:
106  _LOGGER.warning("Incorrect callback received:: %s", event_data)
107  return
108 
109  self.async_update_listenersasync_update_listeners()
110 
111  async def ensure_webhooks(self) -> None:
112  """Register webhook on LOQED bridge."""
113  webhook_id = self._entry_entry.data[CONF_WEBHOOK_ID]
114 
115  webhook.async_register(
116  self.hasshass, DOMAIN, "Loqed", webhook_id, self._handle_webhook_handle_webhook
117  )
118 
119  if cloud.async_active_subscription(self.hasshass):
120  webhook_url = await async_cloudhook_generate_url(self.hasshass, self._entry_entry)
121  else:
122  webhook_url = webhook.async_generate_url(
123  self.hasshass, self._entry_entry.data[CONF_WEBHOOK_ID]
124  )
125 
126  _LOGGER.debug("Webhook URL: %s", webhook_url)
127 
128  webhooks = await self.locklock.getWebhooks()
129 
130  webhook_index = next(
131  (x["id"] for x in webhooks if x["url"] == webhook_url), None
132  )
133 
134  if not webhook_index:
135  await self.locklock.registerWebhook(webhook_url)
136  webhooks = await self.locklock.getWebhooks()
137  webhook_index = next(x["id"] for x in webhooks if x["url"] == webhook_url)
138 
139  _LOGGER.debug("Webhook got index %s", webhook_index)
140 
141  async def remove_webhooks(self) -> None:
142  """Remove webhook from LOQED bridge."""
143  webhook_id = self._entry_entry.data[CONF_WEBHOOK_ID]
144 
145  if CONF_CLOUDHOOK_URL in self._entry_entry.data:
146  webhook_url = self._entry_entry.data[CONF_CLOUDHOOK_URL]
147  else:
148  webhook_url = webhook.async_generate_url(self.hasshass, webhook_id)
149 
150  webhook.async_unregister(
151  self.hasshass,
152  webhook_id,
153  )
154  _LOGGER.debug("Webhook URL: %s", webhook_url)
155 
156  webhooks = await self.locklock.getWebhooks()
157 
158  webhook_index = next(
159  (x["id"] for x in webhooks if x["url"] == webhook_url), None
160  )
161 
162  if webhook_index:
163  await self.locklock.deleteWebhook(webhook_index)
164 
165 
166 async def async_cloudhook_generate_url(hass: HomeAssistant, entry: ConfigEntry) -> str:
167  """Generate the full URL for a webhook_id."""
168  if CONF_CLOUDHOOK_URL not in entry.data:
169  webhook_url = await cloud.async_create_cloudhook(
170  hass, entry.data[CONF_WEBHOOK_ID]
171  )
172  data = {**entry.data, CONF_CLOUDHOOK_URL: webhook_url}
173  hass.config_entries.async_update_entry(entry, data=data)
174  return webhook_url
175  return str(entry.data[CONF_CLOUDHOOK_URL])
None _handle_webhook(self, HomeAssistant hass, str webhook_id, Request request)
Definition: coordinator.py:95
None __init__(self, HomeAssistant hass, loqed.LoqedAPI api, loqed.Lock lock, ConfigEntry entry)
Definition: coordinator.py:80
str async_cloudhook_generate_url(HomeAssistant hass, ConfigEntry entry)
Definition: coordinator.py:166