8000 Stabilise on_since value for smart devices (#1144) · msz-coder/python-kasa@1fcf3e4 · GitHub
[go: up one dir, main page]

Skip to content

Commit 1fcf3e4

Browse files
authored
Stabilise on_since value for smart devices (python-kasa#1144)
Caches the `on_since` value to prevent jitter caused by the device calculations.
1 parent 81e2685 commit 1fcf3e4

File tree

4 files changed

+43
-13
lines changed

4 files changed

+43
-13
lines changed

kasa/device.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -435,7 +435,11 @@ def has_emeter(self) -> bool:
435435
@property
436436
@abstractmethod
437437
def on_since(self) -> datetime | None:
438-
"""Return the time that the device was turned on or None if turned off."""
438+
"""Return the time that the device was turned on or None if turned off.
439+
440+
This returns a cached value if the device reported value difference is under
441+
five seconds to avoid device-caused jitter.
442+
"""
439443

440444
@abstractmethod
441445
async def wifi_scan(self) -> list[WifiNetwork]:

kasa/iot/iotdevice.py

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ def __init__(
181181
self._legacy_features: set[str] = set()
182182
self._children: Mapping[str, IotDevice] = {}
183183
self._modules: dict[str | ModuleName[Module], IotModule] = {}
184+
self._on_since: datetime | None = None
184185

185186
@property
186187
def children(self) -> Sequence[IotDevice]:
@@ -594,18 +595,25 @@ async def set_state(self, on: bool):
594595
@property # type: ignore
595596
@requires_update
596597
def on_since(self) -> datetime | None:
597-
"""Return pretty-printed on-time, or None if not available."""
598-
if "on_time" not in self._sys_info:
599-
return None
598+
"""Return the time that the device was turned on or None if turned off.
600599
601-
if self.is_off:
600+
This returns a cached value if the device reported value difference is under
601+
five seconds to avoid device-caused jitter.
602+
"""
603+
if self.is_off or "on_time" not in self._sys_info:
604+
self._on_since = None
602605
return None
603606

604607
on_time = self._sys_info["on_time"]
605608

606-
return datetime.now(timezone.utc).astimezone().replace(
607-
microsecond=0
608-
) - timedelta(seconds=on_time)
609+
time = datetime.now(timezone.utc).astimezone().replace(microsecond=0)
610+
611+
on_since = time - timedelta(seconds=on_time)
612+
if not self._on_since or timedelta(
613+
seconds=0
614+
) < on_since - self._on_since > timedelta(seconds=5):
615+
self._on_since = on_since
616+
return self._on_since
609617

610618
@property # type: ignore
611619
@requires_update

kasa/iot/iotstrip.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,7 @@ def __init__(self, host: str, parent: IotStrip, child_id: str) -> None:
318318
self._set_sys_info(parent.sys_info)
319319
self._device_type = DeviceType.StripSocket
320320
self.protocol = parent.protocol # Must use the same connection as the parent
321+
self._on_since: datetime | None = None
321322

322323
async def _initialize_modules(self):
323324
"""Initialize modules not added in init."""
@@ -438,14 +439,20 @@ def next_action(self) -> dict:
438439
def on_since(self) -> datetime | None:
439440
"""Return on-time, if available."""
440441
if self.is_off:
442+
self._on_since = None
441443
return None
442444

443445
info = self._get_child_info()
444446
on_time = info["on_time"]
445447

446-
return datetime.now(timezone.utc).astimezone().replace(
447-
microsecond=0
448-
) - timedelta(seconds=on_time)
448+
time = datetime.now(timezone.utc).astimezone().replace(microsecond=0)
449+
450+
on_since = time - timedelta(seconds=on_time)
451+
if not self._on_since or timedelta(
452+
seconds=0
453+
) < on_since - self._on_since > timedelta(seconds=5):
454+
self._on_since = on_since
455+
return self._on_since
449456

450457
@property # type: ignore
451458
@requires_update

kasa/smart/smartdevice.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ def __init__(
6666
self._children: Mapping[str, SmartDevice] = {}
6767
self._last_update = {}
6868
self._last_update_time: float | None = None
69+
self._on_since: datetime | None = None
6970

7071
async def _initialize_children(self):
7172
"""Initialize children for power strips."""
@@ -494,15 +495,25 @@ def time(self) -> datetime:
494495

495496
@property
496497
def on_since(self) -> datetime | None:
497-
"""Return the time that the device was turned on or None if turned off."""
498+
"""Return the time that the device was turned on or None if turned off.
499+
500+
This returns a cached value if the device reported value difference is under
501+
five seconds to avoid device-caused jitter.
502+
"""
498503
if (
499504
not self._info.get("device_on")
500505
or (on_time := self._info.get("on_time")) is None
501506
):
507+
self._on_since = None
502508
return None
503509

504510
on_time = cast(float, on_time)
505-
return self.time - timedelta(seconds=on_time)
511+
on_since = self.time - timedelta(seconds=on_time)
512+
if not self._on_since or timedelta(
513+
seconds=0
514+
) < on_since - self._on_since > timedelta(seconds=5):
515+
self._on_since = on_since
516+
return self._on_since
506517

507518
@property
508519
def timezone(self) -> dict:

0 commit comments

Comments
 (0)
0