mirror of
https://github.com/home-assistant/core.git
synced 2025-07-14 08:47:10 +00:00
Add konnected multi output (#33412)
* add test to for importing multiple output settings * provide option to set multiple output states * tweaks after testing * Update homeassistant/components/konnected/config_flow.py Co-Authored-By: Martin Hjelmare <marhje52@gmail.com> Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
parent
d59209ff47
commit
0e6b905cdf
@ -33,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": {
|
||||||
@ -91,11 +92,12 @@
|
|||||||
"data": {
|
"data": {
|
||||||
"activation": "Output when on",
|
"activation": "Output when on",
|
||||||
"momentary": "Pulse duration (ms) (optional)",
|
"momentary": "Pulse duration (ms) (optional)",
|
||||||
|
"more_states": "Configure additional states for this zone",
|
||||||
"name": "Name (optional)",
|
"name": "Name (optional)",
|
||||||
"pause": "Pause between pulses (ms) (optional)",
|
"pause": "Pause between pulses (ms) (optional)",
|
||||||
"repeat": "Times to repeat (-1=infinite) (optional)"
|
"repeat": "Times to repeat (-1=infinite) (optional)"
|
||||||
},
|
},
|
||||||
"description": "Please select the output options for {zone}",
|
"description": "Please select the output options for {zone}: state {state}",
|
||||||
"title": "Configure Switchable Output"
|
"title": "Configure Switchable Output"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -57,6 +57,10 @@ CONF_IO_BIN = "Binary Sensor"
|
|||||||
CONF_IO_DIG = "Digital Sensor"
|
CONF_IO_DIG = "Digital Sensor"
|
||||||
CONF_IO_SWI = "Switchable Output"
|
CONF_IO_SWI = "Switchable Output"
|
||||||
|
|
||||||
|
CONF_MORE_STATES = "more_states"
|
||||||
|
CONF_YES = "Yes"
|
||||||
|
CONF_NO = "No"
|
||||||
|
|
||||||
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",
|
||||||
@ -117,7 +121,7 @@ SWITCH_SCHEMA = vol.Schema(
|
|||||||
vol.Required(CONF_ZONE): vol.In(ZONES),
|
vol.Required(CONF_ZONE): vol.In(ZONES),
|
||||||
vol.Optional(CONF_NAME): cv.string,
|
vol.Optional(CONF_NAME): cv.string,
|
||||||
vol.Optional(CONF_ACTIVATION, default=STATE_HIGH): vol.All(
|
vol.Optional(CONF_ACTIVATION, default=STATE_HIGH): vol.All(
|
||||||
vol.Lower, vol.Any(STATE_HIGH, STATE_LOW)
|
vol.Lower, vol.In([STATE_HIGH, STATE_LOW])
|
||||||
),
|
),
|
||||||
vol.Optional(CONF_MOMENTARY): vol.All(vol.Coerce(int), vol.Range(min=10)),
|
vol.Optional(CONF_MOMENTARY): vol.All(vol.Coerce(int), vol.Range(min=10)),
|
||||||
vol.Optional(CONF_PAUSE): vol.All(vol.Coerce(int), vol.Range(min=10)),
|
vol.Optional(CONF_PAUSE): vol.All(vol.Coerce(int), vol.Range(min=10)),
|
||||||
@ -361,6 +365,8 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
self.new_opt = {CONF_IO: {}}
|
self.new_opt = {CONF_IO: {}}
|
||||||
self.active_cfg = None
|
self.active_cfg = None
|
||||||
self.io_cfg = {}
|
self.io_cfg = {}
|
||||||
|
self.current_states = []
|
||||||
|
self.current_state = 1
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def get_current_cfg(self, io_type, zone):
|
def get_current_cfg(self, io_type, zone):
|
||||||
@ -666,12 +672,21 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
zone = {"zone": self.active_cfg}
|
zone = {"zone": self.active_cfg}
|
||||||
zone.update(user_input)
|
zone.update(user_input)
|
||||||
|
del zone[CONF_MORE_STATES]
|
||||||
self.new_opt[CONF_SWITCHES] = self.new_opt.get(CONF_SWITCHES, []) + [zone]
|
self.new_opt[CONF_SWITCHES] = self.new_opt.get(CONF_SWITCHES, []) + [zone]
|
||||||
self.io_cfg.pop(self.active_cfg)
|
|
||||||
self.active_cfg = None
|
# iterate through multiple switch states
|
||||||
|
if self.current_states:
|
||||||
|
self.current_states.pop(0)
|
||||||
|
|
||||||
|
# only go to next zone if all states are entered
|
||||||
|
self.current_state += 1
|
||||||
|
if user_input[CONF_MORE_STATES] == CONF_NO:
|
||||||
|
self.io_cfg.pop(self.active_cfg)
|
||||||
|
self.active_cfg = None
|
||||||
|
|
||||||
if self.active_cfg:
|
if self.active_cfg:
|
||||||
current_cfg = self.get_current_cfg(CONF_SWITCHES, self.active_cfg)
|
current_cfg = next(iter(self.current_states), {})
|
||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
step_id="options_switch",
|
step_id="options_switch",
|
||||||
data_schema=vol.Schema(
|
data_schema=vol.Schema(
|
||||||
@ -682,7 +697,7 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
vol.Optional(
|
vol.Optional(
|
||||||
CONF_ACTIVATION,
|
CONF_ACTIVATION,
|
||||||
default=current_cfg.get(CONF_ACTIVATION, STATE_HIGH),
|
default=current_cfg.get(CONF_ACTIVATION, STATE_HIGH),
|
||||||
): vol.All(vol.Lower, vol.Any(STATE_HIGH, STATE_LOW)),
|
): vol.All(vol.Lower, vol.In([STATE_HIGH, STATE_LOW])),
|
||||||
vol.Optional(
|
vol.Optional(
|
||||||
CONF_MOMENTARY,
|
CONF_MOMENTARY,
|
||||||
default=current_cfg.get(CONF_MOMENTARY, vol.UNDEFINED),
|
default=current_cfg.get(CONF_MOMENTARY, vol.UNDEFINED),
|
||||||
@ -695,12 +710,19 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
CONF_REPEAT,
|
CONF_REPEAT,
|
||||||
default=current_cfg.get(CONF_REPEAT, vol.UNDEFINED),
|
default=current_cfg.get(CONF_REPEAT, vol.UNDEFINED),
|
||||||
): vol.All(vol.Coerce(int), vol.Range(min=-1)),
|
): vol.All(vol.Coerce(int), vol.Range(min=-1)),
|
||||||
|
vol.Required(
|
||||||
|
CONF_MORE_STATES,
|
||||||
|
default=CONF_YES
|
||||||
|
if len(self.current_states) > 1
|
||||||
|
else CONF_NO,
|
||||||
|
): vol.In([CONF_YES, CONF_NO]),
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
description_placeholders={
|
description_placeholders={
|
||||||
"zone": f"Zone {self.active_cfg}"
|
"zone": f"Zone {self.active_cfg}"
|
||||||
if len(self.active_cfg) < 3
|
if len(self.active_cfg) < 3
|
||||||
else self.active_cfg.upper()
|
else self.active_cfg.upper(),
|
||||||
|
"state": str(self.current_state),
|
||||||
},
|
},
|
||||||
errors=errors,
|
errors=errors,
|
||||||
)
|
)
|
||||||
@ -709,7 +731,13 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
for key, value in self.io_cfg.items():
|
for key, value in self.io_cfg.items():
|
||||||
if value == CONF_IO_SWI:
|
if value == CONF_IO_SWI:
|
||||||
self.active_cfg = key
|
self.active_cfg = key
|
||||||
current_cfg = self.get_current_cfg(CONF_SWITCHES, self.active_cfg)
|
self.current_states = [
|
||||||
|
cfg
|
||||||
|
for cfg in self.current_opt.get(CONF_SWITCHES, [])
|
||||||
|
if cfg[CONF_ZONE] == self.active_cfg
|
||||||
|
]
|
||||||
|
current_cfg = next(iter(self.current_states), {})
|
||||||
|
self.current_state = 1
|
||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
step_id="options_switch",
|
step_id="options_switch",
|
||||||
data_schema=vol.Schema(
|
data_schema=vol.Schema(
|
||||||
@ -720,7 +748,7 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
): str,
|
): str,
|
||||||
vol.Optional(
|
vol.Optional(
|
||||||
CONF_ACTIVATION,
|
CONF_ACTIVATION,
|
||||||
default=current_cfg.get(CONF_ACTIVATION, "high"),
|
default=current_cfg.get(CONF_ACTIVATION, STATE_HIGH),
|
||||||
): vol.In(["low", "high"]),
|
): vol.In(["low", "high"]),
|
||||||
vol.Optional(
|
vol.Optional(
|
||||||
CONF_MOMENTARY,
|
CONF_MOMENTARY,
|
||||||
@ -734,12 +762,19 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
CONF_REPEAT,
|
CONF_REPEAT,
|
||||||
default=current_cfg.get(CONF_REPEAT, vol.UNDEFINED),
|
default=current_cfg.get(CONF_REPEAT, vol.UNDEFINED),
|
||||||
): vol.All(vol.Coerce(int), vol.Range(min=-1)),
|
): vol.All(vol.Coerce(int), vol.Range(min=-1)),
|
||||||
|
vol.Required(
|
||||||
|
CONF_MORE_STATES,
|
||||||
|
default=CONF_YES
|
||||||
|
if len(self.current_states) > 1
|
||||||
|
else CONF_NO,
|
||||||
|
): vol.In([CONF_YES, CONF_NO]),
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
description_placeholders={
|
description_placeholders={
|
||||||
"zone": f"Zone {self.active_cfg}"
|
"zone": f"Zone {self.active_cfg}"
|
||||||
if len(self.active_cfg) < 3
|
if len(self.active_cfg) < 3
|
||||||
else self.active_cfg.upper()
|
else self.active_cfg.upper(),
|
||||||
|
"state": str(self.current_state),
|
||||||
},
|
},
|
||||||
errors=errors,
|
errors=errors,
|
||||||
)
|
)
|
||||||
|
@ -80,13 +80,14 @@
|
|||||||
},
|
},
|
||||||
"options_switch": {
|
"options_switch": {
|
||||||
"title": "Configure Switchable Output",
|
"title": "Configure Switchable Output",
|
||||||
"description": "Please select the output options for {zone}",
|
"description": "Please select the output options for {zone}: state {state}",
|
||||||
"data": {
|
"data": {
|
||||||
"name": "Name (optional)",
|
"name": "Name (optional)",
|
||||||
"activation": "Output when on",
|
"activation": "Output when on",
|
||||||
"momentary": "Pulse duration (ms) (optional)",
|
"momentary": "Pulse duration (ms) (optional)",
|
||||||
"pause": "Pause between pulses (ms) (optional)",
|
"pause": "Pause between pulses (ms) (optional)",
|
||||||
"repeat": "Times to repeat (-1=infinite) (optional)"
|
"repeat": "Times to repeat (-1=infinite) (optional)",
|
||||||
|
"more_states": "Configure additional states for this zone"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"options_misc": {
|
"options_misc": {
|
||||||
|
@ -403,6 +403,14 @@ async def test_import_existing_config(hass, mock_panel):
|
|||||||
"pause": 100,
|
"pause": 100,
|
||||||
"repeat": 4,
|
"repeat": 4,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"zone": 8,
|
||||||
|
"name": "alarm",
|
||||||
|
"activation": "low",
|
||||||
|
"momentary": 100,
|
||||||
|
"pause": 100,
|
||||||
|
"repeat": -1,
|
||||||
|
},
|
||||||
{"zone": "out1"},
|
{"zone": "out1"},
|
||||||
{"zone": "alarm1"},
|
{"zone": "alarm1"},
|
||||||
],
|
],
|
||||||
@ -463,6 +471,14 @@ async def test_import_existing_config(hass, mock_panel):
|
|||||||
"pause": 100,
|
"pause": 100,
|
||||||
"repeat": 4,
|
"repeat": 4,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"zone": "8",
|
||||||
|
"name": "alarm",
|
||||||
|
"activation": "low",
|
||||||
|
"momentary": 100,
|
||||||
|
"pause": 100,
|
||||||
|
"repeat": -1,
|
||||||
|
},
|
||||||
{"activation": "high", "zone": "out1"},
|
{"activation": "high", "zone": "out1"},
|
||||||
{"activation": "high", "zone": "alarm1"},
|
{"activation": "high", "zone": "alarm1"},
|
||||||
],
|
],
|
||||||
@ -713,6 +729,7 @@ async def test_option_flow(hass, mock_panel):
|
|||||||
assert result["step_id"] == "options_switch"
|
assert result["step_id"] == "options_switch"
|
||||||
assert result["description_placeholders"] == {
|
assert result["description_placeholders"] == {
|
||||||
"zone": "Zone 4",
|
"zone": "Zone 4",
|
||||||
|
"state": "1",
|
||||||
}
|
}
|
||||||
|
|
||||||
# zone 4
|
# zone 4
|
||||||
@ -723,6 +740,7 @@ async def test_option_flow(hass, mock_panel):
|
|||||||
assert result["step_id"] == "options_switch"
|
assert result["step_id"] == "options_switch"
|
||||||
assert result["description_placeholders"] == {
|
assert result["description_placeholders"] == {
|
||||||
"zone": "OUT",
|
"zone": "OUT",
|
||||||
|
"state": "1",
|
||||||
}
|
}
|
||||||
|
|
||||||
# zone out
|
# zone out
|
||||||
@ -734,6 +752,27 @@ async def test_option_flow(hass, mock_panel):
|
|||||||
"momentary": 50,
|
"momentary": 50,
|
||||||
"pause": 100,
|
"pause": 100,
|
||||||
"repeat": 4,
|
"repeat": 4,
|
||||||
|
"more_states": "Yes",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == "form"
|
||||||
|
assert result["step_id"] == "options_switch"
|
||||||
|
assert result["description_placeholders"] == {
|
||||||
|
"zone": "OUT",
|
||||||
|
"state": "2",
|
||||||
|
}
|
||||||
|
|
||||||
|
# zone out - state 2
|
||||||
|
result = await hass.config_entries.options.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
user_input={
|
||||||
|
"name": "alarm",
|
||||||
|
"activation": "low",
|
||||||
|
"momentary": 100,
|
||||||
|
"pause": 100,
|
||||||
|
"repeat": -1,
|
||||||
|
"more_states": "No",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -768,6 +807,14 @@ async def test_option_flow(hass, mock_panel):
|
|||||||
"pause": 100,
|
"pause": 100,
|
||||||
"repeat": 4,
|
"repeat": 4,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"zone": "out",
|
||||||
|
"name": "alarm",
|
||||||
|
"activation": "low",
|
||||||
|
"momentary": 100,
|
||||||
|
"pause": 100,
|
||||||
|
"repeat": -1,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -977,6 +1024,14 @@ async def test_option_flow_import(hass, mock_panel):
|
|||||||
"pause": 100,
|
"pause": 100,
|
||||||
"repeat": 4,
|
"repeat": 4,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"zone": "3",
|
||||||
|
"name": "alarm",
|
||||||
|
"activation": "low",
|
||||||
|
"momentary": 100,
|
||||||
|
"pause": 100,
|
||||||
|
"repeat": -1,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -1056,8 +1111,9 @@ async def test_option_flow_import(hass, mock_panel):
|
|||||||
assert schema["momentary"] == 50
|
assert schema["momentary"] == 50
|
||||||
assert schema["pause"] == 100
|
assert schema["pause"] == 100
|
||||||
assert schema["repeat"] == 4
|
assert schema["repeat"] == 4
|
||||||
|
assert schema["more_states"] == "Yes"
|
||||||
result = await hass.config_entries.options.async_configure(
|
result = await hass.config_entries.options.async_configure(
|
||||||
result["flow_id"], user_input={"activation": "high"}
|
result["flow_id"], user_input={"activation": "high", "more_states": "No"}
|
||||||
)
|
)
|
||||||
assert result["type"] == "form"
|
assert result["type"] == "form"
|
||||||
assert result["step_id"] == "options_misc"
|
assert result["step_id"] == "options_misc"
|
||||||
|
@ -124,7 +124,7 @@ async def test_config_schema(hass):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# check pin to zone
|
# check pin to zone and multiple output
|
||||||
config = {
|
config = {
|
||||||
konnected.DOMAIN: {
|
konnected.DOMAIN: {
|
||||||
konnected.CONF_ACCESS_TOKEN: "abcdefgh",
|
konnected.CONF_ACCESS_TOKEN: "abcdefgh",
|
||||||
@ -135,6 +135,22 @@ async def test_config_schema(hass):
|
|||||||
{"pin": 2, "type": "door"},
|
{"pin": 2, "type": "door"},
|
||||||
{"zone": 1, "type": "door"},
|
{"zone": 1, "type": "door"},
|
||||||
],
|
],
|
||||||
|
"switches": [
|
||||||
|
{
|
||||||
|
"zone": 3,
|
||||||
|
"name": "Beep Beep",
|
||||||
|
"momentary": 65,
|
||||||
|
"pause": 55,
|
||||||
|
"repeat": 4,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"zone": 3,
|
||||||
|
"name": "Warning",
|
||||||
|
"momentary": 100,
|
||||||
|
"pause": 100,
|
||||||
|
"repeat": -1,
|
||||||
|
},
|
||||||
|
],
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
@ -153,7 +169,7 @@ async def test_config_schema(hass):
|
|||||||
"11": "Disabled",
|
"11": "Disabled",
|
||||||
"12": "Disabled",
|
"12": "Disabled",
|
||||||
"2": "Binary Sensor",
|
"2": "Binary Sensor",
|
||||||
"3": "Disabled",
|
"3": "Switchable Output",
|
||||||
"4": "Disabled",
|
"4": "Disabled",
|
||||||
"5": "Disabled",
|
"5": "Disabled",
|
||||||
"6": "Disabled",
|
"6": "Disabled",
|
||||||
@ -169,6 +185,24 @@ async def test_config_schema(hass):
|
|||||||
{"inverse": False, "type": "door", "zone": "2"},
|
{"inverse": False, "type": "door", "zone": "2"},
|
||||||
{"inverse": False, "type": "door", "zone": "1"},
|
{"inverse": False, "type": "door", "zone": "1"},
|
||||||
],
|
],
|
||||||
|
"switches": [
|
||||||
|
{
|
||||||
|
"zone": "3",
|
||||||
|
"activation": "high",
|
||||||
|
"name": "Beep Beep",
|
||||||
|
"momentary": 65,
|
||||||
|
"pause": 55,
|
||||||
|
"repeat": 4,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"zone": "3",
|
||||||
|
"activation": "high",
|
||||||
|
"name": "Warning",
|
||||||
|
"momentary": 100,
|
||||||
|
"pause": 100,
|
||||||
|
"repeat": -1,
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
"id": "aabbccddeeff",
|
"id": "aabbccddeeff",
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user