diff --git a/custom_components/schellenberg_usb/config_flow.py b/custom_components/schellenberg_usb/config_flow.py index 38b953f..c666554 100644 --- a/custom_components/schellenberg_usb/config_flow.py +++ b/custom_components/schellenberg_usb/config_flow.py @@ -221,26 +221,21 @@ async def _await_subentry_result( """Await a calibration step and cast to SubentryFlowResult for mypy.""" return cast(SubentryFlowResult, await step_coro) - async def async_step_blind( + async def async_step_user( self, user_input: dict[str, Any] | None = None ) -> SubentryFlowResult: """Entry point when the user clicks the 'Add device' button. - Home Assistant calls async_step_{subentry_type}() where subentry_type is - the key returned by async_get_supported_subentry_types. Since our type is - 'blind', we implement async_step_blind(). Delegates to async_step_menu - so the user can choose between auto-pair and manual-add. + Home Assistant initiates user-triggered subentry flows via the `user` + step (per HA config-subentry docs) — NOT async_step_{subentry_type}. + Show the menu so the user can choose between auto-pair and manual-add. + Selecting an option routes to async_step_{option}: 'pair' or + 'manual_add'. """ _LOGGER.debug("Subentry blind flow initiated") - return await self.async_step_menu(user_input) - - async def async_step_menu( - self, user_input: dict[str, Any] | None = None - ) -> SubentryFlowResult: - """Show menu: Pair automatically or Add manually.""" return self.async_show_menu( step_id="menu", - menu_options=["user", "manual_add"], + menu_options=["pair", "manual_add"], ) async def async_step_manual_add( @@ -365,14 +360,14 @@ async def async_step_manual_position( last_step=True, ) - async def async_step_user( + async def async_step_pair( self, user_input: dict[str, Any] | None = None ) -> SubentryFlowResult: - """Handle pairing initialization.""" - _LOGGER.debug("Pairing step user input: %s", user_input) + """Auto-pair: trigger stick pairing and wait for a device to respond.""" + _LOGGER.debug("Pairing step input: %s", user_input) if user_input is None: _LOGGER.info("Showing pairing form") - return self.async_show_form(step_id="user", data_schema=vol.Schema({})) + return self.async_show_form(step_id="pair", data_schema=vol.Schema({})) # Get the hub entry (parent config entry) hub_entry = self._get_entry() diff --git a/custom_components/schellenberg_usb/manifest.json b/custom_components/schellenberg_usb/manifest.json index 5b6a1fe..267c8de 100644 --- a/custom_components/schellenberg_usb/manifest.json +++ b/custom_components/schellenberg_usb/manifest.json @@ -15,5 +15,5 @@ "manufacturer": "van ooijen" } ], - "version": "1.1.0" + "version": "1.1.1" } diff --git a/custom_components/schellenberg_usb/strings.json b/custom_components/schellenberg_usb/strings.json index 12ccdc3..aabfdf8 100644 --- a/custom_components/schellenberg_usb/strings.json +++ b/custom_components/schellenberg_usb/strings.json @@ -44,7 +44,7 @@ "menu": { "title": "Add a blind", "menu_options": { - "user": "Pair automatically", + "pair": "Pair automatically", "manual_add": "Add manually (already paired)" } }, @@ -64,7 +64,7 @@ "initial_position": "Current position" } }, - "user": { + "pair": { "title": "Pair a new device", "description": "Press the pair button to activate pairing mode on your Schellenberg USB stick. Then press the pairing button on your blind motor within 2 minutes.", "submit": "Pair" diff --git a/custom_components/schellenberg_usb/translations/de.json b/custom_components/schellenberg_usb/translations/de.json index b73969c..d01f824 100644 --- a/custom_components/schellenberg_usb/translations/de.json +++ b/custom_components/schellenberg_usb/translations/de.json @@ -42,7 +42,14 @@ "reconfigure": "Kalibrieren" }, "step": { - "user": { + "menu": { + "title": "Rollladen hinzufügen", + "menu_options": { + "pair": "Automatisch koppeln", + "manual_add": "Manuell hinzufügen (bereits gekoppelt)" + } + }, + "pair": { "title": "Neues Gerät koppeln", "description": "Drücken Sie die Kopplungstaste, um den Kopplungsmodus an Ihrem Schellenberg USB-Stick zu aktivieren. Drücken Sie dann innerhalb von 2 Minuten die Kopplungstaste an Ihrem Rollladenmotor.", "submit": "Koppeln" diff --git a/custom_components/schellenberg_usb/translations/en.json b/custom_components/schellenberg_usb/translations/en.json index 6a86371..bd4ba18 100644 --- a/custom_components/schellenberg_usb/translations/en.json +++ b/custom_components/schellenberg_usb/translations/en.json @@ -44,7 +44,7 @@ "menu": { "title": "Add a blind", "menu_options": { - "user": "Pair automatically", + "pair": "Pair automatically", "manual_add": "Add manually (already paired)" } }, @@ -64,7 +64,7 @@ "initial_position": "Current position" } }, - "user": { + "pair": { "title": "Pair a new device", "description": "Press the pair button to activate pairing mode on your Schellenberg USB stick. Then press the pairing button on your blind motor within 2 minutes.", "submit": "Pair" diff --git a/custom_components/schellenberg_usb/translations/es.json b/custom_components/schellenberg_usb/translations/es.json index fde3c04..a389f2c 100644 --- a/custom_components/schellenberg_usb/translations/es.json +++ b/custom_components/schellenberg_usb/translations/es.json @@ -42,7 +42,14 @@ "reconfigure": "Calibrar" }, "step": { - "user": { + "menu": { + "title": "Añadir una persiana", + "menu_options": { + "pair": "Emparejar automáticamente", + "manual_add": "Añadir manualmente (ya emparejado)" + } + }, + "pair": { "title": "Emparejar un nuevo dispositivo", "description": "Presione el botón de emparejamiento para activar el modo de emparejamiento en su memoria USB Schellenberg. Luego presione el botón de emparejamiento en su motor de persiana en los próximos 2 minutos.", "submit": "Emparejar" diff --git a/custom_components/schellenberg_usb/translations/fr.json b/custom_components/schellenberg_usb/translations/fr.json index 4058640..6cc1fcc 100644 --- a/custom_components/schellenberg_usb/translations/fr.json +++ b/custom_components/schellenberg_usb/translations/fr.json @@ -42,7 +42,14 @@ "reconfigure": "Calibrer" }, "step": { - "user": { + "menu": { + "title": "Ajouter un volet", + "menu_options": { + "pair": "Appairer automatiquement", + "manual_add": "Ajouter manuellement (déjà appairé)" + } + }, + "pair": { "title": "Appairer un nouvel appareil", "description": "Appuyez sur le bouton d'appairage pour activer le mode d'appairage sur votre clé USB Schellenberg. Ensuite, appuyez sur le bouton d'appairage de votre moteur de volet dans les 2 minutes.", "submit": "Appairer" diff --git a/tests/test_config_flow.py b/tests/test_config_flow.py index 0dd2051..62a4ef8 100644 --- a/tests/test_config_flow.py +++ b/tests/test_config_flow.py @@ -60,16 +60,34 @@ def mock_hub_entry(hass: HomeAssistant) -> ConfigEntry: async def test_manual_add_menu_shown( hass: HomeAssistant, mock_hub_entry: ConfigEntry ) -> None: - """Menu step returns show_menu with 'user' and 'manual_add' options.""" + """The flow entry step (async_step_user) shows the menu. + + Regression: HA initiates user-triggered subentry flows via async_step_user, + NOT async_step_{subentry_type}. The menu must therefore be served from + async_step_user, with options routing to async_step_pair / async_step_manual_add. + """ handler = _make_handler(hass, mock_hub_entry.entry_id) - result = await handler.async_step_blind(None) + result = await handler.async_step_user(None) assert result["type"] == "menu" - assert "user" in result["menu_options"] + assert "pair" in result["menu_options"] assert "manual_add" in result["menu_options"] +@pytest.mark.asyncio +async def test_pair_step_shows_form( + hass: HomeAssistant, mock_hub_entry: ConfigEntry +) -> None: + """Selecting 'Pair automatically' (async_step_pair) shows the pairing form.""" + handler = _make_handler(hass, mock_hub_entry.entry_id) + + result = await handler.async_step_pair(None) + + assert result["type"] == "form" + assert result["step_id"] == "pair" + + @pytest.mark.asyncio async def test_manual_add_creates_subentry( hass: HomeAssistant, mock_hub_entry: ConfigEntry