Add yale_smart_alarm config options flow (#54097)

This commit is contained in:
G Johansson 2022-01-07 16:32:49 +01:00 committed by GitHub
parent ecb921949a
commit 91900f8e4e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 195 additions and 36 deletions

View File

@ -1,15 +1,27 @@
"""Adds config flow for Yale Smart Alarm integration.""" """Adds config flow for Yale Smart Alarm integration."""
from __future__ import annotations from __future__ import annotations
from typing import Any
import voluptuous as vol import voluptuous as vol
from yalesmartalarmclient.client import YaleSmartAlarmClient from yalesmartalarmclient.client import YaleSmartAlarmClient
from yalesmartalarmclient.exceptions import AuthenticationError from yalesmartalarmclient.exceptions import AuthenticationError
from homeassistant import config_entries from homeassistant.config_entries import ConfigEntry, ConfigFlow, OptionsFlow
from homeassistant.const import CONF_NAME, CONF_PASSWORD, CONF_USERNAME from homeassistant.const import CONF_CODE, CONF_NAME, CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import callback
from homeassistant.data_entry_flow import FlowResult
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from .const import CONF_AREA_ID, DEFAULT_AREA_ID, DEFAULT_NAME, DOMAIN, LOGGER from .const import (
CONF_AREA_ID,
CONF_LOCK_CODE_DIGITS,
DEFAULT_AREA_ID,
DEFAULT_LOCK_CODE_DIGITS,
DEFAULT_NAME,
DOMAIN,
LOGGER,
)
DATA_SCHEMA = vol.Schema( DATA_SCHEMA = vol.Schema(
{ {
@ -28,12 +40,18 @@ DATA_SCHEMA_AUTH = vol.Schema(
) )
class YaleConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): class YaleConfigFlow(ConfigFlow, domain=DOMAIN):
"""Handle a config flow for Yale integration.""" """Handle a config flow for Yale integration."""
VERSION = 1 VERSION = 1
entry: config_entries.ConfigEntry entry: ConfigEntry
@staticmethod
@callback
def async_get_options_flow(config_entry: ConfigEntry) -> YaleOptionsFlowHandler:
"""Get the options flow for this handler."""
return YaleOptionsFlowHandler(config_entry)
async def async_step_import(self, config: dict): async def async_step_import(self, config: dict):
"""Import a configuration from config.yaml.""" """Import a configuration from config.yaml."""
@ -127,3 +145,41 @@ class YaleConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
data_schema=DATA_SCHEMA, data_schema=DATA_SCHEMA,
errors=errors, errors=errors,
) )
class YaleOptionsFlowHandler(OptionsFlow):
"""Handle Yale options."""
def __init__(self, entry: ConfigEntry) -> None:
"""Initialize Yale options flow."""
self.entry = entry
async def async_step_init(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
"""Manage Yale options."""
errors = {}
if user_input:
if len(user_input[CONF_CODE]) not in [0, user_input[CONF_LOCK_CODE_DIGITS]]:
errors["base"] = "code_format_mismatch"
else:
return self.async_create_entry(title="", data=user_input)
return self.async_show_form(
step_id="init",
data_schema=vol.Schema(
{
vol.Optional(
CONF_CODE, default=self.entry.options.get(CONF_CODE)
): str,
vol.Optional(
CONF_LOCK_CODE_DIGITS,
default=self.entry.options.get(
CONF_LOCK_CODE_DIGITS, DEFAULT_LOCK_CODE_DIGITS
),
): int,
}
),
errors=errors,
)

View File

@ -15,8 +15,10 @@ from homeassistant.const import (
) )
CONF_AREA_ID = "area_id" CONF_AREA_ID = "area_id"
CONF_LOCK_CODE_DIGITS = "lock_code_digits"
DEFAULT_NAME = "Yale Smart Alarm" DEFAULT_NAME = "Yale Smart Alarm"
DEFAULT_AREA_ID = "1" DEFAULT_AREA_ID = "1"
DEFAULT_LOCK_CODE_DIGITS = 4
MANUFACTURER = "Yale" MANUFACTURER = "Yale"
MODEL = "main" MODEL = "main"

View File

@ -21,9 +21,22 @@
"username": "[%key:common::config_flow::data::username%]", "username": "[%key:common::config_flow::data::username%]",
"password": "[%key:common::config_flow::data::password%]", "password": "[%key:common::config_flow::data::password%]",
"name": "[%key:common::config_flow::data::name%]", "name": "[%key:common::config_flow::data::name%]",
"area_id": "[%key:component::yale_smart_alarm::config::step::user::data::area_id%]" "area_id": "Area ID"
} }
} }
} }
},
"options": {
"step": {
"init": {
"data": {
"code": "Default code for locks, used if none is given",
"lock_code_digits": "Number of digits in PIN code for locks"
}
}
},
"error": {
"code_format_mismatch": "The code does not match the required number of digits"
}
} }
} }

View File

