From 95a0b76ea8cd653b5c21b0868f728ed9de883f0d Mon Sep 17 00:00:00 2001 From: Konstantin Pivnov Date: Mon, 11 Nov 2024 15:40:07 +0300 Subject: [PATCH] added component option flow --- custom_components/tion/__init__.py | 18 ++++- custom_components/tion/config_flow.py | 88 +++++++++++++++------ custom_components/tion/manifest.json | 2 +- custom_components/tion/strings.json | 13 +++ custom_components/tion/translations/ru.json | 13 +++ 5 files changed, 107 insertions(+), 27 deletions(-) diff --git a/custom_components/tion/__init__.py b/custom_components/tion/__init__.py index 60ebdd0..a00e959 100644 --- a/custom_components/tion/__init__.py +++ b/custom_components/tion/__init__.py @@ -98,9 +98,15 @@ async def async_setup(hass: HomeAssistant, config: ConfigType): async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): """Set up this integration using UI.""" + if hass.data.get(DOMAIN) is None: hass.data.setdefault(DOMAIN, {}) + if entry.options: + entry_data = dict(entry.data) + entry_data.update(entry.options) + hass.config_entries.async_update_entry(entry, data=entry_data, options={}) + async def update_auth_data(**kwargs): hass.config_entries.async_update_entry(entry, data=kwargs) @@ -141,13 +147,19 @@ async def update_auth_data(**kwargs): else: _LOGGER.info("Unsupported device type: %s", device.type) - await hass.async_create_task( - hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) - ) + if not entry.update_listeners: + entry.add_update_listener(async_update_options) + + await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) return True +async def async_update_options(hass: HomeAssistant, entry: ConfigEntry): + """Handle updating entry options.""" + await hass.config_entries.async_reload(entry.entry_id) + + async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Handle removal of an entry.""" unloaded = await hass.config_entries.async_unload_platforms(entry, PLATFORMS) diff --git a/custom_components/tion/config_flow.py b/custom_components/tion/config_flow.py index cb07df2..504fee5 100644 --- a/custom_components/tion/config_flow.py +++ b/custom_components/tion/config_flow.py @@ -6,9 +6,16 @@ import voluptuous as vol -from homeassistant.config_entries import ConfigFlow, ConfigFlowResult +from homeassistant.config_entries import ( + ConfigEntry, + ConfigFlow, + ConfigFlowResult, + OptionsFlow, +) from homeassistant.const import CONF_PASSWORD, CONF_SCAN_INTERVAL, CONF_USERNAME +from homeassistant.core import callback from homeassistant.data_entry_flow import FlowResult +from homeassistant.exceptions import ConfigEntryAuthFailed from homeassistant.helpers.aiohttp_client import async_create_clientsession from .client import TionClient @@ -33,6 +40,12 @@ async def _get_auth_data( ) return await api.authorization + @staticmethod + @callback + def async_get_options_flow(config_entry: ConfigEntry) -> OptionsFlow: + """Create the options flow.""" + return TionOptionsFlow(config_entry) + async def async_step_user( self, user_input: dict[str, Any] | None = None ) -> FlowResult: @@ -42,10 +55,6 @@ async def async_step_user( if user_input is not None: self._async_abort_entries_match({CONF_USERNAME: user_input[CONF_USERNAME]}) - sha256_hash = hashlib.new("sha256") - sha256_hash.update(user_input[CONF_USERNAME].encode()) - sha256_hex = sha256_hash.hexdigest() - try: interval = int(user_input.get(CONF_SCAN_INTERVAL)) except ValueError: @@ -61,22 +70,25 @@ async def async_step_user( if auth_data is None: errors["base"] = "invalid_auth" - else: - unique_id = f"{sha256_hex}" - - # Checks that the device is actually unique, otherwise abort - await self.async_set_unique_id(unique_id) - self._abort_if_unique_id_configured() - - return self.async_create_entry( - title=user_input[CONF_USERNAME], - data={ - CONF_USERNAME: user_input[CONF_USERNAME], - CONF_PASSWORD: user_input[CONF_PASSWORD], - CONF_SCAN_INTERVAL: interval, - AUTH_DATA: auth_data, - }, - ) + raise ConfigEntryAuthFailed + + sha256_hash = hashlib.new("sha256") + sha256_hash.update(user_input[CONF_USERNAME].encode()) + unique_id = f"{sha256_hash.hexdigest()}" + + # Checks that the device is actually unique, otherwise abort + await self.async_set_unique_id(unique_id) + self._abort_if_unique_id_configured() + + return self.async_create_entry( + title=user_input[CONF_USERNAME], + data={ + CONF_USERNAME: user_input[CONF_USERNAME], + CONF_PASSWORD: user_input[CONF_PASSWORD], + CONF_SCAN_INTERVAL: interval, + AUTH_DATA: auth_data, + }, + ) return self.async_show_form( step_id="user", @@ -85,8 +97,8 @@ async def async_step_user( vol.Required(CONF_USERNAME, default=""): str, vol.Required(CONF_PASSWORD, default=""): str, vol.Required( - CONF_SCAN_INTERVAL, default=f"{DEFAULT_SCAN_INTERVAL}" - ): str, + CONF_SCAN_INTERVAL, default=DEFAULT_SCAN_INTERVAL + ): vol.Coerce(int), } ), errors=errors, @@ -100,3 +112,33 @@ async def async_step_import( {CONF_USERNAME: import_config.get(CONF_USERNAME)} ) return await self.async_step_user(import_config) + + +class TionOptionsFlow(OptionsFlow): + """Tion options flow handler.""" + + def __init__(self, config_entry: ConfigEntry) -> None: + """Initialize Tion options flow.""" + self._entry_data = dict(config_entry.data) + + async def async_step_init(self, user_input=None): + """Manage the 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.Required( + CONF_PASSWORD, + default=self.config_entry.data.get(CONF_PASSWORD), + ): str, + vol.Required( + CONF_SCAN_INTERVAL, + default=self.config_entry.data.get(CONF_SCAN_INTERVAL), + ): vol.Coerce(int), + } + ), + ) diff --git a/custom_components/tion/manifest.json b/custom_components/tion/manifest.json index e87d6a1..a2ff113 100644 --- a/custom_components/tion/manifest.json +++ b/custom_components/tion/manifest.json @@ -17,5 +17,5 @@ "iot_class": "cloud_polling", "issue_tracker": "https://github.com/vaproloff/tion_home_assistant/issues", "requirements": [], - "version": "2024.11.1" + "version": "2024.11.2" } \ No newline at end of file diff --git a/custom_components/tion/strings.json b/custom_components/tion/strings.json index fc3123f..388885a 100644 --- a/custom_components/tion/strings.json +++ b/custom_components/tion/strings.json @@ -24,6 +24,19 @@ "unknown": "Unexpected error" } }, + "options": { + "step": { + "init": { + "data": { + "password": "Password", + "scan_interval": "API poll interval" + }, + "data_description": { + "scan_interval": "Tion devices data minimum update period." + } + } + } + }, "entity": { "climate": { "tion_breezer": { diff --git a/custom_components/tion/translations/ru.json b/custom_components/tion/translations/ru.json index 211edae..f8e36a7 100644 --- a/custom_components/tion/translations/ru.json +++ b/custom_components/tion/translations/ru.json @@ -22,6 +22,19 @@ "unknown": "Неизвестная ошибка" } }, + "options": { + "step": { + "init": { + "data": { + "password": "Пароль", + "scan_interval": "Интервал обновления" + }, + "data_description": { + "scan_interval": "Минимальный период обновления данных устройств Tion от API." + } + } + } + }, "entity": { "climate": { "tion_breezer": {