diff --git a/src/demetriek/device.py b/src/demetriek/device.py index 7b802b66..937b7144 100644 --- a/src/demetriek/device.py +++ b/src/demetriek/device.py @@ -13,6 +13,7 @@ from aiohttp.helpers import BasicAuth from yarl import URL +from .const import ScreensaverMode from .exceptions import ( LaMetricAuthenticationError, LaMetricConnectionError, @@ -32,7 +33,7 @@ if TYPE_CHECKING: from datetime import time - from .const import BrightnessMode, DeviceMode, ScreensaverMode + from .const import BrightnessMode, DeviceMode @dataclass @@ -203,7 +204,25 @@ async def display( # noqa: PLR0913 # pylint: disable=too-many-arguments A Display object, with latest or updated information about the display of the LaMetric device. + Raises: + ------ + ValueError: The time based mode was given without both of + its times. + """ + # The device wants both times on every time based write, even one + # that only toggles the mode. It rejects a lone end time and + # quietly ignores a lone start time, so catch that here rather + # than let it look like it worked. + if screensaver_mode is ScreensaverMode.TIME_BASED and ( + screensaver_start_time is None or screensaver_end_time is None + ): + msg = ( + "The time based screensaver mode needs both" + " screensaver_start_time and screensaver_end_time" + ) + raise ValueError(msg) + data: dict[str, Any] = {} if brightness is not None: diff --git a/tests/test_display.py b/tests/test_display.py index 8e233eea..0e51ada7 100644 --- a/tests/test_display.py +++ b/tests/test_display.py @@ -4,6 +4,7 @@ from datetime import time import aiohttp +import pytest from aresponses import Response, ResponsesMockServer from demetriek import LaMetricDevice @@ -161,3 +162,52 @@ async def response_handler(request: aiohttp.ClientResponse) -> Response: async with aiohttp.ClientSession() as session: demetriek = LaMetricDevice(host="127.0.0.2", api_key="abc", session=session) await demetriek.display(screensaver_mode=ScreensaverMode.WHEN_DARK) + + +@pytest.mark.parametrize( + ("start_time", "end_time"), + [ + (time(23, 0, 0), None), + (None, time(7, 0, 0)), + (None, None), + ], +) +async def test_set_display_screensaver_time_based_needs_both_times( + start_time: time | None, + end_time: time | None, +) -> None: + """Test the time based mode is rejected without both of its times.""" + demetriek = LaMetricDevice(host="127.0.0.2", api_key="abc") + with pytest.raises(ValueError, match="needs both"): + await demetriek.display( + screensaver_mode=ScreensaverMode.TIME_BASED, + screensaver_mode_enabled=True, + screensaver_start_time=start_time, + screensaver_end_time=end_time, + ) + + +async def test_set_display_screensaver_when_dark_needs_no_times( + aresponses: ResponsesMockServer, +) -> None: + """Test the other modes are unaffected by the time based requirement.""" + + async def response_handler(request: aiohttp.ClientResponse) -> Response: + """Response handler for this test.""" + assert await request.json() == { + "screensaver": {"mode": "when_dark", "mode_params": {"enabled": True}}, + } + return aresponses.Response( + status=200, + headers={"Content-Type": "application/json"}, + text=load_fixture("display_set_screensaver.json"), + ) + + aresponses.add("127.0.0.2:4343", "/api/v2/device/display", "PUT", response_handler) + + async with aiohttp.ClientSession() as session: + demetriek = LaMetricDevice(host="127.0.0.2", api_key="abc", session=session) + await demetriek.display( + screensaver_mode=ScreensaverMode.WHEN_DARK, + screensaver_mode_enabled=True, + )