@ -1,29 +1,41 @@
{ {
"config": { "config": {
"abort": { "abort": {
"already_configured": "Account is already configured", "already_configured": "Account is already configured"
"reauth_successful": "Re-authentication was successful" },
"error": {
"invalid_auth": "Invalid authentication"
},
"step": {
"reauth_confirm": {
"data": {
"area_id": "Area ID",
"name": "Name",
"password": "Password",
"username": "Username"
}
}, },
"error": { "user": {
"invalid_auth": "Invalid authentication" "data": {
}, "area_id": "Area ID",
"step": { "name": "Name",
"reauth_confirm": { "password": "Password",
"data": { "username": "Username"
"area_id": "Area ID", }
"name": "Name",
"password": "Password",
"username": "Username"
}
},
"user": {
"data": {
"area_id": "Area ID",
"name": "Name",
"password": "Password",
"username": "Username"
}
}
} }
}
},
"options": {
"step": {
"init": {
"data": {
"code": "Default code for locks, used if none is given",
"lock_code_digits": "Number of digits in PIN code for locks"
}
}
},
"error": {
"code_format_mismatch": "The code does not match the required number of digits"
}
} }
} }

View File

@ -9,7 +9,11 @@ from yalesmartalarmclient.exceptions import AuthenticationError
from homeassistant import config_entries from homeassistant import config_entries
from homeassistant.components.yale_smart_alarm.const import DOMAIN from homeassistant.components.yale_smart_alarm.const import DOMAIN
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import RESULT_TYPE_ABORT, RESULT_TYPE_FORM from homeassistant.data_entry_flow import (
RESULT_TYPE_ABORT,
RESULT_TYPE_CREATE_ENTRY,
RESULT_TYPE_FORM,
)
from tests.common import MockConfigEntry from tests.common import MockConfigEntry
@ -77,7 +81,7 @@ async def test_form_invalid_auth(hass: HomeAssistant) -> None:
@pytest.mark.parametrize( @pytest.mark.parametrize(
"input,output", "p_input,p_output",
[ [
( (
{ {
@ -107,7 +111,9 @@ async def test_form_invalid_auth(hass: HomeAssistant) -> None:
), ),
], ],
) )
async def test_import_flow_success(hass, input: dict[str, str], output: dict[str, str]): async def test_import_flow_success(
hass, p_input: dict[str, str], p_output: dict[str, str]
):
"""Test a successful import of yaml.""" """Test a successful import of yaml."""
with patch( with patch(
@ -119,13 +125,13 @@ async def test_import_flow_success(hass, input: dict[str, str], output: dict[str
result2 = await hass.config_entries.flow.async_init( result2 = await hass.config_entries.flow.async_init(
DOMAIN, DOMAIN,
context={"source": config_entries.SOURCE_IMPORT}, context={"source": config_entries.SOURCE_IMPORT},
data=input, data=p_input,
) )
await hass.async_block_till_done() await hass.async_block_till_done()
assert result2["type"] == "create_entry" assert result2["type"] == "create_entry"
assert result2["title"] == "test-username" assert result2["title"] == "test-username"
assert result2["data"] == output assert result2["data"] == p_output
assert len(mock_setup_entry.mock_calls) == 1 assert len(mock_setup_entry.mock_calls) == 1
@ -222,3 +228,73 @@ async def test_reauth_flow_invalid_login(hass: HomeAssistant) -> None:
assert result2["step_id"] == "reauth_confirm" assert result2["step_id"] == "reauth_confirm"
assert result2["type"] == "form" assert result2["type"] == "form"
assert result2["errors"] == {"base": "invalid_auth"} assert result2["errors"] == {"base": "invalid_auth"}
async def test_options_flow(hass: HomeAssistant) -> None:
"""Test options config flow."""
entry = MockConfigEntry(
domain=DOMAIN,
unique_id="test-username",
data={},
)
entry.add_to_hass(hass)
with patch(
"homeassistant.components.yale_smart_alarm.async_setup_entry",
return_value=True,
):
assert 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"] == RESULT_TYPE_FORM
assert result["step_id"] == "init"
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={"code": "123456", "lock_code_digits": 6},
)
assert result["type"] == RESULT_TYPE_CREATE_ENTRY
assert result["data"] == {"code": "123456", "lock_code_digits": 6}
async def test_options_flow_format_mismatch(hass: HomeAssistant) -> None:
"""Test options config flow with a code format mismatch error."""
entry = MockConfigEntry(
domain=DOMAIN,
unique_id="test-username",
data={},
)
entry.add_to_hass(hass)
with patch(
"homeassistant.components.yale_smart_alarm.async_setup_entry",
return_value=True,
):
assert 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"] == RESULT_TYPE_FORM
assert result["step_id"] == "init"
assert result["errors"] == {}
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={"code": "123", "lock_code_digits": 6},
)
assert result["type"] == RESULT_TYPE_FORM
assert result["step_id"] == "init"
assert result["errors"] == {"base": "code_format_mismatch"}
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={"code": "123456", "lock_code_digits": 6},
)
assert result["type"] == RESULT_TYPE_CREATE_ENTRY
assert result["data"] == {"code": "123456", "lock_code_digits": 6}