diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 78e20fac..af561e73 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -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: diff --git a/changelog.md b/changelog.md index b3aab47f..891e00e2 100644 --- a/changelog.md +++ b/changelog.md @@ -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 diff --git a/hahomematic/const.py b/hahomematic/const.py index 6b817d66..7e27f377 100644 --- a/hahomematic/const.py +++ b/hahomematic/const.py @@ -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" diff --git a/hahomematic/model/custom/light.py b/hahomematic/model/custom/light.py index c984ee72..0cdcfcf4 100644 --- a/hahomematic/model/custom/light.py +++ b/hahomematic/model/custom/light.py @@ -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" @@ -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 @@ -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 @@ -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: @@ -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 @@ -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() @@ -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) @@ -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: @@ -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 ) @@ -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: @@ -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 ) diff --git a/requirements.txt b/requirements.txt index b6c664d6..c2feb7a4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -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 diff --git a/requirements_test.txt b/requirements_test.txt index e941cf5c..df1c2196 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -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 @@ -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 diff --git a/requirements_test_pre_commit.txt b/requirements_test_pre_commit.txt index 254593bf..2bc18010 100644 --- a/requirements_test_pre_commit.txt +++ b/requirements_test_pre_commit.txt @@ -1,4 +1,4 @@ bandit==1.8.0 codespell==2.3.0 -ruff==0.8.1 +ruff==0.8.2 yamllint==1.35.1 diff --git a/tests/test_light.py b/tests/test_light.py index 9df958a5..c58467ef 100644 --- a/tests/test_light.py +++ b/tests/test_light.py @@ -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 @@ -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 @@ -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 @@ -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", @@ -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) @@ -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 @@ -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 @@ -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 @@ -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) @@ -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