diff --git a/OWNd/message/lighting.py b/OWNd/message/lighting.py index 0fe5472..edeb5b6 100644 --- a/OWNd/message/lighting.py +++ b/OWNd/message/lighting.py @@ -408,6 +408,83 @@ def switch_off(cls, where: str | int, _transition: int | None = None) -> OWNLigh message._human_readable_log = f"Switching OFF light or switch {message._where}{message._interface_log_text}." return message + @classmethod + def set_brightness_preset(cls, where: str | int, preset: int) -> OWNLightingCommand: + """Dimmer preset WHAT 2..10 (20 %..100 %), the frame libqtdevices + DimmerDevice::setLevel100 sends (lighting_device.cpp:240-245).""" + preset = int(preset) + if not 2 <= preset <= 10: + raise ValueError("preset must be between 2 and 10") + message = cls(f"*1*{preset}*{where}##") + message._human_readable_log = f"Setting light {message._where}{message._interface_log_text} to preset level {preset}." # pylint: disable=line-too-long + return message + + @classmethod + def step_up( + cls, where: str | int, delta: int | None = None, speed: int | None = None + ) -> OWNLightingCommand: + """One level up (WHAT 30), or ``30#delta#speed`` for a dimmer 100 + (libqtdevices lighting_device.cpp:230-233, 409-412).""" + return cls._step(where, 30, "up", delta, speed) + + @classmethod + def step_down( + cls, where: str | int, delta: int | None = None, speed: int | None = None + ) -> OWNLightingCommand: + """One level down (WHAT 31), or ``31#delta#speed`` for a dimmer 100 + (libqtdevices lighting_device.cpp:235-238, 414-417).""" + return cls._step(where, 31, "down", delta, speed) + + @classmethod + def _step( + cls, where: str | int, what: int, direction: str, delta: int | None, speed: int | None + ) -> OWNLightingCommand: + if delta is None: + message = cls(f"*1*{what}*{where}##") + message._human_readable_log = f"Dimming light {message._where}{message._interface_log_text} one level {direction}." # pylint: disable=line-too-long + return message + delta = int(delta) + speed = int(speed if speed is not None else 0) + if not 1 <= delta <= 100 or not 0 <= speed <= 255: + raise ValueError("delta must be 1..100 and speed 0..255") + message = cls(f"*1*{what}#{delta}#{speed}*{where}##") + message._human_readable_log = f"Dimming light {message._where}{message._interface_log_text} {direction} by {delta}% at speed {speed}." # pylint: disable=line-too-long + return message + + _FIXED_TIMERS = {11: 60, 12: 120, 13: 180, 14: 240, 15: 300, 16: 900, 17: 30, 18: 0.5} + + @classmethod + def switch_on_timed(cls, where: str | int, what: int) -> OWNLightingCommand: + """Fixed timer WHAT 11..18 (1, 2, 3, 4, 5, 15 min, 30 s, 0.5 s), the + frame libqtdevices LightingDevice::fixedTiming sends (lighting_device.cpp:110-117).""" + what = int(what) + if what not in cls._FIXED_TIMERS: + raise ValueError("timer WHAT must be between 11 and 18") + message = cls(f"*1*{what}*{where}##") + message._human_readable_log = f"Switching ON light {message._where}{message._interface_log_text} for {cls._FIXED_TIMERS[what]}s." # pylint: disable=line-too-long + return message + + @classmethod + def set_variable_timer( + cls, where: str | int, hours: int, minutes: int, seconds: int + ) -> OWNLightingCommand: + """``*#1*W*#2*H*M*S##`` (libqtdevices LightingDevice::variableTiming, + lighting_device.cpp:119-124).""" + h, m, s = int(hours), int(minutes), int(seconds) + if not (0 <= h <= 255 and 0 <= m <= 59 and 0 <= s <= 59): + raise ValueError("hours 0..255, minutes and seconds 0..59") + message = cls(f"*#1*{where}*#2*{h}*{m}*{s}##") + message._human_readable_log = f"Setting light {message._where}{message._interface_log_text} timer to {h}h {m}m {s}s." # pylint: disable=line-too-long + return message + + @classmethod + def get_variable_timer(cls, where: str | int) -> OWNLightingCommand: + """``*#1*W*2##`` (libqtdevices LightingDevice::requestVariableTiming, + lighting_device.cpp:131-134).""" + message = cls(f"*#1*{where}*2##") + message._human_readable_log = f"Requesting light {message._where}{message._interface_log_text} timer." + return message + @classmethod def set_brightness( cls, where: str | int, _level: int = 30, _transition: int = 0 diff --git a/tests/test_who1_ts10_builders.py b/tests/test_who1_ts10_builders.py new file mode 100644 index 0000000..688bc83 --- /dev/null +++ b/tests/test_who1_ts10_builders.py @@ -0,0 +1,47 @@ +"""WHO 1 commands libqtdevices TS10_1_0_23 sends (lighting_device.cpp:34-39 +FIXED_TIMING_MIN/MAX = 11/18, DIMMER_INC/DEC = 30/31; :110-124 fixedTiming, +variableTiming; :131-134 requestVariableTiming; :230-245 increase/decrease/ +setLevel100; :409-417 Dimmer100 increaseLevel100/decreaseLevel100). +Firmware replay on bt_luci (MyHomeServer1, F454, F459): *1*2*11## ACK bus +11 00 12 1D, *1*5*11## 4D, *1*10*11## 9D, *1*30*11## 03, *1*31*11## 04, +*1*30#20#1*11## D1 11 01 42 0D 03 14 01, *1*31#20#255*11## ... 0D 04 14 FF, +*1*11*11## 16, *1*18*11## 86, *#1*11*#2*0*1*0## D1 11 01 42 06 00 01 00, +*#1*11*2## request frame D1 11 01 43 06 00 00 00.""" + +import pytest + +from OWNd.message import OWNLightingCommand + + +def test_presets(): + assert str(OWNLightingCommand.set_brightness_preset(11, 2)) == "*1*2*11##" + assert str(OWNLightingCommand.set_brightness_preset(11, 10)) == "*1*10*11##" + with pytest.raises(ValueError): + OWNLightingCommand.set_brightness_preset(11, 1) + with pytest.raises(ValueError): + OWNLightingCommand.set_brightness_preset(11, 11) + + +def test_steps(): + assert str(OWNLightingCommand.step_up(11)) == "*1*30*11##" + assert str(OWNLightingCommand.step_down(11)) == "*1*31*11##" + assert str(OWNLightingCommand.step_up(11, 20, 1)) == "*1*30#20#1*11##" + assert str(OWNLightingCommand.step_down(11, 20, 255)) == "*1*31#20#255*11##" + with pytest.raises(ValueError): + OWNLightingCommand.step_up(11, 0) + with pytest.raises(ValueError): + OWNLightingCommand.step_up(11, 10, 256) + + +def test_fixed_timers(): + assert str(OWNLightingCommand.switch_on_timed(11, 11)) == "*1*11*11##" + assert str(OWNLightingCommand.switch_on_timed(11, 18)) == "*1*18*11##" + with pytest.raises(ValueError): + OWNLightingCommand.switch_on_timed(11, 19) + + +def test_variable_timer(): + assert str(OWNLightingCommand.set_variable_timer(11, 0, 1, 0)) == "*#1*11*#2*0*1*0##" + assert str(OWNLightingCommand.get_variable_timer(11)) == "*#1*11*2##" + with pytest.raises(ValueError): + OWNLightingCommand.set_variable_timer(11, 0, 60, 0)