diff --git a/custom_components/opensprinkler/__init__.py b/custom_components/opensprinkler/__init__.py index a31bafd..0d62672 100755 --- a/custom_components/opensprinkler/__init__.py +++ b/custom_components/opensprinkler/__init__.py @@ -8,6 +8,7 @@ from aiohttp.client_exceptions import InvalidURL from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( + CONF_NAME, CONF_PASSWORD, CONF_SCAN_INTERVAL, CONF_URL, @@ -32,6 +33,7 @@ from .const import ( CONF_INDEX, CONF_RUN_SECONDS, + DEFAULT_NAME, DEFAULT_SCAN_INTERVAL, DOMAIN, SCHEMA_SERVICE_PAUSE_STATIONS, @@ -69,53 +71,48 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): url = entry.data.get(CONF_URL) password = entry.data.get(CONF_PASSWORD) verify_ssl = entry.data.get(CONF_VERIFY_SSL) - try: - opts = {"session": async_get_clientsession(hass), "verify_ssl": verify_ssl} - controller = OpenSprinkler(url, password, opts) - controller.refresh_on_update = False - - async def async_update_data(): - """Fetch data from OpenSprinkler.""" - _LOGGER.debug("refreshing data") - async with async_timeout.timeout(TIMEOUT): - try: - await controller.refresh() - except OpenSprinklerAuthError as e: - # wrong password, tell user to re-enter the password - raise ConfigEntryAuthFailed from e - except ( - InvalidURL, - OpenSprinklerConnectionError, - ) as e: - raise UpdateFailed from e - - if not controller._state: - raise UpdateFailed("Error fetching OpenSprinkler state") - - return controller._state - - scan_interval = entry.options.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL) - coordinator = DataUpdateCoordinator( - hass, - _LOGGER, - name="OpenSprinkler resource status", - update_method=async_update_data, - update_interval=timedelta(seconds=scan_interval), - ) + opts = {"session": async_get_clientsession(hass), "verify_ssl": verify_ssl} - # initial load before loading platforms - await coordinator.async_refresh() - if not coordinator.last_update_success: - raise ConfigEntryNotReady + controller = OpenSprinkler(url, password, opts) + controller.refresh_on_update = False - hass.data[DOMAIN][entry.entry_id] = { - "coordinator": coordinator, - "controller": controller, - } + async def async_update_data(): + """Fetch data from OpenSprinkler.""" + _LOGGER.debug("refreshing data") + async with async_timeout.timeout(TIMEOUT): + try: + await controller.refresh() + except OpenSprinklerAuthError as e: + # wrong password, tell user to re-enter the password + _LOGGER.debug(f"auth failure: {e}") + raise ConfigEntryAuthFailed from e + except ( + InvalidURL, + OpenSprinklerConnectionError, + ) as e: + raise UpdateFailed from e + + if not controller._state: + raise UpdateFailed("Error fetching OpenSprinkler state") + + return controller._state + + scan_interval = entry.options.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL) + coordinator = DataUpdateCoordinator( + hass, + _LOGGER, + name=f"{entry.data.get(CONF_NAME, DEFAULT_NAME)} resource status", + update_method=async_update_data, + update_interval=timedelta(seconds=scan_interval), + ) + + # initial load before loading platforms + await coordinator.async_config_entry_first_refresh() - except (OpenSprinklerAuthError, OpenSprinklerConnectionError) as exc: - _LOGGER.error("Unable to connect to OpenSprinkler controller: %s", str(exc)) - raise ConfigEntryNotReady + hass.data[DOMAIN][entry.entry_id] = { + "coordinator": coordinator, + "controller": controller, + } await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) diff --git a/custom_components/opensprinkler/config_flow.py b/custom_components/opensprinkler/config_flow.py index 714f8a2..151c88f 100644 --- a/custom_components/opensprinkler/config_flow.py +++ b/custom_components/opensprinkler/config_flow.py @@ -1,6 +1,7 @@ """Config flow for OpenSprinkler integration.""" import logging +from typing import Any import voluptuous as vol from aiohttp.client_exceptions import InvalidURL @@ -12,6 +13,7 @@ CONF_URL, CONF_VERIFY_SSL, ) +from homeassistant.data_entry_flow import FlowResult from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.util import slugify from pyopensprinkler import Controller as OpenSprinkler @@ -30,6 +32,11 @@ vol.Optional(CONF_NAME, default=DEFAULT_NAME): str, } ) +REAUTH_SCHEMA = vol.Schema( + { + vol.Required(CONF_PASSWORD): str, + } +) class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): @@ -93,6 +100,47 @@ async def async_step_import(self, user_input): """Handle import.""" return await self.async_step_user(user_input) + async def async_step_reauth(self, user_input: dict[str, Any]) -> FlowResult: + """Handle reauthorization.""" + + existing_entry = self.hass.config_entries.async_get_entry( + self.context["entry_id"] + ) + errors = {} + if user_input is not None: + password = user_input[CONF_PASSWORD] + + url = existing_entry.data[CONF_URL] + verify_ssl = existing_entry.data[CONF_VERIFY_SSL] + opts = { + "session": async_get_clientsession(self.hass), + "verify_ssl": verify_ssl, + } + + try: + controller = OpenSprinkler(url, password, opts) + await controller.refresh() + except InvalidURL: + errors["base"] = "invalid_url" + except OpenSprinklerConnectionError: + errors["base"] = "cannot_connect" + except OpenSprinklerAuthError: + errors["base"] = "invalid_auth" + else: + self.hass.config_entries.async_update_entry( + existing_entry, + data={ + **existing_entry.data, + CONF_PASSWORD: password, + }, + ) + await self.hass.config_entries.async_reload(existing_entry.entry_id) + return self.async_abort(reason="reauth_successful") + + return self.async_show_form( + step_id="reauth", data_schema=REAUTH_SCHEMA, errors=errors + ) + class MacAddressRequiredError(Exception): """Error to mac address required.""" diff --git a/custom_components/opensprinkler/strings.json b/custom_components/opensprinkler/strings.json index 68a4070..a797a1f 100644 --- a/custom_components/opensprinkler/strings.json +++ b/custom_components/opensprinkler/strings.json @@ -21,6 +21,13 @@ "name": "Controller Name" }, "title": "Connect to the OpenSprinkler controller" + }, + "reauth": { + "data": { + "url": "URL", + "password": "Password" + }, + "title": "Enter new password" } } }, diff --git a/custom_components/opensprinkler/translations/de.json b/custom_components/opensprinkler/translations/de.json index ab54090..cf80393 100644 --- a/custom_components/opensprinkler/translations/de.json +++ b/custom_components/opensprinkler/translations/de.json @@ -20,6 +20,13 @@ "name": "Controller Name" }, "title": "Stellen Sie eine Verbindung zum OpenSprinkler-Controller her" + }, + "reauth": { + "data": { + "url": "URL", + "password": "Passwort" + }, + "title": "Neues Passwort eingeben" } } }, diff --git a/custom_components/opensprinkler/translations/en.json b/custom_components/opensprinkler/translations/en.json index ecbb7bc..a797a1f 100644 --- a/custom_components/opensprinkler/translations/en.json +++ b/custom_components/opensprinkler/translations/en.json @@ -21,6 +21,13 @@ "name": "Controller Name" }, "title": "Connect to the OpenSprinkler controller" + }, + "reauth": { + "data": { + "url": "URL", + "password": "Password" + }, + "title": "Enter new password" } } }, @@ -91,7 +98,7 @@ }, "pause_duration": { "name": "Pause duration", - "description": "Pause duration in seconds." + "description": "Duration to pause in seconds." } } }, diff --git a/custom_components/opensprinkler/translations/sk.json b/custom_components/opensprinkler/translations/sk.json index e0c89e4..89dc711 100644 --- a/custom_components/opensprinkler/translations/sk.json +++ b/custom_components/opensprinkler/translations/sk.json @@ -21,6 +21,13 @@ "name": "Názov ovládača" }, "title": "Pripojte sa k ovládaču OpenSprinkler" + }, + "reauth": { + "data": { + "url": "URL", + "password": "Heslo" + }, + "title": "Zadajte nové heslo" } } },