Add options flow fallback for ports

This commit is contained in:
2026-02-13 19:33:55 +01:00
parent 8b20cf4744
commit 502a33dbac
7 changed files with 202 additions and 13 deletions

View File

@@ -18,6 +18,7 @@ from .const import (
CONF_INVERT_OUTGOING,
CONF_MOVE_LONG_ADDRESS,
CONF_MOVE_SHORT_ADDRESS,
CONF_PORTS,
CONF_POSITION_ADDRESS,
CONF_POSITION_STATE_ADDRESS,
CONF_STATE_ADDRESS,
@@ -117,10 +118,8 @@ class BridgeManager:
switch_ports: list[SwitchPort] = []
cover_ports: list[CoverPort] = []
subentries = getattr(self.entry, "subentries", [])
for subentry in subentries:
data = subentry.data
if subentry.type == "binary_sensor":
for port_type, data in _iter_port_configs(self.entry):
if port_type == "binary_sensor":
binary_ports.append(
BinarySensorPort(
entity_id=data["entity_id"],
@@ -133,7 +132,7 @@ class BridgeManager:
),
)
)
elif subentry.type == "switch":
elif port_type == "switch":
switch_ports.append(
SwitchPort(
entity_id=data["entity_id"],
@@ -155,7 +154,7 @@ class BridgeManager:
),
)
)
elif subentry.type == "cover":
elif port_type == "cover":
cover_ports.append(
CoverPort(
entity_id=data["entity_id"],
@@ -632,3 +631,17 @@ def _invert_out_key(address_key: str) -> str:
def _get_value_type(address_key: str) -> str | None:
return ADDRESS_VALUE_TYPE.get(address_key)
def _iter_port_configs(entry: ConfigEntry) -> list[tuple[str, dict[str, Any]]]:
ports: list[tuple[str, dict[str, Any]]] = []
subentries = getattr(entry, "subentries", [])
for subentry in subentries:
ports.append((subentry.type, subentry.data))
option_ports = entry.options.get(CONF_PORTS, [])
for port in option_ports:
port_type = port.get("type")
data = port.get("data", {})
if port_type and isinstance(data, dict):
ports.append((port_type, data))
return ports

View File

@@ -1,6 +1,7 @@
from __future__ import annotations
import logging
import uuid
import voluptuous as vol
@@ -18,6 +19,8 @@ from .const import (
CONF_KNX_ENTRY_ID,
CONF_MOVE_LONG_ADDRESS,
CONF_MOVE_SHORT_ADDRESS,
CONF_PORTS,
CONF_PORT_ID,
CONF_POSITION_ADDRESS,
CONF_POSITION_STATE_ADDRESS,
CONF_STATE_ADDRESS,
@@ -89,11 +92,114 @@ class HAKnxBridgeOptionsFlow(config_entries.OptionsFlow):
self._config_entry = config_entry
async def async_step_init(self, user_input: dict | None = None):
if user_input is not None:
return self.async_create_entry(title="", data=user_input)
return self.async_show_menu(
step_id="init",
menu_options=[
"add_binary_sensor",
"add_switch",
"add_cover",
"remove_port",
],
)
schema = vol.Schema({})
return self.async_show_form(step_id="init", data_schema=schema)
async def async_step_add_binary_sensor(self, user_input: dict | None = None):
if user_input is not None:
user_input, errors = _validate_knx_addresses(
user_input,
[
CONF_STATE_ADDRESS,
],
)
if errors:
return self.async_show_form(
step_id="add_binary_sensor",
data_schema=_binary_sensor_schema(),
errors=errors,
)
return await self._async_store_port("binary_sensor", user_input)
return self.async_show_form(
step_id="add_binary_sensor", data_schema=_binary_sensor_schema()
)
async def async_step_add_switch(self, user_input: dict | None = None):
if user_input is not None:
user_input, errors = _validate_knx_addresses(
user_input, [CONF_COMMAND_ADDRESS, CONF_STATE_ADDRESS]
)
if errors:
return self.async_show_form(
step_id="add_switch",
data_schema=_switch_schema(),
errors=errors,
)
return await self._async_store_port("switch", user_input)
return self.async_show_form(
step_id="add_switch", data_schema=_switch_schema()
)
async def async_step_add_cover(self, user_input: dict | None = None):
if user_input is not None:
user_input, errors = _validate_knx_addresses(
user_input,
[
CONF_MOVE_LONG_ADDRESS,
CONF_MOVE_SHORT_ADDRESS,
CONF_STOP_ADDRESS,
CONF_POSITION_ADDRESS,
CONF_POSITION_STATE_ADDRESS,
CONF_ANGLE_ADDRESS,
CONF_ANGLE_STATE_ADDRESS,
],
)
if errors:
return self.async_show_form(
step_id="add_cover", data_schema=_cover_schema(), errors=errors
)
return await self._async_store_port("cover", user_input)
return self.async_show_form(step_id="add_cover", data_schema=_cover_schema())
async def async_step_remove_port(self, user_input: dict | None = None):
ports = list(self._config_entry.options.get(CONF_PORTS, []))
if not ports:
return self.async_abort(reason="no_ports_to_remove")
if user_input is not None:
port_id = user_input[CONF_PORT_ID]
ports = [port for port in ports if port.get("id") != port_id]
return self.async_create_entry(title="", data={CONF_PORTS: ports})
options = [
{
"value": port.get("id"),
"label": port.get("title", port.get("id")),
}
for port in ports
if port.get("id")
]
schema = vol.Schema(
{
vol.Required(CONF_PORT_ID): selector.SelectSelector(
selector.SelectSelectorConfig(options=options, mode="dropdown")
)
}
)
return self.async_show_form(step_id="remove_port", data_schema=schema)
async def _async_store_port(self, port_type: str, user_input: dict):
ports = list(self._config_entry.options.get(CONF_PORTS, []))
title = _entity_title(self.hass, user_input[CONF_ENTITY_ID])
ports.append(
{
"id": uuid.uuid4().hex,
"type": port_type,
"title": title,
"data": user_input,
}
)
return self.async_create_entry(title="", data={CONF_PORTS: ports})
class BinarySensorPortSubentryFlow(config_entries.ConfigSubentryFlow):

View File

@@ -6,6 +6,8 @@ CONF_STATE_ADDRESS = "state_address"
CONF_COMMAND_ADDRESS = "command_address"
CONF_INVERT_INCOMING = "invert_incoming"
CONF_INVERT_OUTGOING = "invert_outgoing"
CONF_PORTS = "ports"
CONF_PORT_ID = "port_id"
CONF_MOVE_LONG_ADDRESS = "move_long_address"
CONF_MOVE_SHORT_ADDRESS = "move_short_address"

View File

@@ -1,7 +1,7 @@
{
"domain": "ha_knx_bridge",
"name": "HA KNX Bridge",
"version": "0.0.8",
"version": "0.0.9",
"config_flow": true,
"documentation": "https://github.com/bahmcloud/HA-KNX-Bridge",
"issue_tracker": "https://github.com/bahmcloud/HA-KNX-Bridge/issues",

View File

@@ -17,10 +17,73 @@
}
},
"options": {
"abort": {
"no_ports_to_remove": "There are no ports to remove yet."
},
"step": {
"init": {
"title": "HA KNX Bridge Options",
"description": "No options available yet."
"description": "Manage ports when subentries are unavailable.",
"menu_options": {
"add_binary_sensor": "Add binary sensor port",
"add_switch": "Add switch port",
"add_cover": "Add cover port",
"remove_port": "Remove port"
}
},
"add_binary_sensor": {
"title": "Add Binary Sensor Port",
"data": {
"entity_id": "Binary sensor entity",
"state_address": "State group address (DPT 1)",
"state_address_invert_incoming": "Invert incoming",
"state_address_invert_outgoing": "Invert outgoing"
}
},
"add_switch": {
"title": "Add Switch Port",
"data": {
"entity_id": "Switch entity",
"command_address": "Command group address (DPT 1)",
"command_address_invert_incoming": "Invert incoming",
"command_address_invert_outgoing": "Invert outgoing",
"state_address": "State group address (DPT 1)",
"state_address_invert_incoming": "Invert incoming",
"state_address_invert_outgoing": "Invert outgoing"
}
},
"add_cover": {
"title": "Add Cover Port",
"data": {
"entity_id": "Cover entity",
"move_long_address": "Move long (DPT 1.008 Up/Down)",
"move_long_address_invert_incoming": "Invert incoming",
"move_long_address_invert_outgoing": "Invert outgoing",
"move_short_address": "Move short (DPT 1.007 Step)",
"move_short_address_invert_incoming": "Invert incoming",
"move_short_address_invert_outgoing": "Invert outgoing",
"stop_address": "Stop (DPT 1)",
"stop_address_invert_incoming": "Invert incoming",
"stop_address_invert_outgoing": "Invert outgoing",
"position_address": "Set position (DPT 5.001)",
"position_address_invert_incoming": "Invert incoming",
"position_address_invert_outgoing": "Invert outgoing",
"position_state_address": "State position (DPT 5.001)",
"position_state_address_invert_incoming": "Invert incoming",
"position_state_address_invert_outgoing": "Invert outgoing",
"angle_address": "Set tilt (DPT 5.001)",
"angle_address_invert_incoming": "Invert incoming",
"angle_address_invert_outgoing": "Invert outgoing",
"angle_state_address": "State tilt (DPT 5.001)",
"angle_state_address_invert_incoming": "Invert incoming",
"angle_state_address_invert_outgoing": "Invert outgoing"
}
},
"remove_port": {
"title": "Remove Port",
"data": {
"port_id": "Port to remove"
}
}
}
},