Skip to content

Commit

Permalink
Use kelvin instead of mireds for color temp (#1905)
Browse files Browse the repository at this point in the history
  • Loading branch information
SukramJ authored Dec 10, 2024
1 parent 1000575 commit 0f79e97
Show file tree
Hide file tree
Showing 8 changed files with 52 additions and 44 deletions.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.8.1
rev: v0.8.2
hooks:
- id: ruff
args:
Expand Down
4 changes: 4 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# Version 2024.12.2 (2024-12-10)

- Use kelvin instead of mireds for color temp

# Version 2024.12.1 (2024-12-10)

- Catch orjson.JSONDecodeError on faulthy json script response
Expand Down
2 changes: 1 addition & 1 deletion hahomematic/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import re
from typing import Any, Final, Required, TypedDict

VERSION: Final = "2024.12.1"
VERSION: Final = "2024.12.2"

DEFAULT_CONNECTION_CHECKER_INTERVAL: Final = 15 # check if connection is available via rpc ping
DEFAULT_CUSTOM_ID: Final = "custom_id"
Expand Down
50 changes: 27 additions & 23 deletions hahomematic/model/custom/light.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ class _StateChangeArg(StrEnum):
"""Enum with light state change arguments."""

BRIGHTNESS = "brightness"
COLOR_TEMP = "color_temp"
COLOR_TEMP_KELVIN = "color_temp_kelvin"
EFFECT = "effect"
HS_COLOR = "hs_color"
OFF = "off"
Expand Down Expand Up @@ -129,7 +129,7 @@ class LightOnArgs(TypedDict, total=False):
"""Matcher for the light turn on arguments."""

brightness: int
color_temp: int
color_temp_kelvin: int
effect: str
hs_color: tuple[float, float]
on_time: float
Expand Down Expand Up @@ -193,8 +193,8 @@ def channel_brightness_pct(self) -> int | None:
return None

@state_property
def color_temp(self) -> int | None:
"""Return the color temperature in mireds of this light between min/max mireds."""
def color_temp_kelvin(self) -> int | None:
"""Return the color temperature in kelvin."""
return None

@state_property
Expand All @@ -210,7 +210,7 @@ def supports_brightness(self) -> bool:
@property
def supports_color_temperature(self) -> bool:
"""Flag if light supports color temperature."""
return self.color_temp is not None
return self.color_temp_kelvin is not None

@property
def supports_effects(self) -> bool:
Expand Down Expand Up @@ -320,8 +320,8 @@ def is_state_change(self, **kwargs: Any) -> bool:
) is not None and hs_color != self.hs_color:
return True
if (
color_temp := kwargs.get(_StateChangeArg.COLOR_TEMP)
) is not None and color_temp != self.color_temp:
color_temp_kelvin := kwargs.get(_StateChangeArg.COLOR_TEMP_KELVIN)
) is not None and color_temp_kelvin != self.color_temp_kelvin:
return True
if (effect := kwargs.get(_StateChangeArg.EFFECT)) is not None and effect != self.effect:
return True
Expand Down Expand Up @@ -434,10 +434,14 @@ def _init_data_point_fields(self) -> None:
)

