Files
core/tests/components/switchbot/test_config_flow.py

1587 lines
53 KiB
Python

"""Test the switchbot config flow."""
from collections.abc import Generator
from unittest.mock import Mock, patch
import pytest
from switchbot import SwitchbotAccountConnectionError, SwitchbotAuthenticationError
from homeassistant.components.bluetooth import BluetoothScanningMode
from homeassistant.components.switchbot.const import (
CONF_ENCRYPTION_KEY,
CONF_KEY_ID,
CONF_LOCK_NIGHTLATCH,
CONF_RETRY_COUNT,
)
from homeassistant.config_entries import SOURCE_BLUETOOTH, SOURCE_IGNORE, SOURCE_USER
from homeassistant.const import (
CONF_ADDRESS,
CONF_NAME,
CONF_PASSWORD,
CONF_SENSOR_TYPE,
CONF_USERNAME,
)
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
from . import (
NOT_SWITCHBOT_INFO,
USER_INPUT,
WOCURTAIN_SERVICE_INFO,
WOHAND_ENCRYPTED_SERVICE_INFO,
WOHAND_SERVICE_ALT_ADDRESS_INFO,
WOHAND_SERVICE_INFO,
WOHAND_SERVICE_INFO_NOT_CONNECTABLE,
WOLOCK_SERVICE_INFO,
WORELAY_SWITCH_1PM_SERVICE_INFO,
WOSENSORTH_SERVICE_INFO,
init_integration,
patch_async_setup_entry,
)
from tests.common import MockConfigEntry
DOMAIN = "switchbot"
@pytest.fixture
def mock_scanners_all_active() -> Generator[None]:
"""Mock all scanners as active mode."""
mock_scanner = Mock()
mock_scanner.current_mode = BluetoothScanningMode.ACTIVE
with patch(
"homeassistant.components.switchbot.config_flow.async_current_scanners",
return_value=[mock_scanner],
):
yield
@pytest.fixture
def mock_scanners_all_passive() -> Generator[None]:
"""Mock all scanners as passive mode."""
mock_scanner = Mock()
mock_scanner.current_mode = BluetoothScanningMode.PASSIVE
with patch(
"homeassistant.components.switchbot.config_flow.async_current_scanners",
return_value=[mock_scanner],
):
yield
async def test_bluetooth_discovery(hass: HomeAssistant) -> None:
"""Test discovery via bluetooth with a valid device."""
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_BLUETOOTH},
data=WOHAND_SERVICE_INFO,
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "confirm"
with patch_async_setup_entry() as mock_setup_entry:
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{},
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == "Bot EEFF"
assert result["data"] == {
CONF_ADDRESS: "AA:BB:CC:DD:EE:FF",
CONF_SENSOR_TYPE: "bot",
}
assert len(mock_setup_entry.mock_calls) == 1
async def test_bluetooth_discovery_requires_password(hass: HomeAssistant) -> None:
"""Test discovery via bluetooth with a valid device that needs a password."""
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_BLUETOOTH},
data=WOHAND_ENCRYPTED_SERVICE_INFO,
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "password"
with patch_async_setup_entry() as mock_setup_entry:
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{CONF_PASSWORD: "abc123"},
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == "Bot 923B"
assert result["data"] == {
CONF_ADDRESS: "798A8547-2A3D-C609-55FF-73FA824B923B",
CONF_SENSOR_TYPE: "bot",
CONF_PASSWORD: "abc123",
}
assert len(mock_setup_entry.mock_calls) == 1
async def test_bluetooth_discovery_encrypted_key(hass: HomeAssistant) -> None:
"""Test discovery via bluetooth with a lock."""
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_BLUETOOTH},
data=WOLOCK_SERVICE_INFO,
)
assert result["type"] is FlowResultType.MENU
assert result["step_id"] == "encrypted_choose_method"
result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input={"next_step_id": "encrypted_key"}
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "encrypted_key"
assert result["errors"] == {}
with patch(
"switchbot.SwitchbotLock.verify_encryption_key",
return_value=False,
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_KEY_ID: "",
CONF_ENCRYPTION_KEY: "",
},
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "encrypted_key"
assert result["errors"] == {"base": "encryption_key_invalid"}
with (
patch_async_setup_entry() as mock_setup_entry,
patch(
"switchbot.SwitchbotLock.verify_encryption_key",
return_value=True,
),
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_KEY_ID: "ff",
CONF_ENCRYPTION_KEY: "ffffffffffffffffffffffffffffffff",
},
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == "Lock EEFF"
assert result["data"] == {
CONF_ADDRESS: "aa:bb:cc:dd:ee:ff",
CONF_KEY_ID: "ff",
CONF_ENCRYPTION_KEY: "ffffffffffffffffffffffffffffffff",
CONF_SENSOR_TYPE: "lock",
}
assert len(mock_setup_entry.mock_calls) == 1
async def test_bluetooth_discovery_key(hass: HomeAssistant) -> None:
"""Test discovery via bluetooth with a encrypted device."""
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_BLUETOOTH},
data=WORELAY_SWITCH_1PM_SERVICE_INFO,
)
assert result["type"] is FlowResultType.MENU
assert result["step_id"] == "encrypted_choose_method"
result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input={"next_step_id": "encrypted_key"}
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "encrypted_key"
assert result["errors"] == {}
with (
patch_async_setup_entry() as mock_setup_entry,
patch(
"switchbot.SwitchbotRelaySwitch.verify_encryption_key", return_value=True
),
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_KEY_ID: "ff",
CONF_ENCRYPTION_KEY: "ffffffffffffffffffffffffffffffff",
},
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == "Relay Switch 1PM EEFF"
assert result["data"] == {
CONF_ADDRESS: "AA:BB:CC:DD:EE:FF",
CONF_KEY_ID: "ff",
CONF_ENCRYPTION_KEY: "ffffffffffffffffffffffffffffffff",
CONF_SENSOR_TYPE: "relay_switch_1pm",
}
assert len(mock_setup_entry.mock_calls) == 1
async def test_bluetooth_discovery_already_setup(hass: HomeAssistant) -> None:
"""Test discovery via bluetooth with a valid device when already setup."""
entry = MockConfigEntry(
domain=DOMAIN,
data={
CONF_ADDRESS: "aa:bb:cc:dd:ee:ff",
CONF_NAME: "test-name",
CONF_PASSWORD: "test-password",
CONF_SENSOR_TYPE: "bot",
},
unique_id="aabbccddeeff",
)
entry.add_to_hass(hass)
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_BLUETOOTH},
data=WOHAND_SERVICE_INFO,
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "already_configured"
async def test_async_step_bluetooth_not_switchbot(hass: HomeAssistant) -> None:
"""Test discovery via bluetooth not switchbot."""
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_BLUETOOTH},
data=NOT_SWITCHBOT_INFO,
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "not_supported"
async def test_async_step_bluetooth_not_connectable(hass: HomeAssistant) -> None:
"""Test discovery via bluetooth and its not connectable switchbot."""
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_BLUETOOTH},
data=WOHAND_SERVICE_INFO_NOT_CONNECTABLE,
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "not_supported"
@pytest.mark.usefixtures("mock_scanners_all_passive")
async def test_user_setup_wohand(hass: HomeAssistant) -> None:
"""Test the user initiated form with password and valid mac."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
assert result["type"] is FlowResultType.MENU
assert result["step_id"] == "user"
with patch(
"homeassistant.components.switchbot.config_flow.async_discovered_service_info",
return_value=[WOHAND_SERVICE_INFO],
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{"next_step_id": "select_device"},
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "confirm"
assert result["errors"] is None
with patch_async_setup_entry() as mock_setup_entry:
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{},
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == "Bot EEFF"
assert result["data"] == {
CONF_ADDRESS: "AA:BB:CC:DD:EE:FF",
CONF_SENSOR_TYPE: "bot",
}
assert len(mock_setup_entry.mock_calls) == 1
@pytest.mark.usefixtures("mock_scanners_all_passive")
async def test_user_setup_wohand_already_configured(hass: HomeAssistant) -> None:
"""Test the user initiated form with password and valid mac."""
entry = MockConfigEntry(
domain=DOMAIN,
data={
CONF_ADDRESS: "aa:bb:cc:dd:ee:ff",
CONF_NAME: "test-name",
CONF_PASSWORD: "test-password",
CONF_SENSOR_TYPE: "bot",
},
unique_id="aabbccddeeff",
)
entry.add_to_hass(hass)
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
assert result["type"] is FlowResultType.MENU
assert result["step_id"] == "user"
with patch(
"homeassistant.components.switchbot.config_flow.async_discovered_service_info",
return_value=[WOHAND_SERVICE_INFO],
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{"next_step_id": "select_device"},
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "no_devices_found"
@pytest.mark.usefixtures("mock_scanners_all_passive")
async def test_user_setup_wohand_replaces_ignored(hass: HomeAssistant) -> None:
"""Test setting up a switchbot replaces an ignored entry."""
entry = MockConfigEntry(
domain=DOMAIN, data={}, unique_id="aabbccddeeff", source=SOURCE_IGNORE
)
entry.add_to_hass(hass)
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
assert result["type"] is FlowResultType.MENU
assert result["step_id"] == "user"
with patch(
"homeassistant.components.switchbot.config_flow.async_discovered_service_info",
return_value=[WOHAND_SERVICE_INFO],
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{"next_step_id": "select_device"},
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "confirm"
with patch_async_setup_entry() as mock_setup_entry:
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{},
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == "Bot EEFF"
assert result["data"] == {
CONF_ADDRESS: "AA:BB:CC:DD:EE:FF",
CONF_SENSOR_TYPE: "bot",
}
assert len(mock_setup_entry.mock_calls) == 1
@pytest.mark.usefixtures("mock_scanners_all_passive")
async def test_user_setup_wocurtain(hass: HomeAssistant) -> None:
"""Test the user initiated form with password and valid mac."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
assert result["type"] is FlowResultType.MENU
assert result["step_id"] == "user"
with patch(
"homeassistant.components.switchbot.config_flow.async_discovered_service_info",
return_value=[WOCURTAIN_SERVICE_INFO],
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{"next_step_id": "select_device"},
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "confirm"
assert result["errors"] is None
with patch_async_setup_entry() as mock_setup_entry:
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{},
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == "Curtain EEFF"
assert result["data"] == {
CONF_ADDRESS: "aa:bb:cc:dd:ee:ff",
CONF_SENSOR_TYPE: "curtain",
}
assert len(mock_setup_entry.mock_calls) == 1
@pytest.mark.usefixtures("mock_scanners_all_passive")
async def test_user_setup_wocurtain_or_bot(hass: HomeAssistant) -> None:
"""Test the user initiated form with valid address."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
assert result["type"] is FlowResultType.MENU
assert result["step_id"] == "user"
with patch(
"homeassistant.components.switchbot.config_flow.async_discovered_service_info",
return_value=[
NOT_SWITCHBOT_INFO,
WOCURTAIN_SERVICE_INFO,
WOHAND_SERVICE_ALT_ADDRESS_INFO,
WOHAND_SERVICE_INFO_NOT_CONNECTABLE,
],
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{"next_step_id": "select_device"},
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "select_device"
assert result["errors"] == {}
with patch_async_setup_entry() as mock_setup_entry:
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
USER_INPUT,
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == "Curtain EEFF"
assert result["data"] == {
CONF_ADDRESS: "aa:bb:cc:dd:ee:ff",
CONF_SENSOR_TYPE: "curtain",
}
assert len(mock_setup_entry.mock_calls) == 1
@pytest.mark.usefixtures("mock_scanners_all_passive")
async def test_user_setup_wocurtain_or_bot_with_password(hass: HomeAssistant) -> None:
"""Test the user initiated form and valid address and a bot with a password."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
assert result["type"] is FlowResultType.MENU
assert result["step_id"] == "user"
with patch(
"homeassistant.components.switchbot.config_flow.async_discovered_service_info",
return_value=[
WOCURTAIN_SERVICE_INFO,
WOHAND_ENCRYPTED_SERVICE_INFO,
WOHAND_SERVICE_INFO_NOT_CONNECTABLE,
],
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{"next_step_id": "select_device"},
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "select_device"
assert result["errors"] == {}
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{CONF_ADDRESS: "798A8547-2A3D-C609-55FF-73FA824B923B"},
)
assert result2["type"] is FlowResultType.FORM
assert result2["step_id"] == "password"
assert result2["errors"] is None
with patch_async_setup_entry() as mock_setup_entry:
result3 = await hass.config_entries.flow.async_configure(
result2["flow_id"],
{CONF_PASSWORD: "abc123"},
)
await hass.async_block_till_done()
assert result3["type"] is FlowResultType.CREATE_ENTRY
assert result3["title"] == "Bot 923B"
assert result3["data"] == {
CONF_ADDRESS: "798A8547-2A3D-C609-55FF-73FA824B923B",
CONF_PASSWORD: "abc123",
CONF_SENSOR_TYPE: "bot",
}
assert len(mock_setup_entry.mock_calls) == 1
@pytest.mark.usefixtures("mock_scanners_all_passive")
async def test_user_setup_single_bot_with_password(hass: HomeAssistant) -> None:
"""Test the user initiated form for a bot with a password."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
assert result["type"] is FlowResultType.MENU
assert result["step_id"] == "user"
with patch(
"homeassistant.components.switchbot.config_flow.async_discovered_service_info",
return_value=[WOHAND_ENCRYPTED_SERVICE_INFO],
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{"next_step_id": "select_device"},
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "password"
assert result["errors"] is None
with patch_async_setup_entry() as mock_setup_entry:
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{CONF_PASSWORD: "abc123"},
)
await hass.async_block_till_done()
assert result2["type"] is FlowResultType.CREATE_ENTRY
assert result2["title"] == "Bot 923B"
assert result2["data"] == {
CONF_ADDRESS: "798A8547-2A3D-C609-55FF-73FA824B923B",
CONF_PASSWORD: "abc123",
CONF_SENSOR_TYPE: "bot",
}
assert len(mock_setup_entry.mock_calls) == 1
@pytest.mark.usefixtures("mock_scanners_all_passive")
async def test_user_setup_woencrypted_key(hass: HomeAssistant) -> None:
"""Test the user initiated form for a lock."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
assert result["type"] is FlowResultType.MENU
assert result["step_id"] == "user"
with patch(
"homeassistant.components.switchbot.config_flow.async_discovered_service_info",
return_value=[WOLOCK_SERVICE_INFO],
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{"next_step_id": "select_device"},
)
assert result["type"] is FlowResultType.MENU
assert result["step_id"] == "encrypted_choose_method"
result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input={"next_step_id": "encrypted_key"}
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "encrypted_key"
assert result["errors"] == {}
with patch(
"switchbot.SwitchbotLock.verify_encryption_key",
return_value=False,
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_KEY_ID: "",
CONF_ENCRYPTION_KEY: "",
},
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "encrypted_key"
assert result["errors"] == {"base": "encryption_key_invalid"}
with (
patch_async_setup_entry() as mock_setup_entry,
patch(
"switchbot.SwitchbotLock.verify_encryption_key",
return_value=True,
),
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_KEY_ID: "ff",
CONF_ENCRYPTION_KEY: "ffffffffffffffffffffffffffffffff",
},
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == "Lock EEFF"
assert result["data"] == {
CONF_ADDRESS: "aa:bb:cc:dd:ee:ff",
CONF_KEY_ID: "ff",
CONF_ENCRYPTION_KEY: "ffffffffffffffffffffffffffffffff",
CONF_SENSOR_TYPE: "lock",
}
assert len(mock_setup_entry.mock_calls) == 1
@pytest.mark.usefixtures("mock_scanners_all_passive")
async def test_user_setup_woencrypted_auth(hass: HomeAssistant) -> None:
"""Test the user initiated form for a lock."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
assert result["type"] is FlowResultType.MENU
assert result["step_id"] == "user"
with patch(
"homeassistant.components.switchbot.config_flow.async_discovered_service_info",
return_value=[WOLOCK_SERVICE_INFO],
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{"next_step_id": "select_device"},
)
assert result["type"] is FlowResultType.MENU
assert result["step_id"] == "encrypted_choose_method"
result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input={"next_step_id": "encrypted_auth"}
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "encrypted_auth"
assert result["errors"] == {}
with patch(
"switchbot.SwitchbotLock.async_retrieve_encryption_key",
side_effect=SwitchbotAuthenticationError("error from api"),
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_USERNAME: "",
CONF_PASSWORD: "",
},
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "encrypted_auth"
assert result["errors"] == {"base": "auth_failed"}
assert "error from api" in result["description_placeholders"]["error_detail"]
with (
patch_async_setup_entry() as mock_setup_entry,
patch(
"switchbot.SwitchbotLock.verify_encryption_key",
return_value=True,
),
patch(
"switchbot.SwitchbotLock.async_retrieve_encryption_key",
return_value={
CONF_KEY_ID: "ff",
CONF_ENCRYPTION_KEY: "ffffffffffffffffffffffffffffffff",
},
),
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_USERNAME: "username",
CONF_PASSWORD: "password",
},
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == "Lock EEFF"
assert result["data"] == {
CONF_ADDRESS: "aa:bb:cc:dd:ee:ff",
CONF_KEY_ID: "ff",
CONF_ENCRYPTION_KEY: "ffffffffffffffffffffffffffffffff",
CONF_SENSOR_TYPE: "lock",
}
assert len(mock_setup_entry.mock_calls) == 1
@pytest.mark.usefixtures("mock_scanners_all_passive")
async def test_user_setup_woencrypted_auth_switchbot_api_down(
hass: HomeAssistant,
) -> None:
"""Test the user initiated form for a lock when the switchbot api is down."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
assert result["type"] is FlowResultType.MENU
assert result["step_id"] == "user"
with patch(
"homeassistant.components.switchbot.config_flow.async_discovered_service_info",
return_value=[WOLOCK_SERVICE_INFO],
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{"next_step_id": "select_device"},
)
assert result["type"] is FlowResultType.MENU
assert result["step_id"] == "encrypted_choose_method"
result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input={"next_step_id": "encrypted_auth"}
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "encrypted_auth"
assert result["errors"] == {}
with patch(
"switchbot.SwitchbotLock.async_retrieve_encryption_key",
side_effect=SwitchbotAccountConnectionError("Switchbot API down"),
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_USERNAME: "",
CONF_PASSWORD: "",
},
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "api_error"
assert result["description_placeholders"] == {"error_detail": "Switchbot API down"}
@pytest.mark.usefixtures("mock_scanners_all_passive")
async def test_user_setup_wolock_or_bot(hass: HomeAssistant) -> None:
"""Test the user initiated form for a lock."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
assert result["type"] is FlowResultType.MENU
assert result["step_id"] == "user"
with patch(
"homeassistant.components.switchbot.config_flow.async_discovered_service_info",
return_value=[
WOLOCK_SERVICE_INFO,
WOHAND_SERVICE_ALT_ADDRESS_INFO,
],
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{"next_step_id": "select_device"},
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "select_device"
assert result["errors"] == {}
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
USER_INPUT,
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.MENU
assert result["step_id"] == "encrypted_choose_method"
result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input={"next_step_id": "encrypted_key"}
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "encrypted_key"
assert result["errors"] == {}
with (
patch_async_setup_entry() as mock_setup_entry,
patch(
"switchbot.SwitchbotLock.verify_encryption_key",
return_value=True,
),
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_KEY_ID: "ff",
CONF_ENCRYPTION_KEY: "ffffffffffffffffffffffffffffffff",
},
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == "Lock EEFF"
assert result["data"] == {
CONF_ADDRESS: "aa:bb:cc:dd:ee:ff",
CONF_KEY_ID: "ff",
CONF_ENCRYPTION_KEY: "ffffffffffffffffffffffffffffffff",
CONF_SENSOR_TYPE: "lock",
}
assert len(mock_setup_entry.mock_calls) == 1
@pytest.mark.usefixtures("mock_scanners_all_passive")
async def test_user_setup_wosensor(hass: HomeAssistant) -> None:
"""Test the user initiated form with password and valid mac."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
assert result["type"] is FlowResultType.MENU
assert result["step_id"] == "user"
with patch(
"homeassistant.components.switchbot.config_flow.async_discovered_service_info",
return_value=[WOSENSORTH_SERVICE_INFO],
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{"next_step_id": "select_device"},
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "confirm"
assert result["errors"] is None
with patch_async_setup_entry() as mock_setup_entry:
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{},
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == "Meter EEFF"
assert result["data"] == {
CONF_ADDRESS: "aa:bb:cc:dd:ee:ff",
CONF_SENSOR_TYPE: "hygrometer",
}
assert len(mock_setup_entry.mock_calls) == 1
@pytest.mark.usefixtures("mock_scanners_all_passive")
async def test_user_cloud_login(hass: HomeAssistant) -> None:
"""Test the cloud login flow."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
assert result["type"] is FlowResultType.MENU
assert result["step_id"] == "user"
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{"next_step_id": "cloud_login"},
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "cloud_login"
# Test successful cloud login
with (
patch(
"homeassistant.components.switchbot.config_flow.fetch_cloud_devices",
return_value=None,
),
patch(
"homeassistant.components.switchbot.config_flow.async_discovered_service_info",
return_value=[WOHAND_SERVICE_INFO],
),
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_USERNAME: "test@example.com",
CONF_PASSWORD: "testpass",
},
)
# Should proceed to device selection with single device, so go to confirm
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "confirm"
# Confirm device setup
with patch_async_setup_entry():
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{},
)
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == "Bot EEFF"
assert result["data"] == {
CONF_ADDRESS: "AA:BB:CC:DD:EE:FF",
CONF_SENSOR_TYPE: "bot",
}
@pytest.mark.usefixtures("mock_scanners_all_passive")
async def test_user_cloud_login_auth_failed(hass: HomeAssistant) -> None:
"""Test the cloud login flow with authentication failure."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
assert result["type"] is FlowResultType.MENU
assert result["step_id"] == "user"
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{"next_step_id": "cloud_login"},
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "cloud_login"
# Test authentication failure
with patch(
"homeassistant.components.switchbot.config_flow.fetch_cloud_devices",
side_effect=SwitchbotAuthenticationError("Invalid credentials"),
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_USERNAME: "test@example.com",
CONF_PASSWORD: "wrongpass",
},
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "cloud_login"
assert result["errors"] == {"base": "auth_failed"}
assert "Invalid credentials" in result["description_placeholders"]["error_detail"]
@pytest.mark.usefixtures("mock_scanners_all_passive")
async def test_user_cloud_login_api_error(hass: HomeAssistant) -> None:
"""Test the cloud login flow with API error."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
assert result["type"] is FlowResultType.MENU
assert result["step_id"] == "user"
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{"next_step_id": "cloud_login"},
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "cloud_login"
# Test API connection error
with patch(
"homeassistant.components.switchbot.config_flow.fetch_cloud_devices",
side_effect=SwitchbotAccountConnectionError("API is down"),
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_USERNAME: "test@example.com",
CONF_PASSWORD: "testpass",
},
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "api_error"
assert result["description_placeholders"] == {"error_detail": "API is down"}
@pytest.mark.usefixtures("mock_scanners_all_passive")
async def test_user_cloud_login_then_encrypted_device(hass: HomeAssistant) -> None:
"""Test cloud login followed by encrypted device setup using saved credentials."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
assert result["type"] is FlowResultType.MENU
assert result["step_id"] == "user"
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{"next_step_id": "cloud_login"},
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "cloud_login"
with (
patch(
"homeassistant.components.switchbot.config_flow.fetch_cloud_devices",
return_value=None,
),
patch(
"homeassistant.components.switchbot.config_flow.async_discovered_service_info",
return_value=[WOLOCK_SERVICE_INFO],
),
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_USERNAME: "test@example.com",
CONF_PASSWORD: "testpass",
},
)
# Should go to encrypted device choice menu
assert result["type"] is FlowResultType.MENU
assert result["step_id"] == "encrypted_choose_method"
# Choose encrypted auth
result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input={"next_step_id": "encrypted_auth"}
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "encrypted_auth"
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
None,
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "encrypted_auth"
with (
patch_async_setup_entry() as mock_setup_entry,
patch(
"switchbot.SwitchbotLock.async_retrieve_encryption_key",
return_value={
CONF_KEY_ID: "ff",
CONF_ENCRYPTION_KEY: "ffffffffffffffffffffffffffffffff",
},
),
patch("switchbot.SwitchbotLock.verify_encryption_key", return_value=True),
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_USERNAME: "test@example.com",
CONF_PASSWORD: "testpass",
},
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == "Lock EEFF"
assert result["data"] == {
CONF_ADDRESS: "aa:bb:cc:dd:ee:ff",
CONF_KEY_ID: "ff",
CONF_ENCRYPTION_KEY: "ffffffffffffffffffffffffffffffff",
CONF_SENSOR_TYPE: "lock",
}
assert len(mock_setup_entry.mock_calls) == 1
@pytest.mark.usefixtures("mock_scanners_all_passive")
async def test_user_no_devices(hass: HomeAssistant) -> None:
"""Test the user initiated form with password and valid mac."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
assert result["type"] is FlowResultType.MENU
assert result["step_id"] == "user"
with patch(
"homeassistant.components.switchbot.config_flow.async_discovered_service_info",
return_value=[],
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{"next_step_id": "select_device"},
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "no_devices_found"
@pytest.mark.usefixtures("mock_scanners_all_passive")
async def test_async_step_user_takes_precedence_over_discovery(
hass: HomeAssistant,
) -> None:
"""Test manual setup takes precedence over discovery."""
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_BLUETOOTH},
data=WOCURTAIN_SERVICE_INFO,
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "confirm"
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_USER},
)
assert result["type"] is FlowResultType.MENU
assert result["step_id"] == "user"
with patch(
"homeassistant.components.switchbot.config_flow.async_discovered_service_info",
return_value=[WOCURTAIN_SERVICE_INFO],
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{"next_step_id": "select_device"},
)
assert result["type"] is FlowResultType.FORM
with patch_async_setup_entry() as mock_setup_entry:
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input={},
)
assert result2["type"] is FlowResultType.CREATE_ENTRY
assert result2["title"] == "Curtain EEFF"
assert result2["data"] == {
CONF_ADDRESS: "aa:bb:cc:dd:ee:ff",
CONF_SENSOR_TYPE: "curtain",
}
assert len(mock_setup_entry.mock_calls) == 1
# Verify the original one was aborted
assert not hass.config_entries.flow.async_progress(DOMAIN)
async def test_options_flow(hass: HomeAssistant) -> None:
"""Test updating options."""
entry = MockConfigEntry(
domain=DOMAIN,
data={
CONF_ADDRESS: "aa:bb:cc:dd:ee:ff",
CONF_NAME: "test-name",
CONF_PASSWORD: "test-password",
CONF_SENSOR_TYPE: "bot",
},
options={
CONF_RETRY_COUNT: 10,
},
unique_id="aabbccddeeff",
)
entry.add_to_hass(hass)
with patch_async_setup_entry() as mock_setup_entry:
entry = await init_integration(hass)
result = await hass.config_entries.options.async_init(entry.entry_id)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "init"
assert result["errors"] is None
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={
CONF_RETRY_COUNT: 3,
},
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["data"][CONF_RETRY_COUNT] == 3
assert len(mock_setup_entry.mock_calls) == 2
# Test changing of entry options.
with patch_async_setup_entry() as mock_setup_entry:
entry = await init_integration(hass)
result = await hass.config_entries.options.async_init(entry.entry_id)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "init"
assert result["errors"] is None
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={
CONF_RETRY_COUNT: 6,
},
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["data"][CONF_RETRY_COUNT] == 6
assert len(mock_setup_entry.mock_calls) == 1
assert entry.options[CONF_RETRY_COUNT] == 6
async def test_options_flow_lock_pro(hass: HomeAssistant) -> None:
"""Test updating options."""
entry = MockConfigEntry(
domain=DOMAIN,
data={
CONF_ADDRESS: "aa:bb:cc:dd:ee:ff",
CONF_NAME: "test-name",
CONF_PASSWORD: "test-password",
CONF_SENSOR_TYPE: "lock_pro",
},
options={CONF_RETRY_COUNT: 10},
unique_id="aabbccddeeff",
)
entry.add_to_hass(hass)
# Test Force night_latch should be disabled by default.
with patch_async_setup_entry() as mock_setup_entry:
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
result = await hass.config_entries.options.async_init(entry.entry_id)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "init"
assert result["errors"] is None
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={
CONF_RETRY_COUNT: 3,
},
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["data"][CONF_LOCK_NIGHTLATCH] is False
assert len(mock_setup_entry.mock_calls) == 1
# Test Set force night_latch to be enabled.
with patch_async_setup_entry() as mock_setup_entry:
result = await hass.config_entries.options.async_init(entry.entry_id)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "init"
assert result["errors"] is None
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={
CONF_LOCK_NIGHTLATCH: True,
},
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["data"][CONF_LOCK_NIGHTLATCH] is True
assert len(mock_setup_entry.mock_calls) == 0
assert entry.options[CONF_LOCK_NIGHTLATCH] is True
@pytest.mark.usefixtures("mock_scanners_all_passive")
async def test_user_setup_worelay_switch_1pm_key(hass: HomeAssistant) -> None:
"""Test the user initiated form for a relay switch 1pm."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
assert result["type"] is FlowResultType.MENU
assert result["step_id"] == "user"
with patch(
"homeassistant.components.switchbot.config_flow.async_discovered_service_info",
return_value=[WORELAY_SWITCH_1PM_SERVICE_INFO],
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{"next_step_id": "select_device"},
)
assert result["type"] is FlowResultType.MENU
assert result["step_id"] == "encrypted_choose_method"
result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input={"next_step_id": "encrypted_key"}
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "encrypted_key"
assert result["errors"] == {}
with (
patch_async_setup_entry() as mock_setup_entry,
patch(
"switchbot.SwitchbotRelaySwitch.verify_encryption_key", return_value=True
),
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_KEY_ID: "ff",
CONF_ENCRYPTION_KEY: "ffffffffffffffffffffffffffffffff",
},
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == "Relay Switch 1PM EEFF"
assert result["data"] == {
CONF_ADDRESS: "AA:BB:CC:DD:EE:FF",
CONF_KEY_ID: "ff",
CONF_ENCRYPTION_KEY: "ffffffffffffffffffffffffffffffff",
CONF_SENSOR_TYPE: "relay_switch_1pm",
}
assert len(mock_setup_entry.mock_calls) == 1
@pytest.mark.usefixtures("mock_scanners_all_passive")
async def test_user_setup_worelay_switch_1pm_auth(hass: HomeAssistant) -> None:
"""Test the user initiated form for a relay switch 1pm."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
assert result["type"] is FlowResultType.MENU
assert result["step_id"] == "user"
with patch(
"homeassistant.components.switchbot.config_flow.async_discovered_service_info",
return_value=[WORELAY_SWITCH_1PM_SERVICE_INFO],
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{"next_step_id": "select_device"},
)
assert result["type"] is FlowResultType.MENU
assert result["step_id"] == "encrypted_choose_method"
result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input={"next_step_id": "encrypted_auth"}
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "encrypted_auth"
assert result["errors"] == {}
with patch(
"switchbot.SwitchbotRelaySwitch.async_retrieve_encryption_key",
side_effect=SwitchbotAuthenticationError("error from api"),
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_USERNAME: "",
CONF_PASSWORD: "",
},
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "encrypted_auth"
assert result["errors"] == {"base": "auth_failed"}
assert "error from api" in result["description_placeholders"]["error_detail"]
with (
patch_async_setup_entry() as mock_setup_entry,
patch(
"switchbot.SwitchbotRelaySwitch.async_retrieve_encryption_key",
return_value={
CONF_KEY_ID: "ff",
CONF_ENCRYPTION_KEY: "ffffffffffffffffffffffffffffffff",
},
),
patch(
"switchbot.SwitchbotRelaySwitch.verify_encryption_key", return_value=True
),
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_USERNAME: "username",
CONF_PASSWORD: "password",
},
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == "Relay Switch 1PM EEFF"
assert result["data"] == {
CONF_ADDRESS: "AA:BB:CC:DD:EE:FF",
CONF_KEY_ID: "ff",
CONF_ENCRYPTION_KEY: "ffffffffffffffffffffffffffffffff",
CONF_SENSOR_TYPE: "relay_switch_1pm",
}
assert len(mock_setup_entry.mock_calls) == 1
@pytest.mark.usefixtures("mock_scanners_all_passive")
async def test_user_setup_worelay_switch_1pm_auth_switchbot_api_down(
hass: HomeAssistant,
) -> None:
"""Test the user initiated form for a relay switch 1pm when the switchbot api is down."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
assert result["type"] is FlowResultType.MENU
assert result["step_id"] == "user"
with patch(
"homeassistant.components.switchbot.config_flow.async_discovered_service_info",
return_value=[WORELAY_SWITCH_1PM_SERVICE_INFO],
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{"next_step_id": "select_device"},
)
assert result["type"] is FlowResultType.MENU
assert result["step_id"] == "encrypted_choose_method"
result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input={"next_step_id": "encrypted_auth"}
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "encrypted_auth"
assert result["errors"] == {}
with patch(
"switchbot.SwitchbotRelaySwitch.async_retrieve_encryption_key",
side_effect=SwitchbotAccountConnectionError("Switchbot API down"),
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_USERNAME: "",
CONF_PASSWORD: "",
},
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "api_error"
assert result["description_placeholders"] == {"error_detail": "Switchbot API down"}
@pytest.mark.usefixtures("mock_scanners_all_active")
async def test_user_skip_menu_when_all_scanners_active(hass: HomeAssistant) -> None:
"""Test that menu is skipped when all scanners are in active mode."""
with (
patch(
"homeassistant.components.switchbot.config_flow.async_discovered_service_info",
return_value=[WOHAND_SERVICE_INFO],
),
patch_async_setup_entry() as mock_setup_entry,
):
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
# Should skip menu and go directly to select_device -> confirm
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "confirm"
result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input={}
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == "Bot EEFF"
assert result["data"] == {
CONF_ADDRESS: "AA:BB:CC:DD:EE:FF",
CONF_SENSOR_TYPE: "bot",
}
assert len(mock_setup_entry.mock_calls) == 1
async def test_user_show_menu_when_passive_scanner_present(hass: HomeAssistant) -> None:
"""Test that menu is shown when any scanner is in passive mode."""
mock_scanner_active = Mock()
mock_scanner_active.current_mode = BluetoothScanningMode.ACTIVE
mock_scanner_passive = Mock()
mock_scanner_passive.current_mode = BluetoothScanningMode.PASSIVE
with (
patch(
"homeassistant.components.switchbot.config_flow.async_current_scanners",
return_value=[mock_scanner_active, mock_scanner_passive],
),
patch(
"homeassistant.components.switchbot.config_flow.async_discovered_service_info",
return_value=[WOHAND_SERVICE_INFO],
),
patch_async_setup_entry() as mock_setup_entry,
):
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
# Should show menu since not all scanners are active
assert result["type"] is FlowResultType.MENU
assert result["step_id"] == "user"
assert set(result["menu_options"]) == {"cloud_login", "select_device"}
# Choose select_device from menu
result = await hass.config_entries.flow.async_configure(
result["flow_id"], {"next_step_id": "select_device"}
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "confirm"
# Confirm the device
result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input={}
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == "Bot EEFF"
assert result["data"] == {
CONF_ADDRESS: "AA:BB:CC:DD:EE:FF",
CONF_SENSOR_TYPE: "bot",
}
assert len(mock_setup_entry.mock_calls) == 1
async def test_user_show_menu_when_no_scanners(hass: HomeAssistant) -> None:
"""Test that menu is shown when no scanners are available."""
with (
patch(
"homeassistant.components.switchbot.config_flow.async_current_scanners",
return_value=[],
),
patch(
"homeassistant.components.switchbot.config_flow.async_discovered_service_info",
return_value=[WOHAND_SERVICE_INFO],
),
patch_async_setup_entry() as mock_setup_entry,
):
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
# Should show menu when no scanners are available
assert result["type"] is FlowResultType.MENU
assert result["step_id"] == "user"
assert set(result["menu_options"]) == {"cloud_login", "select_device"}
# Choose select_device from menu
result = await hass.config_entries.flow.async_configure(
result["flow_id"], {"next_step_id": "select_device"}
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "confirm"
# Confirm the device
result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input={}
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == "Bot EEFF"
assert result["data"] == {
CONF_ADDRESS: "AA:BB:CC:DD:EE:FF",
CONF_SENSOR_TYPE: "bot",
}
assert len(mock_setup_entry.mock_calls) == 1