Skip to content

Commit

Permalink
Fix roborock timers' next_schedule on repeated requests (#1520)
Browse files Browse the repository at this point in the history
Co-authored-by: Philipp Stehle <anderschwiedu@gmail.com>
  • Loading branch information
phil9909 and phil9909 authored Sep 14, 2022
1 parent ff4e18f commit de3290a
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 7 deletions.
22 changes: 16 additions & 6 deletions miio/integrations/vacuum/roborock/vacuumcontainers.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from datetime import datetime, time, timedelta, tzinfo
from datetime import datetime, time, timedelta
from enum import IntEnum
from typing import Any, Dict, List, Optional, Union

from croniter import croniter
from pytz import BaseTzInfo

from miio.device import DeviceStatus
from miio.devicestatus import sensor, setting
Expand Down Expand Up @@ -449,19 +450,19 @@ class Timer(DeviceStatus):
the creation time.
"""

def __init__(self, data: List[Any], timezone: tzinfo) -> None:
def __init__(self, data: List[Any], timezone: BaseTzInfo) -> None:
# id / timestamp, enabled, ['<cron string>', ['command', 'params']
# [['1488667794112', 'off', ['49 22 * * 6', ['start_clean', '']]],
# ['1488667777661', 'off', ['49 21 * * 3,4,5,6', ['start_clean', '']]
# ],
self.data = data
self.timezone = timezone

# ignoring the type here, as the localize is not provided directly by datetime.tzinfo
localized_ts = timezone.localize(datetime.now()) # type: ignore
localized_ts = timezone.localize(self._now())

# Initialize croniter to cause an exception on invalid entries (#847)
self.croniter = croniter(self.cron, start_time=localized_ts)
self._next_schedule: Optional[datetime] = None

@property
def id(self) -> str:
Expand Down Expand Up @@ -501,8 +502,17 @@ def action(self) -> str:

@property
def next_schedule(self) -> datetime:
"""Next schedule for the timer."""
return self.croniter.get_next(ret_type=datetime)
"""Next schedule for the timer.
Note, this value will not be updated after the Timer object has been created.
"""
if self._next_schedule is None:
self._next_schedule = self.croniter.get_next(ret_type=datetime)
return self._next_schedule

@staticmethod
def _now() -> datetime:
return datetime.now()


class SoundStatus(DeviceStatus):
Expand Down
26 changes: 25 additions & 1 deletion miio/tests/test_vacuums.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
"""Test of vacuum devices."""
from collections.abc import Iterable
from datetime import datetime
from typing import List, Sequence, Tuple, Type

import pytest
from pytz import UTC

from miio.device import Device
from miio.integrations.vacuum.roborock.vacuum import ROCKROBO_V1
from miio.integrations.vacuum.roborock.vacuum import ROCKROBO_V1, Timer
from miio.interfaces import VacuumInterface

# list of all supported vacuum classes
Expand Down Expand Up @@ -53,3 +55,25 @@ def test_vacuum_set_fan_speed_presets_fails(cls: Type[Device], model: str) -> No
assert isinstance(dev, VacuumInterface)
with pytest.raises(ValueError):
dev.set_fan_speed_preset(-1)


def test_vacuum_timer(mocker):
"""Test Timer class."""

mock = mocker.patch.object(Timer, attribute="_now")
mock.return_value = datetime(2000, 1, 1)

t = Timer(
data=["1488667794112", "off", ["49 22 * * 6", ["start_clean", ""]]],
timezone=UTC,
)

assert t.id == "1488667794112"
assert t.enabled is False
assert t.cron == "49 22 * * 6"
assert t.next_schedule == datetime(
2000, 1, 1, 22, 49, tzinfo=UTC
), "should figure out the next run"
assert t.next_schedule == datetime(
2000, 1, 1, 22, 49, tzinfo=UTC
), "should return the same value twice"

0 comments on commit de3290a

Please sign in to comment.