Dedup and clarify imported konnected config flows (#32138)

* dedup config flows

* use default (imported) options until user goes thru options flow

* address pr feedback

* correct key used to distinguish pro model
This commit is contained in:
Kit Klein 2020-02-25 07:55:06 -05:00 committed by GitHub
parent 438c4acf07
commit 5488389244
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 478 additions and 181 deletions

View File

@ -11,9 +11,13 @@
}, },
"step": { "step": {
"confirm": { "confirm": {
"description": "Model: {model}\nHost: {host}\nPort: {port}\n\nYou can configure the IO and panel behavior in the Konnected Alarm Panel settings.", "description": "Model: {model}\nID: {id}\nHost: {host}\nPort: {port}\n\nYou can configure the IO and panel behavior in the Konnected Alarm Panel settings.",
"title": "Konnected Device Ready" "title": "Konnected Device Ready"
}, },
"import_confirm": {
"description": "A Konnected Alarm Panel with ID {id} has been discovered in configuration.yaml. This flow will allow you to import it into a config entry.",
"title": "Import Konnected Device"
},
"user": { "user": {
"data": { "data": {
"host": "Konnected device IP address", "host": "Konnected device IP address",
@ -29,6 +33,7 @@
"abort": { "abort": {
"not_konn_panel": "Not a recognized Konnected.io device" "not_konn_panel": "Not a recognized Konnected.io device"
}, },
"error": {},
"step": { "step": {
"options_binary": { "options_binary": {
"data": { "data": {

View File

@ -32,6 +32,7 @@ from homeassistant.helpers import config_validation as cv
from .const import ( from .const import (
CONF_ACTIVATION, CONF_ACTIVATION,
CONF_BLINK, CONF_BLINK,
CONF_DEFAULT_OPTIONS,
CONF_DISCOVERY, CONF_DISCOVERY,
CONF_INVERSE, CONF_INVERSE,
CONF_MODEL, CONF_MODEL,
@ -138,7 +139,6 @@ OPTIONS_SCHEMA = vol.Schema(
extra=vol.REMOVE_EXTRA, extra=vol.REMOVE_EXTRA,
) )
CONF_DEFAULT_OPTIONS = "default_options"
CONFIG_ENTRY_SCHEMA = vol.Schema( CONFIG_ENTRY_SCHEMA = vol.Schema(
{ {
vol.Required(CONF_ID): cv.matches_regex("[0-9a-f]{12}"), vol.Required(CONF_ID): cv.matches_regex("[0-9a-f]{12}"),
@ -158,6 +158,9 @@ class KonnectedFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
VERSION = 1 VERSION = 1
CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_PUSH CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_PUSH
# class variable to store/share discovered host information
discovered_hosts = {}
# pylint: disable=no-member # https://github.com/PyCQA/pylint/issues/3167 # pylint: disable=no-member # https://github.com/PyCQA/pylint/issues/3167
def __init__(self): def __init__(self):
@ -178,7 +181,7 @@ class KonnectedFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
except (CannotConnect, KeyError): except (CannotConnect, KeyError):
raise CannotConnect raise CannotConnect
else: else:
self.data[CONF_MODEL] = status.get("name", KONN_MODEL) self.data[CONF_MODEL] = status.get("model", KONN_MODEL)
self.data[CONF_ACCESS_TOKEN] = "".join( self.data[CONF_ACCESS_TOKEN] = "".join(
random.choices(f"{string.ascii_uppercase}{string.digits}", k=20) random.choices(f"{string.ascii_uppercase}{string.digits}", k=20)
) )
@ -196,6 +199,7 @@ class KonnectedFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
# config schema ensures we have port if we have host # config schema ensures we have port if we have host
if device_config.get(CONF_HOST): if device_config.get(CONF_HOST):
# automatically connect if we have host info
return await self.async_step_user( return await self.async_step_user(
user_input={ user_input={
CONF_HOST: device_config[CONF_HOST], CONF_HOST: device_config[CONF_HOST],
@ -205,6 +209,28 @@ class KonnectedFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
# if we have no host info wait for it or abort if previously configured # if we have no host info wait for it or abort if previously configured
self._abort_if_unique_id_configured() self._abort_if_unique_id_configured()
return await self.async_step_import_confirm()
async def async_step_import_confirm(self, user_input=None):
"""Confirm the user wants to import the config entry."""
if user_input is None:
return self.async_show_form(
step_id="import_confirm",
description_placeholders={"id": self.unique_id},
)
# if we have ssdp discovered applicable host info use it
if KonnectedFlowHandler.discovered_hosts.get(self.unique_id):
return await self.async_step_user(
user_input={
CONF_HOST: KonnectedFlowHandler.discovered_hosts[self.unique_id][
CONF_HOST
],
CONF_PORT: KonnectedFlowHandler.discovered_hosts[self.unique_id][
CONF_PORT
],
}
)
return await self.async_step_user() return await self.async_step_user()
async def async_step_ssdp(self, discovery_info): async def async_step_ssdp(self, discovery_info):
@ -265,7 +291,13 @@ class KonnectedFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
errors["base"] = "cannot_connect" errors["base"] = "cannot_connect"
else: else:
self.data[CONF_ID] = status["mac"].replace(":", "") self.data[CONF_ID] = status["mac"].replace(":", "")
self.data[CONF_MODEL] = status.get("name", KONN_MODEL) self.data[CONF_MODEL] = status.get("model", KONN_MODEL)
# save off our discovered host info
KonnectedFlowHandler.discovered_hosts[self.data[CONF_ID]] = {
CONF_HOST: self.data[CONF_HOST],
CONF_PORT: self.data[CONF_PORT],
}
return await self.async_step_confirm() return await self.async_step_confirm()
return self.async_show_form( return self.async_show_form(
@ -290,23 +322,14 @@ class KonnectedFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
the connection. the connection.
""" """
if user_input is None: if user_input is None:
# update an existing config entry if host info changes # abort and update an existing config entry if host info changes
entry = await self.async_set_unique_id( await self.async_set_unique_id(self.data[CONF_ID])
self.data[CONF_ID], raise_on_progress=False self._abort_if_unique_id_configured(updates=self.data)
)
if entry and (
entry.data[CONF_HOST] != self.data[CONF_HOST]
or entry.data[CONF_PORT] != self.data[CONF_PORT]
):
entry_data = copy.deepcopy(entry.data)
entry_data.update(self.data)
self.hass.config_entries.async_update_entry(entry, data=entry_data)
self._abort_if_unique_id_configured()
return self.async_show_form( return self.async_show_form(
step_id="confirm", step_id="confirm",
description_placeholders={ description_placeholders={
"model": KONN_PANEL_MODEL_NAMES[self.data[CONF_MODEL]], "model": KONN_PANEL_MODEL_NAMES[self.data[CONF_MODEL]],
"id": self.unique_id,
"host": self.data[CONF_HOST], "host": self.data[CONF_HOST],
"port": self.data[CONF_PORT], "port": self.data[CONF_PORT],
}, },

View File

@ -4,6 +4,7 @@ DOMAIN = "konnected"
CONF_ACTIVATION = "activation" CONF_ACTIVATION = "activation"
CONF_API_HOST = "api_host" CONF_API_HOST = "api_host"
CONF_DEFAULT_OPTIONS = "default_options"
CONF_MOMENTARY = "momentary" CONF_MOMENTARY = "momentary"
CONF_PAUSE = "pause" CONF_PAUSE = "pause"
CONF_POLL_INTERVAL = "poll_interval" CONF_POLL_INTERVAL = "poll_interval"

View File

@ -28,6 +28,7 @@ from .const import (
CONF_ACTIVATION, CONF_ACTIVATION,
CONF_API_HOST, CONF_API_HOST,
CONF_BLINK, CONF_BLINK,
CONF_DEFAULT_OPTIONS,
CONF_DHT_SENSORS, CONF_DHT_SENSORS,
CONF_DISCOVERY, CONF_DISCOVERY,
CONF_DS18B20_SENSORS, CONF_DS18B20_SENSORS,
@ -64,7 +65,9 @@ class AlarmPanel:
self.hass = hass self.hass = hass
self.config_entry = config_entry self.config_entry = config_entry
self.config = config_entry.data self.config = config_entry.data
self.options = config_entry.options self.options = config_entry.options or config_entry.data.get(
CONF_DEFAULT_OPTIONS, {}
)
self.host = self.config.get(CONF_HOST) self.host = self.config.get(CONF_HOST)
self.port = self.config.get(CONF_PORT) self.port = self.config.get(CONF_PORT)
self.client = None self.client = None

View File

@ -2,6 +2,10 @@
"config": { "config": {
"title": "Konnected.io", "title": "Konnected.io",
"step": { "step": {
"import_confirm": {
"title": "Import Konnected Device",
"description": "A Konnected Alarm Panel with ID {id} has been discovered in configuration.yaml. This flow will allow you to import it into a config entry."
},
"user": { "user": {
"title": "Discover Konnected Device", "title": "Discover Konnected Device",
"description": "Please enter the host information for your Konnected Panel.", "description": "Please enter the host information for your Konnected Panel.",
@ -12,7 +16,7 @@
}, },
"confirm": { "confirm": {
"title": "Konnected Device Ready", "title": "Konnected Device Ready",
"description": "Model: {model}\nHost: {host}\nPort: {port}\n\nYou can configure the IO and panel behavior in the Konnected Alarm Panel settings." "description": "Model: {model}\nID: {id}\nHost: {host}\nPort: {port}\n\nYou can configure the IO and panel behavior in the Konnected Alarm Panel settings."
} }
}, },
"error": { "error": {

View File

@ -34,7 +34,7 @@ async def test_flow_works(hass, mock_panel):
mock_panel.get_status.return_value = { mock_panel.get_status.return_value = {
"mac": "11:22:33:44:55:66", "mac": "11:22:33:44:55:66",
"name": "Konnected", "model": "Konnected",
} }
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input={"port": 1234, "host": "1.2.3.4"} result["flow_id"], user_input={"port": 1234, "host": "1.2.3.4"}
@ -43,6 +43,7 @@ async def test_flow_works(hass, mock_panel):
assert result["step_id"] == "confirm" assert result["step_id"] == "confirm"
assert result["description_placeholders"] == { assert result["description_placeholders"] == {
"model": "Konnected Alarm Panel", "model": "Konnected Alarm Panel",
"id": "112233445566",
"host": "1.2.3.4", "host": "1.2.3.4",
"port": 1234, "port": 1234,
} }
@ -70,7 +71,7 @@ async def test_pro_flow_works(hass, mock_panel):
mock_panel.get_status.return_value = { mock_panel.get_status.return_value = {
"mac": "11:22:33:44:55:66", "mac": "11:22:33:44:55:66",
"name": "Konnected Pro", "model": "Konnected Pro",
} }
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input={"port": 1234, "host": "1.2.3.4"} result["flow_id"], user_input={"port": 1234, "host": "1.2.3.4"}
@ -79,6 +80,7 @@ async def test_pro_flow_works(hass, mock_panel):
assert result["step_id"] == "confirm" assert result["step_id"] == "confirm"
assert result["description_placeholders"] == { assert result["description_placeholders"] == {
"model": "Konnected Alarm Panel Pro", "model": "Konnected Alarm Panel Pro",
"id": "112233445566",
"host": "1.2.3.4", "host": "1.2.3.4",
"port": 1234, "port": 1234,
} }
@ -100,7 +102,7 @@ async def test_ssdp(hass, mock_panel):
"""Test a panel being discovered.""" """Test a panel being discovered."""
mock_panel.get_status.return_value = { mock_panel.get_status.return_value = {
"mac": "11:22:33:44:55:66", "mac": "11:22:33:44:55:66",
"name": "Konnected", "model": "Konnected",
} }
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
@ -117,6 +119,7 @@ async def test_ssdp(hass, mock_panel):
assert result["step_id"] == "confirm" assert result["step_id"] == "confirm"
assert result["description_placeholders"] == { assert result["description_placeholders"] == {
"model": "Konnected Alarm Panel", "model": "Konnected Alarm Panel",
"id": "112233445566",
"host": "1.2.3.4", "host": "1.2.3.4",
"port": 1234, "port": 1234,
} }
@ -125,8 +128,8 @@ async def test_ssdp(hass, mock_panel):
async def test_import_no_host_user_finish(hass, mock_panel): async def test_import_no_host_user_finish(hass, mock_panel):
"""Test importing a panel with no host info.""" """Test importing a panel with no host info."""
mock_panel.get_status.return_value = { mock_panel.get_status.return_value = {
"mac": "11:22:33:44:55:66", "mac": "aa:bb:cc:dd:ee:ff",
"name": "Konnected Pro", "model": "Konnected Pro",
} }
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
@ -159,6 +162,13 @@ async def test_import_no_host_user_finish(hass, mock_panel):
}, },
) )
assert result["type"] == "form" assert result["type"] == "form"
assert result["step_id"] == "import_confirm"
assert result["description_placeholders"]["id"] == "aabbccddeeff"
result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input={}
)
assert result["type"] == "form"
assert result["step_id"] == "user" assert result["step_id"] == "user"
# confirm user is prompted to enter host # confirm user is prompted to enter host
@ -169,6 +179,7 @@ async def test_import_no_host_user_finish(hass, mock_panel):
assert result["step_id"] == "confirm" assert result["step_id"] == "confirm"
assert result["description_placeholders"] == { assert result["description_placeholders"] == {
"model": "Konnected Alarm Panel Pro", "model": "Konnected Alarm Panel Pro",
"id": "aabbccddeeff",
"host": "1.1.1.1", "host": "1.1.1.1",
"port": 1234, "port": 1234,
} }
@ -180,6 +191,78 @@ async def test_import_no_host_user_finish(hass, mock_panel):
assert result["type"] == "create_entry" assert result["type"] == "create_entry"
async def test_import_ssdp_host_user_finish(hass, mock_panel):
"""Test importing a panel with no host info which ssdp discovers."""
mock_panel.get_status.return_value = {
"mac": "11:22:33:44:55:66",
"model": "Konnected Pro",
}
result = await hass.config_entries.flow.async_init(
config_flow.DOMAIN,
context={"source": "import"},
data={
"default_options": {
"blink": True,
"discovery": True,
"io": {
"1": "Disabled",
"10": "Disabled",
"11": "Disabled",
"12": "Disabled",
"2": "Disabled",
"3": "Disabled",
"4": "Disabled",
"5": "Disabled",
"6": "Disabled",
"7": "Disabled",
"8": "Disabled",
"9": "Disabled",
"alarm1": "Disabled",
"alarm2_out2": "Disabled",
"out": "Disabled",
"out1": "Disabled",
},
},
"id": "112233445566",
},
)
assert result["type"] == "form"
assert result["step_id"] == "import_confirm"
assert result["description_placeholders"]["id"] == "112233445566"
# discover the panel via ssdp
ssdp_result = await hass.config_entries.flow.async_init(
config_flow.DOMAIN,
context={"source": "ssdp"},
data={
"ssdp_location": "http://0.0.0.0:1234/Device.xml",
"manufacturer": config_flow.KONN_MANUFACTURER,
"modelName": config_flow.KONN_MODEL_PRO,
},
)
assert ssdp_result["type"] == "abort"
assert ssdp_result["reason"] == "already_in_progress"
result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input={}
)
assert result["type"] == "form"
assert result["step_id"] == "confirm"
assert result["description_placeholders"] == {
"model": "Konnected Alarm Panel Pro",
"id": "112233445566",
"host": "0.0.0.0",
"port": 1234,
}
# final confirmation
result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input={}
)
assert result["type"] == "create_entry"
async def test_ssdp_already_configured(hass, mock_panel): async def test_ssdp_already_configured(hass, mock_panel):
"""Test if a discovered panel has already been configured.""" """Test if a discovered panel has already been configured."""
MockConfigEntry( MockConfigEntry(
@ -189,7 +272,7 @@ async def test_ssdp_already_configured(hass, mock_panel):
).add_to_hass(hass) ).add_to_hass(hass)
mock_panel.get_status.return_value = { mock_panel.get_status.return_value = {
"mac": "11:22:33:44:55:66", "mac": "11:22:33:44:55:66",
"name": "Konnected Pro", "model": "Konnected Pro",
} }
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
@ -265,7 +348,7 @@ async def test_ssdp_host_update(hass, mock_panel):
).add_to_hass(hass) ).add_to_hass(hass)
mock_panel.get_status.return_value = { mock_panel.get_status.return_value = {
"mac": "11:22:33:44:55:66", "mac": "11:22:33:44:55:66",
"name": "Konnected Pro", "model": "Konnected Pro",
} }
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
@ -289,7 +372,7 @@ async def test_import_existing_config(hass, mock_panel):
"""Test importing a host with an existing config file.""" """Test importing a host with an existing config file."""
mock_panel.get_status.return_value = { mock_panel.get_status.return_value = {
"mac": "11:22:33:44:55:66", "mac": "11:22:33:44:55:66",
"name": "Konnected Pro", "model": "Konnected Pro",
} }
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
@ -402,7 +485,7 @@ async def test_import_existing_config_entry(hass, mock_panel):
mock_panel.get_status.return_value = { mock_panel.get_status.return_value = {
"mac": "11:22:33:44:55:66", "mac": "11:22:33:44:55:66",
"name": "Konnected Pro", "model": "Konnected Pro",
} }
# utilize a global access token this time # utilize a global access token this time
@ -462,7 +545,7 @@ async def test_import_pin_config(hass, mock_panel):
"""Test importing a host with an existing config file that specifies pin configs.""" """Test importing a host with an existing config file that specifies pin configs."""
mock_panel.get_status.return_value = { mock_panel.get_status.return_value = {
"mac": "11:22:33:44:55:66", "mac": "11:22:33:44:55:66",
"name": "Konnected Pro", "model": "Konnected Pro",
} }
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(

View File

@ -3,6 +3,7 @@ from asynctest import patch
import pytest import pytest
from homeassistant.components.konnected import config_flow, panel from homeassistant.components.konnected import config_flow, panel
from homeassistant.setup import async_setup_component
from tests.common import MockConfigEntry from tests.common import MockConfigEntry
@ -92,9 +93,6 @@ async def test_create_and_setup(hass, mock_panel):
options=device_options, options=device_options,
) )
entry.add_to_hass(hass) entry.add_to_hass(hass)
hass.data[panel.DOMAIN] = {
panel.CONF_API_HOST: "192.168.1.1",
}
# override get_status to reflect non-pro board # override get_status to reflect non-pro board
mock_panel.get_status.return_value = { mock_panel.get_status.return_value = {
@ -111,19 +109,35 @@ async def test_create_and_setup(hass, mock_panel):
"mac": "11:22:33:44:55:66", "mac": "11:22:33:44:55:66",
"settings": {}, "settings": {},
} }
device = panel.AlarmPanel(hass, entry)
await device.async_save_data() # setup the integration and inspect panel behavior
await device.async_connect() assert (
await async_setup_component(
hass,
panel.DOMAIN,
{
panel.DOMAIN: {
panel.CONF_ACCESS_TOKEN: "arandomstringvalue",
panel.CONF_API_HOST: "http://192.168.1.1:8123",
}
},
)
is True
)
# confirm panel instance was created and configured
# hass.data is the only mechanism to get a reference to the created panel instance
device = hass.data[panel.DOMAIN][panel.CONF_DEVICES]["112233445566"]["panel"]
await device.update_switch("1", 0) await device.update_switch("1", 0)
# confirm the correct api is used # confirm the correct api is used
# pylint: disable=no-member # pylint: disable=no-member
assert device.client.put_device.call_count == 1 assert mock_panel.put_device.call_count == 1
assert device.client.put_zone.call_count == 0 assert mock_panel.put_zone.call_count == 0
# confirm the settings are sent to the panel # confirm the settings are sent to the panel
# pylint: disable=no-member # pylint: disable=no-member
assert device.client.put_settings.call_args_list[0][1] == { assert mock_panel.put_settings.call_args_list[0][1] == {
"sensors": [{"pin": "1"}, {"pin": "2"}, {"pin": "5"}], "sensors": [{"pin": "1"}, {"pin": "2"}, {"pin": "5"}],
"actuators": [{"trigger": 0, "pin": "8"}, {"trigger": 1, "pin": "9"}], "actuators": [{"trigger": 0, "pin": "8"}, {"trigger": 1, "pin": "9"}],
"dht_sensors": [{"poll_interval": 3, "pin": "6"}], "dht_sensors": [{"poll_interval": 3, "pin": "6"}],
@ -131,67 +145,60 @@ async def test_create_and_setup(hass, mock_panel):
"auth_token": "11223344556677889900", "auth_token": "11223344556677889900",
"blink": True, "blink": True,
"discovery": True, "discovery": True,
"endpoint": "192.168.1.1/api/konnected", "endpoint": "http://192.168.1.1:8123/api/konnected",
} }
# confirm the device settings are saved in hass.data # confirm the device settings are saved in hass.data
assert hass.data[panel.DOMAIN][panel.CONF_DEVICES] == { assert device.stored_configuration == {
"112233445566": { "binary_sensors": {
"binary_sensors": { "1": {
"1": { "inverse": False,
"inverse": False, "name": "Konnected 445566 Zone 1",
"name": "Konnected 445566 Zone 1", "state": None,
"state": None, "type": "door",
"type": "door",
},
"2": {
"inverse": True,
"name": "winder",
"state": None,
"type": "window",
},
"3": {
"inverse": False,
"name": "Konnected 445566 Zone 3",
"state": None,
"type": "door",
},
}, },
"blink": True, "2": {"inverse": True, "name": "winder", "state": None, "type": "window"},
"panel": device, "3": {
"discovery": True, "inverse": False,
"host": "1.2.3.4", "name": "Konnected 445566 Zone 3",
"port": 1234, "state": None,
"sensors": [ "type": "door",
{ },
"name": "Konnected 445566 Sensor 4", },
"poll_interval": 3, "blink": True,
"type": "dht", "panel": device,
"zone": "4", "discovery": True,
}, "host": "1.2.3.4",
{"name": "temper", "poll_interval": 3, "type": "ds18b20", "zone": "5"}, "port": 1234,
], "sensors": [
"switches": [ {
{ "name": "Konnected 445566 Sensor 4",
"activation": "low", "poll_interval": 3,
"momentary": 50, "type": "dht",
"name": "switcher", "zone": "4",
"pause": 100, },
"repeat": 4, {"name": "temper", "poll_interval": 3, "type": "ds18b20", "zone": "5"},
"state": None, ],
"zone": "out", "switches": [
}, {
{ "activation": "low",
"activation": "high", "momentary": 50,
"momentary": None, "name": "switcher",
"name": "Konnected 445566 Actuator 6", "pause": 100,
"pause": None, "repeat": 4,
"repeat": None, "state": None,
"state": None, "zone": "out",
"zone": "6", },
}, {
], "activation": "high",
} "momentary": None,
"name": "Konnected 445566 Actuator 6",
"pause": None,
"repeat": None,
"state": None,
"zone": "6",
},
],
} }
@ -255,23 +262,35 @@ async def test_create_and_setup_pro(hass, mock_panel):
options=device_options, options=device_options,
) )
entry.add_to_hass(hass) entry.add_to_hass(hass)
hass.data[panel.DOMAIN] = {
panel.CONF_API_HOST: "192.168.1.1",
}
device = panel.AlarmPanel(hass, entry) # setup the integration and inspect panel behavior
await device.async_save_data() assert (
await device.async_connect() await async_setup_component(
hass,
panel.DOMAIN,
{
panel.DOMAIN: {
panel.CONF_ACCESS_TOKEN: "arandomstringvalue",
panel.CONF_API_HOST: "http://192.168.1.1:8123",
}
},
)
is True
)
# confirm panel instance was created and configured
# hass.data is the only mechanism to get a reference to the created panel instance
device = hass.data[panel.DOMAIN][panel.CONF_DEVICES]["112233445566"]["panel"]
await device.update_switch("2", 1) await device.update_switch("2", 1)
# confirm the correct api is used # confirm the correct api is used
# pylint: disable=no-member # pylint: disable=no-member
assert device.client.put_device.call_count == 0 assert mock_panel.put_device.call_count == 0
assert device.client.put_zone.call_count == 1 assert mock_panel.put_zone.call_count == 1
# confirm the settings are sent to the panel # confirm the settings are sent to the panel
# pylint: disable=no-member # pylint: disable=no-member
assert device.client.put_settings.call_args_list[0][1] == { assert mock_panel.put_settings.call_args_list[0][1] == {
"sensors": [{"zone": "2"}, {"zone": "6"}, {"zone": "10"}], "sensors": [{"zone": "2"}, {"zone": "6"}, {"zone": "10"}],
"actuators": [ "actuators": [
{"trigger": 1, "zone": "4"}, {"trigger": 1, "zone": "4"},
@ -287,89 +306,248 @@ async def test_create_and_setup_pro(hass, mock_panel):
"auth_token": "11223344556677889900", "auth_token": "11223344556677889900",
"blink": True, "blink": True,
"discovery": True, "discovery": True,
"endpoint": "192.168.1.1/api/konnected", "endpoint": "http://192.168.1.1:8123/api/konnected",
} }
# confirm the device settings are saved in hass.data # confirm the device settings are saved in hass.data
assert hass.data[panel.DOMAIN][panel.CONF_DEVICES] == { assert device.stored_configuration == {
"112233445566": { "binary_sensors": {
"binary_sensors": { "10": {
"10": { "inverse": False,
"inverse": False, "name": "Konnected 445566 Zone 10",
"name": "Konnected 445566 Zone 10", "state": None,
"state": None, "type": "door",
"type": "door",
},
"2": {
"inverse": False,
"name": "Konnected 445566 Zone 2",
"state": None,
"type": "door",
},
"6": {
"inverse": True,
"name": "winder",
"state": None,
"type": "window",
},
}, },
"blink": True, "2": {
"panel": device, "inverse": False,
"discovery": True, "name": "Konnected 445566 Zone 2",
"state": None,
"type": "door",
},
"6": {"inverse": True, "name": "winder", "state": None, "type": "window"},
},
"blink": True,
"panel": device,
"discovery": True,
"host": "1.2.3.4",
"port": 1234,
"sensors": [
{
"name": "Konnected 445566 Sensor 3",
"poll_interval": 3,
"type": "dht",
"zone": "3",
},
{"name": "temper", "poll_interval": 3, "type": "ds18b20", "zone": "7"},
{
"name": "Konnected 445566 Sensor 11",
"poll_interval": 5,
"type": "dht",
"zone": "11",
},
],
"switches": [
{
"activation": "high",
"momentary": None,
"name": "Konnected 445566 Actuator 4",
"pause": None,
"repeat": None,
"state": None,
"zone": "4",
},
{
"activation": "low",
"momentary": 50,
"name": "switcher",
"pause": 100,
"repeat": 4,
"state": None,
"zone": "8",
},
{
"activation": "high",
"momentary": None,
"name": "Konnected 445566 Actuator out1",
"pause": None,
"repeat": None,
"state": None,
"zone": "out1",
},
{
"activation": "high",
"momentary": None,
"name": "Konnected 445566 Actuator alarm1",
"pause": None,
"repeat": None,
"state": None,
"zone": "alarm1",
},
],
}
async def test_default_options(hass, mock_panel):
"""Test that we create a Konnected Panel and save the data."""
device_config = config_flow.CONFIG_ENTRY_SCHEMA(
{
"host": "1.2.3.4", "host": "1.2.3.4",
"port": 1234, "port": 1234,
"sensors": [ "id": "112233445566",
"model": "Konnected Pro",
"access_token": "11223344556677889900",
"default_options": config_flow.OPTIONS_SCHEMA(
{ {
"name": "Konnected 445566 Sensor 3", "io": {
"poll_interval": 3, "1": "Binary Sensor",
"type": "dht", "2": "Binary Sensor",
"zone": "3", "3": "Binary Sensor",
}, "4": "Digital Sensor",
{"name": "temper", "poll_interval": 3, "type": "ds18b20", "zone": "7"}, "5": "Digital Sensor",
{ "6": "Switchable Output",
"name": "Konnected 445566 Sensor 11", "out": "Switchable Output",
"poll_interval": 5, },
"type": "dht", "binary_sensors": [
"zone": "11", {"zone": "1", "type": "door"},
}, {
], "zone": "2",
"switches": [ "type": "window",
{ "name": "winder",
"activation": "high", "inverse": True,
"momentary": None, },
"name": "Konnected 445566 Actuator 4", {"zone": "3", "type": "door"},
"pause": None, ],
"repeat": None, "sensors": [
"state": None, {"zone": "4", "type": "dht"},
"zone": "4", {"zone": "5", "type": "ds18b20", "name": "temper"},
}, ],
{ "switches": [
"activation": "low", {
"momentary": 50, "zone": "out",
"name": "switcher", "name": "switcher",
"pause": 100, "activation": "low",
"repeat": 4, "momentary": 50,
"state": None, "pause": 100,
"zone": "8", "repeat": 4,
}, },
{ {"zone": "6"},
"activation": "high", ],
"momentary": None, }
"name": "Konnected 445566 Actuator out1", ),
"pause": None,
"repeat": None,
"state": None,
"zone": "out1",
},
{
"activation": "high",
"momentary": None,
"name": "Konnected 445566 Actuator alarm1",
"pause": None,
"repeat": None,
"state": None,
"zone": "alarm1",
},
],
} }
)
entry = MockConfigEntry(
domain="konnected",
title="Konnected Alarm Panel",
data=device_config,
options={},
)
entry.add_to_hass(hass)
# override get_status to reflect non-pro board
mock_panel.get_status.return_value = {
"hwVersion": "2.3.0",
"swVersion": "2.3.1",
"heap": 10000,
"uptime": 12222,
"ip": "192.168.1.90",
"port": 9123,
"sensors": [],
"actuators": [],
"dht_sensors": [],
"ds18b20_sensors": [],
"mac": "11:22:33:44:55:66",
"settings": {},
}
# setup the integration and inspect panel behavior
assert (
await async_setup_component(
hass,
panel.DOMAIN,
{
panel.DOMAIN: {
panel.CONF_ACCESS_TOKEN: "arandomstringvalue",
panel.CONF_API_HOST: "http://192.168.1.1:8123",
}
},
)
is True
)
# confirm panel instance was created and configured.
# hass.data is the only mechanism to get a reference to the created panel instance
device = hass.data[panel.DOMAIN][panel.CONF_DEVICES]["112233445566"]["panel"]
await device.update_switch("1", 0)
# confirm the correct api is used
# pylint: disable=no-member
assert mock_panel.put_device.call_count == 1
assert mock_panel.put_zone.call_count == 0
# confirm the settings are sent to the panel
# pylint: disable=no-member
assert mock_panel.put_settings.call_args_list[0][1] == {
"sensors": [{"pin": "1"}, {"pin": "2"}, {"pin": "5"}],
"actuators": [{"trigger": 0, "pin": "8"}, {"trigger": 1, "pin": "9"}],
"dht_sensors": [{"poll_interval": 3, "pin": "6"}],
"ds18b20_sensors": [{"pin": "7"}],
"auth_token": "11223344556677889900",
"blink": True,
"discovery": True,
"endpoint": "http://192.168.1.1:8123/api/konnected",
}
# confirm the device settings are saved in hass.data
assert device.stored_configuration == {
"binary_sensors": {
"1": {
"inverse": False,
"name": "Konnected 445566 Zone 1",
"state": None,
"type": "door",
},
"2": {"inverse": True, "name": "winder", "state": None, "type": "window"},
"3": {
"inverse": False,
"name": "Konnected 445566 Zone 3",
"state": None,
"type": "door",
},
},
"blink": True,
"panel": device,
"discovery": True,
"host": "1.2.3.4",
"port": 1234,
"sensors": [
{
"name": "Konnected 445566 Sensor 4",
"poll_interval": 3,
"type": "dht",
"zone": "4",
},
{"name": "temper", "poll_interval": 3, "type": "ds18b20", "zone": "5"},
],
"switches": [
{
"activation": "low",
"momentary": 50,
"name": "switcher",
"pause": 100,
"repeat": 4,
"state": None,
"zone": "out",
},
{
"activation": "high",
"momentary": None,
"name": "Konnected 445566 Actuator 6",
"pause": None,
"repeat": None,
"state": None,
"zone": "6",
},
],
} }