9 Commits

9 changed files with 478 additions and 34 deletions

View File

@@ -1,5 +1,36 @@
# Changelog # Changelog
## 0.0.15 - 2026-02-13
- Remove unsupported `payload_length` field from KNX send calls.
## 0.0.14 - 2026-02-13
- Improve KNX event decoding for cover commands and address the up/down mapping.
## 0.0.13 - 2026-02-13
- Clamp percent payloads to avoid invalid KNX DPT 5.001 values.
## 0.0.12 - 2026-02-13
- Fix KNX event listener setup for HA versions without async_track_event helper.
## 0.0.11 - 2026-02-13
- Move translations into the integration folder so UI labels render.
- Downgrade missing subentry support log to debug.
## 0.0.10 - 2026-02-13
- Add translations for options flow menu labels.
## 0.0.9 - 2026-02-13
- Add options flow fallback to add/remove ports when subentries are unavailable.
## 0.0.8 - 2026-02-13
- Improve subentry type detection for HA versions exposing different class names.
## 0.0.7 - 2026-02-13
- Avoid crashing config entries when subentries are unsupported.
## 0.0.6 - 2026-02-13
- Fix config flow subentry type compatibility with older Home Assistant versions.
## 0.0.5 - 2026-02-13 ## 0.0.5 - 2026-02-13
- Centralize DPT auto-selection for KNX event registration per address. - Centralize DPT auto-selection for KNX event registration per address.

View File

@@ -21,6 +21,8 @@ Current minimal scope:
## Configure ## Configure
1. During setup, select the existing Home Assistant KNX integration entry. 1. During setup, select the existing Home Assistant KNX integration entry.
2. Add Ports from the integration's configuration page. 2. Add Ports from the integration's configuration page.
If the "Add Port" UI is missing, open the integration options and manage ports
there (fallback for HA versions without subentry support).
### Binary Sensor Port ### Binary Sensor Port
- `entity_id`: the HA binary_sensor to mirror. - `entity_id`: the HA binary_sensor to mirror.
@@ -65,6 +67,6 @@ Each group address has `invert incoming` and `invert outgoing` toggles to flip K
- Advanced DPT mapping options and inversion settings. - Advanced DPT mapping options and inversion settings.
## Versioning and Releases ## Versioning and Releases
- Current version: 0.0.5 - Current version: 0.0.15
- `CHANGELOG.md` lists versions with the newest entries at the top. - `CHANGELOG.md` lists versions with the newest entries at the top.
- Release creation is manual and only done when explicitly requested. - Release creation is manual and only done when explicitly requested.

View File

