mirror of
https://github.com/home-assistant/core.git
synced 2025-12-24 21:06:19 +00:00
Combine Shelly BLE WiFi provisioning SSID and password steps (#157199)
This commit is contained in:
@@ -708,7 +708,8 @@ class ShellyConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
"""Scan for WiFi networks via BLE."""
|
||||
if user_input is not None:
|
||||
self.selected_ssid = user_input[CONF_SSID]
|
||||
return await self.async_step_wifi_credentials()
|
||||
password = user_input[CONF_PASSWORD]
|
||||
return await self.async_step_do_provision({"password": password})
|
||||
|
||||
# Scan for WiFi networks via BLE
|
||||
if TYPE_CHECKING:
|
||||
@@ -726,22 +727,34 @@ class ShellyConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
LOGGER.exception("Unexpected exception during WiFi scan")
|
||||
return self.async_abort(reason="unknown")
|
||||
|
||||
# Create list of SSIDs for selection
|
||||
# If no networks found, still allow custom SSID entry
|
||||
ssid_options = [network["ssid"] for network in self.wifi_networks]
|
||||
# Sort by RSSI (strongest signal first - higher/less negative values first)
|
||||
# and create list of SSIDs for selection
|
||||
sorted_networks = sorted(
|
||||
self.wifi_networks, key=lambda n: n["rssi"], reverse=True
|
||||
)
|
||||
ssid_options = [network["ssid"] for network in sorted_networks]
|
||||
|
||||
# Pre-select SSID if returning from failed provisioning attempt
|
||||
suggested_values: dict[str, Any] = {}
|
||||
if self.selected_ssid:
|
||||
suggested_values[CONF_SSID] = self.selected_ssid
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="wifi_scan",
|
||||
data_schema=vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_SSID): SelectSelector(
|
||||
SelectSelectorConfig(
|
||||
options=ssid_options,
|
||||
mode=SelectSelectorMode.DROPDOWN,
|
||||
custom_value=True,
|
||||
)
|
||||
),
|
||||
}
|
||||
data_schema=self.add_suggested_values_to_schema(
|
||||
vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_SSID): SelectSelector(
|
||||
SelectSelectorConfig(
|
||||
options=ssid_options,
|
||||
mode=SelectSelectorMode.DROPDOWN,
|
||||
custom_value=True,
|
||||
)
|
||||
),
|
||||
vol.Required(CONF_PASSWORD): str,
|
||||
}
|
||||
),
|
||||
suggested_values,
|
||||
),
|
||||
)
|
||||
|
||||
@@ -769,25 +782,6 @@ class ShellyConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
finally:
|
||||
provisioning_registry.pop(normalized_mac, None)
|
||||
|
||||
async def async_step_wifi_credentials(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
"""Get WiFi credentials and provision device."""
|
||||
if user_input is not None:
|
||||
self.selected_ssid = user_input.get(CONF_SSID, self.selected_ssid)
|
||||
password = user_input[CONF_PASSWORD]
|
||||
return await self.async_step_do_provision({"password": password})
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="wifi_credentials",
|
||||
data_schema=vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_PASSWORD): str,
|
||||
}
|
||||
),
|
||||
description_placeholders={"ssid": self.selected_ssid},
|
||||
)
|
||||
|
||||
async def _async_secure_device_after_provision(self, host: str, port: int) -> None:
|
||||
"""Disable AP and/or BLE RPC after successful WiFi provisioning.
|
||||
|
||||
@@ -1010,8 +1004,7 @@ class ShellyConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
) -> ConfigFlowResult:
|
||||
"""Handle failed provisioning - allow retry."""
|
||||
if user_input is not None:
|
||||
# User wants to retry - clear state and go back to wifi_scan
|
||||
self.selected_ssid = ""
|
||||
# User wants to retry - keep selected_ssid so it's pre-selected
|
||||
self.wifi_networks = []
|
||||
return await self.async_step_wifi_scan()
|
||||
|
||||
|
||||
@@ -100,23 +100,16 @@
|
||||
},
|
||||
"description": "Before setup, battery-powered devices must be woken up, you can now wake the device up using a button on it."
|
||||
},
|
||||
"wifi_credentials": {
|
||||
"data": {
|
||||
"password": "[%key:common::config_flow::data::password%]"
|
||||
},
|
||||
"data_description": {
|
||||
"password": "Password for the WiFi network."
|
||||
},
|
||||
"description": "Enter the password for {ssid}."
|
||||
},
|
||||
"wifi_scan": {
|
||||
"data": {
|
||||
"password": "[%key:common::config_flow::data::password%]",
|
||||
"ssid": "WiFi network"
|
||||
},
|
||||
"data_description": {
|
||||
"password": "Password for the WiFi network.",
|
||||
"ssid": "Select a WiFi network from the list or enter a custom SSID for hidden networks."
|
||||
},
|
||||
"description": "Select a WiFi network from the list or enter a custom SSID for hidden networks."
|
||||
"description": "Select a WiFi network and enter the password to provision the device."
|
||||
},
|
||||
"wifi_scan_failed": {
|
||||
"description": "Failed to scan for WiFi networks via Bluetooth. The device may be out of range or Bluetooth connection failed. Would you like to try again?"
|
||||
|
||||
@@ -1172,13 +1172,7 @@ async def test_user_flow_with_ble_devices(
|
||||
{},
|
||||
)
|
||||
|
||||
# Select network
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_SSID: "TestNetwork"},
|
||||
)
|
||||
|
||||
# Enter WiFi credentials and complete
|
||||
# Select network and enter WiFi credentials to complete
|
||||
with (
|
||||
patch("homeassistant.components.shelly.config_flow.async_provision_wifi"),
|
||||
patch(
|
||||
@@ -1201,7 +1195,7 @@ async def test_user_flow_with_ble_devices(
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_PASSWORD: "test_password"},
|
||||
{CONF_SSID: "TestNetwork", CONF_PASSWORD: "test_password"},
|
||||
)
|
||||
|
||||
await hass.async_block_till_done()
|
||||
@@ -1854,16 +1848,10 @@ async def test_user_flow_select_ble_device(
|
||||
{},
|
||||
)
|
||||
|
||||
# Select network
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_SSID: "MyNetwork"},
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "wifi_credentials"
|
||||
assert result["step_id"] == "wifi_scan"
|
||||
|
||||
# Enter password and provision
|
||||
# Select network and enter password to provision
|
||||
with (
|
||||
patch("homeassistant.components.shelly.config_flow.async_provision_wifi"),
|
||||
patch(
|
||||
@@ -1886,7 +1874,7 @@ async def test_user_flow_select_ble_device(
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_PASSWORD: "wifi_password"},
|
||||
{CONF_SSID: "MyNetwork", CONF_PASSWORD: "wifi_password"},
|
||||
)
|
||||
|
||||
# Should show progress
|
||||
@@ -3180,13 +3168,7 @@ async def test_bluetooth_discovery(
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
||||
|
||||
# Select network
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_SSID: "MyNetwork"},
|
||||
)
|
||||
|
||||
# Enter password and provision
|
||||
# Select network and enter password to provision
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.shelly.config_flow.async_provision_wifi",
|
||||
@@ -3202,7 +3184,7 @@ async def test_bluetooth_discovery(
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_PASSWORD: "my_password"},
|
||||
{CONF_SSID: "MyNetwork", CONF_PASSWORD: "my_password"},
|
||||
)
|
||||
|
||||
# Provisioning happens in background, shows progress
|
||||
@@ -3257,16 +3239,10 @@ async def test_bluetooth_provisioning_clears_match_history(
|
||||
result["flow_id"], {}
|
||||
)
|
||||
|
||||
# Select network
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_SSID: "MyNetwork"},
|
||||
)
|
||||
|
||||
# Reset mock to only count calls during provisioning
|
||||
mock_clear.reset_mock()
|
||||
|
||||
# Enter password and provision
|
||||
# Select network and enter password to provision
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.shelly.config_flow.async_provision_wifi",
|
||||
@@ -3282,7 +3258,7 @@ async def test_bluetooth_provisioning_clears_match_history(
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_PASSWORD: "my_password"},
|
||||
{CONF_SSID: "MyNetwork", CONF_PASSWORD: "my_password"},
|
||||
)
|
||||
|
||||
# Provisioning happens in background, shows progress
|
||||
@@ -3368,13 +3344,7 @@ async def test_bluetooth_factory_reset_rediscovery(
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
||||
|
||||
# Select network
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_SSID: "MyNetwork"},
|
||||
)
|
||||
|
||||
# Enter password and provision
|
||||
# Select network and enter password to provision
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.shelly.config_flow.async_provision_wifi",
|
||||
@@ -3390,7 +3360,7 @@ async def test_bluetooth_factory_reset_rediscovery(
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_PASSWORD: "my_password"},
|
||||
{CONF_SSID: "MyNetwork", CONF_PASSWORD: "my_password"},
|
||||
)
|
||||
|
||||
# Provisioning happens in background
|
||||
@@ -3581,12 +3551,7 @@ async def test_bluetooth_wifi_scan_success(
|
||||
# Check that SSIDs are in the selector options
|
||||
assert "data_schema" in result
|
||||
|
||||
# Select network and complete flow
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_SSID: "Network1"},
|
||||
)
|
||||
|
||||
# Select network and enter password to complete flow
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.shelly.config_flow.async_provision_wifi",
|
||||
@@ -3602,7 +3567,7 @@ async def test_bluetooth_wifi_scan_success(
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_PASSWORD: "my_password"},
|
||||
{CONF_SSID: "Network1", CONF_PASSWORD: "my_password"},
|
||||
)
|
||||
|
||||
# Provisioning shows progress
|
||||
@@ -3668,13 +3633,7 @@ async def test_bluetooth_wifi_scan_failure(
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "wifi_scan"
|
||||
|
||||
# Select network
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_SSID: "Network1"},
|
||||
)
|
||||
|
||||
# Complete provisioning
|
||||
# Select network and enter password to complete provisioning
|
||||
with (
|
||||
patch("homeassistant.components.shelly.config_flow.async_provision_wifi"),
|
||||
patch(
|
||||
@@ -3692,7 +3651,7 @@ async def test_bluetooth_wifi_scan_failure(
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_PASSWORD: "my_password"},
|
||||
{CONF_SSID: "Network1", CONF_PASSWORD: "my_password"},
|
||||
)
|
||||
|
||||
# Provisioning shows progress
|
||||
@@ -3772,16 +3731,10 @@ async def test_bluetooth_wifi_credentials_and_provision_success(
|
||||
{},
|
||||
)
|
||||
|
||||
# Select network
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_SSID: "MyNetwork"},
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "wifi_credentials"
|
||||
assert result["step_id"] == "wifi_scan"
|
||||
|
||||
# Enter password and provision
|
||||
# Select network and enter password to provision
|
||||
mock_device = create_mock_rpc_device("Test name")
|
||||
|
||||
with (
|
||||
@@ -3803,7 +3756,7 @@ async def test_bluetooth_wifi_credentials_and_provision_success(
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_PASSWORD: "my_password"},
|
||||
{CONF_SSID: "MyNetwork", CONF_PASSWORD: "my_password"},
|
||||
)
|
||||
|
||||
# Should show progress
|
||||
@@ -3854,12 +3807,6 @@ async def test_bluetooth_wifi_provision_failure(
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
||||
|
||||
# Select network
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_SSID: "MyNetwork"},
|
||||
)
|
||||
|
||||
# Provision fails
|
||||
with (
|
||||
patch(
|
||||
@@ -3873,7 +3820,7 @@ async def test_bluetooth_wifi_provision_failure(
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_PASSWORD: "my_password"},
|
||||
{CONF_SSID: "MyNetwork", CONF_PASSWORD: "my_password"},
|
||||
)
|
||||
|
||||
# Provisioning shows progress
|
||||
@@ -3896,12 +3843,6 @@ async def test_bluetooth_wifi_provision_failure(
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "wifi_scan"
|
||||
|
||||
# Select network again
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_SSID: "MyNetwork"},
|
||||
)
|
||||
|
||||
# Provision succeeds this time
|
||||
with (
|
||||
patch(
|
||||
@@ -3918,7 +3859,7 @@ async def test_bluetooth_wifi_provision_failure(
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_PASSWORD: "my_password"},
|
||||
{CONF_SSID: "MyNetwork", CONF_PASSWORD: "my_password"},
|
||||
)
|
||||
|
||||
# Provisioning shows progress
|
||||
@@ -3990,12 +3931,6 @@ async def test_bluetooth_provision_unexpected_exception(
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
||||
|
||||
# Select network
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_SSID: "MyNetwork"},
|
||||
)
|
||||
|
||||
# Provision raises unexpected exception in background task
|
||||
with (
|
||||
patch(
|
||||
@@ -4009,7 +3944,7 @@ async def test_bluetooth_provision_unexpected_exception(
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_PASSWORD: "my_password"},
|
||||
{CONF_SSID: "MyNetwork", CONF_PASSWORD: "my_password"},
|
||||
)
|
||||
|
||||
# Provisioning shows progress
|
||||
@@ -4044,12 +3979,6 @@ async def test_bluetooth_provision_device_connection_error_after_wifi(
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
||||
|
||||
# Select network
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_SSID: "MyNetwork"},
|
||||
)
|
||||
|
||||
# Provision but get_info fails
|
||||
with (
|
||||
patch("homeassistant.components.shelly.config_flow.async_provision_wifi"),
|
||||
@@ -4064,7 +3993,7 @@ async def test_bluetooth_provision_device_connection_error_after_wifi(
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_PASSWORD: "my_password"},
|
||||
{CONF_SSID: "MyNetwork", CONF_PASSWORD: "my_password"},
|
||||
)
|
||||
|
||||
# Provisioning shows progress
|
||||
@@ -4111,12 +4040,6 @@ async def test_bluetooth_provision_requires_auth(
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
||||
|
||||
# Select network
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_SSID: "MyNetwork"},
|
||||
)
|
||||
|
||||
# Provision but device requires auth
|
||||
with (
|
||||
patch("homeassistant.components.shelly.config_flow.async_provision_wifi"),
|
||||
@@ -4136,7 +4059,7 @@ async def test_bluetooth_provision_requires_auth(
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_PASSWORD: "my_password"},
|
||||
{CONF_SSID: "MyNetwork", CONF_PASSWORD: "my_password"},
|
||||
)
|
||||
|
||||
# Provisioning shows progress
|
||||
@@ -4193,12 +4116,6 @@ async def test_bluetooth_provision_validate_input_fails(
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
||||
|
||||
# Select network
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_SSID: "MyNetwork"},
|
||||
)
|
||||
|
||||
# Provision but validate_input fails
|
||||
with (
|
||||
patch("homeassistant.components.shelly.config_flow.async_provision_wifi"),
|
||||
@@ -4217,7 +4134,7 @@ async def test_bluetooth_provision_validate_input_fails(
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_PASSWORD: "my_password"},
|
||||
{CONF_SSID: "MyNetwork", CONF_PASSWORD: "my_password"},
|
||||
)
|
||||
|
||||
# Provisioning shows progress
|
||||
@@ -4262,12 +4179,6 @@ async def test_bluetooth_provision_firmware_not_fully_provisioned(
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
||||
|
||||
# Select network
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_SSID: "MyNetwork"},
|
||||
)
|
||||
|
||||
# Provision but device has no model (firmware not fully provisioned)
|
||||
with (
|
||||
patch("homeassistant.components.shelly.config_flow.async_provision_wifi"),
|
||||
@@ -4286,7 +4197,7 @@ async def test_bluetooth_provision_firmware_not_fully_provisioned(
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_PASSWORD: "my_password"},
|
||||
{CONF_SSID: "MyNetwork", CONF_PASSWORD: "my_password"},
|
||||
)
|
||||
|
||||
# Provisioning shows progress
|
||||
@@ -4323,12 +4234,6 @@ async def test_bluetooth_provision_with_zeroconf_discovery_fast_path(
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
||||
|
||||
# Select network
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_SSID: "MyNetwork"},
|
||||
)
|
||||
|
||||
# Patch async_provision_wifi to trigger zeroconf discovery
|
||||
async def mock_provision_wifi(*args, **kwargs):
|
||||
"""Mock provision that triggers zeroconf discovery."""
|
||||
@@ -4376,7 +4281,7 @@ async def test_bluetooth_provision_with_zeroconf_discovery_fast_path(
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_PASSWORD: "my_password"},
|
||||
{CONF_SSID: "MyNetwork", CONF_PASSWORD: "my_password"},
|
||||
)
|
||||
|
||||
# Provisioning shows progress
|
||||
@@ -4414,12 +4319,6 @@ async def test_bluetooth_provision_timeout_active_lookup_fails(
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
||||
|
||||
# Select network
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_SSID: "MyNetwork"},
|
||||
)
|
||||
|
||||
# Provision WiFi but no zeroconf discovery arrives, and active lookup fails
|
||||
with (
|
||||
patch(
|
||||
@@ -4434,7 +4333,7 @@ async def test_bluetooth_provision_timeout_active_lookup_fails(
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_PASSWORD: "my_password"},
|
||||
{CONF_SSID: "MyNetwork", CONF_PASSWORD: "my_password"},
|
||||
)
|
||||
|
||||
# Provisioning shows progress
|
||||
@@ -4474,19 +4373,6 @@ async def test_bluetooth_provision_timeout_ble_fallback_succeeds(
|
||||
context={"source": config_entries.SOURCE_BLUETOOTH},
|
||||
)
|
||||
|
||||
# Confirm and scan
|
||||
with patch(
|
||||
"homeassistant.components.shelly.config_flow.async_scan_wifi_networks",
|
||||
return_value=[{"ssid": "MyNetwork", "rssi": -50, "auth": 2}],
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
||||
|
||||
# Select network
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_SSID: "MyNetwork"},
|
||||
)
|
||||
|
||||
# Mock device for BLE status query
|
||||
mock_ble_status_device = AsyncMock()
|
||||
mock_ble_status_device.status = {"wifi": {"sta_ip": "192.168.1.100"}}
|
||||
@@ -4502,8 +4388,13 @@ async def test_bluetooth_provision_timeout_ble_fallback_succeeds(
|
||||
mock_device.ble_setconfig = AsyncMock(return_value={"restart_required": False})
|
||||
mock_device.shutdown = AsyncMock()
|
||||
|
||||
# Confirm and scan, then select network and enter password
|
||||
# Provision WiFi but no zeroconf discovery arrives, active lookup fails, BLE fallback succeeds
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.shelly.config_flow.async_scan_wifi_networks",
|
||||
return_value=[{"ssid": "MyNetwork", "rssi": -50, "auth": 2}],
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.shelly.config_flow.PROVISIONING_TIMEOUT",
|
||||
0.01, # Short timeout to trigger timeout path
|
||||
@@ -4528,9 +4419,13 @@ async def test_bluetooth_provision_timeout_ble_fallback_succeeds(
|
||||
# Configure BLE RPC mock to return device with IP
|
||||
mock_ble_rpc.return_value.__aenter__.return_value = mock_ble_status_device
|
||||
|
||||
# Scan for networks
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
||||
|
||||
# Select network and enter password in single step
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_PASSWORD: "my_password"},
|
||||
{CONF_SSID: "MyNetwork", CONF_PASSWORD: "my_password"},
|
||||
)
|
||||
|
||||
# Provisioning shows progress
|
||||
@@ -4560,21 +4455,13 @@ async def test_bluetooth_provision_timeout_ble_fallback_fails(
|
||||
context={"source": config_entries.SOURCE_BLUETOOTH},
|
||||
)
|
||||
|
||||
# Confirm and scan
|
||||
with patch(
|
||||
"homeassistant.components.shelly.config_flow.async_scan_wifi_networks",
|
||||
return_value=[{"ssid": "MyNetwork", "rssi": -50, "auth": 2}],
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
||||
|
||||
# Select network
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_SSID: "MyNetwork"},
|
||||
)
|
||||
|
||||
# Confirm and scan, select network and enter password
|
||||
# Provision WiFi but no zeroconf discovery, active lookup fails, BLE fallback fails
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.shelly.config_flow.async_scan_wifi_networks",
|
||||
return_value=[{"ssid": "MyNetwork", "rssi": -50, "auth": 2}],
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.shelly.config_flow.PROVISIONING_TIMEOUT",
|
||||
0.01, # Short timeout to trigger timeout path
|
||||
@@ -4589,9 +4476,13 @@ async def test_bluetooth_provision_timeout_ble_fallback_fails(
|
||||
return_value=None, # BLE fallback also fails
|
||||
),
|
||||
):
|
||||
# Scan for networks
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
||||
|
||||
# Select network and enter password in single step
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_PASSWORD: "my_password"},
|
||||
{CONF_SSID: "MyNetwork", CONF_PASSWORD: "my_password"},
|
||||
)
|
||||
|
||||
# Provisioning shows progress
|
||||
@@ -4629,21 +4520,13 @@ async def test_bluetooth_provision_timeout_ble_exception(
|
||||
context={"source": config_entries.SOURCE_BLUETOOTH},
|
||||
)
|
||||
|
||||
# Confirm and scan
|
||||
with patch(
|
||||
"homeassistant.components.shelly.config_flow.async_scan_wifi_networks",
|
||||
return_value=[{"ssid": "MyNetwork", "rssi": -50, "auth": 2}],
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
||||
|
||||
# Select network
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_SSID: "MyNetwork"},
|
||||
)
|
||||
|
||||
# Confirm and scan, select network and enter password
|
||||
# Provision WiFi but no zeroconf discovery, active lookup fails, BLE raises exception
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.shelly.config_flow.async_scan_wifi_networks",
|
||||
return_value=[{"ssid": "MyNetwork", "rssi": -50, "auth": 2}],
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.shelly.config_flow.PROVISIONING_TIMEOUT",
|
||||
0.01, # Short timeout to trigger timeout path
|
||||
@@ -4658,9 +4541,13 @@ async def test_bluetooth_provision_timeout_ble_exception(
|
||||
side_effect=DeviceConnectionError, # BLE raises exception
|
||||
),
|
||||
):
|
||||
# Scan for networks
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
||||
|
||||
# Select network and enter password in single step
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_PASSWORD: "my_password"},
|
||||
{CONF_SSID: "MyNetwork", CONF_PASSWORD: "my_password"},
|
||||
)
|
||||
|
||||
# Provisioning shows progress
|
||||
@@ -4709,12 +4596,6 @@ async def test_bluetooth_provision_secure_device_both_enabled(
|
||||
{"disable_ap": True, "disable_ble_rpc": True},
|
||||
)
|
||||
|
||||
# Select network
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_SSID: "MyNetwork"},
|
||||
)
|
||||
|
||||
# Provision and verify security calls
|
||||
mock_device = AsyncMock()
|
||||
mock_device.initialize = AsyncMock()
|
||||
@@ -4739,7 +4620,7 @@ async def test_bluetooth_provision_secure_device_both_enabled(
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_PASSWORD: "my_password"},
|
||||
{CONF_SSID: "MyNetwork", CONF_PASSWORD: "my_password"},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||
@@ -4777,12 +4658,6 @@ async def test_bluetooth_provision_secure_device_both_disabled(
|
||||
{"disable_ap": False, "disable_ble_rpc": False},
|
||||
)
|
||||
|
||||
# Select network
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_SSID: "MyNetwork"},
|
||||
)
|
||||
|
||||
# Provision - with both disabled, secure device method should not create device
|
||||
with (
|
||||
patch("homeassistant.components.shelly.config_flow.async_provision_wifi"),
|
||||
@@ -4797,7 +4672,7 @@ async def test_bluetooth_provision_secure_device_both_disabled(
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_PASSWORD: "my_password"},
|
||||
{CONF_SSID: "MyNetwork", CONF_PASSWORD: "my_password"},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||
@@ -4830,12 +4705,6 @@ async def test_bluetooth_provision_secure_device_only_ap_disabled(
|
||||
{"disable_ap": True, "disable_ble_rpc": False},
|
||||
)
|
||||
|
||||
# Select network
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_SSID: "MyNetwork"},
|
||||
)
|
||||
|
||||
# Provision and verify only AP disabled
|
||||
mock_device = AsyncMock()
|
||||
mock_device.initialize = AsyncMock()
|
||||
@@ -4859,7 +4728,7 @@ async def test_bluetooth_provision_secure_device_only_ap_disabled(
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_PASSWORD: "my_password"},
|
||||
{CONF_SSID: "MyNetwork", CONF_PASSWORD: "my_password"},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||
@@ -4896,12 +4765,6 @@ async def test_bluetooth_provision_secure_device_only_ble_disabled(
|
||||
{"disable_ap": False, "disable_ble_rpc": True},
|
||||
)
|
||||
|
||||
# Select network
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_SSID: "MyNetwork"},
|
||||
)
|
||||
|
||||
# Provision and verify only BLE disabled
|
||||
mock_device = AsyncMock()
|
||||
mock_device.initialize = AsyncMock()
|
||||
@@ -4925,7 +4788,7 @@ async def test_bluetooth_provision_secure_device_only_ble_disabled(
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_PASSWORD: "my_password"},
|
||||
{CONF_SSID: "MyNetwork", CONF_PASSWORD: "my_password"},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||
@@ -4962,12 +4825,6 @@ async def test_bluetooth_provision_secure_device_with_restart_required(
|
||||
{"disable_ap": True, "disable_ble_rpc": True},
|
||||
)
|
||||
|
||||
# Select network
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_SSID: "MyNetwork"},
|
||||
)
|
||||
|
||||
# Provision and verify restart is triggered
|
||||
mock_device = AsyncMock()
|
||||
mock_device.initialize = AsyncMock()
|
||||
@@ -4993,7 +4850,7 @@ async def test_bluetooth_provision_secure_device_with_restart_required(
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_PASSWORD: "my_password"},
|
||||
{CONF_SSID: "MyNetwork", CONF_PASSWORD: "my_password"},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||
@@ -5030,12 +4887,6 @@ async def test_bluetooth_provision_secure_device_fails_gracefully(
|
||||
{"disable_ap": True, "disable_ble_rpc": True},
|
||||
)
|
||||
|
||||
# Select network
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_SSID: "MyNetwork"},
|
||||
)
|
||||
|
||||
# Provision with security calls failing - wifi_setconfig will fail
|
||||
mock_device = AsyncMock()
|
||||
mock_device.initialize = AsyncMock()
|
||||
@@ -5059,7 +4910,7 @@ async def test_bluetooth_provision_secure_device_fails_gracefully(
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_PASSWORD: "my_password"},
|
||||
{CONF_SSID: "MyNetwork", CONF_PASSWORD: "my_password"},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||
|
||||
Reference in New Issue
Block a user