1 """Config flow for Network UPS Tools (NUT) integration."""
3 from __future__
import annotations
5 from collections.abc
import Mapping
9 from aionut
import NUTError, NUTLoginError
10 import voluptuous
as vol
31 from .
import PyNUTData
32 from .const
import DEFAULT_HOST, DEFAULT_PORT, DEFAULT_SCAN_INTERVAL, DOMAIN
34 _LOGGER = logging.getLogger(__name__)
36 AUTH_SCHEMA = {vol.Optional(CONF_USERNAME): str, vol.Optional(CONF_PASSWORD): str}
40 """Generate base schema."""
42 vol.Optional(CONF_HOST, default=nut_config.get(CONF_HOST)
or DEFAULT_HOST): str,
43 vol.Optional(CONF_PORT, default=nut_config.get(CONF_PORT)
or DEFAULT_PORT): int,
45 base_schema.update(AUTH_SCHEMA)
46 return vol.Schema(base_schema)
50 """UPS selection schema."""
51 return vol.Schema({vol.Required(CONF_ALIAS): vol.In(ups_list)})
54 async
def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> dict[str, Any]:
55 """Validate the user input allows us to connect.
57 Data has the keys from _base_schema with values provided by the user.
60 host = data[CONF_HOST]
61 port = data[CONF_PORT]
62 alias = data.get(CONF_ALIAS)
63 username = data.get(CONF_USERNAME)
64 password = data.get(CONF_PASSWORD)
66 nut_data = PyNUTData(host, port, alias, username, password, persistent=
False)
67 status = await nut_data.async_update()
69 if not alias
and not nut_data.ups_list:
72 return {
"ups_list": nut_data.ups_list,
"available_resources": status}
76 """Format a host, port, and alias so it can be used for comparison or display."""
77 host = user_input[CONF_HOST]
78 port = user_input[CONF_PORT]
79 alias = user_input.get(CONF_ALIAS)
81 return f
"{alias}@{host}:{port}"
82 return f
"{host}:{port}"
86 """Handle a config flow for Network UPS Tools (NUT)."""
91 """Initialize the nut config flow."""
93 self.
ups_listups_list: dict[str, str] |
None =
None
94 self.title: str |
None =
None
98 self, discovery_info: zeroconf.ZeroconfServiceInfo
99 ) -> ConfigFlowResult:
100 """Prepare configuration for a discovered nut device."""
103 CONF_HOST: discovery_info.host
or DEFAULT_HOST,
104 CONF_PORT: discovery_info.port
or DEFAULT_PORT,
106 self.context[
"title_placeholders"] = self.
nut_confignut_config.copy()
110 self, user_input: dict[str, Any] |
None =
None
111 ) -> ConfigFlowResult:
112 """Handle the user input."""
113 errors: dict[str, str] = {}
114 placeholders: dict[str, str] = {}
116 if user_input
is not None:
117 nut_config.update(user_input)
122 if len(info[
"ups_list"]) > 1:
135 description_placeholders=placeholders,
139 self, user_input: dict[str, Any] |
None =
None
140 ) -> ConfigFlowResult:
141 """Handle the picking the ups."""
142 errors: dict[str, str] = {}
143 placeholders: dict[str, str] = {}
146 if user_input
is not None:
159 description_placeholders=placeholders,
163 """See if we already have a nut entry matching user input configured."""
164 existing_host_port_aliases = {
167 if CONF_HOST
in entry.data
172 self, config: dict[str, Any]
173 ) -> tuple[dict[str, Any], dict[str, str], dict[str, str]]:
174 errors: dict[str, str] = {}
175 info: dict[str, Any] = {}
176 description_placeholders: dict[str, str] = {}
179 except NUTLoginError:
180 errors[CONF_PASSWORD] =
"invalid_auth"
181 except NUTError
as ex:
182 errors[CONF_BASE] =
"cannot_connect"
183 description_placeholders[
"error"] =
str(ex)
187 _LOGGER.exception(
"Unexpected exception")
188 errors[CONF_BASE] =
"unknown"
189 return info, errors, description_placeholders
192 self, entry_data: Mapping[str, Any]
193 ) -> ConfigFlowResult:
195 entry_id = self.context[
"entry_id"]
196 self.
reauth_entryreauth_entry = self.hass.config_entries.async_get_entry(entry_id)
200 self, user_input: dict[str, Any] |
None =
None
201 ) -> ConfigFlowResult:
202 """Handle reauth input."""
203 errors: dict[str, str] = {}
205 assert existing_entry
206 existing_data = existing_entry.data
207 description_placeholders: dict[str, str] = {
208 CONF_HOST: existing_data[CONF_HOST],
209 CONF_PORT: existing_data[CONF_PORT],
211 if user_input
is not None:
217 CONF_USERNAME: user_input.get(CONF_USERNAME),
218 CONF_PASSWORD: user_input.get(CONF_PASSWORD),
223 existing_entry, data=new_config
225 description_placeholders.update(placeholders)
228 description_placeholders=description_placeholders,
229 step_id=
"reauth_confirm",
230 data_schema=vol.Schema(AUTH_SCHEMA),
237 """Get the options flow for this handler."""
242 """Handle a option flow for nut."""
245 self, user_input: dict[str, Any] |
None =
None
246 ) -> ConfigFlowResult:
247 """Handle options flow."""
248 if user_input
is not None:
252 CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL
256 vol.Optional(CONF_SCAN_INTERVAL, default=scan_interval): vol.All(
257 vol.Coerce(int), vol.Clamp(min=10, max=300)
261 return self.
async_show_formasync_show_form(step_id=
"init", data_schema=vol.Schema(base_schema))
ConfigFlowResult async_step_reauth_confirm(self, dict[str, Any]|None user_input=None)
ConfigFlowResult async_step_reauth(self, Mapping[str, Any] entry_data)
ConfigFlowResult async_step_zeroconf(self, zeroconf.ZeroconfServiceInfo discovery_info)
ConfigFlowResult async_step_user(self, dict[str, Any]|None user_input=None)
bool _host_port_alias_already_configured(self, dict[str, Any] user_input)
OptionsFlow async_get_options_flow(ConfigEntry config_entry)
tuple[dict[str, Any], dict[str, str], dict[str, str]] _async_validate_or_error(self, dict[str, Any] config)
ConfigFlowResult async_step_ups(self, dict[str, Any]|None user_input=None)
ConfigFlowResult async_step_init(self, dict[str, Any]|None user_input=None)
None _async_handle_discovery_without_unique_id(self)
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_update_reload_and_abort(self, ConfigEntry entry, *str|None|UndefinedType unique_id=UNDEFINED, str|UndefinedType title=UNDEFINED, Mapping[str, Any]|UndefinedType data=UNDEFINED, Mapping[str, Any]|UndefinedType data_updates=UNDEFINED, Mapping[str, Any]|UndefinedType options=UNDEFINED, str|UndefinedType reason=UNDEFINED, bool reload_even_if_entry_is_unchanged=True)
ConfigFlowResult async_step_user(self, dict[str, Any]|None user_input=None)
ConfigFlowResult async_abort(self, *str reason, Mapping[str, str]|None description_placeholders=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)
IssData update(pyiss.ISS iss)
dict[str, Any] validate_input(HomeAssistant hass, dict[str, Any] data)
vol.Schema _ups_schema(dict[str, str] ups_list)
vol.Schema _base_schema(dict[str, Any] nut_config)
str _format_host_port_alias(Mapping[str, Any] user_input)