diff --git a/homeassistant/components/philips_js/__init__.py b/homeassistant/components/philips_js/__init__.py index 3145e82a942..55ac33d198f 100644 --- a/homeassistant/components/philips_js/__init__.py +++ b/homeassistant/components/philips_js/__init__.py @@ -19,8 +19,9 @@ from homeassistant.const import ( Platform, ) from homeassistant.core import HomeAssistant, callback +from homeassistant.exceptions import ConfigEntryAuthFailed from homeassistant.helpers.debounce import Debouncer -from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed +from homeassistant.helpers.update_coordinator import DataUpdateCoordinator from .const import CONF_ALLOW_NOTIFY, CONF_SYSTEM, DOMAIN @@ -171,4 +172,4 @@ class PhilipsTVDataUpdateCoordinator(DataUpdateCoordinator[None]): except ConnectionFailure: pass except AutenticationFailure as exception: - raise UpdateFailed(str(exception)) from exception + raise ConfigEntryAuthFailed(str(exception)) from exception diff --git a/homeassistant/components/philips_js/config_flow.py b/homeassistant/components/philips_js/config_flow.py index dab8d4fbe24..9b7e52c2119 100644 --- a/homeassistant/components/philips_js/config_flow.py +++ b/homeassistant/components/philips_js/config_flow.py @@ -1,6 +1,7 @@ """Config flow for Philips TV integration.""" from __future__ import annotations +from collections.abc import Mapping import platform from typing import Any @@ -20,6 +21,18 @@ from homeassistant.data_entry_flow import FlowResult from . import LOGGER from .const import CONF_ALLOW_NOTIFY, CONF_SYSTEM, CONST_APP_ID, CONST_APP_NAME, DOMAIN +USER_SCHEMA = vol.Schema( + { + vol.Required( + CONF_HOST, + ): str, + vol.Required( + CONF_API_VERSION, + default=1, + ): vol.In([1, 5, 6]), + } +) + async def _validate_input( hass: core.HomeAssistant, host: str, api_version: int @@ -47,9 +60,19 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): self._current: dict[str, Any] = {} self._hub: PhilipsTV | None = None self._pair_state: Any = None + self._entry: config_entries.ConfigEntry | None = None async def _async_create_current(self) -> FlowResult: system = self._current[CONF_SYSTEM] + if self._entry: + self.hass.config_entries.async_update_entry( + self._entry, data=self._entry.data | self._current + ) + self.hass.async_create_task( + self.hass.config_entries.async_reload(self._entry.entry_id) + ) + return self.async_abort(reason="reauth_successful") + return self.async_create_entry( title=f"{system['name']} ({system['serialnumber']})", data=self._current, @@ -108,6 +131,13 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): self._current[CONF_PASSWORD] = password return await self._async_create_current() + async def async_step_reauth(self, entry_data: Mapping[str, Any]) -> FlowResult: + """Handle configuration by re-auth.""" + self._entry = self.hass.config_entries.async_get_entry(self.context["entry_id"]) + self._current[CONF_HOST] = entry_data[CONF_HOST] + self._current[CONF_API_VERSION] = entry_data[CONF_API_VERSION] + return await self.async_step_user() + async def async_step_user( self, user_input: dict[str, Any] | None = None ) -> FlowResult: @@ -128,7 +158,8 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): else: if serialnumber := hub.system.get("serialnumber"): await self.async_set_unique_id(serialnumber) - self._abort_if_unique_id_configured() + if self._entry is None: + self._abort_if_unique_id_configured() self._current[CONF_SYSTEM] = hub.system self._current[CONF_API_VERSION] = hub.api_version @@ -138,14 +169,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): return await self.async_step_pair() return await self._async_create_current() - schema = vol.Schema( - { - vol.Required(CONF_HOST, default=self._current.get(CONF_HOST)): str, - vol.Required( - CONF_API_VERSION, default=self._current.get(CONF_API_VERSION, 1) - ): vol.In([1, 5, 6]), - } - ) + schema = self.add_suggested_values_to_schema(USER_SCHEMA, self._current) return self.async_show_form(step_id="user", data_schema=schema, errors=errors) @staticmethod diff --git a/homeassistant/components/philips_js/strings.json b/homeassistant/components/philips_js/strings.json index dc258385804..302e1b9accf 100644 --- a/homeassistant/components/philips_js/strings.json +++ b/homeassistant/components/philips_js/strings.json @@ -22,7 +22,8 @@ "invalid_pin": "Invalid PIN" }, "abort": { - "already_configured": "[%key:common::config_flow::abort::already_configured_device%]" + "already_configured": "[%key:common::config_flow::abort::already_configured_device%]", + "reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]" } }, "options": { diff --git a/tests/components/philips_js/test_config_flow.py b/tests/components/philips_js/test_config_flow.py index 1662a2a3fc2..603e278d592 100644 --- a/tests/components/philips_js/test_config_flow.py +++ b/tests/components/philips_js/test_config_flow.py @@ -12,6 +12,7 @@ from . import ( MOCK_CONFIG, MOCK_CONFIG_PAIRED, MOCK_PASSWORD, + MOCK_SYSTEM, MOCK_SYSTEM_UNPAIRED, MOCK_USERINPUT, MOCK_USERNAME, @@ -56,6 +57,42 @@ async def test_form(hass: HomeAssistant, mock_setup_entry) -> None: assert len(mock_setup_entry.mock_calls) == 1 +async def test_reauth( + hass: HomeAssistant, mock_setup_entry, mock_config_entry, mock_tv +) -> None: + """Test we get the form.""" + + mock_tv.system = MOCK_SYSTEM | {"model": "changed"} + + assert await hass.config_entries.async_setup(mock_config_entry.entry_id) + assert len(mock_setup_entry.mock_calls) == 1 + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={ + "source": config_entries.SOURCE_REAUTH, + "unique_id": mock_config_entry.unique_id, + "entry_id": mock_config_entry.entry_id, + }, + data=mock_config_entry.data, + ) + + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert result["step_id"] == "user" + assert result["errors"] == {} + + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + MOCK_USERINPUT, + ) + await hass.async_block_till_done() + + assert result2["type"] == data_entry_flow.FlowResultType.ABORT + assert result2["reason"] == "reauth_successful" + assert mock_config_entry.data == MOCK_CONFIG | {"system": mock_tv.system} + assert len(mock_setup_entry.mock_calls) == 2 + + async def test_form_cannot_connect(hass: HomeAssistant, mock_tv) -> None: """Test we handle cannot connect error.""" result = await hass.config_entries.flow.async_init(