Home Assistant Unofficial Reference 2024.12.1
config_flow.py
Go to the documentation of this file.
1 """Config flow for Elexa Guardian integration."""
2 
3 from __future__ import annotations
4 
5 from typing import Any
6 
7 from aioguardian import Client
8 from aioguardian.errors import GuardianError
9 import voluptuous as vol
10 
11 from homeassistant.components import dhcp, zeroconf
12 from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
13 from homeassistant.const import CONF_IP_ADDRESS, CONF_PORT
14 from homeassistant.core import HomeAssistant, callback
15 
16 from .const import CONF_UID, DOMAIN, LOGGER
17 
18 DEFAULT_PORT = 7777
19 
20 DATA_SCHEMA = vol.Schema(
21  {
22  vol.Required(CONF_IP_ADDRESS): str,
23  vol.Required(CONF_PORT, default=DEFAULT_PORT): int,
24  }
25 )
26 
27 UNIQUE_ID = "guardian_{0}"
28 
29 
30 @callback
31 def async_get_pin_from_discovery_hostname(hostname: str) -> str:
32  """Get the device's 4-digit PIN from its zeroconf-discovered hostname."""
33  return hostname.split(".")[0].split("-")[1]
34 
35 
36 @callback
37 def async_get_pin_from_uid(uid: str) -> str:
38  """Get the device's 4-digit PIN from its UID."""
39  return uid[-4:]
40 
41 
42 async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> dict[str, Any]:
43  """Validate the user input allows us to connect.
44 
45  Data has the keys from DATA_SCHEMA with values provided by the user.
46  """
47  async with Client(data[CONF_IP_ADDRESS]) as client:
48  ping_data = await client.system.ping()
49 
50  return {
51  CONF_UID: ping_data["data"]["uid"],
52  }
53 
54 
55 class GuardianConfigFlow(ConfigFlow, domain=DOMAIN):
56  """Handle a config flow for Elexa Guardian."""
57 
58  VERSION = 1
59 
60  def __init__(self) -> None:
61  """Initialize."""
62  self.discovery_infodiscovery_info: dict[str, Any] = {}
63 
64  async def _async_set_unique_id(self, pin: str) -> None:
65  """Set the config entry's unique ID (based on the device's 4-digit PIN)."""
66  await self.async_set_unique_idasync_set_unique_id(UNIQUE_ID.format(pin))
67  if self.discovery_infodiscovery_info:
68  self._abort_if_unique_id_configured_abort_if_unique_id_configured(
69  updates={CONF_IP_ADDRESS: self.discovery_infodiscovery_info[CONF_IP_ADDRESS]}
70  )
71  self._async_abort_entries_match_async_abort_entries_match(
72  {CONF_IP_ADDRESS: self.discovery_infodiscovery_info[CONF_IP_ADDRESS]}
73  )
74  else:
75  self._abort_if_unique_id_configured_abort_if_unique_id_configured()
76 
77  async def async_step_user(
78  self, user_input: dict[str, Any] | None = None
79  ) -> ConfigFlowResult:
80  """Handle configuration via the UI."""
81  if user_input is None:
82  return self.async_show_formasync_show_formasync_show_form(
83  step_id="user", data_schema=DATA_SCHEMA, errors={}
84  )
85 
86  try:
87  info = await validate_input(self.hass, user_input)
88  except GuardianError as err:
89  LOGGER.error("Error while connecting to unit: %s", err)
90  return self.async_show_formasync_show_formasync_show_form(
91  step_id="user",
92  data_schema=DATA_SCHEMA,
93  errors={CONF_IP_ADDRESS: "cannot_connect"},
94  )
95 
96  pin = async_get_pin_from_uid(info[CONF_UID])
97  await self._async_set_unique_id_async_set_unique_id(pin)
98 
99  return self.async_create_entryasync_create_entryasync_create_entry(
100  title=info[CONF_UID], data={CONF_UID: info["uid"], **user_input}
101  )
102 
103  async def async_step_dhcp(
104  self, discovery_info: dhcp.DhcpServiceInfo
105  ) -> ConfigFlowResult:
106  """Handle the configuration via dhcp."""
107  self.discovery_infodiscovery_info = {
108  CONF_IP_ADDRESS: discovery_info.ip,
109  CONF_PORT: DEFAULT_PORT,
110  }
111  await self._async_set_unique_id_async_set_unique_id(
112  async_get_pin_from_uid(discovery_info.macaddress.replace(":", "").upper())
113  )
114  return await self.async_step_discovery_confirmasync_step_discovery_confirm()
115 
117  self, discovery_info: zeroconf.ZeroconfServiceInfo
118  ) -> ConfigFlowResult:
119  """Handle the configuration via zeroconf."""
120  self.discovery_infodiscovery_info = {
121  CONF_IP_ADDRESS: discovery_info.host,
122  CONF_PORT: discovery_info.port,
123  }
124  pin = async_get_pin_from_discovery_hostname(discovery_info.hostname)
125  await self._async_set_unique_id_async_set_unique_id(pin)
126  return await self.async_step_discovery_confirmasync_step_discovery_confirm()
127 
129  self, user_input: dict[str, Any] | None = None
130  ) -> ConfigFlowResult:
131  """Finish the configuration via any discovery."""
132  if user_input is None:
133  self._set_confirm_only_set_confirm_only()
134  return self.async_show_formasync_show_formasync_show_form(step_id="discovery_confirm")
135  return await self.async_step_userasync_step_userasync_step_user(self.discovery_infodiscovery_info)
ConfigFlowResult async_step_user(self, dict[str, Any]|None user_input=None)
Definition: config_flow.py:79
ConfigFlowResult async_step_zeroconf(self, zeroconf.ZeroconfServiceInfo discovery_info)
Definition: config_flow.py:118
ConfigFlowResult async_step_dhcp(self, dhcp.DhcpServiceInfo discovery_info)
Definition: config_flow.py:105
ConfigFlowResult async_step_discovery_confirm(self, dict[str, Any]|None user_input=None)
Definition: config_flow.py:130
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)
None _async_abort_entries_match(self, dict[str, Any]|None match_dict=None)
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)
dict[str, Any] validate_input(HomeAssistant hass, dict[str, Any] data)
Definition: config_flow.py:42
str async_get_pin_from_discovery_hostname(str hostname)
Definition: config_flow.py:31