2024-02-19 19:39:20 +00:00
|
|
|
"""Module for smooth light transitions."""
|
2024-04-16 18:21:20 +00:00
|
|
|
|
2024-04-17 13:39:24 +00:00
|
|
|
from __future__ import annotations
|
|
|
|
|
2024-02-19 19:39:20 +00:00
|
|
|
from typing import TYPE_CHECKING
|
|
|
|
|
2024-02-24 01:16:43 +00:00
|
|
|
from ...exceptions import KasaException
|
2024-04-24 16:38:52 +00:00
|
|
|
from ...feature import Feature
|
2024-02-19 19:39:20 +00:00
|
|
|
from ..smartmodule import SmartModule
|
|
|
|
|
|
|
|
if TYPE_CHECKING:
|
|
|
|
from ..smartdevice import SmartDevice
|
|
|
|
|
|
|
|
|
2024-05-11 18:28:18 +00:00
|
|
|
class LightTransition(SmartModule):
|
2024-02-19 19:39:20 +00:00
|
|
|
"""Implementation of gradual on/off."""
|
|
|
|
|
|
|
|
REQUIRED_COMPONENT = "on_off_gradually"
|
|
|
|
QUERY_GETTER_NAME = "get_on_off_gradually_info"
|
2024-02-24 01:16:43 +00:00
|
|
|
MAXIMUM_DURATION = 60
|
2024-02-19 19:39:20 +00:00
|
|
|
|
2024-04-17 13:39:24 +00:00
|
|
|
def __init__(self, device: SmartDevice, module: str):
|
2024-02-19 19:39:20 +00:00
|
|
|
super().__init__(device, module)
|
2024-02-24 01:16:43 +00:00
|
|
|
self._create_features()
|
|
|
|
|
|
|
|
def _create_features(self):
|
|
|
|
"""Create features based on the available version."""
|
|
|
|
icon = "mdi:transition"
|
|
|
|
if self.supported_version == 1:
|
|
|
|
self._add_feature(
|
|
|
|
Feature(
|
|
|
|
device=self._device,
|
|
|
|
container=self,
|
2024-05-07 09:13:35 +00:00
|
|
|
id="smooth_transitions",
|
2024-02-24 01:16:43 +00:00
|
|
|
name="Smooth transitions",
|
|
|
|
icon=icon,
|
|
|
|
attribute_getter="enabled_v1",
|
|
|
|
attribute_setter="set_enabled_v1",
|
2024-04-24 16:38:52 +00:00
|
|
|
type=Feature.Type.Switch,
|
2024-02-24 01:16:43 +00:00
|
|
|
)
|
|
|
|
)
|
|
|
|
elif self.supported_version >= 2:
|
|
|
|
# v2 adds separate on & off states
|
|
|
|
# v3 adds max_duration
|
|
|
|
# TODO: note, hardcoding the maximums for now as the features get
|
|
|
|
# initialized before the first update.
|
|
|
|
self._add_feature(
|
|
|
|
Feature(
|
|
|
|
self._device,
|
2024-05-07 09:13:35 +00:00
|
|
|
id="smooth_transition_on",
|
|
|
|
name="Smooth transition on",
|
2024-02-24 01:16:43 +00:00
|
|
|
container=self,
|
|
|
|
attribute_getter="turn_on_transition",
|
|
|
|
attribute_setter="set_turn_on_transition",
|
|
|
|
icon=icon,
|
2024-04-24 16:38:52 +00:00
|
|
|
type=Feature.Type.Number,
|
2024-02-24 01:16:43 +00:00
|
|
|
maximum_value=self.MAXIMUM_DURATION,
|
|
|
|
)
|
|
|
|
) # self._turn_on_transition_max
|
|
|
|
self._add_feature(
|
|
|
|
Feature(
|
|
|
|
self._device,
|
2024-05-07 09:13:35 +00:00
|
|
|
id="smooth_transition_off",
|
|
|
|
name="Smooth transition off",
|
2024-02-24 01:16:43 +00:00
|
|
|
container=self,
|
|
|
|
attribute_getter="turn_off_transition",
|
|
|
|
attribute_setter="set_turn_off_transition",
|
|
|
|
icon=icon,
|
2024-04-24 16:38:52 +00:00
|
|
|
type=Feature.Type.Number,
|
2024-02-24 01:16:43 +00:00
|
|
|
maximum_value=self.MAXIMUM_DURATION,
|
|
|
|
)
|
|
|
|
) # self._turn_off_transition_max
|
|
|
|
|
|
|
|
@property
|
|
|
|
def _turn_on(self):
|
|
|
|
"""Internal getter for turn on settings."""
|
|
|
|
if "on_state" not in self.data:
|
|
|
|
raise KasaException(
|
|
|
|
f"Unsupported for {self.REQUIRED_COMPONENT} v{self.supported_version}"
|
|
|
|
)
|
|
|
|
|
|
|
|
return self.data["on_state"]
|
|
|
|
|
|
|
|
@property
|
|
|
|
def _turn_off(self):
|
|
|
|
"""Internal getter for turn off settings."""
|
|
|
|
if "off_state" not in self.data:
|
|
|
|
raise KasaException(
|
|
|
|
f"Unsupported for {self.REQUIRED_COMPONENT} v{self.supported_version}"
|
2024-02-19 19:39:20 +00:00
|
|
|
)
|
|
|
|
|
2024-02-24 01:16:43 +00:00
|
|
|
return self.data["off_state"]
|
|
|
|
|
2024-05-02 13:32:06 +00:00
|
|
|
async def set_enabled_v1(self, enable: bool):
|
2024-02-19 19:39:20 +00:00
|
|
|
"""Enable gradual on/off."""
|
2024-05-02 13:32:06 +00:00
|
|
|
return await self.call("set_on_off_gradually_info", {"enable": enable})
|
2024-02-19 19:39:20 +00:00
|
|
|
|
|
|
|
@property
|
2024-02-24 01:16:43 +00:00
|
|
|
def enabled_v1(self) -> bool:
|
2024-02-19 19:39:20 +00:00
|
|
|
"""Return True if gradual on/off is enabled."""
|
|
|
|
return bool(self.data["enable"])
|
|
|
|
|
2024-02-24 01:16:43 +00:00
|
|
|
@property
|
|
|
|
def turn_on_transition(self) -> int:
|
|
|
|
"""Return transition time for turning the light on.
|
|
|
|
|
|
|
|
Available only from v2.
|
|
|
|
"""
|
2024-04-24 18:17:49 +00:00
|
|
|
if "fade_on_time" in self._device.sys_info:
|
|
|
|
return self._device.sys_info["fade_on_time"]
|
2024-02-24 01:16:43 +00:00
|
|
|
return self._turn_on["duration"]
|
|
|
|
|
|
|
|
@property
|
|
|
|
def _turn_on_transition_max(self) -> int:
|
|
|
|
"""Maximum turn on duration."""
|
|
|
|
# v3 added max_duration, we default to 60 when it's not available
|
|
|
|
return self._turn_on.get("max_duration", 60)
|
|
|
|
|
|
|
|
async def set_turn_on_transition(self, seconds: int):
|
|
|
|
"""Set turn on transition in seconds.
|
|
|
|
|
|
|
|
Setting to 0 turns the feature off.
|
|
|
|
"""
|
|
|
|
if seconds > self._turn_on_transition_max:
|
|
|
|
raise ValueError(
|
|
|
|
f"Value {seconds} out of range, max {self._turn_on_transition_max}"
|
|
|
|
)
|
|
|
|
|
|
|
|
if seconds <= 0:
|
|
|
|
return await self.call(
|
|
|
|
"set_on_off_gradually_info",
|
|
|
|
{"on_state": {**self._turn_on, "enable": False}},
|
|
|
|
)
|
|
|
|
|
|
|
|
return await self.call(
|
|
|
|
"set_on_off_gradually_info",
|
|
|
|
{"on_state": {**self._turn_on, "duration": seconds}},
|
|
|
|
)
|
|
|
|
|
|
|
|
@property
|
|
|
|
def turn_off_transition(self) -> int:
|
|
|
|
"""Return transition time for turning the light off.
|
|
|
|
|
|
|
|
Available only from v2.
|
|
|
|
"""
|
2024-04-24 18:17:49 +00:00
|
|
|
if "fade_off_time" in self._device.sys_info:
|
|
|
|
return self._device.sys_info["fade_off_time"]
|
2024-02-24 01:16:43 +00:00
|
|
|
return self._turn_off["duration"]
|
|
|
|
|
|
|
|
@property
|
|
|
|
def _turn_off_transition_max(self) -> int:
|
|
|
|
"""Maximum turn on duration."""
|
|
|
|
# v3 added max_duration, we default to 60 when it's not available
|
|
|
|
return self._turn_off.get("max_duration", 60)
|
|
|
|
|
|
|
|
async def set_turn_off_transition(self, seconds: int):
|
|
|
|
"""Set turn on transition in seconds.
|
|
|
|
|
|
|
|
Setting to 0 turns the feature off.
|
|
|
|
"""
|
|
|
|
if seconds > self._turn_off_transition_max:
|
|
|
|
raise ValueError(
|
|
|
|
f"Value {seconds} out of range, max {self._turn_off_transition_max}"
|
|
|
|
)
|
|
|
|
|
|
|
|
if seconds <= 0:
|
|
|
|
return await self.call(
|
|
|
|
"set_on_off_gradually_info",
|
|
|
|
{"off_state": {**self._turn_off, "enable": False}},
|
|
|
|
)
|
|
|
|
|
|
|
|
return await self.call(
|
|
|
|
"set_on_off_gradually_info",
|
|
|
|
{"off_state": {**self._turn_on, "duration": seconds}},
|
|
|
|
)
|
2024-04-24 18:17:49 +00:00
|
|
|
|
|
|
|
def query(self) -> dict:
|
|
|
|
"""Query to execute during the update cycle."""
|
|
|
|
# Some devices have the required info in the device info.
|
|
|
|
if "gradually_on_mode" in self._device.sys_info:
|
|
|
|
return {}
|
|
|
|
else:
|
|
|
|
return {self.QUERY_GETTER_NAME: None}
|