@@ -12,12 +12,14 @@ from homeassistant.helpers import event as event_helper
from .const import ( from .const import (
CONF_ANGLE_ADDRESS, CONF_ANGLE_ADDRESS,
CONF_ANGLE_STATE_ADDRESS, CONF_ANGLE_STATE_ADDRESS,
ADDRESS_EVENT_TYPE,
ADDRESS_VALUE_TYPE, ADDRESS_VALUE_TYPE,
CONF_COMMAND_ADDRESS, CONF_COMMAND_ADDRESS,
CONF_INVERT_INCOMING, CONF_INVERT_INCOMING,
CONF_INVERT_OUTGOING, CONF_INVERT_OUTGOING,
CONF_MOVE_LONG_ADDRESS, CONF_MOVE_LONG_ADDRESS,
CONF_MOVE_SHORT_ADDRESS, CONF_MOVE_SHORT_ADDRESS,
CONF_PORTS,
CONF_POSITION_ADDRESS, CONF_POSITION_ADDRESS,
CONF_POSITION_STATE_ADDRESS, CONF_POSITION_STATE_ADDRESS,
CONF_STATE_ADDRESS, CONF_STATE_ADDRESS,
@@ -117,10 +119,8 @@ class BridgeManager:
switch_ports: list[SwitchPort] = [] switch_ports: list[SwitchPort] = []
cover_ports: list[CoverPort] = [] cover_ports: list[CoverPort] = []
subentries = getattr(self.entry, "subentries", []) for port_type, data in _iter_port_configs(self.entry):
for subentry in subentries: if port_type == "binary_sensor":
data = subentry.data
if subentry.type == "binary_sensor":
binary_ports.append( binary_ports.append(
BinarySensorPort( BinarySensorPort(
entity_id=data["entity_id"], entity_id=data["entity_id"],
@@ -133,7 +133,7 @@ class BridgeManager:
), ),
) )
) )
elif subentry.type == "switch": elif port_type == "switch":
switch_ports.append( switch_ports.append(
SwitchPort( SwitchPort(
entity_id=data["entity_id"], entity_id=data["entity_id"],
@@ -155,7 +155,7 @@ class BridgeManager:
), ),
) )
) )
elif subentry.type == "cover": elif port_type == "cover":
cover_ports.append( cover_ports.append(
CoverPort( CoverPort(
entity_id=data["entity_id"], entity_id=data["entity_id"],
@@ -330,9 +330,14 @@ class BridgeManager:
) )
if self._address_handlers: if self._address_handlers:
self._knx_event_unsub = event_helper.async_track_event( if hasattr(event_helper, "async_track_event"):
self.hass, "knx_event", self._handle_knx_event self._knx_event_unsub = event_helper.async_track_event(
) self.hass, "knx_event", self._handle_knx_event
)
else:
self._knx_event_unsub = self.hass.bus.async_listen(
"knx_event", self._handle_knx_event
)
await self._register_knx_events() await self._register_knx_events()
@@ -352,10 +357,11 @@ class BridgeManager:
port, action, event port, action, event
) )
value_type = _get_value_type(address_key) value_type = _get_value_type(address_key)
event_type = _get_event_type(address_key)
self._remember_address_options( self._remember_address_options(
address, value_type, invert_incoming, invert_outgoing address, value_type, invert_incoming, invert_outgoing
) )
self._registered_addresses.append((address, value_type)) self._registered_addresses.append((address, event_type))
def _register_knx_switch_address( def _register_knx_switch_address(
self, self,
@@ -375,7 +381,7 @@ class BridgeManager:
invert_incoming, invert_incoming,
invert_outgoing, invert_outgoing,
) )
self._registered_addresses.append((address, None)) self._registered_addresses.append((address, _get_event_type(CONF_COMMAND_ADDRESS)))
def _remember_address_options( def _remember_address_options(
self, self,
@@ -391,19 +397,19 @@ class BridgeManager:
) )
async def _register_knx_events(self) -> None: async def _register_knx_events(self) -> None:
for address, value_type in self._registered_addresses: for address, event_type in self._registered_addresses:
data: dict[str, Any] = {"address": address} data: dict[str, Any] = {"address": address}
if value_type: if event_type:
data["type"] = value_type data["type"] = event_type
await self.hass.services.async_call( await self.hass.services.async_call(
KNX_DOMAIN, "event_register", data, blocking=False KNX_DOMAIN, "event_register", data, blocking=False
) )
async def _unregister_knx_events(self) -> None: async def _unregister_knx_events(self) -> None:
for address, value_type in self._registered_addresses: for address, event_type in self._registered_addresses:
data: dict[str, Any] = {"address": address, "remove": True} data: dict[str, Any] = {"address": address, "remove": True}
if value_type: if event_type:
data["type"] = value_type data["type"] = event_type
await self.hass.services.async_call( await self.hass.services.async_call(
KNX_DOMAIN, "event_register", data, blocking=False KNX_DOMAIN, "event_register", data, blocking=False
) )
@@ -472,10 +478,15 @@ class BridgeManager:
return _handler return _handler
async def _handle_knx_event(self, event: Event) -> None: async def _handle_knx_event(self, event: Event) -> None:
if event.data.get("direction") != "Incoming": direction = event.data.get("direction")
if direction is not None and not str(direction).lower().startswith("incoming"):
return return
destination = event.data.get("destination") destination = (
event.data.get("destination")
or event.data.get("destination_address")
or event.data.get("address")
)
if not destination: if not destination:
return return
@@ -553,13 +564,14 @@ class BridgeManager:
await self.hass.services.async_call( await self.hass.services.async_call(
KNX_DOMAIN, KNX_DOMAIN,
"send", "send",
{"address": address, "payload": payload, "payload_length": 0}, {"address": address, "payload": payload},
blocking=False, blocking=False,
) )
async def _knx_send_percent(self, address: str | None, value: int) -> None: async def _knx_send_percent(self, address: str | None, value: int) -> None:
if not address: if not address:
return return
value = _clamp_percent(value)
await self.hass.services.async_call( await self.hass.services.async_call(
KNX_DOMAIN, KNX_DOMAIN,
"send", "send",
@@ -570,15 +582,18 @@ class BridgeManager:
def _extract_event_value(event: Event) -> int | None: def _extract_event_value(event: Event) -> int | None:
if "value" in event.data: if "value" in event.data:
try: value = event.data["value"]
return int(event.data["value"]) mapped = _map_scalar_value(value)
except (TypeError, ValueError): if mapped is not None:
return None return mapped
data = event.data.get("data") data = event.data.get("data")
if data is None: if data is None:
return None return None
if isinstance(data, list) and data: if isinstance(data, list) and data:
data = data[0] data = data[0]
mapped = _map_scalar_value(data)
if mapped is not None:
return mapped
try: try:
return int(data) & 1 return int(data) & 1
except (TypeError, ValueError): except (TypeError, ValueError):
@@ -616,6 +631,7 @@ def _invert_value(
if not options.invert_outgoing: if not options.invert_outgoing:
return value return value
if options.value_type == "percent": if options.value_type == "percent":
value = _clamp_percent(value)
return 100 - value return 100 - value
if value not in (0, 1): if value not in (0, 1):
return value return value
@@ -632,3 +648,47 @@ def _invert_out_key(address_key: str) -> str:
def _get_value_type(address_key: str) -> str | None: def _get_value_type(address_key: str) -> str | None:
return ADDRESS_VALUE_TYPE.get(address_key) return ADDRESS_VALUE_TYPE.get(address_key)
def _get_event_type(address_key: str) -> str | None:
return ADDRESS_EVENT_TYPE.get(address_key)
def _clamp_percent(value: int) -> int:
if value < 0:
return 0
if value > 100:
return 100
return value
def _map_scalar_value(value: Any) -> int | None:
if isinstance(value, bool):
return 1 if value else 0
if isinstance(value, (int, float)):
return int(value)
if isinstance(value, str):
text = value.strip().lower()
if text in ("on", "true", "yes", "1", "down", "close", "closed"):
return 1
if text in ("off", "false", "no", "0", "up", "open", "opened"):
return 0
try:
return int(text)
except ValueError:
return None
return None
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 from __future__ import annotations
import logging import logging
import uuid
import voluptuous as vol import voluptuous as vol
@@ -18,6 +19,8 @@ from .const import (
CONF_KNX_ENTRY_ID, CONF_KNX_ENTRY_ID,
CONF_MOVE_LONG_ADDRESS, CONF_MOVE_LONG_ADDRESS,
CONF_MOVE_SHORT_ADDRESS, CONF_MOVE_SHORT_ADDRESS,
CONF_PORTS,
CONF_PORT_ID,
CONF_POSITION_ADDRESS, CONF_POSITION_ADDRESS,
CONF_POSITION_STATE_ADDRESS, CONF_POSITION_STATE_ADDRESS,
CONF_STATE_ADDRESS, CONF_STATE_ADDRESS,
@@ -65,14 +68,20 @@ class HAKnxBridgeConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
@staticmethod @staticmethod
@callback @callback
def async_get_supported_subentry_types(config_entry): def async_get_supported_subentry_types(config_entry):
subentry_type = _get_subentry_type()
if subentry_type is None:
_LOGGER.debug(
"Config subentries are not supported in this Home Assistant version"
)
return {}
return { return {
"binary_sensor": config_entries.SubentryType( "binary_sensor": subentry_type(
name="Binary Sensor Port", flow_class=BinarySensorPortSubentryFlow name="Binary Sensor Port", flow_class=BinarySensorPortSubentryFlow
), ),
"switch": config_entries.SubentryType( "switch": subentry_type(
name="Switch Port", flow_class=SwitchPortSubentryFlow name="Switch Port", flow_class=SwitchPortSubentryFlow
), ),
"cover": config_entries.SubentryType( "cover": subentry_type(
name="Cover Port", flow_class=CoverPortSubentryFlow name="Cover Port", flow_class=CoverPortSubentryFlow
), ),
} }
@@ -83,11 +92,114 @@ class HAKnxBridgeOptionsFlow(config_entries.OptionsFlow):
self._config_entry = config_entry self._config_entry = config_entry
async def async_step_init(self, user_input: dict | None = None): async def async_step_init(self, user_input: dict | None = None):
if user_input is not None: return self.async_show_menu(
return self.async_create_entry(title="", data=user_input) step_id="init",
menu_options=[
"add_binary_sensor",
"add_switch",
"add_cover",
"remove_port",
],
)
schema = vol.Schema({}) async def async_step_add_binary_sensor(self, user_input: dict | None = None):
return self.async_show_form(step_id="init", data_schema=schema) 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): class BinarySensorPortSubentryFlow(config_entries.ConfigSubentryFlow):
@@ -346,3 +458,22 @@ def _invert_in_key(address_key: str) -> str:
def _invert_out_key(address_key: str) -> str: def _invert_out_key(address_key: str) -> str:
return f"{address_key}_{CONF_INVERT_OUTGOING}" return f"{address_key}_{CONF_INVERT_OUTGOING}"
def _get_subentry_type():
candidates = [
"SubentryType",
"ConfigEntrySubentryType",
"ConfigSubentryType",
"SubEntryType",
]
for name in candidates:
subentry_type = getattr(config_entries, name, None)
if subentry_type is not None:
return subentry_type
_LOGGER.debug(
"Subentry type class not found on homeassistant.config_entries. "
"Available attrs: %s",
", ".join(sorted(dir(config_entries))),
)
return None

View File

@@ -6,6 +6,8 @@ CONF_STATE_ADDRESS = "state_address"
CONF_COMMAND_ADDRESS = "command_address" CONF_COMMAND_ADDRESS = "command_address"
CONF_INVERT_INCOMING = "invert_incoming" CONF_INVERT_INCOMING = "invert_incoming"
CONF_INVERT_OUTGOING = "invert_outgoing" CONF_INVERT_OUTGOING = "invert_outgoing"
CONF_PORTS = "ports"
CONF_PORT_ID = "port_id"
CONF_MOVE_LONG_ADDRESS = "move_long_address" CONF_MOVE_LONG_ADDRESS = "move_long_address"
CONF_MOVE_SHORT_ADDRESS = "move_short_address" CONF_MOVE_SHORT_ADDRESS = "move_short_address"
@@ -33,3 +35,12 @@ ADDRESS_VALUE_TYPE: dict[str, str] = {
CONF_ANGLE_ADDRESS: "percent", CONF_ANGLE_ADDRESS: "percent",
CONF_ANGLE_STATE_ADDRESS: "percent", CONF_ANGLE_STATE_ADDRESS: "percent",
} }
ADDRESS_EVENT_TYPE: dict[str, str] = {
CONF_MOVE_LONG_ADDRESS: "up_down",
CONF_MOVE_SHORT_ADDRESS: "step",
CONF_POSITION_ADDRESS: "percent",
CONF_POSITION_STATE_ADDRESS: "percent",
CONF_ANGLE_ADDRESS: "percent",
CONF_ANGLE_STATE_ADDRESS: "percent",
}

View File

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

View File

@@ -17,10 +17,73 @@
} }
}, },
"options": { "options": {
"abort": {
"no_ports_to_remove": "There are no ports to remove yet."
},
"step": { "step": {
"init": { "init": {
"title": "HA KNX Bridge Options", "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"
}
} }
} }
}, },

