mirror of
https://github.com/bahmcloud/HA-KNX-Bridge.git
synced 2026-04-06 20:21:13 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 91f51cb414 |
3
.idea/PROJECT_STATE.md
generated
3
.idea/PROJECT_STATE.md
generated
@@ -48,7 +48,8 @@ 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.
|
||||
- Project version set to 0.0.30 and `CHANGELOG.md` maintained.
|
||||
- Relative color temperature adjustment now accepts step_code 0 as a single-step and registers control_dimming event types for relative dimming/CT.
|
||||
- Project version set to 0.0.31 and `CHANGELOG.md` maintained.
|
||||
|
||||
Files created:
|
||||
- `custom_components/ha_knx_bridge/__init__.py`
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
# Changelog
|
||||
|
||||
## 0.0.31 - 2026-02-15
|
||||
- Treat DPT 3.007 step_code 0 as a single-step for relative dimming/CT and register control_dimming event types.
|
||||
|
||||
## 0.0.30 - 2026-02-15
|
||||
- Improve relative dimming/color temperature decoding for control/stepcode payloads.
|
||||
|
||||
|
||||
@@ -112,5 +112,5 @@ Notes:
|
||||
- Advanced DPT mapping options and inversion settings.
|
||||
|
||||
## Versioning and Releases
|
||||
- Current version: 0.0.30
|
||||
- Current version: 0.0.31
|
||||
- `CHANGELOG.md` lists versions with the newest entries at the top.
|
||||
|
||||
@@ -678,13 +678,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",
|
||||
)
|
||||
@@ -1172,6 +1172,18 @@ class BridgeManager:
|
||||
return
|
||||
|
||||
if action == "relative_dimming":
|
||||
dim_info = _extract_dimming_info(event)
|
||||
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._apply_light_dimming_step(
|
||||
port.entity_id, direction, 1
|
||||
)
|
||||
return
|
||||
value = _extract_dimming_value(event)
|
||||
if value is None:
|
||||
return
|
||||
@@ -1188,6 +1200,18 @@ class BridgeManager:
|
||||
return
|
||||
|
||||
if action == "relative_color_temperature":
|
||||
dim_info = _extract_dimming_info(event)
|
||||
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._apply_light_ct_step_once(
|
||||
port, direction, 1
|
||||
)
|
||||
return
|
||||
value = _extract_dimming_value(event)
|
||||
if value is None:
|
||||
return
|
||||
@@ -1448,6 +1472,21 @@ class BridgeManager:
|
||||
_runner()
|
||||
)
|
||||
|
||||
def _apply_light_dimming_step(
|
||||
self, entity_id: str, direction: str, step_code: int
|
||||
) -> None:
|
||||
percent = _relative_dimming_percent(step_code)
|
||||
if percent is None or percent <= 0:
|
||||
return
|
||||
|
||||
async def _runner() -> None:
|
||||
step = percent if direction == "up" else -percent
|
||||
await self._call_light_service(
|
||||
entity_id, "turn_on", {"brightness_step_pct": step}
|
||||
)
|
||||
|
||||
self.hass.async_create_task(_runner())
|
||||
|
||||
def _stop_light_dimming(self, entity_id: str) -> None:
|
||||
task = self._light_dimming_tasks.pop(entity_id, None)
|
||||
if task is not None:
|
||||
@@ -1485,6 +1524,36 @@ class BridgeManager:
|
||||
_runner()
|
||||
)
|
||||
|
||||
def _apply_light_ct_step_once(
|
||||
self, port: LightPort, direction: str, step_code: int
|
||||
) -> None:
|
||||
percent = _relative_dimming_percent(step_code)
|
||||
if percent is None or percent <= 0:
|
||||
return
|
||||
|
||||
async def _runner() -> None:
|
||||
current_kelvin = _current_color_temp_kelvin(self.hass, port)
|
||||
if current_kelvin is None:
|
||||
current_kelvin = _light_min_kelvin(port)
|
||||
delta = (_light_max_kelvin(port) - _light_min_kelvin(port)) * (
|
||||
percent / 100
|
||||
)
|
||||
if direction == "up":
|
||||
next_kelvin = current_kelvin + delta
|
||||
else:
|
||||
next_kelvin = current_kelvin - delta
|
||||
next_kelvin = min(
|
||||
max(next_kelvin, _light_min_kelvin(port)),
|
||||
_light_max_kelvin(port),
|
||||
)
|
||||
await self._call_light_service(
|
||||
port.entity_id,
|
||||
"turn_on",
|
||||
{"color_temp": _kelvin_to_mireds(next_kelvin)},
|
||||
)
|
||||
|
||||
self.hass.async_create_task(_runner())
|
||||
|
||||
def _stop_light_ct_adjust(self, entity_id: str) -> None:
|
||||
task = self._light_ct_tasks.pop(entity_id, None)
|
||||
if task is not None:
|
||||
@@ -1512,6 +1581,14 @@ def _extract_event_value(event: Event) -> int | None:
|
||||
|
||||
|
||||
def _extract_dimming_value(event: Event) -> int | None:
|
||||
info = _extract_dimming_info(event)
|
||||
if info is not None:
|
||||
control, step_code = info
|
||||
return ((1 if control else 0) << 3) | (step_code & 0x07)
|
||||
return _extract_event_value(event)
|
||||
|
||||
|
||||
def _extract_dimming_info(event: Event) -> tuple[bool, int] | None:
|
||||
if "value" in event.data:
|
||||
value = event.data["value"]
|
||||
if isinstance(value, dict):
|
||||
@@ -1519,20 +1596,24 @@ def _extract_dimming_value(event: Event) -> int | None:
|
||||
step = value.get("stepcode", value.get("step_code"))
|
||||
if control is not None and step is not None:
|
||||
try:
|
||||
control_bit = 1 if int(control) else 0
|
||||
step_code = int(step) & 0x07
|
||||
return (control_bit << 3) | step_code
|
||||
return bool(int(control)), int(step) & 0x07
|
||||
except (TypeError, ValueError):
|
||||
pass
|
||||
return None
|
||||
if isinstance(value, (int, float)):
|
||||
raw = int(value)
|
||||
return bool(raw & 0x08), raw & 0x07
|
||||
if isinstance(value, (list, tuple)) and len(value) >= 2:
|
||||
try:
|
||||
return bool(int(value[0])), int(value[1]) & 0x07
|
||||
except (TypeError, ValueError):
|
||||
return None
|
||||
data = event.data.get("data")
|
||||
if isinstance(data, (list, tuple)) and len(data) >= 2:
|
||||
try:
|
||||
control_bit = 1 if int(data[0]) else 0
|
||||
step_code = int(data[1]) & 0x07
|
||||
return (control_bit << 3) | step_code
|
||||
return bool(int(data[0])), int(data[1]) & 0x07
|
||||
except (TypeError, ValueError):
|
||||
return None
|
||||
return _extract_event_value(event)
|
||||
return None
|
||||
|
||||
|
||||
def _clean_address(address: Any) -> str | None:
|
||||
@@ -1827,6 +1908,24 @@ def _relative_dimming_step(value: int) -> tuple[str, int] | None:
|
||||
return direction, percent
|
||||
|
||||
|
||||
def _relative_dimming_percent(step_code: int) -> int | None:
|
||||
percent_map = {
|
||||
1: 10,
|
||||
2: 8,
|
||||
3: 6,
|
||||
4: 4,
|
||||
5: 3,
|
||||
6: 2,
|
||||
7: 1,
|
||||
}
|
||||
step = int(step_code)
|
||||
if step <= 0:
|
||||
step = 1
|
||||
if step > 7:
|
||||
step = 7
|
||||
return percent_map.get(step)
|
||||
|
||||
|
||||
def _map_scalar_value(value: Any) -> int | None:
|
||||
if isinstance(value, bool):
|
||||
return 1 if value else 0
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"domain": "ha_knx_bridge",
|
||||
"name": "HA KNX Bridge",
|
||||
"version": "0.0.30",
|
||||
"version": "0.0.31",
|
||||
"config_flow": true,
|
||||
"documentation": "https://github.com/bahmcloud/HA-KNX-Bridge",
|
||||
"issue_tracker": "https://github.com/bahmcloud/HA-KNX-Bridge/issues",
|
||||
|
||||
Reference in New Issue
Block a user