Remove YAML configuration from Verisure (#50076)
parent
b7cd75b134
commit
26b5a067bd
|
@ -3,9 +3,6 @@ from __future__ import annotations
|
|||
|
||||
from contextlib import suppress
|
||||
import os
|
||||
from typing import Any
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.alarm_control_panel import (
|
||||
DOMAIN as ALARM_CONTROL_PANEL_DOMAIN,
|
||||
|
@ -15,27 +12,14 @@ from homeassistant.components.camera import DOMAIN as CAMERA_DOMAIN
|
|||
from homeassistant.components.lock import DOMAIN as LOCK_DOMAIN
|
||||
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
|
||||
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
|
||||
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
|
||||
from homeassistant.const import (
|
||||
CONF_EMAIL,
|
||||
CONF_PASSWORD,
|
||||
CONF_USERNAME,
|
||||
EVENT_HOMEASSISTANT_STOP,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryAuthFailed
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.storage import STORAGE_DIR
|
||||
|
||||
from .const import (
|
||||
CONF_CODE_DIGITS,
|
||||
CONF_DEFAULT_LOCK_CODE,
|
||||
CONF_GIID,
|
||||
CONF_LOCK_CODE_DIGITS,
|
||||
CONF_LOCK_DEFAULT_CODE,
|
||||
DEFAULT_LOCK_CODE_DIGITS,
|
||||
DOMAIN,
|
||||
)
|
||||
from .const import DOMAIN
|
||||
from .coordinator import VerisureDataUpdateCoordinator
|
||||
|
||||
PLATFORMS = [
|
||||
|
@ -47,80 +31,11 @@ PLATFORMS = [
|
|||
SWITCH_DOMAIN,
|
||||
]
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema(
|
||||
vol.All(
|
||||
cv.deprecated(DOMAIN),
|
||||
{
|
||||
DOMAIN: vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_PASSWORD): cv.string,
|
||||
vol.Required(CONF_USERNAME): cv.string,
|
||||
vol.Optional(CONF_CODE_DIGITS): cv.positive_int,
|
||||
vol.Optional(CONF_GIID): cv.string,
|
||||
vol.Optional(CONF_DEFAULT_LOCK_CODE): cv.string,
|
||||
},
|
||||
extra=vol.ALLOW_EXTRA,
|
||||
)
|
||||
},
|
||||
),
|
||||
extra=vol.ALLOW_EXTRA,
|
||||
)
|
||||
|
||||
|
||||
async def async_setup(hass: HomeAssistant, config: dict[str, Any]) -> bool:
|
||||
"""Set up the Verisure integration."""
|
||||
if DOMAIN in config:
|
||||
hass.async_create_task(
|
||||
hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_IMPORT},
|
||||
data={
|
||||
CONF_EMAIL: config[DOMAIN][CONF_USERNAME],
|
||||
CONF_PASSWORD: config[DOMAIN][CONF_PASSWORD],
|
||||
CONF_GIID: config[DOMAIN].get(CONF_GIID),
|
||||
CONF_LOCK_CODE_DIGITS: config[DOMAIN].get(CONF_CODE_DIGITS),
|
||||
CONF_LOCK_DEFAULT_CODE: config[DOMAIN].get(CONF_LOCK_DEFAULT_CODE),
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
return True
|
||||
CONFIG_SCHEMA = cv.deprecated(DOMAIN)
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up Verisure from a config entry."""
|
||||
# Migrate old YAML settings (hidden in the config entry),
|
||||
# to config entry options. Can be removed after YAML support is gone.
|
||||
if CONF_LOCK_CODE_DIGITS in entry.data or CONF_DEFAULT_LOCK_CODE in entry.data:
|
||||
options = entry.options.copy()
|
||||
|
||||
if (
|
||||
CONF_LOCK_CODE_DIGITS in entry.data
|
||||
and CONF_LOCK_CODE_DIGITS not in entry.options
|
||||
and entry.data[CONF_LOCK_CODE_DIGITS] != DEFAULT_LOCK_CODE_DIGITS
|
||||
):
|
||||
options.update(
|
||||
{
|
||||
CONF_LOCK_CODE_DIGITS: entry.data[CONF_LOCK_CODE_DIGITS],
|
||||
}
|
||||
)
|
||||
|
||||
if (
|
||||
CONF_DEFAULT_LOCK_CODE in entry.data
|
||||
and CONF_DEFAULT_LOCK_CODE not in entry.options
|
||||
):
|
||||
options.update(
|
||||
{
|
||||
CONF_DEFAULT_LOCK_CODE: entry.data[CONF_DEFAULT_LOCK_CODE],
|
||||
}
|
||||
)
|
||||
|
||||
data = entry.data.copy()
|
||||
data.pop(CONF_LOCK_CODE_DIGITS, None)
|
||||
data.pop(CONF_DEFAULT_LOCK_CODE, None)
|
||||
hass.config_entries.async_update_entry(entry, data=data, options=options)
|
||||
|
||||
# Continue as normal...
|
||||
coordinator = VerisureDataUpdateCoordinator(hass, entry=entry)
|
||||
|
||||
if not await coordinator.async_login():
|
||||
|
|
|
@ -36,14 +36,6 @@ class VerisureConfigFlowHandler(ConfigFlow, domain=DOMAIN):
|
|||
installations: dict[str, str]
|
||||
password: str
|
||||
|
||||
# These can be removed after YAML import has been removed.
|
||||
giid: str | None = None
|
||||
settings: dict[str, int | str]
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize."""
|
||||
self.settings = {}
|
||||
|
||||
@staticmethod
|
||||
@callback
|
||||
def async_get_options_flow(config_entry: ConfigEntry) -> VerisureOptionsFlowHandler:
|
||||
|
@ -95,8 +87,6 @@ class VerisureConfigFlowHandler(ConfigFlow, domain=DOMAIN):
|
|||
"""Select Verisure installation to add."""
|
||||
if len(self.installations) == 1:
|
||||
user_input = {CONF_GIID: list(self.installations)[0]}
|
||||
elif self.giid and self.giid in self.installations:
|
||||
user_input = {CONF_GIID: self.giid}
|
||||
|
||||
if user_input is None:
|
||||
return self.async_show_form(
|
||||
|
@ -115,7 +105,6 @@ class VerisureConfigFlowHandler(ConfigFlow, domain=DOMAIN):
|
|||
CONF_EMAIL: self.email,
|
||||
CONF_PASSWORD: self.password,
|
||||
CONF_GIID: user_input[CONF_GIID],
|
||||
**self.settings,
|
||||
},
|
||||
)
|
||||
|
||||
|
@ -168,26 +157,6 @@ class VerisureConfigFlowHandler(ConfigFlow, domain=DOMAIN):
|
|||
errors=errors,
|
||||
)
|
||||
|
||||
async def async_step_import(self, user_input: dict[str, Any]) -> FlowResult:
|
||||
"""Import Verisure YAML configuration."""
|
||||
if user_input[CONF_GIID]:
|
||||
self.giid = user_input[CONF_GIID]
|
||||
await self.async_set_unique_id(self.giid)
|
||||
self._abort_if_unique_id_configured()
|
||||
else:
|
||||
# The old YAML configuration could handle 1 single Verisure instance.
|
||||
# Therefore, if we don't know the GIID, we can use the discovery
|
||||
# without a unique ID logic, to prevent re-import/discovery.
|
||||
await self._async_handle_discovery_without_unique_id()
|
||||
|
||||
# Settings, later to be converted to config entry options
|
||||
if user_input[CONF_LOCK_CODE_DIGITS]:
|
||||
self.settings[CONF_LOCK_CODE_DIGITS] = user_input[CONF_LOCK_CODE_DIGITS]
|
||||
if user_input[CONF_LOCK_DEFAULT_CODE]:
|
||||
self.settings[CONF_LOCK_DEFAULT_CODE] = user_input[CONF_LOCK_DEFAULT_CODE]
|
||||
|
||||
return await self.async_step_user(user_input)
|
||||
|
||||
|
||||
class VerisureOptionsFlowHandler(OptionsFlow):
|
||||
"""Handle Verisure options."""
|
||||
|
|
|
@ -44,7 +44,3 @@ ALARM_STATE_TO_HA = {
|
|||
"ARMED_AWAY": STATE_ALARM_ARMED_AWAY,
|
||||
"PENDING": STATE_ALARM_PENDING,
|
||||
}
|
||||
|
||||
# Legacy; to remove after YAML removal
|
||||
CONF_CODE_DIGITS = "code_digits"
|
||||
CONF_DEFAULT_LOCK_CODE = "default_lock_code"
|
||||
|
|
|
@ -44,8 +44,6 @@ async def test_full_user_flow_single_installation(hass: HomeAssistant) -> None:
|
|||
with patch(
|
||||
"homeassistant.components.verisure.config_flow.Verisure",
|
||||
) as mock_verisure, patch(
|
||||
"homeassistant.components.verisure.async_setup", return_value=True
|
||||
) as mock_setup, patch(
|
||||
"homeassistant.components.verisure.async_setup_entry",
|
||||
return_value=True,
|
||||
) as mock_setup_entry:
|
||||
|
@ -72,7 +70,6 @@ async def test_full_user_flow_single_installation(hass: HomeAssistant) -> None:
|
|||
}
|
||||
|
||||
assert len(mock_verisure.mock_calls) == 2
|
||||
assert len(mock_setup.mock_calls) == 1
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
|
@ -107,8 +104,6 @@ async def test_full_user_flow_multiple_installations(hass: HomeAssistant) -> Non
|
|||
assert result2["errors"] is None
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.verisure.async_setup", return_value=True
|
||||
) as mock_setup, patch(
|
||||
"homeassistant.components.verisure.async_setup_entry",
|
||||
return_value=True,
|
||||
) as mock_setup_entry:
|
||||
|
@ -126,7 +121,6 @@ async def test_full_user_flow_multiple_installations(hass: HomeAssistant) -> Non
|
|||
}
|
||||
|
||||
assert len(mock_verisure.mock_calls) == 2
|
||||
assert len(mock_setup.mock_calls) == 1
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
|
@ -220,8 +214,6 @@ async def test_reauth_flow(hass: HomeAssistant) -> None:
|
|||
"homeassistant.components.verisure.config_flow.Verisure.login",
|
||||
return_value=True,
|
||||
) as mock_verisure, patch(
|
||||
"homeassistant.components.verisure.async_setup", return_value=True
|
||||
) as mock_setup, patch(
|
||||
"homeassistant.components.verisure.async_setup_entry",
|
||||
return_value=True,
|
||||
) as mock_setup_entry:
|
||||
|
@ -243,7 +235,6 @@ async def test_reauth_flow(hass: HomeAssistant) -> None:
|
|||
}
|
||||
|
||||
assert len(mock_verisure.mock_calls) == 1
|
||||
assert len(mock_setup.mock_calls) == 1
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
|
@ -365,8 +356,6 @@ async def test_options_flow(
|
|||
entry.add_to_hass(hass)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.verisure.async_setup", return_value=True
|
||||
), patch(
|
||||
"homeassistant.components.verisure.async_setup_entry",
|
||||
return_value=True,
|
||||
):
|
||||
|
@ -397,8 +386,6 @@ async def test_options_flow_code_format_mismatch(hass: HomeAssistant) -> None:
|
|||
entry.add_to_hass(hass)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.verisure.async_setup", return_value=True
|
||||
), patch(
|
||||
"homeassistant.components.verisure.async_setup_entry",
|
||||
return_value=True,
|
||||
):
|
||||
|
@ -422,185 +409,3 @@ async def test_options_flow_code_format_mismatch(hass: HomeAssistant) -> None:
|
|||
assert result["type"] == RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "init"
|
||||
assert result["errors"] == {"base": "code_format_mismatch"}
|
||||
|
||||
|
||||
#
|
||||
# Below this line are tests that can be removed once the YAML configuration
|
||||
# has been removed from this integration.
|
||||
#
|
||||
@pytest.mark.parametrize(
|
||||
"giid,installations",
|
||||
[
|
||||
("12345", TEST_INSTALLATION),
|
||||
("12345", TEST_INSTALLATIONS),
|
||||
(None, TEST_INSTALLATION),
|
||||
],
|
||||
)
|
||||
async def test_imports(
|
||||
hass: HomeAssistant, giid: str | None, installations: dict[str, str]
|
||||
) -> None:
|
||||
"""Test a YAML import with/without known giid on single/multiple installations."""
|
||||
with patch(
|
||||
"homeassistant.components.verisure.config_flow.Verisure",
|
||||
) as mock_verisure, patch(
|
||||
"homeassistant.components.verisure.async_setup", return_value=True
|
||||
) as mock_setup, patch(
|
||||
"homeassistant.components.verisure.async_setup_entry",
|
||||
return_value=True,
|
||||
) as mock_setup_entry:
|
||||
type(mock_verisure.return_value).installations = PropertyMock(
|
||||
return_value=installations
|
||||
)
|
||||
mock_verisure.login.return_value = True
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": config_entries.SOURCE_IMPORT},
|
||||
data={
|
||||
CONF_EMAIL: "verisure_my_pages@example.com",
|
||||
CONF_GIID: giid,
|
||||
CONF_LOCK_CODE_DIGITS: 10,
|
||||
CONF_LOCK_DEFAULT_CODE: "123456",
|
||||
CONF_PASSWORD: "SuperS3cr3t!",
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] == RESULT_TYPE_CREATE_ENTRY
|
||||
assert result["title"] == "ascending (12345th street)"
|
||||
assert result["data"] == {
|
||||
CONF_EMAIL: "verisure_my_pages@example.com",
|
||||
CONF_GIID: "12345",
|
||||
CONF_LOCK_CODE_DIGITS: 10,
|
||||
CONF_LOCK_DEFAULT_CODE: "123456",
|
||||
CONF_PASSWORD: "SuperS3cr3t!",
|
||||
}
|
||||
|
||||
assert len(mock_verisure.mock_calls) == 2
|
||||
assert len(mock_setup.mock_calls) == 1
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
async def test_imports_invalid_login(hass: HomeAssistant) -> None:
|
||||
"""Test a YAML import that results in a invalid login."""
|
||||
with patch(
|
||||
"homeassistant.components.verisure.config_flow.Verisure.login",
|
||||
side_effect=VerisureLoginError,
|
||||
):
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": config_entries.SOURCE_IMPORT},
|
||||
data={
|
||||
CONF_EMAIL: "verisure_my_pages@example.com",
|
||||
CONF_GIID: None,
|
||||
CONF_LOCK_CODE_DIGITS: None,
|
||||
CONF_LOCK_DEFAULT_CODE: None,
|
||||
CONF_PASSWORD: "SuperS3cr3t!",
|
||||
},
|
||||
)
|
||||
|
||||
assert result["step_id"] == "user"
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
assert result["errors"] == {"base": "invalid_auth"}
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.verisure.config_flow.Verisure",
|
||||
) as mock_verisure, patch(
|
||||
"homeassistant.components.verisure.async_setup", return_value=True
|
||||
) as mock_setup, patch(
|
||||
"homeassistant.components.verisure.async_setup_entry",
|
||||
return_value=True,
|
||||
) as mock_setup_entry:
|
||||
type(mock_verisure.return_value).installations = PropertyMock(
|
||||
return_value=TEST_INSTALLATION
|
||||
)
|
||||
mock_verisure.login.return_value = True
|
||||
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
"email": "verisure_my_pages@example.com",
|
||||
"password": "SuperS3cr3t!",
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result2["type"] == RESULT_TYPE_CREATE_ENTRY
|
||||
assert result2["title"] == "ascending (12345th street)"
|
||||
assert result2["data"] == {
|
||||
CONF_GIID: "12345",
|
||||
CONF_EMAIL: "verisure_my_pages@example.com",
|
||||
CONF_PASSWORD: "SuperS3cr3t!",
|
||||
}
|
||||
|
||||
assert len(mock_verisure.mock_calls) == 2
|
||||
assert len(mock_setup.mock_calls) == 1
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
async def test_imports_needs_user_installation_choice(hass: HomeAssistant) -> None:
|
||||
"""Test a YAML import that needs to use to decide on the installation."""
|
||||
with patch(
|
||||
"homeassistant.components.verisure.config_flow.Verisure",
|
||||
) as mock_verisure:
|
||||
type(mock_verisure.return_value).installations = PropertyMock(
|
||||
return_value=TEST_INSTALLATIONS
|
||||
)
|
||||
mock_verisure.login.return_value = True
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": config_entries.SOURCE_IMPORT},
|
||||
data={
|
||||
CONF_EMAIL: "verisure_my_pages@example.com",
|
||||
CONF_GIID: None,
|
||||
CONF_LOCK_CODE_DIGITS: None,
|
||||
CONF_LOCK_DEFAULT_CODE: None,
|
||||
CONF_PASSWORD: "SuperS3cr3t!",
|
||||
},
|
||||
)
|
||||
|
||||
assert result["step_id"] == "installation"
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
assert result["errors"] is None
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.verisure.async_setup", return_value=True
|
||||
) as mock_setup, patch(
|
||||
"homeassistant.components.verisure.async_setup_entry",
|
||||
return_value=True,
|
||||
) as mock_setup_entry:
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], {"giid": "12345"}
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result2["type"] == RESULT_TYPE_CREATE_ENTRY
|
||||
assert result2["title"] == "ascending (12345th street)"
|
||||
assert result2["data"] == {
|
||||
CONF_GIID: "12345",
|
||||
CONF_EMAIL: "verisure_my_pages@example.com",
|
||||
CONF_PASSWORD: "SuperS3cr3t!",
|
||||
}
|
||||
|
||||
assert len(mock_verisure.mock_calls) == 2
|
||||
assert len(mock_setup.mock_calls) == 1
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
@pytest.mark.parametrize("giid", ["12345", None])
|
||||
async def test_import_already_exists(hass: HomeAssistant, giid: str | None) -> None:
|
||||
"""Test that import flow aborts if exists."""
|
||||
MockConfigEntry(domain=DOMAIN, data={}, unique_id="12345").add_to_hass(hass)
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": config_entries.SOURCE_IMPORT},
|
||||
data={
|
||||
CONF_EMAIL: "verisure_my_pages@example.com",
|
||||
CONF_PASSWORD: "SuperS3cr3t!",
|
||||
CONF_GIID: giid,
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] == RESULT_TYPE_ABORT
|
||||
assert result["reason"] == "already_configured"
|
||||
|
|
Loading…
Reference in New Issue