View File

@@ -0,0 +1,73 @@
{
"options": {
"abort": {
"no_ports_to_remove": "Es gibt noch keine Ports zum Entfernen."
},
"step": {
"init": {
"title": "HA KNX Bridge Optionen",
"description": "Ports verwalten, wenn Subentries nicht verfügbar sind.",
"menu_options": {
"add_binary_sensor": "Binary-Sensor-Port hinzufügen",
"add_switch": "Schalter-Port hinzufügen",
"add_cover": "Cover-Port hinzufügen",
"remove_port": "Port entfernen"
}
},
"add_binary_sensor": {
"title": "Binary-Sensor-Port hinzufügen",
"data": {
"entity_id": "Binary-Sensor-Entity",
"state_address": "State-Gruppenadresse (DPT 1)",
"state_address_invert_incoming": "Eingehend invertieren",
"state_address_invert_outgoing": "Ausgehend invertieren"
}
},
"add_switch": {
"title": "Schalter-Port hinzufügen",
"data": {
"entity_id": "Schalter-Entity",
"command_address": "Command-Gruppenadresse (DPT 1)",
"command_address_invert_incoming": "Eingehend invertieren",
"command_address_invert_outgoing": "Ausgehend invertieren",
"state_address": "State-Gruppenadresse (DPT 1)",
"state_address_invert_incoming": "Eingehend invertieren",
"state_address_invert_outgoing": "Ausgehend invertieren"
}
},
"add_cover": {
"title": "Cover-Port hinzufügen",
"data": {
"entity_id": "Cover-Entity",
"move_long_address": "Move long (DPT 1.008 Auf/Ab)",
"move_long_address_invert_incoming": "Eingehend invertieren",
"move_long_address_invert_outgoing": "Ausgehend invertieren",
"move_short_address": "Move short (DPT 1.007 Schritt)",
"move_short_address_invert_incoming": "Eingehend invertieren",
"move_short_address_invert_outgoing": "Ausgehend invertieren",
"stop_address": "Stop (DPT 1)",
"stop_address_invert_incoming": "Eingehend invertieren",
"stop_address_invert_outgoing": "Ausgehend invertieren",
"position_address": "Position setzen (DPT 5.001)",
"position_address_invert_incoming": "Eingehend invertieren",
"position_address_invert_outgoing": "Ausgehend invertieren",
"position_state_address": "Positionsstatus (DPT 5.001)",
"position_state_address_invert_incoming": "Eingehend invertieren",
"position_state_address_invert_outgoing": "Ausgehend invertieren",
"angle_address": "Tilt setzen (DPT 5.001)",
"angle_address_invert_incoming": "Eingehend invertieren",
"angle_address_invert_outgoing": "Ausgehend invertieren",
"angle_state_address": "Tilt-Status (DPT 5.001)",
"angle_state_address_invert_incoming": "Eingehend invertieren",
"angle_state_address_invert_outgoing": "Ausgehend invertieren"
}
},
"remove_port": {
"title": "Port entfernen",
"data": {
"port_id": "Zu entfernender Port"
}
}
}
}
}

View File

@@ -0,0 +1,73 @@
{
"options": {
"abort": {
"no_ports_to_remove": "There are no ports to remove yet."
},
"step": {
"init": {
"title": "HA KNX Bridge Options",
"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"
}
}
}
}
}