1 """Config flow for Flux LED/MagicLight."""
3 from __future__
import annotations
6 from typing
import Any, Self, cast
8 from flux_led.const
import (
12 ATTR_MODEL_DESCRIPTION,
16 from flux_led.scanner
import FluxLEDDiscovery
17 import voluptuous
as vol
35 from .
import async_wifi_bulb_for_host
37 CONF_CUSTOM_EFFECT_COLORS,
38 CONF_CUSTOM_EFFECT_SPEED_PCT,
39 CONF_CUSTOM_EFFECT_TRANSITION,
41 DISCOVER_SCAN_TIMEOUT,
43 FLUX_LED_DISCOVERY_SIGNAL,
49 from .discovery
import (
50 async_discover_device,
51 async_discover_devices,
52 async_name_from_discovery,
53 async_populate_data_from_discovery,
54 async_update_entry_from_discovery,
56 from .util
import format_as_flux_mac, mac_matches_by_one
60 """Handle a config flow for Magic Home Integration."""
64 host: str |
None =
None
67 """Initialize the config flow."""
75 config_entry: ConfigEntry,
76 ) -> FluxLedOptionsFlow:
77 """Get the options flow for the Flux LED component."""
81 self, discovery_info: dhcp.DhcpServiceInfo
82 ) -> ConfigFlowResult:
83 """Handle discovery via dhcp."""
85 ipaddr=discovery_info.ip,
92 model_description=
None,
93 remote_access_enabled=
None,
94 remote_access_host=
None,
95 remote_access_port=
None,
100 self, discovery_info: DiscoveryInfoType
101 ) -> ConfigFlowResult:
102 """Handle integration discovery."""
108 self, device: FluxLEDDiscovery, allow_update_mac: bool
110 """Set the discovered mac.
112 We only allow it to be updated if it comes from udp
113 discovery since the dhcp mac can be one digit off from
114 the udp discovery mac for devices with multiple network interfaces
116 mac_address = device[ATTR_ID]
117 assert mac_address
is not None
118 mac = dr.format_mac(mac_address)
122 entry.data.get(CONF_HOST) == device[ATTR_IPADDR]
125 and ":" in entry.unique_id
130 if entry.source == SOURCE_IGNORE:
134 self.hass, entry, device,
None, allow_update_mac
138 ConfigEntryState.SETUP_IN_PROGRESS,
139 ConfigEntryState.NOT_LOADED,
141 )
or entry.state == ConfigEntryState.SETUP_RETRY:
142 self.hass.config_entries.async_schedule_reload(entry.entry_id)
146 FLUX_LED_DISCOVERY_SIGNAL.format(entry_id=entry.entry_id),
151 """Handle any discovery."""
153 assert device
is not None
155 host = device[ATTR_IPADDR]
157 if self.hass.config_entries.flow.async_has_matching_flow(self):
159 if not device[ATTR_MODEL_DESCRIPTION]:
160 mac_address = device[ATTR_ID]
161 assert mac_address
is not None
162 mac = dr.format_mac(mac_address)
165 except FLUX_LED_EXCEPTIONS:
168 discovered_mac = device[ATTR_ID]
169 if device[ATTR_MODEL_DESCRIPTION]
or (
170 discovered_mac
is not None
171 and (formatted_discovered_mac := dr.format_mac(discovered_mac))
172 and formatted_discovered_mac != mac
180 """Return True if other_flow is matching this flow."""
181 return other_flow.host == self.
hosthost
184 self, user_input: dict[str, Any] |
None =
None
185 ) -> ConfigFlowResult:
186 """Confirm discovery."""
189 mac_address = device[ATTR_ID]
190 assert mac_address
is not None
191 if user_input
is not None:
196 "model": device[ATTR_MODEL_DESCRIPTION]
197 or device[ATTR_MODEL]
199 "id": mac_address[-6:],
200 "ipaddr": device[ATTR_IPADDR],
202 self.context[
"title_placeholders"] = placeholders
204 step_id=
"discovery_confirm", description_placeholders=placeholders
209 self, device: FluxLEDDiscovery
210 ) -> ConfigFlowResult:
211 """Create a config entry from a device."""
214 data: dict[str, Any] = {CONF_HOST: device[ATTR_IPADDR]}
222 self, user_input: dict[str, Any] |
None =
None
223 ) -> ConfigFlowResult:
224 """Handle the initial step."""
226 if user_input
is not None:
227 if not (host := user_input[CONF_HOST]):
231 except FLUX_LED_EXCEPTIONS:
232 errors[
"base"] =
"cannot_connect"
234 if (mac_address := device[ATTR_ID])
is not None:
236 dr.format_mac(mac_address), raise_on_progress=
False
243 data_schema=vol.Schema({vol.Optional(CONF_HOST, default=
""): str}),
248 self, user_input: dict[str, Any] |
None =
None
249 ) -> ConfigFlowResult:
250 """Handle the step to pick discovered device."""
251 if user_input
is not None:
252 mac = user_input[CONF_DEVICE]
255 if not device.get(ATTR_MODEL_DESCRIPTION):
256 with contextlib.suppress(*FLUX_LED_EXCEPTIONS):
262 entry.data[CONF_HOST]
266 self.hass, DISCOVER_SCAN_TIMEOUT
269 for device
in discovered_devices:
270 mac_address = device[ATTR_ID]
271 assert mac_address
is not None
274 mac: f
"{async_name_from_discovery(device)} ({device[ATTR_IPADDR]})"
276 if mac
not in current_unique_ids
277 and device[ATTR_IPADDR]
not in current_hosts
283 step_id=
"pick_device",
284 data_schema=vol.Schema({vol.Required(CONF_DEVICE): vol.In(devices_name)}),
288 self, host: str, discovery: FluxLEDDiscovery |
None
289 ) -> FluxLEDDiscovery:
290 """Try to connect."""
293 ATTR_MODEL_DESCRIPTION
302 bulb.discovery = discovery
304 await bulb.async_setup(
lambda:
None)
306 await bulb.async_stop()
307 return FluxLEDDiscovery(
309 model=discovery[ATTR_MODEL]
if discovery
else None,
310 id=discovery[ATTR_ID]
if discovery
else None,
311 model_num=bulb.model_num,
312 version_num=discovery[ATTR_VERSION_NUM]
if discovery
else None,
314 model_info=discovery[ATTR_MODEL_INFO]
if discovery
else None,
315 model_description=bulb.model_data.description,
316 remote_access_enabled=
None,
317 remote_access_host=
None,
318 remote_access_port=
None,
323 """Handle flux_led options."""
326 self, user_input: dict[str, Any] |
None =
None
327 ) -> ConfigFlowResult:
328 """Configure the options."""
329 errors: dict[str, str] = {}
330 if user_input
is not None:
334 options_schema = vol.Schema(
337 CONF_CUSTOM_EFFECT_COLORS,
338 default=options.get(CONF_CUSTOM_EFFECT_COLORS,
""),
341 CONF_CUSTOM_EFFECT_SPEED_PCT,
343 CONF_CUSTOM_EFFECT_SPEED_PCT, DEFAULT_EFFECT_SPEED
345 ): vol.All(vol.Coerce(int), vol.Range(min=1, max=100)),
347 CONF_CUSTOM_EFFECT_TRANSITION,
349 CONF_CUSTOM_EFFECT_TRANSITION, TRANSITION_GRADUAL
351 ): vol.In([TRANSITION_GRADUAL, TRANSITION_JUMP, TRANSITION_STROBE]),
356 step_id=
"init", data_schema=options_schema, errors=errors
ConfigFlowResult _async_create_entry_from_device(self, FluxLEDDiscovery device)
ConfigFlowResult async_step_user(self, dict[str, Any]|None user_input=None)
FluxLEDDiscovery _async_try_connect(self, str host, FluxLEDDiscovery|None discovery)
ConfigFlowResult async_step_discovery_confirm(self, dict[str, Any]|None user_input=None)
bool is_matching(self, Self other_flow)
FluxLedOptionsFlow async_get_options_flow(ConfigEntry config_entry)
ConfigFlowResult async_step_pick_device(self, dict[str, Any]|None user_input=None)
ConfigFlowResult _async_handle_discovery(self)
None _async_set_discovered_mac(self, FluxLEDDiscovery device, bool allow_update_mac)
ConfigFlowResult async_step_dhcp(self, dhcp.DhcpServiceInfo discovery_info)
ConfigFlowResult async_step_integration_discovery(self, DiscoveryInfoType discovery_info)
ConfigFlowResult async_step_init(self, dict[str, Any]|None user_input=None)
None _abort_if_unique_id_configured(self, dict[str, Any]|None updates=None, bool reload_on_update=True, *str error="already_configured")
None _set_confirm_only(self)
set[str|None] _async_current_ids(self, bool include_ignore=True)
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)
list[ConfigEntry] _async_current_entries(self, bool|None include_ignore=None)
ConfigFlowResult async_abort(self, *str reason, Mapping[str, str]|None description_placeholders=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)
ConfigEntry config_entry(self)
None config_entry(self, ConfigEntry value)
_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)
_FlowResultT async_abort(self, *str reason, Mapping[str, str]|None description_placeholders=None)
ElkSystem|None async_discover_device(HomeAssistant hass, str host)
list[ElkSystem] async_discover_devices(HomeAssistant hass, int timeout, str|None address=None)
bool async_update_entry_from_discovery(HomeAssistant hass, config_entries.ConfigEntry entry, ElkSystem device)
str async_name_from_discovery(FluxLEDDiscovery device, int|None model_num=None)
None async_populate_data_from_discovery(Mapping[str, Any] current_data, dict[str, Any] data_updates, FluxLEDDiscovery device)
bool mac_matches_by_one(str formatted_mac_1, str formatted_mac_2)
str|None format_as_flux_mac(str|None mac)
AIOWifiLedBulb async_wifi_bulb_for_host(str host, FluxLEDDiscovery|None discovery)
None async_dispatcher_send(HomeAssistant hass, str signal, *Any args)