From 221454d5e8f38e63d8cd921481c5186635cdbc58 Mon Sep 17 00:00:00 2001 From: bahmcloud Date: Mon, 9 Mar 2026 08:20:49 +0100 Subject: [PATCH] Fix HA 2026.03 light color temp compatibility and bump 0.0.34 --- .idea/PROJECT_STATE.md | 9 ++- CHANGELOG.md | 7 ++ README.md | 8 +- custom_components/ha_knx_bridge/bridge.py | 73 +++++++++---------- custom_components/ha_knx_bridge/manifest.json | 2 +- 5 files changed, 50 insertions(+), 49 deletions(-) diff --git a/.idea/PROJECT_STATE.md b/.idea/PROJECT_STATE.md index b178671..59ab849 100644 --- a/.idea/PROJECT_STATE.md +++ b/.idea/PROJECT_STATE.md @@ -10,7 +10,7 @@ The integration should: - Auto-select appropriate KNX DPTs and request only the group addresses needed. - Ignore missing/empty group addresses without errors. -Target compatibility: Home Assistant 2025.12 with forward compatibility to 2026.2. +Target compatibility: Home Assistant 2025.12 with forward compatibility to 2026.03. Project rules: - Keep `README.md` updated for user-relevant changes after each new version. @@ -18,7 +18,7 @@ Project rules: - Releases are created only when explicitly requested. - Version number must match everywhere it is referenced (manifest, changelog, README, HACS if used). -## Current State (as of 2026-02-15) +## Current State (as of 2026-03-09) Completed: - Repo initialized with `main` branch and pushed to GitHub. - HACS metadata (`hacs.json`) and base integration scaffold created. @@ -48,8 +48,9 @@ Completed: - Relative color temperature control wired into light schema, UI order adjusted, and KNX color temperature types aligned. - Color temperature service calls now use mireds for better compatibility. - Relative dimming/color temperature decoding improved for control/stepcode payloads. -- Relative dimming/CT now treat step_code 0 as a start/stop toggle and rely on raw payload parsing. -- Project version set to 0.0.32 and `CHANGELOG.md` maintained. +- Relative dimming/CT now parse control strings from DPT 3.007 payloads and treat raw step values as percent steps (0/8 stop). +- Home Assistant 2026.03 compatibility fix: graceful fallback for removed `ATTR_COLOR_TEMP` import and color temperature service calls now use Kelvin. +- Project version set to 0.0.34 and `CHANGELOG.md` maintained. Files created: - `custom_components/ha_knx_bridge/__init__.py` diff --git a/CHANGELOG.md b/CHANGELOG.md index 3af5cdb..87449c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## 0.0.34 - 2026-03-09 +- Fix startup on Home Assistant 2026.03 by handling removed `ATTR_COLOR_TEMP` imports. +- Switch light color temperature service calls to `color_temp_kelvin`. + +## 0.0.33 - 2026-02-15 +- Parse control strings from DPT 3.007 payloads and use raw step values as percent steps (0/8 stop). + ## 0.0.32 - 2026-02-15 - Treat DPT 3.007 step_code 0 as a start/stop toggle for relative dimming/CT and rely on raw payload parsing. diff --git a/README.md b/README.md index 1aa5a69..a42a922 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Current minimal scope: - Light port (KNX -> HA commands, HA state -> KNX) ## Requirements -- Home Assistant 2025.12 or newer (tested for compatibility with 2026.2). +- Home Assistant 2025.12 or newer (tested for compatibility with 2026.03). - The built-in KNX integration must be set up and running. ## Install (HACS) @@ -77,8 +77,8 @@ Notes: - Relative dimming (DPT 3.007) maps KNX step values (control/stepcode) to small `brightness_step_pct` changes in Home Assistant. - For relative dimming, the bridge repeats steps until a KNX stop telegram (0 or 8) is received. - Relative color temperature (DPT 3.007) adjusts Kelvin in the same start/stop pattern. -- Some KNX keypads send `step_code 0` for a start/stop toggle; the bridge treats `step_code 0` as start when idle and stop when already dimming. -- Color temperature mode must match the KNX telegram DPT: `relative` for 5.001, `absolute` for 7.600 (2-byte unsigned), `absolute_float` for DPT 9 (2-byte float). The bridge sends HA color temperature using `color_temp` (mireds) for maximum compatibility. +- Step values `1..7` dim down, `9..15` dim up, and `0/8` stop; the bridge uses the raw step value as the percent step size. +- Color temperature mode must match the KNX telegram DPT: `relative` for 5.001, `absolute` for 7.600 (2-byte unsigned), `absolute_float` for DPT 9 (2-byte float). The bridge sends HA color temperature using `color_temp_kelvin`. ## Notes - For DPT 1.008 (Up/Down), the bridge treats `0 = Up/Open` and `1 = Down/Close`. @@ -113,5 +113,5 @@ Notes: - Advanced DPT mapping options and inversion settings. ## Versioning and Releases -- Current version: 0.0.32 +- Current version: 0.0.34 - `CHANGELOG.md` lists versions with the newest entries at the top. diff --git a/custom_components/ha_knx_bridge/bridge.py b/custom_components/ha_knx_bridge/bridge.py index 2e1f6c5..3c22131 100644 --- a/custom_components/ha_knx_bridge/bridge.py +++ b/custom_components/ha_knx_bridge/bridge.py @@ -8,7 +8,6 @@ from typing import Any from homeassistant.components.light import ( ATTR_BRIGHTNESS, ATTR_COLOR_MODE, - ATTR_COLOR_TEMP, ATTR_HS_COLOR, ATTR_RGB_COLOR, ATTR_RGBW_COLOR, @@ -79,6 +78,11 @@ _LOGGER = logging.getLogger(__name__) KNX_DOMAIN = "knx" +try: + from homeassistant.components.light import ATTR_COLOR_TEMP +except ImportError: # pragma: no cover - fallback for newer HA + ATTR_COLOR_TEMP = "color_temp" + try: from homeassistant.components.light import ATTR_COLOR_TEMP_KELVIN except ImportError: # pragma: no cover - fallback for older HA @@ -678,13 +682,13 @@ class BridgeManager: ) self._register_knx_light_command( port.relative_color_temperature_address, - None, + "control_dimming", port, "relative_color_temperature", ) self._register_knx_light_command( port.relative_dimming_address, - None, + "control_dimming", port, "relative_dimming", ) @@ -1167,7 +1171,7 @@ class BridgeManager: await self._call_light_service( port.entity_id, "turn_on", - {"color_temp": _kelvin_to_mireds(kelvin)}, + {ATTR_COLOR_TEMP_KELVIN: int(round(kelvin))}, ) return @@ -1176,13 +1180,7 @@ class BridgeManager: if dim_info is not None: control, step_code = dim_info if step_code == 0: - direction = "up" if control else "down" - if port.entity_id in self._light_dimming_tasks: - self._stop_light_dimming(port.entity_id) - return - self._start_light_dimming( - port.entity_id, direction, 1 - ) + self._stop_light_dimming(port.entity_id) return value = _extract_dimming_value(event) if value is None: @@ -1204,13 +1202,7 @@ class BridgeManager: if dim_info is not None: control, step_code = dim_info if step_code == 0: - direction = "up" if control else "down" - if port.entity_id in self._light_ct_tasks: - self._stop_light_ct_adjust(port.entity_id) - return - self._start_light_ct_adjust( - port, direction, 1 - ) + self._stop_light_ct_adjust(port.entity_id) return value = _extract_dimming_value(event) if value is None: @@ -1516,7 +1508,7 @@ class BridgeManager: await self._call_light_service( port.entity_id, "turn_on", - {"color_temp": _kelvin_to_mireds(next_kelvin)}, + {ATTR_COLOR_TEMP_KELVIN: int(round(next_kelvin))}, ) await asyncio.sleep(0.3) @@ -1549,7 +1541,7 @@ class BridgeManager: await self._call_light_service( port.entity_id, "turn_on", - {"color_temp": _kelvin_to_mireds(next_kelvin)}, + {ATTR_COLOR_TEMP_KELVIN: int(round(next_kelvin))}, ) self.hass.async_create_task(_runner()) @@ -1595,8 +1587,11 @@ def _extract_dimming_info(event: Event) -> tuple[bool, int] | None: control = value.get("control") step = value.get("stepcode", value.get("step_code")) if control is not None and step is not None: + control_bit = _parse_control_value(control) + if control_bit is None: + return None try: - return bool(int(control)), int(step) & 0x07 + return control_bit, int(step) & 0x07 except (TypeError, ValueError): return None if isinstance(value, (int, float)): @@ -1616,6 +1611,20 @@ def _extract_dimming_info(event: Event) -> tuple[bool, int] | None: return None +def _parse_control_value(value: Any) -> bool | None: + if isinstance(value, bool): + return value + if isinstance(value, (int, float)): + return bool(int(value)) + if isinstance(value, str): + text = value.strip().lower() + if text in ("increase", "up", "raise", "1", "true", "on"): + return True + if text in ("decrease", "down", "lower", "0", "false", "off"): + return False + return None + + def _clean_address(address: Any) -> str | None: if not address: return None @@ -1692,12 +1701,6 @@ def _percent_to_brightness(percent: int) -> int: return int(round(percent / 100 * 255)) -def _kelvin_to_mireds(kelvin: float) -> int: - if kelvin <= 0: - return 0 - return int(round(1000000 / kelvin)) - - def _mireds_to_kelvin(mireds: float) -> float: if mireds <= 0: return 0 @@ -1889,21 +1892,11 @@ def _relative_dimming_step(value: int) -> tuple[str, int] | None: return None if value <= 7: direction = "down" - step = value + percent = value else: direction = "up" - step = value - 8 - percent_map = { - 1: 10, - 2: 8, - 3: 6, - 4: 4, - 5: 3, - 6: 2, - 7: 1, - } - percent = percent_map.get(step) - if percent is None: + percent = value + if percent <= 0: return None return direction, percent diff --git a/custom_components/ha_knx_bridge/manifest.json b/custom_components/ha_knx_bridge/manifest.json index 653e24b..a3bb13b 100644 --- a/custom_components/ha_knx_bridge/manifest.json +++ b/custom_components/ha_knx_bridge/manifest.json @@ -1,7 +1,7 @@ { "domain": "ha_knx_bridge", "name": "HA KNX Bridge", - "version": "0.0.32", + "version": "0.0.34", "config_flow": true, "documentation": "https://github.com/bahmcloud/HA-KNX-Bridge", "issue_tracker": "https://github.com/bahmcloud/HA-KNX-Bridge/issues",