Add Config Flow to AlarmDecoder (#37998)
This commit is contained in:
parent
17efa1bda5
commit
c32f698671
14 changed files with 1169 additions and 210 deletions
424
tests/components/alarmdecoder/test_config_flow.py
Normal file
424
tests/components/alarmdecoder/test_config_flow.py
Normal file
|
@ -0,0 +1,424 @@
|
|||
"""Test the AlarmDecoder config flow."""
|
||||
from alarmdecoder.util import NoDeviceError
|
||||
import pytest
|
||||
|
||||
from homeassistant import config_entries, data_entry_flow
|
||||
from homeassistant.components.alarmdecoder import config_flow
|
||||
from homeassistant.components.alarmdecoder.const import (
|
||||
CONF_ALT_NIGHT_MODE,
|
||||
CONF_AUTO_BYPASS,
|
||||
CONF_CODE_ARM_REQUIRED,
|
||||
CONF_DEVICE_BAUD,
|
||||
CONF_DEVICE_PATH,
|
||||
CONF_RELAY_ADDR,
|
||||
CONF_RELAY_CHAN,
|
||||
CONF_ZONE_LOOP,
|
||||
CONF_ZONE_NAME,
|
||||
CONF_ZONE_NUMBER,
|
||||
CONF_ZONE_RFID,
|
||||
CONF_ZONE_TYPE,
|
||||
DEFAULT_ARM_OPTIONS,
|
||||
DEFAULT_ZONE_OPTIONS,
|
||||
DOMAIN,
|
||||
OPTIONS_ARM,
|
||||
OPTIONS_ZONES,
|
||||
PROTOCOL_SERIAL,
|
||||
PROTOCOL_SOCKET,
|
||||
)
|
||||
from homeassistant.components.binary_sensor import DEVICE_CLASS_WINDOW
|
||||
from homeassistant.const import CONF_HOST, CONF_PORT, CONF_PROTOCOL
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from tests.async_mock import patch
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"protocol,connection,baud,title",
|
||||
[
|
||||
(
|
||||
PROTOCOL_SOCKET,
|
||||
{
|
||||
CONF_HOST: "alarmdecoder123",
|
||||
CONF_PORT: 10001,
|
||||
},
|
||||
None,
|
||||
"alarmdecoder123:10001",
|
||||
),
|
||||
(
|
||||
PROTOCOL_SERIAL,
|
||||
{
|
||||
CONF_DEVICE_PATH: "/dev/ttyUSB123",
|
||||
CONF_DEVICE_BAUD: 115000,
|
||||
},
|
||||
115000,
|
||||
"/dev/ttyUSB123",
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_setups(hass: HomeAssistant, protocol, connection, baud, title):
|
||||
"""Test flow for setting up the available AlarmDecoder protocols."""
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "user"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_PROTOCOL: protocol},
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "protocol"
|
||||
|
||||
with patch("homeassistant.components.alarmdecoder.config_flow.AdExt.open"), patch(
|
||||
"homeassistant.components.alarmdecoder.config_flow.AdExt.close"
|
||||
), patch(
|
||||
"homeassistant.components.alarmdecoder.async_setup", return_value=True
|
||||
) as mock_setup, patch(
|
||||
"homeassistant.components.alarmdecoder.async_setup_entry",
|
||||
return_value=True,
|
||||
) as mock_setup_entry:
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], connection
|
||||
)
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||
assert result["title"] == title
|
||||
assert result["data"] == {
|
||||
**connection,
|
||||
CONF_PROTOCOL: protocol,
|
||||
CONF_DEVICE_BAUD: baud,
|
||||
}
|
||||
|
||||
await hass.async_block_till_done()
|
||||
assert len(mock_setup.mock_calls) == 1
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
async def test_setup_connection_error(hass: HomeAssistant):
|
||||
"""Test flow for setup with a connection error."""
|
||||
|
||||
port = 1001
|
||||
host = "alarmdecoder"
|
||||
protocol = PROTOCOL_SOCKET
|
||||
connection_settings = {CONF_HOST: host, CONF_PORT: port}
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "user"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_PROTOCOL: protocol},
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "protocol"
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.alarmdecoder.config_flow.AdExt.open",
|
||||
side_effect=NoDeviceError,
|
||||
), patch("homeassistant.components.alarmdecoder.config_flow.AdExt.close"):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], connection_settings
|
||||
)
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["errors"] == {"base": "service_unavailable"}
|
||||
|
||||
|
||||
async def test_options_arm_flow(hass: HomeAssistant):
|
||||
"""Test arm options flow."""
|
||||
user_input = {
|
||||
CONF_ALT_NIGHT_MODE: True,
|
||||
CONF_AUTO_BYPASS: True,
|
||||
CONF_CODE_ARM_REQUIRED: True,
|
||||
}
|
||||
entry = MockConfigEntry(domain=DOMAIN)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
result = await hass.config_entries.options.async_init(entry.entry_id)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "init"
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={"edit_selection": "Arming Settings"},
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "arm_settings"
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.alarmdecoder.async_setup_entry", return_value=True
|
||||
):
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input=user_input,
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||
assert entry.options == {
|
||||
OPTIONS_ARM: user_input,
|
||||
OPTIONS_ZONES: DEFAULT_ZONE_OPTIONS,
|
||||
}
|
||||
|
||||
|
||||
async def test_options_zone_flow(hass: HomeAssistant):
|
||||
"""Test options flow for adding/deleting zones."""
|
||||
zone_number = "2"
|
||||
zone_settings = {CONF_ZONE_NAME: "Front Entry", CONF_ZONE_TYPE: DEVICE_CLASS_WINDOW}
|
||||
entry = MockConfigEntry(domain=DOMAIN)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
result = await hass.config_entries.options.async_init(entry.entry_id)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "init"
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={"edit_selection": "Zones"},
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "zone_select"
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={CONF_ZONE_NUMBER: zone_number},
|
||||
)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.alarmdecoder.async_setup_entry", return_value=True
|
||||
):
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input=zone_settings,
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||
assert entry.options == {
|
||||
OPTIONS_ARM: DEFAULT_ARM_OPTIONS,
|
||||
OPTIONS_ZONES: {zone_number: zone_settings},
|
||||
}
|
||||
|
||||
# Make sure zone can be removed...
|
||||
result = await hass.config_entries.options.async_init(entry.entry_id)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "init"
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={"edit_selection": "Zones"},
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "zone_select"
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={CONF_ZONE_NUMBER: zone_number},
|
||||
)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.alarmdecoder.async_setup_entry", return_value=True
|
||||
):
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={},
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||
assert entry.options == {
|
||||
OPTIONS_ARM: DEFAULT_ARM_OPTIONS,
|
||||
OPTIONS_ZONES: {},
|
||||
}
|
||||
|
||||
|
||||
async def test_options_zone_flow_validation(hass: HomeAssistant):
|
||||
"""Test input validation for zone options flow."""
|
||||
zone_number = "2"
|
||||
zone_settings = {CONF_ZONE_NAME: "Front Entry", CONF_ZONE_TYPE: DEVICE_CLASS_WINDOW}
|
||||
entry = MockConfigEntry(domain=DOMAIN)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
result = await hass.config_entries.options.async_init(entry.entry_id)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "init"
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={"edit_selection": "Zones"},
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "zone_select"
|
||||
|
||||
# Zone Number must be int
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={CONF_ZONE_NUMBER: "asd"},
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "zone_select"
|
||||
assert result["errors"] == {CONF_ZONE_NUMBER: "int"}
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={CONF_ZONE_NUMBER: zone_number},
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "zone_details"
|
||||
|
||||
# CONF_RELAY_ADDR & CONF_RELAY_CHAN are inclusive
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={**zone_settings, CONF_RELAY_ADDR: "1"},
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "zone_details"
|
||||
assert result["errors"] == {"base": "relay_inclusive"}
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={**zone_settings, CONF_RELAY_CHAN: "1"},
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "zone_details"
|
||||
assert result["errors"] == {"base": "relay_inclusive"}
|
||||
|
||||
# CONF_RELAY_ADDR, CONF_RELAY_CHAN must be int
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={**zone_settings, CONF_RELAY_ADDR: "abc", CONF_RELAY_CHAN: "abc"},
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "zone_details"
|
||||
assert result["errors"] == {
|
||||
CONF_RELAY_ADDR: "int",
|
||||
CONF_RELAY_CHAN: "int",
|
||||
}
|
||||
|
||||
# CONF_ZONE_LOOP depends on CONF_ZONE_RFID
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={**zone_settings, CONF_ZONE_LOOP: "1"},
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "zone_details"
|
||||
assert result["errors"] == {CONF_ZONE_LOOP: "loop_rfid"}
|
||||
|
||||
# CONF_ZONE_LOOP must be int
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={**zone_settings, CONF_ZONE_RFID: "rfid123", CONF_ZONE_LOOP: "ab"},
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "zone_details"
|
||||
assert result["errors"] == {CONF_ZONE_LOOP: "int"}
|
||||
|
||||
# CONF_ZONE_LOOP must be between [1,4]
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={**zone_settings, CONF_ZONE_RFID: "rfid123", CONF_ZONE_LOOP: "5"},
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "zone_details"
|
||||
assert result["errors"] == {CONF_ZONE_LOOP: "loop_range"}
|
||||
|
||||
# All valid settings
|
||||
with patch(
|
||||
"homeassistant.components.alarmdecoder.async_setup_entry", return_value=True
|
||||
):
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={
|
||||
**zone_settings,
|
||||
CONF_ZONE_RFID: "rfid123",
|
||||
CONF_ZONE_LOOP: "2",
|
||||
CONF_RELAY_ADDR: "12",
|
||||
CONF_RELAY_CHAN: "1",
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||
assert entry.options == {
|
||||
OPTIONS_ARM: DEFAULT_ARM_OPTIONS,
|
||||
OPTIONS_ZONES: {
|
||||
zone_number: {
|
||||
**zone_settings,
|
||||
CONF_ZONE_RFID: "rfid123",
|
||||
CONF_ZONE_LOOP: 2,
|
||||
CONF_RELAY_ADDR: 12,
|
||||
CONF_RELAY_CHAN: 1,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"protocol,connection",
|
||||
[
|
||||
(
|
||||
PROTOCOL_SOCKET,
|
||||
{
|
||||
CONF_HOST: "alarmdecoder123",
|
||||
CONF_PORT: 10001,
|
||||
},
|
||||
),
|
||||
(
|
||||
PROTOCOL_SERIAL,
|
||||
{
|
||||
CONF_DEVICE_PATH: "/dev/ttyUSB123",
|
||||
CONF_DEVICE_BAUD: 115000,
|
||||
},
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_one_device_allowed(hass, protocol, connection):
|
||||
"""Test that only one AlarmDecoder device is allowed."""
|
||||
flow = config_flow.AlarmDecoderFlowHandler()
|
||||
flow.hass = hass
|
||||
|
||||
MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data=connection,
|
||||
).add_to_hass(hass)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "user"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_PROTOCOL: protocol},
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "protocol"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], connection
|
||||
)
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
||||
assert result["reason"] == "already_configured"
|
Loading…
Add table
Add a link
Reference in a new issue