allow overriding host api url in config flow (#33481)

* allow overriding host api url in config flow

* fix typo

* capitalize URL
This commit is contained in:
Kit Klein 2020-03-31 18:50:37 -04:00 committed by GitHub
parent b892dbc6ea
commit 955c94e313
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 91 additions and 20 deletions

View file

@ -33,6 +33,9 @@
"abort": { "abort": {
"not_konn_panel": "Not a recognized Konnected.io device" "not_konn_panel": "Not a recognized Konnected.io device"
}, },
"error": {
"bad_host": "Invalid Override API host url"
},
"step": { "step": {
"options_binary": { "options_binary": {
"data": { "data": {
@ -82,7 +85,9 @@
}, },
"options_misc": { "options_misc": {
"data": { "data": {
"blink": "Blink panel LED on when sending state change" "api_host": "Override API host URL (optional)",
"blink": "Blink panel LED on when sending state change",
"override_api_host": "Override default Home Assistant API host panel URL"
}, },
"description": "Please select the desired behavior for your panel", "description": "Please select the desired behavior for your panel",
"title": "Configure Misc" "title": "Configure Misc"

View file

@ -91,7 +91,7 @@ def ensure_zone(value):
return str(value) return str(value)
def import_validator(config): def import_device_validator(config):
"""Validate zones and reformat for import.""" """Validate zones and reformat for import."""
config = copy.deepcopy(config) config = copy.deepcopy(config)
io_cfgs = {} io_cfgs = {}
@ -117,10 +117,22 @@ def import_validator(config):
config.pop(CONF_SWITCHES, None) config.pop(CONF_SWITCHES, None)
config.pop(CONF_BLINK, None) config.pop(CONF_BLINK, None)
config.pop(CONF_DISCOVERY, None) config.pop(CONF_DISCOVERY, None)
config.pop(CONF_API_HOST, None)
config.pop(CONF_IO, None) config.pop(CONF_IO, None)
return config return config
def import_validator(config):
"""Reformat for import."""
config = copy.deepcopy(config)
# push api_host into device configs
for device in config.get(CONF_DEVICES, []):
device[CONF_API_HOST] = config.get(CONF_API_HOST, "")
return config
# configuration.yaml schemas (legacy) # configuration.yaml schemas (legacy)
BINARY_SENSOR_SCHEMA_YAML = vol.All( BINARY_SENSOR_SCHEMA_YAML = vol.All(
vol.Schema( vol.Schema(
@ -179,23 +191,27 @@ DEVICE_SCHEMA_YAML = vol.All(
vol.Inclusive(CONF_HOST, "host_info"): cv.string, vol.Inclusive(CONF_HOST, "host_info"): cv.string,
vol.Inclusive(CONF_PORT, "host_info"): cv.port, vol.Inclusive(CONF_PORT, "host_info"): cv.port,
vol.Optional(CONF_BLINK, default=True): cv.boolean, vol.Optional(CONF_BLINK, default=True): cv.boolean,
vol.Optional(CONF_API_HOST, default=""): vol.Any("", cv.url),
vol.Optional(CONF_DISCOVERY, default=True): cv.boolean, vol.Optional(CONF_DISCOVERY, default=True): cv.boolean,
} }
), ),
import_validator, import_device_validator,
) )
# pylint: disable=no-value-for-parameter # pylint: disable=no-value-for-parameter
CONFIG_SCHEMA = vol.Schema( CONFIG_SCHEMA = vol.Schema(
{ {
DOMAIN: vol.Schema( DOMAIN: vol.All(
{ import_validator,
vol.Required(CONF_ACCESS_TOKEN): cv.string, vol.Schema(
vol.Optional(CONF_API_HOST): vol.Url(), {
vol.Optional(CONF_DEVICES): vol.All( vol.Required(CONF_ACCESS_TOKEN): cv.string,
cv.ensure_list, [DEVICE_SCHEMA_YAML] vol.Optional(CONF_API_HOST): vol.Url(),
), vol.Optional(CONF_DEVICES): vol.All(
} cv.ensure_list, [DEVICE_SCHEMA_YAML]
),
}
),
) )
}, },
extra=vol.ALLOW_EXTRA, extra=vol.ALLOW_EXTRA,

View file

@ -31,6 +31,7 @@ from homeassistant.helpers import config_validation as cv
from .const import ( from .const import (
CONF_ACTIVATION, CONF_ACTIVATION,
CONF_API_HOST,
CONF_BLINK, CONF_BLINK,
CONF_DEFAULT_OPTIONS, CONF_DEFAULT_OPTIONS,
CONF_DISCOVERY, CONF_DISCOVERY,
@ -61,6 +62,8 @@ CONF_MORE_STATES = "more_states"
CONF_YES = "Yes" CONF_YES = "Yes"
CONF_NO = "No" CONF_NO = "No"
CONF_OVERRIDE_API_HOST = "override_api_host"
KONN_MANUFACTURER = "konnected.io" KONN_MANUFACTURER = "konnected.io"
KONN_PANEL_MODEL_NAMES = { KONN_PANEL_MODEL_NAMES = {
KONN_MODEL: "Konnected Alarm Panel", KONN_MODEL: "Konnected Alarm Panel",
@ -138,6 +141,7 @@ OPTIONS_SCHEMA = vol.Schema(
vol.Optional(CONF_SENSORS): vol.All(cv.ensure_list, [SENSOR_SCHEMA]), vol.Optional(CONF_SENSORS): vol.All(cv.ensure_list, [SENSOR_SCHEMA]),
vol.Optional(CONF_SWITCHES): vol.All(cv.ensure_list, [SWITCH_SCHEMA]), vol.Optional(CONF_SWITCHES): vol.All(cv.ensure_list, [SWITCH_SCHEMA]),
vol.Optional(CONF_BLINK, default=True): cv.boolean, vol.Optional(CONF_BLINK, default=True): cv.boolean,
vol.Optional(CONF_API_HOST, default=""): vol.Any("", cv.url),
vol.Optional(CONF_DISCOVERY, default=True): cv.boolean, vol.Optional(CONF_DISCOVERY, default=True): cv.boolean,
}, },
extra=vol.REMOVE_EXTRA, extra=vol.REMOVE_EXTRA,
@ -785,8 +789,19 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
"""Allow the user to configure the LED behavior.""" """Allow the user to configure the LED behavior."""
errors = {} errors = {}
if user_input is not None: if user_input is not None:
self.new_opt[CONF_BLINK] = user_input[CONF_BLINK] # config schema only does basic schema val so check url here
return self.async_create_entry(title="", data=self.new_opt) try:
if user_input[CONF_OVERRIDE_API_HOST]:
cv.url(user_input.get(CONF_API_HOST, ""))
else:
user_input[CONF_API_HOST] = ""
except vol.Invalid:
errors["base"] = "bad_host"
else:
# no need to store the override - can infer
del user_input[CONF_OVERRIDE_API_HOST]
self.new_opt.update(user_input)
return self.async_create_entry(title="", data=self.new_opt)
return self.async_show_form( return self.async_show_form(
step_id="options_misc", step_id="options_misc",
@ -795,6 +810,13 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
vol.Required( vol.Required(
CONF_BLINK, default=self.current_opt.get(CONF_BLINK, True) CONF_BLINK, default=self.current_opt.get(CONF_BLINK, True)
): bool, ): bool,
vol.Required(
CONF_OVERRIDE_API_HOST,
default=bool(self.current_opt.get(CONF_API_HOST)),
): bool,
vol.Optional(
CONF_API_HOST, default=self.current_opt.get(CONF_API_HOST, "")
): str,
} }
), ),
errors=errors, errors=errors,

View file

@ -294,7 +294,9 @@ class AlarmPanel:
@callback @callback
def async_desired_settings_payload(self): def async_desired_settings_payload(self):
"""Return a dict representing the desired device configuration.""" """Return a dict representing the desired device configuration."""
desired_api_host = ( # keeping self.hass.data check for backwards compatibility
# newly configured integrations store this in the config entry
desired_api_host = self.options.get(CONF_API_HOST) or (
self.hass.data[DOMAIN].get(CONF_API_HOST) or self.hass.config.api.base_url self.hass.data[DOMAIN].get(CONF_API_HOST) or self.hass.config.api.base_url
) )
desired_api_endpoint = desired_api_host + ENDPOINT_ROOT desired_api_endpoint = desired_api_host + ENDPOINT_ROOT

View file

@ -94,11 +94,15 @@
"title": "Configure Misc", "title": "Configure Misc",
"description": "Please select the desired behavior for your panel", "description": "Please select the desired behavior for your panel",
"data": { "data": {
"blink": "Blink panel LED on when sending state change" "blink": "Blink panel LED on when sending state change",
"override_api_host": "Override default Home Assistant API host panel URL",
"api_host": "Override API host URL (optional)"
} }
} }
}, },
"error": {}, "error": {
"bad_host": "Invalid Override API host url"
},
"abort": { "abort": {
"not_konn_panel": "Not a recognized Konnected.io device" "not_konn_panel": "Not a recognized Konnected.io device"
} }

View file

@ -450,6 +450,7 @@ async def test_import_existing_config(hass, mock_panel):
"alarm1": "Switchable Output", "alarm1": "Switchable Output",
}, },
"blink": True, "blink": True,
"api_host": "",
"discovery": True, "discovery": True,
"binary_sensors": [ "binary_sensors": [
{"zone": "2", "type": "door", "inverse": False}, {"zone": "2", "type": "door", "inverse": False},
@ -628,6 +629,7 @@ async def test_import_pin_config(hass, mock_panel):
"out": "Switchable Output", "out": "Switchable Output",
}, },
"blink": True, "blink": True,
"api_host": "",
"discovery": True, "discovery": True,
"binary_sensors": [ "binary_sensors": [
{"zone": "1", "type": "door", "inverse": False}, {"zone": "1", "type": "door", "inverse": False},
@ -778,9 +780,21 @@ async def test_option_flow(hass, mock_panel):
assert result["type"] == "form" assert result["type"] == "form"
assert result["step_id"] == "options_misc" assert result["step_id"] == "options_misc"
# make sure we enforce url format
result = await hass.config_entries.options.async_configure( result = await hass.config_entries.options.async_configure(
result["flow_id"], user_input={"blink": True}, result["flow_id"],
user_input={"blink": True, "override_api_host": True, "api_host": "badhosturl"},
)
assert result["type"] == "form"
assert result["step_id"] == "options_misc"
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={
"blink": True,
"override_api_host": True,
"api_host": "http://overridehost:1111",
},
) )
assert result["type"] == "create_entry" assert result["type"] == "create_entry"
assert result["data"] == { assert result["data"] == {
@ -792,6 +806,7 @@ async def test_option_flow(hass, mock_panel):
"out": "Switchable Output", "out": "Switchable Output",
}, },
"blink": True, "blink": True,
"api_host": "http://overridehost:1111",
"binary_sensors": [ "binary_sensors": [
{"zone": "2", "type": "door", "inverse": False}, {"zone": "2", "type": "door", "inverse": False},
{"zone": "6", "type": "window", "name": "winder", "inverse": True}, {"zone": "6", "type": "window", "name": "winder", "inverse": True},
@ -958,7 +973,7 @@ async def test_option_flow_pro(hass, mock_panel):
assert result["step_id"] == "options_misc" assert result["step_id"] == "options_misc"
result = await hass.config_entries.options.async_configure( result = await hass.config_entries.options.async_configure(
result["flow_id"], user_input={"blink": True}, result["flow_id"], user_input={"blink": True, "override_api_host": False},
) )
assert result["type"] == "create_entry" assert result["type"] == "create_entry"
@ -976,6 +991,7 @@ async def test_option_flow_pro(hass, mock_panel):
"out1": "Switchable Output", "out1": "Switchable Output",
}, },
"blink": True, "blink": True,
"api_host": "",
"binary_sensors": [ "binary_sensors": [
{"zone": "2", "type": "door", "inverse": False}, {"zone": "2", "type": "door", "inverse": False},
{"zone": "6", "type": "window", "name": "winder", "inverse": True}, {"zone": "6", "type": "window", "name": "winder", "inverse": True},
@ -1121,7 +1137,7 @@ async def test_option_flow_import(hass, mock_panel):
schema = result["data_schema"]({}) schema = result["data_schema"]({})
assert schema["blink"] is True assert schema["blink"] is True
result = await hass.config_entries.options.async_configure( result = await hass.config_entries.options.async_configure(
result["flow_id"], user_input={"blink": False}, result["flow_id"], user_input={"blink": False, "override_api_host": False},
) )
# verify the updated fields # verify the updated fields
@ -1129,6 +1145,7 @@ async def test_option_flow_import(hass, mock_panel):
assert result["data"] == { assert result["data"] == {
"io": {"1": "Binary Sensor", "2": "Digital Sensor", "3": "Switchable Output"}, "io": {"1": "Binary Sensor", "2": "Digital Sensor", "3": "Switchable Output"},
"blink": False, "blink": False,
"api_host": "",
"binary_sensors": [ "binary_sensors": [
{"zone": "1", "type": "door", "inverse": True, "name": "winder"}, {"zone": "1", "type": "door", "inverse": True, "name": "winder"},
], ],

View file

@ -43,6 +43,7 @@ async def test_config_schema(hass):
"""Test that config schema is imported properly.""" """Test that config schema is imported properly."""
config = { config = {
konnected.DOMAIN: { konnected.DOMAIN: {
konnected.CONF_API_HOST: "http://1.1.1.1:8888",
konnected.CONF_ACCESS_TOKEN: "abcdefgh", konnected.CONF_ACCESS_TOKEN: "abcdefgh",
konnected.CONF_DEVICES: [{konnected.CONF_ID: "aabbccddeeff"}], konnected.CONF_DEVICES: [{konnected.CONF_ID: "aabbccddeeff"}],
} }
@ -50,10 +51,12 @@ async def test_config_schema(hass):
assert konnected.CONFIG_SCHEMA(config) == { assert konnected.CONFIG_SCHEMA(config) == {
"konnected": { "konnected": {
"access_token": "abcdefgh", "access_token": "abcdefgh",
"api_host": "http://1.1.1.1:8888",
"devices": [ "devices": [
{ {
"default_options": { "default_options": {
"blink": True, "blink": True,
"api_host": "http://1.1.1.1:8888",
"discovery": True, "discovery": True,
"io": { "io": {
"1": "Disabled", "1": "Disabled",
@ -96,6 +99,7 @@ async def test_config_schema(hass):
{ {
"default_options": { "default_options": {
"blink": True, "blink": True,
"api_host": "",
"discovery": True, "discovery": True,
"io": { "io": {
"1": "Disabled", "1": "Disabled",
@ -162,6 +166,7 @@ async def test_config_schema(hass):
{ {
"default_options": { "default_options": {
"blink": True, "blink": True,
"api_host": "",
"discovery": True, "discovery": True,
"io": { "io": {
"1": "Binary Sensor", "1": "Binary Sensor",