Home Assistant Unofficial Reference 2024.12.1
config_flow.py
Go to the documentation of this file.
1 """Config flow for loqed integration."""
2 
3 from __future__ import annotations
4 
5 import logging
6 import re
7 from typing import Any
8 
9 import aiohttp
10 from loqedAPI import cloud_loqed, loqed
11 import voluptuous as vol
12 
13 from homeassistant.components import webhook
14 from homeassistant.components.zeroconf import ZeroconfServiceInfo
15 from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
16 from homeassistant.const import CONF_API_TOKEN, CONF_NAME, CONF_WEBHOOK_ID
17 from homeassistant.core import HomeAssistant
18 from homeassistant.exceptions import HomeAssistantError
19 from homeassistant.helpers.aiohttp_client import async_get_clientsession
20 
21 from .const import DOMAIN
22 
23 _LOGGER = logging.getLogger(__name__)
24 
25 
26 class LoqedConfigFlow(ConfigFlow, domain=DOMAIN):
27  """Handle a config flow for Loqed."""
28 
29  VERSION = 1
30  DOMAIN = DOMAIN
31  _host: str | None = None
32 
33  async def validate_input(
34  self, hass: HomeAssistant, data: dict[str, Any]
35  ) -> dict[str, Any]:
36  """Validate the user input allows us to connect."""
37 
38  # 1. Checking loqed-connection
39  try:
40  session = async_get_clientsession(hass)
41  cloud_api_client = cloud_loqed.CloudAPIClient(
42  session,
43  data[CONF_API_TOKEN],
44  )
45  cloud_client = cloud_loqed.LoqedCloudAPI(cloud_api_client)
46  lock_data = await cloud_client.async_get_locks()
47  except aiohttp.ClientError as err:
48  _LOGGER.error("HTTP Connection error to loqed API")
49  raise CannotConnect from err
50 
51  try:
52  selected_lock = next(
53  lock
54  for lock in lock_data["data"]
55  if lock["bridge_ip"] == self._host_host or lock["name"] == data.get("name")
56  )
57 
58  apiclient = loqed.APIClient(session, f"http://{selected_lock['bridge_ip']}")
59  api = loqed.LoqedAPI(apiclient)
60  lock = await api.async_get_lock(
61  selected_lock["backend_key"],
62  selected_lock["bridge_key"],
63  selected_lock["local_id"],
64  selected_lock["bridge_ip"],
65  )
66 
67  # checking getWebooks to check the bridgeKey
68  await lock.getWebhooks()
69  return {
70  "lock_key_key": selected_lock["key_secret"],
71  "bridge_key": selected_lock["bridge_key"],
72  "lock_key_local_id": selected_lock["local_id"],
73  "bridge_mdns_hostname": selected_lock["bridge_hostname"],
74  "bridge_ip": selected_lock["bridge_ip"],
75  "name": selected_lock["name"],
76  "id": selected_lock["id"],
77  }
78  except StopIteration:
79  raise InvalidAuth from StopIteration
80  except aiohttp.ClientError:
81  _LOGGER.error("HTTP Connection error to loqed lock")
82  raise CannotConnect from aiohttp.ClientError
83 
84  async def async_step_zeroconf(
85  self, discovery_info: ZeroconfServiceInfo
86  ) -> ConfigFlowResult:
87  """Handle zeroconf discovery."""
88  host = discovery_info.host
89  self._host_host = host
90 
91  session = async_get_clientsession(self.hass)
92  apiclient = loqed.APIClient(session, f"http://{host}")
93  api = loqed.LoqedAPI(apiclient)
94  lock_data = await api.async_get_lock_details()
95 
96  # Check if already exists
97  await self.async_set_unique_idasync_set_unique_id(lock_data["bridge_mac_wifi"])
98  self._abort_if_unique_id_configured_abort_if_unique_id_configured({"bridge_ip": host})
99 
100  return await self.async_step_userasync_step_user()
101 
102  async def async_step_user(
103  self, user_input: dict[str, Any] | None = None
104  ) -> ConfigFlowResult:
105  """Show userform to user."""
106  user_data_schema = (
107  vol.Schema(
108  {
109  vol.Required(CONF_API_TOKEN): str,
110  }
111  )
112  if self._host_host
113  else vol.Schema(
114  {
115  vol.Required(CONF_NAME): str,
116  vol.Required(CONF_API_TOKEN): str,
117  }
118  )
119  )
120 
121  if user_input is None:
122  return self.async_show_formasync_show_formasync_show_form(
123  step_id="user",
124  data_schema=user_data_schema,
125  description_placeholders={
126  "config_url": "https://integrations.loqed.com/personal-access-tokens",
127  },
128  )
129 
130  errors = {}
131 
132  try:
133  info = await self.validate_inputvalidate_input(self.hass, user_input)
134  except CannotConnect:
135  errors["base"] = "cannot_connect"
136  except InvalidAuth:
137  errors["base"] = "invalid_auth"
138  else:
139  await self.async_set_unique_idasync_set_unique_id(
140  re.sub(
141  r"LOQED-([a-f0-9]+)\.local", r"\1", info["bridge_mdns_hostname"]
142  ),
143  raise_on_progress=False,
144  )
145  self._abort_if_unique_id_configured_abort_if_unique_id_configured()
146 
147  return self.async_create_entryasync_create_entryasync_create_entry(
148  title="LOQED Touch Smart Lock",
149  data=(
150  user_input | {CONF_WEBHOOK_ID: webhook.async_generate_id()} | info
151  ),
152  )
153 
154  return self.async_show_formasync_show_formasync_show_form(
155  step_id="user",
156  data_schema=user_data_schema,
157  errors=errors,
158  description_placeholders={
159  "config_url": "https://integrations.loqed.com/personal-access-tokens",
160  },
161  )
162 
163 
165  """Error to indicate we cannot connect."""
166 
167 
168 class InvalidAuth(HomeAssistantError):
169  """Error to indicate there is invalid auth."""
dict[str, Any] validate_input(self, HomeAssistant hass, dict[str, Any] data)
Definition: config_flow.py:35
None _abort_if_unique_id_configured(self, dict[str, Any]|None updates=None, bool reload_on_update=True, *str error="already_configured")
ConfigEntry|None async_set_unique_id(self, str|None unique_id=None, *bool raise_on_progress=True)
ConfigFlowResult async_create_entry(self, *str title, Mapping[str, Any] data, str|None description=None, Mapping[str, str]|None description_placeholders=None, Mapping[str, Any]|None options=None)
ConfigFlowResult async_step_user(self, dict[str, Any]|None user_input=None)
ConfigFlowResult async_step_zeroconf(self, ZeroconfServiceInfo discovery_info)
ConfigFlowResult async_show_form(self, *str|None step_id=None, vol.Schema|None data_schema=None, dict[str, str]|None errors=None, Mapping[str, str]|None description_placeholders=None, bool|None last_step=None, str|None preview=None)
_FlowResultT async_show_form(self, *str|None step_id=None, vol.Schema|None data_schema=None, dict[str, str]|None errors=None, Mapping[str, str]|None description_placeholders=None, bool|None last_step=None, str|None preview=None)
_FlowResultT async_create_entry(self, *str|None title=None, Mapping[str, Any] data, str|None description=None, Mapping[str, str]|None description_placeholders=None)
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)