mirror of
https://github.com/python-kasa/python-kasa.git
synced 2024-12-22 19:23:34 +00:00
Add fan module (#764)
Co-authored-by: Steven B <51370195+sdb9696@users.noreply.github.com>
This commit is contained in:
parent
da441bc697
commit
700643d3cf
@ -16,6 +16,7 @@ class DeviceType(Enum):
|
|||||||
LightStrip = "lightstrip"
|
LightStrip = "lightstrip"
|
||||||
Sensor = "sensor"
|
Sensor = "sensor"
|
||||||
Hub = "hub"
|
Hub = "hub"
|
||||||
|
Fan = "fan"
|
||||||
Unknown = "unknown"
|
Unknown = "unknown"
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -9,6 +9,7 @@ from .cloudmodule import CloudModule
|
|||||||
from .colortemp import ColorTemperatureModule
|
from .colortemp import ColorTemperatureModule
|
||||||
from .devicemodule import DeviceModule
|
from .devicemodule import DeviceModule
|
||||||
from .energymodule import EnergyModule
|
from .energymodule import EnergyModule
|
||||||
|
from .fanmodule import FanModule
|
||||||
from .firmware import Firmware
|
from .firmware import Firmware
|
||||||
from .humidity import HumiditySensor
|
from .humidity import HumiditySensor
|
||||||
from .ledmodule import LedModule
|
from .ledmodule import LedModule
|
||||||
@ -30,6 +31,7 @@ __all__ = [
|
|||||||
"AutoOffModule",
|
"AutoOffModule",
|
||||||
"LedModule",
|
"LedModule",
|
||||||
"Brightness",
|
"Brightness",
|
||||||
|
"FanModule",
|
||||||
"Firmware",
|
"Firmware",
|
||||||
"CloudModule",
|
"CloudModule",
|
||||||
"LightTransitionModule",
|
"LightTransitionModule",
|
||||||
|
66
kasa/smart/modules/fanmodule.py
Normal file
66
kasa/smart/modules/fanmodule.py
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
"""Implementation of fan_control module."""
|
||||||
|
from typing import TYPE_CHECKING, Dict
|
||||||
|
|
||||||
|
from ...feature import Feature, FeatureType
|
||||||
|
from ..smartmodule import SmartModule
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from ..smartdevice import SmartDevice
|
||||||
|
|
||||||
|
|
||||||
|
class FanModule(SmartModule):
|
||||||
|
"""Implementation of fan_control module."""
|
||||||
|
|
||||||
|
REQUIRED_COMPONENT = "fan_control"
|
||||||
|
|
||||||
|
def __init__(self, device: "SmartDevice", module: str):
|
||||||
|
super().__init__(device, module)
|
||||||
|
|
||||||
|
self._add_feature(
|
||||||
|
Feature(
|
||||||
|
device,
|
||||||
|
"Fan speed level",
|
||||||
|
container=self,
|
||||||
|
attribute_getter="fan_speed_level",
|
||||||
|
attribute_setter="set_fan_speed_level",
|
||||||
|
icon="mdi:fan",
|
||||||
|
type=FeatureType.Number,
|
||||||
|
minimum_value=1,
|
||||||
|
maximum_value=4,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self._add_feature(
|
||||||
|
Feature(
|
||||||
|
device,
|
||||||
|
"Fan sleep mode",
|
||||||
|
container=self,
|
||||||
|
attribute_getter="sleep_mode",
|
||||||
|
attribute_setter="set_sleep_mode",
|
||||||
|
icon="mdi:sleep",
|
||||||
|
type=FeatureType.Switch
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def query(self) -> Dict:
|
||||||
|
"""Query to execute during the update cycle."""
|
||||||
|
return {}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def fan_speed_level(self) -> int:
|
||||||
|
"""Return fan speed level."""
|
||||||
|
return self.data["fan_speed_level"]
|
||||||
|
|
||||||
|
async def set_fan_speed_level(self, level: int):
|
||||||
|
"""Set fan speed level."""
|
||||||
|
if level < 1 or level > 4:
|
||||||
|
raise ValueError("Invalid level, should be in range 1-4.")
|
||||||
|
return await self.call("set_device_info", {"fan_speed_level": level})
|
||||||
|
|
||||||
|
@property
|
||||||
|
def sleep_mode(self) -> bool:
|
||||||
|
"""Return sleep mode status."""
|
||||||
|
return self.data["fan_sleep_mode_on"]
|
||||||
|
|
||||||
|
async def set_sleep_mode(self, on: bool):
|
||||||
|
"""Set sleep mode."""
|
||||||
|
return await self.call("set_device_info", {"fan_sleep_mode_on": on})
|
@ -49,6 +49,8 @@ class SmartChildDevice(SmartDevice):
|
|||||||
child_device_map = {
|
child_device_map = {
|
||||||
"plug.powerstrip.sub-plug": DeviceType.Plug,
|
"plug.powerstrip.sub-plug": DeviceType.Plug,
|
||||||
"subg.trigger.temp-hmdt-sensor": DeviceType.Sensor,
|
"subg.trigger.temp-hmdt-sensor": DeviceType.Sensor,
|
||||||
|
"kasa.switch.outlet.sub-fan": DeviceType.Fan,
|
||||||
|
"kasa.switch.outlet.sub-dimmer": DeviceType.Dimmer,
|
||||||
}
|
}
|
||||||
dev_type = child_device_map.get(self.sys_info["category"])
|
dev_type = child_device_map.get(self.sys_info["category"])
|
||||||
if dev_type is None:
|
if dev_type is None:
|
||||||
|
@ -314,12 +314,11 @@ class SmartDevice(Device):
|
|||||||
return self._last_update
|
return self._last_update
|
||||||
|
|
||||||
def _update_internal_state(self, info):
|
def _update_internal_state(self, info):
|
||||||
"""Update internal state.
|
"""Update the internal info state.
|
||||||
|
|
||||||
This is used by the parent to push updates to its children
|
This is used by the parent to push updates to its children.
|
||||||
"""
|
"""
|
||||||
# TODO: cleanup the _last_update, _info mess.
|
self._info = info
|
||||||
self._last_update = self._info = info
|
|
||||||
|
|
||||||
async def _query_helper(
|
async def _query_helper(
|
||||||
self, method: str, params: Optional[Dict] = None, child_ids=None
|
self, method: str, params: Optional[Dict] = None, child_ids=None
|
||||||
|
@ -62,7 +62,7 @@ class SmartModule(Module):
|
|||||||
q = self.query()
|
q = self.query()
|
||||||
|
|
||||||
if not q:
|
if not q:
|
||||||
return dev.internal_state["get_device_info"]
|
return dev.sys_info
|
||||||
|
|
||||||
q_keys = list(q.keys())
|
q_keys = list(q.keys())
|
||||||
query_key = q_keys[0]
|
query_key = q_keys[0]
|
||||||
|
43
kasa/tests/smart/modules/test_fan.py
Normal file
43
kasa/tests/smart/modules/test_fan.py
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
from pytest_mock import MockerFixture
|
||||||
|
|
||||||
|
from kasa import SmartDevice
|
||||||
|
from kasa.smart.modules import FanModule
|
||||||
|
from kasa.tests.device_fixtures import parametrize
|
||||||
|
|
||||||
|
fan = parametrize(
|
||||||
|
"has fan", component_filter="fan_control", protocol_filter={"SMART.CHILD"}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@fan
|
||||||
|
async def test_fan_speed(dev: SmartDevice, mocker: MockerFixture):
|
||||||
|
"""Test fan speed feature."""
|
||||||
|
fan: FanModule = dev.modules["FanModule"]
|
||||||
|
level_feature = fan._module_features["fan_speed_level"]
|
||||||
|
assert level_feature.minimum_value <= level_feature.value <= level_feature.maximum_value
|
||||||
|
|
||||||
|
call = mocker.spy(fan, "call")
|
||||||
|
await fan.set_fan_speed_level(3)
|
||||||
|
call.assert_called_with("set_device_info", {"fan_sleep_level": 3})
|
||||||
|
|
||||||
|
await dev.update()
|
||||||
|
|
||||||
|
assert fan.fan_speed_level == 3
|
||||||
|
assert level_feature.value == 3
|
||||||
|
|
||||||
|
|
||||||
|
@fan
|
||||||
|
async def test_sleep_mode(dev: SmartDevice, mocker: MockerFixture):
|
||||||
|
"""Test sleep mode feature."""
|
||||||
|
fan: FanModule = dev.modules["FanModule"]
|
||||||
|
sleep_feature = fan._module_features["fan_sleep_mode"]
|
||||||
|
assert isinstance(sleep_feature.value, bool)
|
||||||
|
|
||||||
|
call = mocker.spy(fan, "call")
|
||||||
|
await fan.set_sleep_mode(True)
|
||||||
|
call.assert_called_with("set_device_info", {"fan_sleep_mode_on": True})
|
||||||
|
|
||||||
|
await dev.update()
|
||||||
|
|
||||||
|
assert fan.sleep_mode is True
|
||||||
|
assert sleep_feature.value is True
|
@ -32,8 +32,8 @@ async def test_childdevice_update(dev, dummy_protocol, mocker):
|
|||||||
|
|
||||||
await dev.update()
|
await dev.update()
|
||||||
|
|
||||||
assert dev._last_update != first._last_update
|
assert dev._info != first._info
|
||||||
assert child_list[0] == first._last_update
|
assert child_list[0] == first._info
|
||||||
|
|
||||||
|
|
||||||
@strip_smart
|
@strip_smart
|
||||||
|
Loading…
Reference in New Issue
Block a user