1 """Config flow for Enphase Envoy integration."""
3 from __future__
import annotations
5 from collections.abc
import Mapping
7 from typing
import TYPE_CHECKING, Any
9 from awesomeversion
import AwesomeVersion
10 from pyenphase
import AUTH_TOKEN_MIN_VERSION, Envoy, EnvoyError
11 import voluptuous
as vol
29 OPTION_DIAGNOSTICS_INCLUDE_FIXTURES,
30 OPTION_DIAGNOSTICS_INCLUDE_FIXTURES_DEFAULT_VALUE,
31 OPTION_DISABLE_KEEP_ALIVE,
32 OPTION_DISABLE_KEEP_ALIVE_DEFAULT_VALUE,
35 _LOGGER = logging.getLogger(__name__)
39 CONF_SERIAL =
"serial"
41 INSTALLER_AUTH_USERNAME =
"installer"
45 hass: HomeAssistant, host: str, username: str, password: str
47 """Validate the user input allows us to connect."""
50 await envoy.authenticate(username=username, password=password)
55 """Handle a config flow for Enphase Envoy."""
59 _reauth_entry: ConfigEntry
62 """Initialize an envoy flow."""
65 self.
protoversprotovers: str |
None =
None
70 config_entry: ConfigEntry,
71 ) -> EnvoyOptionsFlowHandler:
72 """Options flow handler for Enphase_Envoy."""
77 """Generate schema."""
78 schema: VolDictType = {}
81 schema[vol.Required(CONF_HOST, default=self.
ip_addressip_address)] = vol.In(
85 schema[vol.Required(CONF_HOST)] = str
91 and AwesomeVersion(self.
protoversprotovers) < AUTH_TOKEN_MIN_VERSION
93 default_username = INSTALLER_AUTH_USERNAME
96 vol.Optional(CONF_USERNAME, default=self.
usernameusername
or default_username)
98 schema[vol.Optional(CONF_PASSWORD, default=
"")] = str
100 return vol.Schema(schema)
104 """Return a set of hosts."""
106 entry.data[CONF_HOST]
108 if CONF_HOST
in entry.data
112 self, discovery_info: zeroconf.ZeroconfServiceInfo
113 ) -> ConfigFlowResult:
114 """Handle a flow initialized by zeroconf discovery."""
115 if _LOGGER.isEnabledFor(logging.DEBUG):
118 "Zeroconf ip %s processing %s, current hosts: %s",
119 discovery_info.ip_address.version,
123 if discovery_info.ip_address.version != 4:
125 serial = discovery_info.properties[
"serialnum"]
126 self.
protoversprotovers = discovery_info.properties.get(
"protovers")
131 "Zeroconf ip %s, fw %s, no existing entry with serial %s",
138 entry.unique_id
is None
139 and CONF_HOST
in entry.data
140 and entry.data[CONF_HOST] == self.
ip_addressip_address
143 "Zeroconf update envoy with this ip and blank serial in unique_id",
145 title = f
"{ENVOY} {serial}" if entry.title == ENVOY
else ENVOY
147 entry, title=title, unique_id=serial, reason=
"already_configured"
150 _LOGGER.debug(
"Zeroconf ip %s to step user", self.
ip_addressip_address)
154 self, entry_data: Mapping[str, Any]
155 ) -> ConfigFlowResult:
156 """Handle configuration by re-auth."""
163 """Return the name of the envoy."""
164 return f
"{ENVOY} {self.unique_id}" if self.
unique_idunique_id
else ENVOY
167 self, user_input: dict[str, Any] |
None =
None
168 ) -> ConfigFlowResult:
169 """Handle the initial step."""
170 errors: dict[str, str] = {}
171 description_placeholders: dict[str, str] = {}
176 host = (user_input
or {}).
get(CONF_HOST)
or self.
ip_addressip_address
or ""
178 if user_input
is not None:
183 user_input[CONF_USERNAME],
184 user_input[CONF_PASSWORD],
186 except INVALID_AUTH_ERRORS
as e:
187 errors[
"base"] =
"invalid_auth"
188 description_placeholders = {
"reason":
str(e)}
189 except EnvoyError
as e:
190 errors[
"base"] =
"cannot_connect"
191 description_placeholders = {
"reason":
str(e)}
193 _LOGGER.exception(
"Unexpected exception")
194 errors[
"base"] =
"unknown"
213 CONF_USERNAME: user_input[CONF_USERNAME],
214 CONF_PASSWORD: user_input[CONF_PASSWORD],
216 error=
"reauth_successful",
221 title=name, data={CONF_HOST: host, CONF_NAME: name} | user_input
225 self.context[
"title_placeholders"] = {
233 description_placeholders=description_placeholders,
238 self, user_input: dict[str, Any] |
None =
None
239 ) -> ConfigFlowResult:
240 """Add reconfigure step to allow to manually reconfigure a config entry."""
242 errors: dict[str, str] = {}
243 description_placeholders: dict[str, str] = {}
245 if user_input
is not None:
246 host: str = user_input[CONF_HOST]
247 username: str = user_input[CONF_USERNAME]
248 password: str = user_input[CONF_PASSWORD]
256 except INVALID_AUTH_ERRORS
as e:
257 errors[
"base"] =
"invalid_auth"
258 description_placeholders = {
"reason":
str(e)}
259 except EnvoyError
as e:
260 errors[
"base"] =
"cannot_connect"
261 description_placeholders = {
"reason":
str(e)}
263 _LOGGER.exception(
"Unexpected exception")
264 errors[
"base"] =
"unknown"
272 CONF_USERNAME: username,
273 CONF_PASSWORD: password,
277 self.context[
"title_placeholders"] = {
278 CONF_SERIAL: reconfigure_entry.unique_id
or "-",
279 CONF_HOST: reconfigure_entry.data[CONF_HOST],
282 suggested_values: Mapping[str, Any] = user_input
or reconfigure_entry.data
284 step_id=
"reconfigure",
288 description_placeholders=description_placeholders,
294 """Envoy config flow options handler."""
297 self, user_input: dict[str, Any] |
None =
None
298 ) -> ConfigFlowResult:
299 """Manage the options."""
300 if user_input
is not None:
308 data_schema=vol.Schema(
311 OPTION_DIAGNOSTICS_INCLUDE_FIXTURES,
313 OPTION_DIAGNOSTICS_INCLUDE_FIXTURES,
314 OPTION_DIAGNOSTICS_INCLUDE_FIXTURES_DEFAULT_VALUE,
318 OPTION_DISABLE_KEEP_ALIVE,
320 OPTION_DISABLE_KEEP_ALIVE,
321 OPTION_DISABLE_KEEP_ALIVE_DEFAULT_VALUE,
326 description_placeholders={
ConfigFlowResult async_step_user(self, dict[str, Any]|None user_input=None)
str _async_envoy_name(self)
ConfigFlowResult async_step_reconfigure(self, dict[str, Any]|None user_input=None)
EnvoyOptionsFlowHandler async_get_options_flow(ConfigEntry config_entry)
ConfigFlowResult async_step_reauth(self, Mapping[str, Any] entry_data)
vol.Schema _async_generate_schema(self)
ConfigFlowResult async_step_zeroconf(self, zeroconf.ZeroconfServiceInfo discovery_info)
set[str] _async_current_hosts(self)
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")
ConfigEntry _get_reauth_entry(self)
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_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)
ConfigEntry _get_reconfigure_entry(self)
None _abort_if_unique_id_mismatch(self, *str reason="unique_id_mismatch", 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)
vol.Schema add_suggested_values_to_schema(self, vol.Schema data_schema, Mapping[str, Any]|None suggested_values)
_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)
web.Response get(self, web.Request request, str config_key)
Envoy validate_input(HomeAssistant hass, str host, str username, str password)
httpx.AsyncClient get_async_client(HomeAssistant hass, bool verify_ssl=True)