From 06391d4635aaf4dc3b528c78d892738be5b94859 Mon Sep 17 00:00:00 2001 From: dontinelli <73341522+dontinelli@users.noreply.github.com> Date: Sat, 14 Dec 2024 12:10:28 +0100 Subject: [PATCH] Add reconfiguration to slide_local (#133182) Co-authored-by: Joostlek --- .../components/slide_local/__init__.py | 7 ++++ .../components/slide_local/config_flow.py | 35 ++++++++++++++++++- homeassistant/components/slide_local/cover.py | 6 ++-- .../components/slide_local/quality_scale.yaml | 2 +- .../components/slide_local/strings.json | 14 ++++++++ .../slide_local/test_config_flow.py | 27 +++++++++++++- 6 files changed, 85 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/slide_local/__init__.py b/homeassistant/components/slide_local/__init__.py index 878830fe513..dbe4d516d75 100644 --- a/homeassistant/components/slide_local/__init__.py +++ b/homeassistant/components/slide_local/__init__.py @@ -25,9 +25,16 @@ async def async_setup_entry(hass: HomeAssistant, entry: SlideConfigEntry) -> boo await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) + entry.async_on_unload(entry.add_update_listener(update_listener)) + return True +async def update_listener(hass: HomeAssistant, entry: SlideConfigEntry) -> None: + """Handle options update.""" + await hass.config_entries.async_reload(entry.entry_id) + + async def async_unload_entry(hass: HomeAssistant, entry: SlideConfigEntry) -> bool: """Unload a config entry.""" return await hass.config_entries.async_unload_platforms(entry, PLATFORMS) diff --git a/homeassistant/components/slide_local/config_flow.py b/homeassistant/components/slide_local/config_flow.py index bc5033e972b..3ccc89be375 100644 --- a/homeassistant/components/slide_local/config_flow.py +++ b/homeassistant/components/slide_local/config_flow.py @@ -15,10 +15,12 @@ from goslideapi.goslideapi import ( import voluptuous as vol from homeassistant.components.zeroconf import ZeroconfServiceInfo -from homeassistant.config_entries import ConfigFlow, ConfigFlowResult +from homeassistant.config_entries import ConfigFlow, ConfigFlowResult, OptionsFlow from homeassistant.const import CONF_API_VERSION, CONF_HOST, CONF_MAC, CONF_PASSWORD +from homeassistant.core import callback from homeassistant.helpers.device_registry import format_mac +from . import SlideConfigEntry from .const import CONF_INVERT_POSITION, DOMAIN _LOGGER = logging.getLogger(__name__) @@ -34,6 +36,14 @@ class SlideConfigFlow(ConfigFlow, domain=DOMAIN): VERSION = 1 MINOR_VERSION = 1 + @staticmethod + @callback + def async_get_options_flow( + config_entry: SlideConfigEntry, + ) -> SlideOptionsFlowHandler: + """Get the options flow for this handler.""" + return SlideOptionsFlowHandler() + async def async_test_connection( self, user_input: dict[str, str | int] ) -> dict[str, str]: @@ -181,3 +191,26 @@ class SlideConfigFlow(ConfigFlow, domain=DOMAIN): "host": self._host, }, ) + + +class SlideOptionsFlowHandler(OptionsFlow): + """Handle a options flow for slide_local.""" + + async def async_step_init( + self, user_input: dict[str, Any] | None = None + ) -> ConfigFlowResult: + """Manage the options.""" + if user_input is not None: + return self.async_create_entry(data=user_input) + + return self.async_show_form( + step_id="init", + data_schema=self.add_suggested_values_to_schema( + vol.Schema( + { + vol.Required(CONF_INVERT_POSITION): bool, + } + ), + {CONF_INVERT_POSITION: self.config_entry.options[CONF_INVERT_POSITION]}, + ), + ) diff --git a/homeassistant/components/slide_local/cover.py b/homeassistant/components/slide_local/cover.py index 1bf026746c6..cf04f46d139 100644 --- a/homeassistant/components/slide_local/cover.py +++ b/homeassistant/components/slide_local/cover.py @@ -54,7 +54,7 @@ class SlideCoverLocal(SlideEntity, CoverEntity): super().__init__(coordinator) self._attr_name = None - self._invert = entry.options[CONF_INVERT_POSITION] + self.invert = entry.options[CONF_INVERT_POSITION] self._attr_unique_id = coordinator.data["mac"] @property @@ -79,7 +79,7 @@ class SlideCoverLocal(SlideEntity, CoverEntity): if pos is not None: if (1 - pos) <= DEFAULT_OFFSET or pos <= DEFAULT_OFFSET: pos = round(pos) - if not self._invert: + if not self.invert: pos = 1 - pos pos = int(pos * 100) return pos @@ -101,7 +101,7 @@ class SlideCoverLocal(SlideEntity, CoverEntity): async def async_set_cover_position(self, **kwargs: Any) -> None: """Move the cover to a specific position.""" position = kwargs[ATTR_POSITION] / 100 - if not self._invert: + if not self.invert: position = 1 - position if self.coordinator.data["pos"] is not None: diff --git a/homeassistant/components/slide_local/quality_scale.yaml b/homeassistant/components/slide_local/quality_scale.yaml index 048a428f236..4eda62f6497 100644 --- a/homeassistant/components/slide_local/quality_scale.yaml +++ b/homeassistant/components/slide_local/quality_scale.yaml @@ -33,7 +33,7 @@ rules: test-coverage: todo integration-owner: done docs-installation-parameters: done - docs-configuration-parameters: todo + docs-configuration-parameters: done # Gold entity-translations: todo diff --git a/homeassistant/components/slide_local/strings.json b/homeassistant/components/slide_local/strings.json index 38090c7e62d..3e693fe51b9 100644 --- a/homeassistant/components/slide_local/strings.json +++ b/homeassistant/components/slide_local/strings.json @@ -27,6 +27,20 @@ "unknown": "[%key:common::config_flow::error::unknown%]" } }, + "options": { + "step": { + "init": { + "title": "Configure Slide", + "description": "Reconfigure the Slide device", + "data": { + "invert_position": "Invert position" + }, + "data_description": { + "invert_position": "Invert the position of your slide cover." + } + } + } + }, "exceptions": { "update_error": { "message": "Error while updating data from the API." diff --git a/tests/components/slide_local/test_config_flow.py b/tests/components/slide_local/test_config_flow.py index 025f8c323ff..48be7dd7850 100644 --- a/tests/components/slide_local/test_config_flow.py +++ b/tests/components/slide_local/test_config_flow.py @@ -14,10 +14,11 @@ import pytest from homeassistant.components.slide_local.const import CONF_INVERT_POSITION, DOMAIN from homeassistant.components.zeroconf import ZeroconfServiceInfo from homeassistant.config_entries import SOURCE_USER, SOURCE_ZEROCONF -from homeassistant.const import CONF_API_VERSION, CONF_HOST, CONF_PASSWORD +from homeassistant.const import CONF_API_VERSION, CONF_HOST, CONF_PASSWORD, Platform from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import FlowResultType +from . import setup_platform from .const import HOST, SLIDE_INFO_DATA from tests.common import MockConfigEntry @@ -371,3 +372,27 @@ async def test_zeroconf_connection_error( assert result["type"] is FlowResultType.ABORT assert result["reason"] == "discovery_connection_failed" + + +async def test_options_flow( + hass: HomeAssistant, mock_slide_api: AsyncMock, mock_config_entry: MockConfigEntry +) -> None: + """Test options flow works correctly.""" + await setup_platform(hass, mock_config_entry, [Platform.COVER]) + + result = await hass.config_entries.options.async_init(mock_config_entry.entry_id) + + assert result["type"] is FlowResultType.FORM + assert result["step_id"] == "init" + + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={ + CONF_INVERT_POSITION: True, + }, + ) + + assert result["type"] is FlowResultType.CREATE_ENTRY + assert mock_config_entry.options == { + CONF_INVERT_POSITION: True, + }