Compare commits

...

1 Commits

Author SHA1 Message Date
Claude b7a691064b Simplify ONVIF config flow to always discover devices
The config flow now always runs WS-Discovery on startup and presents
discovered devices alongside a "Manually configure ONVIF device" option.
This removes the intermediate step that asked users whether to search
automatically or manually configure.

Changes:
- Remove the intermediate "auto" checkbox step
- Rename async_step_device to async_step_user as the entry point
- Update strings.json to reflect the simplified flow
- Update all tests to match the new flow
2025-11-25 22:26:22 +00:00
3 changed files with 47 additions and 147 deletions
+3 -17
View File
@@ -124,20 +124,6 @@ class OnvifFlowHandler(ConfigFlow, domain=DOMAIN):
self.devices: list[dict[str, Any]] = []
self.onvif_config: dict[str, Any] = {}
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle user flow."""
if user_input:
if user_input["auto"]:
return await self.async_step_device()
return await self.async_step_configure()
return self.async_show_form(
step_id="user",
data_schema=vol.Schema({vol.Required("auto", default=True): bool}),
)
async def async_step_reauth(
self, entry_data: Mapping[str, Any]
) -> ConfigFlowResult:
@@ -202,10 +188,10 @@ class OnvifFlowHandler(ConfigFlow, domain=DOMAIN):
hass.async_create_task(self.hass.config_entries.async_reload(entry_id))
return self.async_abort(reason="already_configured")
async def async_step_device(
async def async_step_user(
self, user_input: dict[str, str] | None = None
) -> ConfigFlowResult:
"""Handle WS-Discovery.
"""Handle user flow with WS-Discovery.
Let user choose between discovered devices and manual configuration.
If no device is found allow user to manually input configuration.
@@ -246,7 +232,7 @@ class OnvifFlowHandler(ConfigFlow, domain=DOMAIN):
devices[device[CONF_HOST]] = description
return self.async_show_form(
step_id="device",
step_id="user",
data_schema=vol.Schema({vol.Optional(CONF_HOST): vol.In(devices)}),
)
+2 -8
View File
@@ -35,12 +35,6 @@
"description": "Create camera entity for {profile} at {resolution} resolution?",
"title": "Configure profiles"
},
"device": {
"data": {
"host": "Select discovered ONVIF device"
},
"title": "Select ONVIF device"
},
"reauth_confirm": {
"data": {
"password": "[%key:common::config_flow::data::password%]",
@@ -51,9 +45,9 @@
},
"user": {
"data": {
"auto": "Search automatically"
"host": "Select discovered ONVIF device"
},
"description": "By selecting **Submit**, we will search your network for ONVIF devices that support Profile S.\n\nSome manufacturers have started to disable ONVIF by default. Please ensure ONVIF is enabled in your camera's configuration.",
"description": "Select a discovered ONVIF device or choose to manually configure a device.\n\nSome manufacturers have started to disable ONVIF by default. Please ensure ONVIF is enabled in your camera's configuration.",
"title": "ONVIF device setup"
}
}
+42 -122
View File
@@ -103,13 +103,6 @@ async def test_flow_discovered_devices(hass: HomeAssistant) -> None:
"""Test that config flow works for discovered devices."""
logging.getLogger("homeassistant.components.onvif").setLevel(logging.DEBUG)
result = await hass.config_entries.flow.async_init(
config_flow.DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "user"
with (
patch(
"homeassistant.components.onvif.config_flow.get_device"
@@ -123,12 +116,12 @@ async def test_flow_discovered_devices(hass: HomeAssistant) -> None:
setup_mock_discovery(mock_discovery)
setup_mock_device(mock_device)
result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input={"auto": True}
result = await hass.config_entries.flow.async_init(
config_flow.DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "device"
assert result["step_id"] == "user"
container = result["data_schema"].schema[config_flow.CONF_HOST].container
assert len(container) == 3
assert container == {
@@ -176,13 +169,6 @@ async def test_flow_discovered_devices_ignore_configured_manual_input(
logging.getLogger("homeassistant.components.onvif").setLevel(logging.DEBUG)
await setup_onvif_integration(hass)
result = await hass.config_entries.flow.async_init(
config_flow.DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "user"
with (
patch(
"homeassistant.components.onvif.config_flow.get_device"
@@ -196,12 +182,12 @@ async def test_flow_discovered_devices_ignore_configured_manual_input(
setup_mock_discovery(mock_discovery, with_mac=True)
setup_mock_device(mock_device)
result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input={"auto": True}
result = await hass.config_entries.flow.async_init(
config_flow.DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "device"
assert result["step_id"] == "user"
assert len(result["data_schema"].schema[config_flow.CONF_HOST].container) == 2
result = await hass.config_entries.flow.async_configure(
@@ -217,13 +203,6 @@ async def test_flow_discovered_no_device(hass: HomeAssistant) -> None:
"""Test that config flow discovery no device."""
await setup_onvif_integration(hass)
result = await hass.config_entries.flow.async_init(
config_flow.DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "user"
with (
patch(
"homeassistant.components.onvif.config_flow.get_device"
@@ -237,8 +216,8 @@ async def test_flow_discovered_no_device(hass: HomeAssistant) -> None:
setup_mock_discovery(mock_discovery, no_devices=True)
setup_mock_device(mock_device)
result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input={"auto": True}
result = await hass.config_entries.flow.async_init(
config_flow.DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] is FlowResultType.FORM
@@ -262,13 +241,6 @@ async def test_flow_discovery_ignore_existing_and_abort(hass: HomeAssistant) ->
entry_id="2",
)
result = await hass.config_entries.flow.async_init(
config_flow.DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "user"
with (
patch(
"homeassistant.components.onvif.config_flow.get_device"
@@ -282,8 +254,8 @@ async def test_flow_discovery_ignore_existing_and_abort(hass: HomeAssistant) ->
setup_mock_discovery(mock_discovery, with_name=True, with_mac=True)
setup_mock_device(mock_device)
result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input={"auto": True}
result = await hass.config_entries.flow.async_init(
config_flow.DOMAIN, context={"source": config_entries.SOURCE_USER}
)
# It should skip to manual entry if the only devices are already configured
@@ -306,14 +278,8 @@ async def test_flow_discovery_ignore_existing_and_abort(hass: HomeAssistant) ->
async def test_flow_manual_entry(hass: HomeAssistant) -> None:
"""Test that config flow works for discovered devices."""
"""Test that config flow works for manual entry when no devices discovered."""
logging.getLogger("homeassistant.components.onvif").setLevel(logging.DEBUG)
result = await hass.config_entries.flow.async_init(
config_flow.DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "user"
with (
patch(
@@ -325,15 +291,14 @@ async def test_flow_manual_entry(hass: HomeAssistant) -> None:
patch("homeassistant.components.onvif.ONVIFDevice") as mock_device,
):
setup_mock_onvif_camera(mock_onvif_camera, two_profiles=True)
# no discovery
mock_discovery.return_value = []
setup_mock_discovery(mock_discovery, no_devices=True)
setup_mock_device(mock_device)
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input={"auto": False},
result = await hass.config_entries.flow.async_init(
config_flow.DOMAIN, context={"source": config_entries.SOURCE_USER}
)
# When no devices are discovered, it goes directly to configure
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "configure"
@@ -367,13 +332,6 @@ async def test_flow_manual_entry(hass: HomeAssistant) -> None:
async def test_flow_manual_entry_no_profiles(hass: HomeAssistant) -> None:
"""Test that config flow when no profiles are returned."""
result = await hass.config_entries.flow.async_init(
config_flow.DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "user"
with (
patch(
"homeassistant.components.onvif.config_flow.get_device"
@@ -384,14 +342,16 @@ async def test_flow_manual_entry_no_profiles(hass: HomeAssistant) -> None:
patch("homeassistant.components.onvif.ONVIFDevice") as mock_device,
):
setup_mock_onvif_camera(mock_onvif_camera, no_profiles=True)
# no discovery
mock_discovery.return_value = []
setup_mock_discovery(mock_discovery, no_devices=True)
setup_mock_device(mock_device)
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input={"auto": False},
result = await hass.config_entries.flow.async_init(
config_flow.DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "configure"
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input={
@@ -409,13 +369,6 @@ async def test_flow_manual_entry_no_profiles(hass: HomeAssistant) -> None:
async def test_flow_manual_entry_no_mac(hass: HomeAssistant) -> None:
"""Test that config flow when no mac address is returned."""
result = await hass.config_entries.flow.async_init(
config_flow.DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "user"
with (
patch(
"homeassistant.components.onvif.config_flow.get_device"
@@ -428,14 +381,16 @@ async def test_flow_manual_entry_no_mac(hass: HomeAssistant) -> None:
setup_mock_onvif_camera(
mock_onvif_camera, with_serial=False, with_interfaces=False
)
# no discovery
mock_discovery.return_value = []
setup_mock_discovery(mock_discovery, no_devices=True)
setup_mock_device(mock_device)
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input={"auto": False},
result = await hass.config_entries.flow.async_init(
config_flow.DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "configure"
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input={
@@ -453,13 +408,6 @@ async def test_flow_manual_entry_no_mac(hass: HomeAssistant) -> None:
async def test_flow_manual_entry_fails(hass: HomeAssistant) -> None:
"""Test that we get a good error when manual entry fails."""
result = await hass.config_entries.flow.async_init(
config_flow.DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "user"
with (
patch(
"homeassistant.components.onvif.config_flow.get_device"
@@ -472,13 +420,11 @@ async def test_flow_manual_entry_fails(hass: HomeAssistant) -> None:
setup_mock_onvif_camera(
mock_onvif_camera, two_profiles=True, profiles_transient_failure=True
)
# no discovery
mock_discovery.return_value = []
setup_mock_discovery(mock_discovery, no_devices=True)
setup_mock_device(mock_device)
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input={"auto": False},
result = await hass.config_entries.flow.async_init(
config_flow.DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] is FlowResultType.FORM
@@ -563,13 +509,6 @@ async def test_flow_manual_entry_fails(hass: HomeAssistant) -> None:
async def test_flow_manual_entry_wrong_password(hass: HomeAssistant) -> None:
"""Test that we get a an auth error with the wrong password."""
result = await hass.config_entries.flow.async_init(
config_flow.DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "user"
with (
patch(
"homeassistant.components.onvif.config_flow.get_device"
@@ -580,13 +519,11 @@ async def test_flow_manual_entry_wrong_password(hass: HomeAssistant) -> None:
patch("homeassistant.components.onvif.ONVIFDevice") as mock_device,
):
setup_mock_onvif_camera(mock_onvif_camera, two_profiles=True, auth_fail=True)
# no discovery
mock_discovery.return_value = []
setup_mock_discovery(mock_discovery, no_devices=True)
setup_mock_device(mock_device)
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input={"auto": False},
result = await hass.config_entries.flow.async_init(
config_flow.DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] is FlowResultType.FORM
@@ -842,13 +779,6 @@ async def test_flow_manual_entry_updates_existing_user_password(
"""Test that the existing username and password can be updated via manual entry."""
entry, _, _ = await setup_onvif_integration(hass)
result = await hass.config_entries.flow.async_init(
config_flow.DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "user"
with (
patch(
"homeassistant.components.onvif.config_flow.get_device"
@@ -859,14 +789,13 @@ async def test_flow_manual_entry_updates_existing_user_password(
patch("homeassistant.components.onvif.ONVIFDevice") as mock_device,
):
setup_mock_onvif_camera(mock_onvif_camera, two_profiles=True)
# no discovery
mock_discovery.return_value = []
setup_mock_discovery(mock_discovery, no_devices=True)
setup_mock_device(mock_device)
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input={"auto": False},
result = await hass.config_entries.flow.async_init(
config_flow.DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "configure"
@@ -895,13 +824,6 @@ async def test_flow_manual_entry_updates_existing_user_password(
async def test_flow_manual_entry_wrong_port(hass: HomeAssistant) -> None:
"""Test that we get a useful error with the wrong port."""
result = await hass.config_entries.flow.async_init(
config_flow.DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "user"
with (
patch(
"homeassistant.components.onvif.config_flow.get_device"
@@ -912,13 +834,11 @@ async def test_flow_manual_entry_wrong_port(hass: HomeAssistant) -> None:
patch("homeassistant.components.onvif.ONVIFDevice") as mock_device,
):
setup_mock_onvif_camera(mock_onvif_camera, wrong_port=True)
# no discovery
mock_discovery.return_value = []
setup_mock_discovery(mock_discovery, no_devices=True)
setup_mock_device(mock_device)
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input={"auto": False},
result = await hass.config_entries.flow.async_init(
config_flow.DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] is FlowResultType.FORM