-
-
Notifications
You must be signed in to change notification settings - Fork 37.4k
Expand file tree
/
Copy pathconfig_flow.py
More file actions
185 lines (160 loc) · 6.59 KB
/
config_flow.py
File metadata and controls
185 lines (160 loc) · 6.59 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
"""Config flow to configure the WLED integration."""
from typing import Any
import voluptuous as vol
from wled import WLED, Device, WLEDConnectionError, WLEDUnsupportedVersionError
import yarl
from homeassistant.components import onboarding
from homeassistant.config_entries import (
SOURCE_RECONFIGURE,
ConfigFlow,
ConfigFlowResult,
OptionsFlowWithReload,
)
from homeassistant.const import CONF_HOST, CONF_MAC
from homeassistant.core import callback
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.device_registry import format_mac
from homeassistant.helpers.service_info.zeroconf import ZeroconfServiceInfo
from .const import CONF_KEEP_MAIN_LIGHT, DEFAULT_KEEP_MAIN_LIGHT, DOMAIN
from .coordinator import WLEDConfigEntry, normalize_mac_address
def _normalize_host(host: str) -> str:
"""Normalize host by extracting hostname if a URL is provided."""
try:
return yarl.URL(host).host or host
except ValueError:
pass
return host
class WLEDFlowHandler(ConfigFlow, domain=DOMAIN):
"""Handle a WLED config flow."""
VERSION = 1
MINOR_VERSION = 3
discovered_host: str
discovered_device: Device
@staticmethod
@callback
def async_get_options_flow(
config_entry: WLEDConfigEntry,
) -> WLEDOptionsFlowHandler:
"""Get the options flow for this handler."""
return WLEDOptionsFlowHandler()
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle a flow initiated by the user."""
errors = {}
if user_input is not None:
host = _normalize_host(user_input[CONF_HOST])
try:
device = await self._async_get_device(host)
except WLEDUnsupportedVersionError:
errors["base"] = "unsupported_version"
except WLEDConnectionError:
errors["base"] = "cannot_connect"
else:
mac_address = normalize_mac_address(device.info.mac_address)
await self.async_set_unique_id(mac_address, raise_on_progress=False)
if self.source == SOURCE_RECONFIGURE:
entry = self._get_reconfigure_entry()
self._abort_if_unique_id_mismatch(
reason="unique_id_mismatch",
description_placeholders={
"expected_mac": format_mac(entry.unique_id).upper(),
"actual_mac": mac_address.upper(),
},
)
return self.async_update_reload_and_abort(
entry,
data_updates={CONF_HOST: host},
)
self._abort_if_unique_id_configured(updates={CONF_HOST: host})
return self.async_create_entry(
title=device.info.name,
data={CONF_HOST: host},
)
data_schema = vol.Schema({vol.Required(CONF_HOST): str})
if self.source == SOURCE_RECONFIGURE:
entry = self._get_reconfigure_entry()
data_schema = self.add_suggested_values_to_schema(
data_schema,
entry.data,
)
return self.async_show_form(
step_id="user",
data_schema=data_schema,
errors=errors or {},
)
async def async_step_reconfigure(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle reconfigure flow for WLED entry."""
return await self.async_step_user(user_input)
async def async_step_zeroconf(
self, discovery_info: ZeroconfServiceInfo
) -> ConfigFlowResult:
"""Handle zeroconf discovery."""
# Abort quick if the mac address is provided by discovery info
if mac := discovery_info.properties.get(CONF_MAC):
await self.async_set_unique_id(normalize_mac_address(mac))
self._abort_if_unique_id_configured(
updates={CONF_HOST: discovery_info.host}
)
self.discovered_host = discovery_info.host
try:
self.discovered_device = await self._async_get_device(discovery_info.host)
except WLEDUnsupportedVersionError:
return self.async_abort(reason="unsupported_version")
except WLEDConnectionError:
return self.async_abort(reason="cannot_connect")
device_mac_address = normalize_mac_address(
self.discovered_device.info.mac_address
)
await self.async_set_unique_id(device_mac_address)
self._abort_if_unique_id_configured(updates={CONF_HOST: discovery_info.host})
self.context.update(
{
"title_placeholders": {"name": self.discovered_device.info.name},
"configuration_url": f"http://{discovery_info.host}",
}
)
return await self.async_step_zeroconf_confirm()
async def async_step_zeroconf_confirm(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle a flow initiated by zeroconf."""
if user_input is not None or not onboarding.async_is_onboarded(self.hass):
return self.async_create_entry(
title=self.discovered_device.info.name,
data={
CONF_HOST: self.discovered_host,
},
)
return self.async_show_form(
step_id="zeroconf_confirm",
description_placeholders={"name": self.discovered_device.info.name},
)
async def _async_get_device(self, host: str) -> Device:
"""Get device information from WLED device."""
session = async_get_clientsession(self.hass)
wled = WLED(host, session=session)
return await wled.update()
class WLEDOptionsFlowHandler(OptionsFlowWithReload):
"""Handle WLED options."""
async def async_step_init(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Manage WLED options."""
if user_input is not None:
return self.async_create_entry(title="", data=user_input)
return self.async_show_form(
step_id="init",
data_schema=vol.Schema(
{
vol.Optional(
CONF_KEEP_MAIN_LIGHT,
default=self.config_entry.options.get(
CONF_KEEP_MAIN_LIGHT, DEFAULT_KEEP_MAIN_LIGHT
),
): bool,
}
),
)