mirror of
https://github.com/home-assistant/core.git
synced 2026-08-24 10:13:52 -05:00
Improve unifi options flow UX and remove advanced mode dependency (#170501)
This commit is contained in:
@@ -30,6 +30,7 @@ from homeassistant.const import (
|
||||
CONF_VERIFY_SSL,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.data_entry_flow import SectionConfig, section
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers.device_registry import format_mac
|
||||
from homeassistant.helpers.typing import DiscoveryInfoType
|
||||
@@ -43,6 +44,7 @@ from .const import (
|
||||
CONF_DETECTION_TIME,
|
||||
CONF_DPI_RESTRICTIONS,
|
||||
CONF_IGNORE_WIRED_BUG,
|
||||
CONF_MORE_OPTIONS,
|
||||
CONF_SITE_ID,
|
||||
CONF_SSID_FILTER,
|
||||
CONF_TRACK_CLIENTS,
|
||||
@@ -253,53 +255,23 @@ class UnifiOptionsFlowHandler(OptionsFlow):
|
||||
self.hub = self.config_entry.runtime_data
|
||||
self.options[CONF_BLOCK_CLIENT] = self.hub.config.option_block_clients
|
||||
|
||||
if self.show_advanced_options:
|
||||
return await self.async_step_configure_entity_sources()
|
||||
|
||||
return await self.async_step_simple_options()
|
||||
|
||||
async def async_step_simple_options(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
"""For users without advanced settings enabled."""
|
||||
if user_input is not None:
|
||||
more_options = user_input.pop(CONF_MORE_OPTIONS, {})
|
||||
user_input.update(more_options)
|
||||
self.options.update(user_input)
|
||||
return await self._update_options()
|
||||
return self.async_create_entry(title="", data=self.options)
|
||||
|
||||
clients_to_block = {}
|
||||
|
||||
for client in self.hub.api.clients.values():
|
||||
clients_to_block[client.mac] = (
|
||||
f"{client.name or client.hostname} ({client.mac})"
|
||||
)
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="simple_options",
|
||||
data_schema=vol.Schema(
|
||||
{
|
||||
vol.Optional(
|
||||
CONF_TRACK_CLIENTS,
|
||||
default=self.hub.config.option_track_clients,
|
||||
): bool,
|
||||
vol.Optional(
|
||||
CONF_TRACK_DEVICES,
|
||||
default=self.hub.config.option_track_devices,
|
||||
): bool,
|
||||
vol.Optional(
|
||||
CONF_BLOCK_CLIENT, default=self.options[CONF_BLOCK_CLIENT]
|
||||
): cv.multi_select(clients_to_block),
|
||||
}
|
||||
),
|
||||
last_step=True,
|
||||
)
|
||||
|
||||
async def async_step_configure_entity_sources(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
"""Select sources for entities."""
|
||||
if user_input is not None:
|
||||
self.options.update(user_input)
|
||||
return await self.async_step_device_tracker()
|
||||
selected_clients_to_block = [
|
||||
client
|
||||
for client in self.options.get(CONF_BLOCK_CLIENT, [])
|
||||
if client in clients_to_block
|
||||
]
|
||||
|
||||
clients = {
|
||||
client.mac: f"{client.name or client.hostname} ({client.mac})"
|
||||
@@ -311,29 +283,6 @@ class UnifiOptionsFlowHandler(OptionsFlow):
|
||||
if mac not in clients
|
||||
}
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="configure_entity_sources",
|
||||
data_schema=vol.Schema(
|
||||
{
|
||||
vol.Optional(
|
||||
CONF_CLIENT_SOURCE,
|
||||
default=self.options.get(CONF_CLIENT_SOURCE, []),
|
||||
): cv.multi_select(
|
||||
dict(sorted(clients.items(), key=operator.itemgetter(1)))
|
||||
),
|
||||
}
|
||||
),
|
||||
last_step=False,
|
||||
)
|
||||
|
||||
async def async_step_device_tracker(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
"""Manage the device tracker options."""
|
||||
if user_input is not None:
|
||||
self.options.update(user_input)
|
||||
return await self.async_step_client_control()
|
||||
|
||||
ssids = (
|
||||
{wlan.name for wlan in self.hub.api.wlans.values()}
|
||||
| {
|
||||
@@ -356,107 +305,76 @@ class UnifiOptionsFlowHandler(OptionsFlow):
|
||||
]
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="device_tracker",
|
||||
step_id="init",
|
||||
data_schema=vol.Schema(
|
||||
{
|
||||
vol.Optional(
|
||||
CONF_TRACK_CLIENTS,
|
||||
default=self.hub.config.option_track_clients,
|
||||
): bool,
|
||||
vol.Optional(
|
||||
CONF_TRACK_WIRED_CLIENTS,
|
||||
default=self.hub.config.option_track_wired_clients,
|
||||
): bool,
|
||||
vol.Optional(
|
||||
CONF_TRACK_DEVICES,
|
||||
default=self.hub.config.option_track_devices,
|
||||
): bool,
|
||||
vol.Optional(
|
||||
CONF_SSID_FILTER, default=selected_ssids_to_filter
|
||||
): cv.multi_select(ssid_filter),
|
||||
vol.Optional(
|
||||
CONF_DETECTION_TIME,
|
||||
default=int(
|
||||
self.hub.config.option_detection_time.total_seconds()
|
||||
),
|
||||
): int,
|
||||
vol.Optional(
|
||||
CONF_IGNORE_WIRED_BUG,
|
||||
default=self.hub.config.option_ignore_wired_bug,
|
||||
): bool,
|
||||
}
|
||||
),
|
||||
last_step=False,
|
||||
)
|
||||
|
||||
async def async_step_client_control(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
"""Manage configuration of network access controlled clients."""
|
||||
if user_input is not None:
|
||||
self.options.update(user_input)
|
||||
return await self.async_step_statistics_sensors()
|
||||
|
||||
clients_to_block = {}
|
||||
|
||||
for client in self.hub.api.clients.values():
|
||||
clients_to_block[client.mac] = (
|
||||
f"{client.name or client.hostname} ({client.mac})"
|
||||
)
|
||||
|
||||
selected_clients_to_block = [
|
||||
client
|
||||
for client in self.options.get(CONF_BLOCK_CLIENT, [])
|
||||
if client in clients_to_block
|
||||
]
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="client_control",
|
||||
data_schema=vol.Schema(
|
||||
{
|
||||
vol.Optional(
|
||||
CONF_BLOCK_CLIENT, default=selected_clients_to_block
|
||||
): cv.multi_select(clients_to_block),
|
||||
vol.Optional(
|
||||
CONF_DPI_RESTRICTIONS,
|
||||
default=self.options.get(
|
||||
CONF_DPI_RESTRICTIONS, DEFAULT_DPI_RESTRICTIONS
|
||||
vol.Required(CONF_MORE_OPTIONS): section(
|
||||
vol.Schema(
|
||||
{
|
||||
vol.Optional(
|
||||
CONF_CLIENT_SOURCE,
|
||||
default=self.options.get(CONF_CLIENT_SOURCE, []),
|
||||
): cv.multi_select(
|
||||
dict(
|
||||
sorted(
|
||||
clients.items(),
|
||||
key=operator.itemgetter(1),
|
||||
)
|
||||
)
|
||||
),
|
||||
vol.Optional(
|
||||
CONF_TRACK_WIRED_CLIENTS,
|
||||
default=self.hub.config.option_track_wired_clients,
|
||||
): bool,
|
||||
vol.Optional(
|
||||
CONF_SSID_FILTER,
|
||||
default=selected_ssids_to_filter,
|
||||
): cv.multi_select(ssid_filter),
|
||||
vol.Optional(
|
||||
CONF_DETECTION_TIME,
|
||||
default=int(
|
||||
self.hub.config.option_detection_time.total_seconds()
|
||||
),
|
||||
): int,
|
||||
vol.Optional(
|
||||
CONF_IGNORE_WIRED_BUG,
|
||||
default=self.hub.config.option_ignore_wired_bug,
|
||||
): bool,
|
||||
vol.Optional(
|
||||
CONF_DPI_RESTRICTIONS,
|
||||
default=self.options.get(
|
||||
CONF_DPI_RESTRICTIONS,
|
||||
DEFAULT_DPI_RESTRICTIONS,
|
||||
),
|
||||
): bool,
|
||||
vol.Optional(
|
||||
CONF_ALLOW_BANDWIDTH_SENSORS,
|
||||
default=self.hub.config.option_allow_bandwidth_sensors,
|
||||
): bool,
|
||||
vol.Optional(
|
||||
CONF_ALLOW_UPTIME_SENSORS,
|
||||
default=self.hub.config.option_allow_uptime_sensors,
|
||||
): bool,
|
||||
}
|
||||
),
|
||||
): bool,
|
||||
}
|
||||
),
|
||||
last_step=False,
|
||||
)
|
||||
|
||||
async def async_step_statistics_sensors(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
"""Manage the statistics sensors options."""
|
||||
if user_input is not None:
|
||||
self.options.update(user_input)
|
||||
return await self._update_options()
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="statistics_sensors",
|
||||
data_schema=vol.Schema(
|
||||
{
|
||||
vol.Optional(
|
||||
CONF_ALLOW_BANDWIDTH_SENSORS,
|
||||
default=self.hub.config.option_allow_bandwidth_sensors,
|
||||
): bool,
|
||||
vol.Optional(
|
||||
CONF_ALLOW_UPTIME_SENSORS,
|
||||
default=self.hub.config.option_allow_uptime_sensors,
|
||||
): bool,
|
||||
SectionConfig(collapsed=True),
|
||||
),
|
||||
}
|
||||
),
|
||||
last_step=True,
|
||||
)
|
||||
|
||||
async def _update_options(self) -> ConfigFlowResult:
|
||||
"""Update config entry options."""
|
||||
return self.async_create_entry(title="", data=self.options)
|
||||
|
||||
|
||||
async def _async_discover_unifi(hass: HomeAssistant) -> str | None:
|
||||
"""Discover UniFi Network address."""
|
||||
|
||||
@@ -33,6 +33,7 @@ CONF_IGNORE_WIRED_BUG = "ignore_wired_bug"
|
||||
CONF_TRACK_CLIENTS = "track_clients"
|
||||
CONF_TRACK_DEVICES = "track_devices"
|
||||
CONF_TRACK_WIRED_CLIENTS = "track_wired_clients"
|
||||
CONF_MORE_OPTIONS = "more_options"
|
||||
CONF_SSID_FILTER = "ssid_filter"
|
||||
|
||||
DEFAULT_ALLOW_BANDWIDTH_SENSORS = False
|
||||
|
||||
@@ -164,74 +164,42 @@
|
||||
"integration_not_setup": "UniFi integration is not set up"
|
||||
},
|
||||
"step": {
|
||||
"client_control": {
|
||||
"init": {
|
||||
"data": {
|
||||
"block_client": "Network access controlled clients",
|
||||
"dpi_restrictions": "Allow control of DPI restriction groups",
|
||||
"poe_clients": "Allow PoE control of clients"
|
||||
"track_clients": "Track network clients",
|
||||
"track_devices": "Track network devices (Ubiquiti devices)"
|
||||
},
|
||||
"data_description": {
|
||||
"block_client": "Select clients whose network access you want to control via switches.",
|
||||
"dpi_restrictions": "Enable switches to control DPI restriction groups.",
|
||||
"poe_clients": "Enable switches to control PoE power for clients."
|
||||
},
|
||||
"description": "Configure client controls\n\nCreate switches for serial numbers you want to control network access for.",
|
||||
"title": "UniFi Network options 2/3"
|
||||
},
|
||||
"configure_entity_sources": {
|
||||
"data": {
|
||||
"client_source": "Create entities from network clients"
|
||||
},
|
||||
"data_description": {
|
||||
"client_source": "Select which network clients to create entities from."
|
||||
},
|
||||
"description": "Select sources to create entities from",
|
||||
"title": "UniFi Network Entity Sources"
|
||||
},
|
||||
"device_tracker": {
|
||||
"data": {
|
||||
"detection_time": "Time in seconds from last seen until considered away",
|
||||
"ignore_wired_bug": "Disable UniFi Network wired bug logic",
|
||||
"ssid_filter": "Select SSIDs to track wireless clients on",
|
||||
"track_clients": "Track network clients",
|
||||
"track_devices": "Track network devices (Ubiquiti devices)",
|
||||
"track_wired_clients": "Include wired network clients"
|
||||
},
|
||||
"data_description": {
|
||||
"detection_time": "Number of seconds since last seen before a client is considered away.",
|
||||
"ignore_wired_bug": "Disable workaround for a UniFi Network bug that sometimes reports wired clients as wireless.",
|
||||
"ssid_filter": "Only track wireless clients connected to selected SSIDs.",
|
||||
"track_clients": "Create device tracker entities for network clients.",
|
||||
"track_devices": "Create device tracker entities for Ubiquiti network devices.",
|
||||
"track_wired_clients": "Include wired clients in device tracking."
|
||||
"track_devices": "Create device tracker entities for Ubiquiti network devices."
|
||||
},
|
||||
"description": "Configure device tracking",
|
||||
"title": "UniFi Network options 1/3"
|
||||
},
|
||||
"simple_options": {
|
||||
"data": {
|
||||
"block_client": "[%key:component::unifi::options::step::client_control::data::block_client%]",
|
||||
"track_clients": "[%key:component::unifi::options::step::device_tracker::data::track_clients%]",
|
||||
"track_devices": "[%key:component::unifi::options::step::device_tracker::data::track_devices%]"
|
||||
},
|
||||
"data_description": {
|
||||
"block_client": "[%key:component::unifi::options::step::client_control::data_description::block_client%]",
|
||||
"track_clients": "[%key:component::unifi::options::step::device_tracker::data_description::track_clients%]",
|
||||
"track_devices": "[%key:component::unifi::options::step::device_tracker::data_description::track_devices%]"
|
||||
},
|
||||
"description": "Configure UniFi Network integration"
|
||||
},
|
||||
"statistics_sensors": {
|
||||
"data": {
|
||||
"allow_bandwidth_sensors": "Bandwidth usage sensors for network clients",
|
||||
"allow_uptime_sensors": "Uptime sensors for network clients"
|
||||
},
|
||||
"data_description": {
|
||||
"allow_bandwidth_sensors": "Create bandwidth usage sensors for network clients.",
|
||||
"allow_uptime_sensors": "Create uptime sensors for network clients."
|
||||
},
|
||||
"description": "Configure statistics sensors",
|
||||
"title": "UniFi Network options 3/3"
|
||||
"sections": {
|
||||
"more_options": {
|
||||
"data": {
|
||||
"allow_bandwidth_sensors": "Bandwidth usage sensors for network clients",
|
||||
"allow_uptime_sensors": "Uptime sensors for network clients",
|
||||
"client_source": "Create entities from network clients",
|
||||
"detection_time": "Time in seconds from last seen until considered away",
|
||||
"dpi_restrictions": "Allow control of DPI restriction groups",
|
||||
"ignore_wired_bug": "Disable UniFi Network wired bug logic",
|
||||
"ssid_filter": "Select SSIDs to track wireless clients on",
|
||||
"track_wired_clients": "Include wired network clients"
|
||||
},
|
||||
"data_description": {
|
||||
"allow_bandwidth_sensors": "Create bandwidth usage sensors for network clients.",
|
||||
"allow_uptime_sensors": "Create uptime sensors for network clients.",
|
||||
"client_source": "Select which network clients to create entities from.",
|
||||
"detection_time": "Number of seconds since last seen before a client is considered away.",
|
||||
"dpi_restrictions": "Enable switches to control DPI restriction groups.",
|
||||
"ignore_wired_bug": "Disable workaround for a UniFi Network bug that sometimes reports wired clients as wireless.",
|
||||
"ssid_filter": "Only track wireless clients connected to selected SSIDs.",
|
||||
"track_wired_clients": "Include wired clients in device tracking."
|
||||
},
|
||||
"name": "More options"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -15,6 +15,7 @@ from homeassistant.components.unifi.const import (
|
||||
CONF_DETECTION_TIME,
|
||||
CONF_DPI_RESTRICTIONS,
|
||||
CONF_IGNORE_WIRED_BUG,
|
||||
CONF_MORE_OPTIONS,
|
||||
CONF_SITE_ID,
|
||||
CONF_SSID_FILTER,
|
||||
CONF_TRACK_CLIENTS,
|
||||
@@ -377,70 +378,33 @@ async def test_reauth_flow_update_configuration_on_not_loaded_entry(
|
||||
@pytest.mark.parametrize("device_payload", [DEVICES])
|
||||
@pytest.mark.parametrize("wlan_payload", [WLANS])
|
||||
@pytest.mark.parametrize("dpi_group_payload", [DPI_GROUPS])
|
||||
async def test_advanced_option_flow(
|
||||
async def test_option_flow(
|
||||
hass: HomeAssistant, config_entry_setup: MockConfigEntry
|
||||
) -> None:
|
||||
"""Test advanced config flow options."""
|
||||
"""Test config flow options."""
|
||||
config_entry = config_entry_setup
|
||||
|
||||
result = await hass.config_entries.options.async_init(
|
||||
config_entry.entry_id, context={"show_advanced_options": True}
|
||||
)
|
||||
result = await hass.config_entries.options.async_init(config_entry.entry_id)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "configure_entity_sources"
|
||||
assert not result["last_step"]
|
||||
assert list(result["data_schema"].schema[CONF_CLIENT_SOURCE].options.keys()) == [
|
||||
"00:00:00:00:00:01"
|
||||
]
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={CONF_CLIENT_SOURCE: ["00:00:00:00:00:01"]},
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "device_tracker"
|
||||
assert not result["last_step"]
|
||||
assert list(result["data_schema"].schema[CONF_SSID_FILTER].options.keys()) == [
|
||||
"",
|
||||
"SSID 1",
|
||||
"SSID 2",
|
||||
"SSID 2_IOT",
|
||||
"SSID 3",
|
||||
"SSID 4",
|
||||
]
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={
|
||||
CONF_TRACK_CLIENTS: False,
|
||||
CONF_TRACK_WIRED_CLIENTS: False,
|
||||
CONF_TRACK_DEVICES: False,
|
||||
CONF_SSID_FILTER: ["SSID 1", "SSID 2_IOT", "SSID 3", "SSID 4"],
|
||||
CONF_DETECTION_TIME: 100,
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "client_control"
|
||||
assert not result["last_step"]
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={
|
||||
CONF_BLOCK_CLIENT: [CLIENTS[0]["mac"]],
|
||||
CONF_DPI_RESTRICTIONS: False,
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "statistics_sensors"
|
||||
assert result["step_id"] == "init"
|
||||
assert result["last_step"]
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={
|
||||
CONF_ALLOW_BANDWIDTH_SENSORS: True,
|
||||
CONF_ALLOW_UPTIME_SENSORS: True,
|
||||
CONF_TRACK_CLIENTS: False,
|
||||
CONF_TRACK_DEVICES: False,
|
||||
CONF_BLOCK_CLIENT: [CLIENTS[0]["mac"]],
|
||||
CONF_MORE_OPTIONS: {
|
||||
CONF_CLIENT_SOURCE: ["00:00:00:00:00:01"],
|
||||
CONF_TRACK_WIRED_CLIENTS: False,
|
||||
CONF_SSID_FILTER: ["SSID 1", "SSID 2_IOT", "SSID 3", "SSID 4"],
|
||||
CONF_DETECTION_TIME: 100,
|
||||
CONF_DPI_RESTRICTIONS: False,
|
||||
CONF_ALLOW_BANDWIDTH_SENSORS: True,
|
||||
CONF_ALLOW_UPTIME_SENSORS: True,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
@@ -460,44 +424,6 @@ async def test_advanced_option_flow(
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("client_payload", [CLIENTS])
|
||||
async def test_simple_option_flow(
|
||||
hass: HomeAssistant, config_entry_setup: MockConfigEntry
|
||||
) -> None:
|
||||
"""Test simple config flow options."""
|
||||
config_entry = config_entry_setup
|
||||
|
||||
result = await hass.config_entries.options.async_init(
|
||||
config_entry.entry_id, context={"show_advanced_options": False}
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "simple_options"
|
||||
assert result["last_step"]
|
||||
|
||||
with patch(
|
||||
"homeassistant.config_entries.ConfigEntries.async_schedule_reload"
|
||||
) as mock_reload:
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={
|
||||
CONF_TRACK_CLIENTS: False,
|
||||
CONF_TRACK_DEVICES: False,
|
||||
CONF_BLOCK_CLIENT: [CLIENTS[0]["mac"]],
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result["data"] == {
|
||||
CONF_TRACK_CLIENTS: False,
|
||||
CONF_TRACK_DEVICES: False,
|
||||
CONF_BLOCK_CLIENT: [CLIENTS[0]["mac"]],
|
||||
}
|
||||
# Check there is no reload on updating these options
|
||||
assert mock_reload.call_count == 0
|
||||
|
||||
|
||||
async def test_discover_unifi_positive(hass: HomeAssistant) -> None:
|
||||
"""Verify positive run of UniFi discovery."""
|
||||
with patch("socket.gethostbyname", return_value="192.168.1.1"):
|
||||
|
||||
Reference in New Issue
Block a user