Add reconfigure flow to Roku (#132986)

* add reconfigure flow to roku

* Update strings.json

* aimplify

* Apply suggestions from code review

Co-authored-by: Josef Zweck <josef@zweck.dev>

* Update test_config_flow.py

* Update config_flow.py

* Update config_flow.py

---------

Co-authored-by: Josef Zweck <josef@zweck.dev>
This commit is contained in:
Chris Talkington 2024-12-16 01:51:01 -06:00 committed by GitHub
parent 5f2b1bd622
commit 4566ebbb3d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 103 additions and 10 deletions

View File

@ -10,7 +10,12 @@ from rokuecp import Roku, RokuError
import voluptuous as vol
from homeassistant.components import ssdp, zeroconf
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult, OptionsFlow
from homeassistant.config_entries import (
SOURCE_RECONFIGURE,
ConfigFlow,
ConfigFlowResult,
OptionsFlow,
)
from homeassistant.const import CONF_HOST, CONF_NAME
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.aiohttp_client import async_get_clientsession
@ -53,20 +58,38 @@ class RokuConfigFlow(ConfigFlow, domain=DOMAIN):
self.discovery_info = {}
@callback
def _show_form(self, errors: dict[str, Any] | None = None) -> ConfigFlowResult:
def _show_form(
self,
user_input: dict[str, Any] | None,
errors: dict[str, Any] | None = None,
) -> ConfigFlowResult:
"""Show the form to the user."""
suggested_values = user_input
if suggested_values is None and self.source == SOURCE_RECONFIGURE:
suggested_values = {
CONF_HOST: self._get_reconfigure_entry().data[CONF_HOST]
}
return self.async_show_form(
step_id="user",
data_schema=DATA_SCHEMA,
data_schema=self.add_suggested_values_to_schema(
DATA_SCHEMA, suggested_values
),
errors=errors or {},
)
async def async_step_reconfigure(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle reconfiguration of the integration."""
return await self.async_step_user(user_input)
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle a flow initialized by the user."""
if not user_input:
return self._show_form()
return self._show_form(user_input)
errors = {}
@ -75,13 +98,21 @@ class RokuConfigFlow(ConfigFlow, domain=DOMAIN):
except RokuError:
_LOGGER.debug("Roku Error", exc_info=True)
errors["base"] = ERROR_CANNOT_CONNECT
return self._show_form(errors)
return self._show_form(user_input, errors)
except Exception:
_LOGGER.exception("Unknown error trying to connect")
return self.async_abort(reason=ERROR_UNKNOWN)
await self.async_set_unique_id(info["serial_number"])
self._abort_if_unique_id_configured(updates={CONF_HOST: user_input[CONF_HOST]})
if self.source == SOURCE_RECONFIGURE:
self._abort_if_unique_id_mismatch(reason="wrong_device")
return self.async_update_reload_and_abort(
self._get_reconfigure_entry(),
data_updates={CONF_HOST: user_input[CONF_HOST]},
)
self._abort_if_unique_id_configured()
return self.async_create_entry(title=info["title"], data=user_input)

View File

@ -21,7 +21,9 @@
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
"already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]",
"unknown": "[%key:common::config_flow::error::unknown%]"
"reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]",
"unknown": "[%key:common::config_flow::error::unknown%]",
"wrong_device": "This Roku device does not match the existing device id. Please make sure you entered the correct host information."
}
},
"options": {

View File

@ -1,13 +1,18 @@
"""Test the Roku config flow."""
import dataclasses
from unittest.mock import MagicMock
from unittest.mock import AsyncMock, MagicMock
import pytest
from rokuecp import RokuConnectionError
from rokuecp import Device as RokuDevice, RokuConnectionError
from homeassistant.components.roku.const import CONF_PLAY_MEDIA_APP_ID, DOMAIN
from homeassistant.config_entries import SOURCE_HOMEKIT, SOURCE_SSDP, SOURCE_USER
from homeassistant.config_entries import (
SOURCE_HOMEKIT,
SOURCE_SSDP,
SOURCE_USER,
ConfigFlowResult,
)
from homeassistant.const import CONF_HOST, CONF_NAME, CONF_SOURCE
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
@ -23,6 +28,8 @@ from . import (
from tests.common import MockConfigEntry
RECONFIGURE_HOST = "192.168.1.190"
async def test_duplicate_error(
hass: HomeAssistant,
@ -276,3 +283,56 @@ async def test_options_flow(
assert result2.get("data") == {
CONF_PLAY_MEDIA_APP_ID: "782875",
}
async def _start_reconfigure_flow(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
) -> ConfigFlowResult:
"""Initialize a reconfigure flow."""
mock_config_entry.add_to_hass(hass)
reconfigure_result = await mock_config_entry.start_reconfigure_flow(hass)
assert reconfigure_result["type"] is FlowResultType.FORM
assert reconfigure_result["step_id"] == "user"
return await hass.config_entries.flow.async_configure(
reconfigure_result["flow_id"],
{CONF_HOST: RECONFIGURE_HOST},
)
async def test_reconfigure_flow(
hass: HomeAssistant,
mock_setup_entry: AsyncMock,
mock_config_entry: MockConfigEntry,
mock_roku_config_flow: MagicMock,
) -> None:
"""Test reconfigure flow."""
result = await _start_reconfigure_flow(hass, mock_config_entry)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "reconfigure_successful"
entry = hass.config_entries.async_get_entry(mock_config_entry.entry_id)
assert entry
assert entry.data == {
CONF_HOST: RECONFIGURE_HOST,
}
async def test_reconfigure_unique_id_mismatch(
hass: HomeAssistant,
mock_device: RokuDevice,
mock_setup_entry: AsyncMock,
mock_config_entry: MockConfigEntry,
mock_roku_config_flow: MagicMock,
) -> None:
"""Ensure reconfigure flow aborts when the device changes."""
mock_device.info.serial_number = "RECONFIG"
result = await _start_reconfigure_flow(hass, mock_config_entry)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "wrong_device"