@state_property
def color_temp(self) -> int | None:
"""Return the color temperature in mireds of this light between min/max mireds."""
return int(
_MAX_MIREDS - (_MAX_MIREDS - _MIN_MIREDS) * (self._dp_color_level.value or _DIMMER_OFF)
def color_temp_kelvin(self) -> int | None:
"""Return the color temperature in kelvin."""
return math.floor(
_MAX_KELVIN
/ int(
_MAX_MIREDS
- (_MAX_MIREDS - _MIN_MIREDS) * (self._dp_color_level.value or _DIMMER_OFF)
)
)

@bind_collector()
Expand All @@ -447,8 +451,10 @@ async def turn_on(
"""Turn the light on."""
if not self.is_state_change(on=True, **kwargs):
return
if (color_temp := kwargs.get("color_temp")) is not None:
color_level = (_MAX_MIREDS - color_temp) / (_MAX_MIREDS - _MIN_MIREDS)
if (color_temp_kelvin := kwargs.get("color_temp_kelvin")) is not None:
color_level = (_MAX_MIREDS - math.floor(_MAX_KELVIN / color_temp_kelvin)) / (
_MAX_MIREDS - _MIN_MIREDS
)
await self._dp_color_level.send_value(value=color_level, collector=collector)

await super().turn_on(collector=collector, **kwargs)
Expand Down Expand Up @@ -490,11 +496,11 @@ def _init_data_point_fields(self) -> None:
)

@state_property
def color_temp(self) -> int | None:
"""Return the color temperature in mireds of this light between min/max mireds."""
def color_temp_kelvin(self) -> int | None:
"""Return the color temperature in kelvin."""
if not self._dp_color_temperature_kelvin.value:
return None
return math.floor(_MAX_KELVIN / self._dp_color_temperature_kelvin.value)
return self._dp_color_temperature_kelvin.value

@state_property
def hs_color(self) -> tuple[float, float] | None:
Expand Down Expand Up @@ -578,8 +584,7 @@ async def turn_on(
saturation = ksaturation / _SATURATION_MULTIPLIER
await self._dp_hue.send_value(value=int(hue), collector=collector)
await self._dp_saturation.send_value(value=saturation, collector=collector)
if color_temp := kwargs.get("color_temp"):
color_temp_kelvin = math.floor(_MAX_KELVIN / color_temp)
if color_temp_kelvin := kwargs.get("color_temp_kelvin"):
await self._dp_color_temperature_kelvin.send_value(
value=color_temp_kelvin, collector=collector
)
Expand Down Expand Up @@ -658,11 +663,11 @@ def _init_data_point_fields(self) -> None:
)

@state_property
def color_temp(self) -> int | None:
"""Return the color temperature in mireds of this light between min/max mireds."""
def color_temp_kelvin(self) -> int | None:
"""Return the color temperature in kelvin."""
if not self._dp_color_temperature_kelvin.value:
return None
return math.floor(_MAX_KELVIN / self._dp_color_temperature_kelvin.value)
return self._dp_color_temperature_kelvin.value

@state_property
def hs_color(self) -> tuple[float, float] | None:
Expand Down Expand Up @@ -693,8 +698,7 @@ async def turn_on(
saturation = ksaturation / _SATURATION_MULTIPLIER
await self._dp_hue.send_value(value=int(hue), collector=collector)
await self._dp_saturation.send_value(value=saturation, collector=collector)
if color_temp := kwargs.get("color_temp"):
color_temp_kelvin = math.floor(_MAX_KELVIN / color_temp)
if color_temp_kelvin := kwargs.get("color_temp_kelvin"):
await self._dp_color_temperature_kelvin.send_value(
value=color_temp_kelvin, collector=collector
)
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
aiohttp>=3.11.8
aiohttp>=3.11.10
orjson>=3.10.12
python-slugify>=8.0.4
voluptuous>=0.15.2
6 changes: 3 additions & 3 deletions requirements_test.txt
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
-r requirements.txt
-r requirements_test_pre_commit.txt

coverage==7.6.8
coverage==7.6.9
freezegun==1.5.1
mypy-dev==1.14.0a4
pip==24.3.1
pre-commit==4.0.1
pur==7.3.2
pydantic==2.10.2
pydantic==2.10.3
pydevccu==0.1.8
pylint-per-file-ignores==1.3.2
pylint-strict-informational==0.1
Expand All @@ -20,4 +20,4 @@ pytest-socket==0.7.0
pytest-timeout==2.3.1
pytest==8.3.4
types-python-slugify==8.0.2.20240310
uv==0.5.5
uv==0.5.7
2 changes: 1 addition & 1 deletion requirements_test_pre_commit.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
bandit==1.8.0
codespell==2.3.0
ruff==0.8.1
ruff==0.8.2
yamllint==1.35.1
28 changes: 14 additions & 14 deletions tests/test_light.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ async def test_cedimmer(
)
assert light.usage == DataPointUsage.CDP_PRIMARY
assert light.service_method_names == ("turn_off", "turn_on")
assert light.color_temp is None
assert light.color_temp_kelvin is None
assert light.hs_color is None
assert light.supports_brightness is True
assert light.supports_color_temperature is False
Expand Down Expand Up @@ -187,7 +187,7 @@ async def test_cecolordimmereffect(
CustomDpColorDimmerEffect, helper.get_prepared_custom_data_point(central, "VCU3747418", 1)
)
assert light.usage == DataPointUsage.CDP_PRIMARY
assert light.color_temp is None
assert light.color_temp_kelvin is None
assert light.hs_color == (0.0, 0.0)
assert light.supports_brightness is True
assert light.supports_color_temperature is False
Expand Down Expand Up @@ -343,7 +343,7 @@ async def test_cecolortempdimmer(
CustomDpColorTempDimmer, helper.get_prepared_custom_data_point(central, "VCU0000115", 1)
)
assert light.usage == DataPointUsage.CDP_PRIMARY
assert light.color_temp == 500
assert light.color_temp_kelvin == 2000
assert light.hs_color is None
assert light.supports_brightness is True
assert light.supports_color_temperature is True
Expand Down Expand Up @@ -381,8 +381,8 @@ async def test_cecolortempdimmer(
)
assert light.brightness == 0

assert light.color_temp == 500
await light.turn_on(color_temp=433)
assert light.color_temp_kelvin == 2000
await light.turn_on(color_temp_kelvin=2309)
assert mock_client.method_calls[-2] == call.set_value(
channel_address="VCU0000115:2",
paramset_key="VALUES",
Expand All @@ -397,7 +397,7 @@ async def test_cecolortempdimmer(
value=1.0,
wait_for_callback=WAIT_FOR_CALLBACK,
)
assert light.color_temp == 433
assert light.color_temp_kelvin == 2309

await light.turn_on()
call_count = len(mock_client.method_calls)
Expand Down Expand Up @@ -433,7 +433,7 @@ async def test_ceipfixedcolorlight(
CustomDpIpFixedColorLight, helper.get_prepared_custom_data_point(central, "VCU3716619", 8)
)
assert light.usage == DataPointUsage.CDP_PRIMARY
assert light.color_temp is None
assert light.color_temp_kelvin is None
assert light.hs_color == (0.0, 0.0)
assert light.supports_brightness is True
assert light.supports_color_temperature is False
Expand Down Expand Up @@ -633,7 +633,7 @@ async def test_ceipfixedcolorlightwired(
CustomDpIpFixedColorLight, helper.get_prepared_custom_data_point(central, "VCU4704397", 8)
)
assert light.usage == DataPointUsage.CDP_PRIMARY
assert light.color_temp is None
assert light.color_temp_kelvin is None
assert light.hs_color == (0.0, 0.0)
assert light.supports_brightness is True
assert light.supports_color_temperature is False
Expand Down Expand Up @@ -921,7 +921,7 @@ async def test_ceiprgbwlight(
CustomDpIpRGBWLight, helper.get_prepared_custom_data_point(central, "VCU5629873", 1)
)
assert light.usage == DataPointUsage.CDP_PRIMARY
assert light.color_temp is None
assert light.color_temp_kelvin is None
assert light.hs_color is None
assert light.supports_brightness is True
assert light.supports_color_temperature is False
Expand Down Expand Up @@ -983,15 +983,15 @@ async def test_ceiprgbwlight(
)
assert light.brightness == 0

assert light.color_temp is None
await light.turn_on(color_temp=300)
assert light.color_temp_kelvin is None
await light.turn_on(color_temp_kelvin=3000)
assert mock_client.method_calls[-1] == call.put_paramset(
channel_address="VCU5629873:1",
paramset_key="VALUES",
values={"COLOR_TEMPERATURE": 3333, "LEVEL": 1.0},
values={"COLOR_TEMPERATURE": 3000, "LEVEL": 1.0},
wait_for_callback=WAIT_FOR_CALLBACK,
)
assert light.color_temp == 300
assert light.color_temp_kelvin == 3000

await light.turn_on()
call_count = len(mock_client.method_calls)
Expand Down Expand Up @@ -1098,7 +1098,7 @@ async def test_cecolordimmer(
CustomDpColorDimmer, helper.get_prepared_custom_data_point(central, "VCU9973336", 13)
)
assert light.usage == DataPointUsage.CDP_PRIMARY
assert light.color_temp is None
assert light.color_temp_kelvin is None
assert light.hs_color == (0.0, 0.0)
assert light.supports_brightness is True
assert light.supports_color_temperature is False
Expand Down

0 comments on commit 0f79e97

Please sign in to comment.