Add temperature control module for smart (#848)

This commit is contained in:
Teemu R 2024-04-22 13:39:07 +02:00 committed by GitHub
parent 890900daf3
commit 72db5c6447
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 125 additions and 0 deletions

View File

@ -19,6 +19,7 @@ class DeviceType(Enum):
Sensor = "sensor" Sensor = "sensor"
Hub = "hub" Hub = "hub"
Fan = "fan" Fan = "fan"
Thermostat = "thermostat"
Unknown = "unknown" Unknown = "unknown"
@staticmethod @staticmethod

View File

@ -17,6 +17,7 @@ from .ledmodule import LedModule
from .lighttransitionmodule import LightTransitionModule from .lighttransitionmodule import LightTransitionModule
from .reportmodule import ReportModule from .reportmodule import ReportModule
from .temperature import TemperatureSensor from .temperature import TemperatureSensor
from .temperaturecontrol import TemperatureControl
from .timemodule import TimeModule from .timemodule import TimeModule
__all__ = [ __all__ = [
@ -28,6 +29,7 @@ __all__ = [
"BatterySensor", "BatterySensor",
"HumiditySensor", "HumiditySensor",
"TemperatureSensor", "TemperatureSensor",
"TemperatureControl",
"ReportModule", "ReportModule",
"AutoOffModule", "AutoOffModule",
"LedModule", "LedModule",

View File

@ -0,0 +1,87 @@
"""Implementation of temperature control module."""
from __future__ import annotations
from typing import TYPE_CHECKING
from ...feature import Feature
from ..smartmodule import SmartModule
if TYPE_CHECKING:
from ..smartdevice import SmartDevice
class TemperatureControl(SmartModule):
"""Implementation of temperature module."""
REQUIRED_COMPONENT = "temperature_control"
def __init__(self, device: SmartDevice, module: str):
super().__init__(device, module)
self._add_feature(
Feature(
device,
"Target temperature",
container=self,
attribute_getter="target_temperature",
attribute_setter="set_target_temperature",
icon="mdi:thermometer",
)
)
# TODO: this might belong into its own module, temperature_correction?
self._add_feature(
Feature(
device,
"Temperature offset",
container=self,
attribute_getter="temperature_offset",
attribute_setter="set_temperature_offset",
minimum_value=-10,
maximum_value=10,
)
)
def query(self) -> dict:
"""Query to execute during the update cycle."""
# Target temperature is contained in the main device info response.
return {}
@property
def minimum_target_temperature(self) -> int:
"""Minimum available target temperature."""
return self._device.sys_info["min_control_temp"]
@property
def maximum_target_temperature(self) -> int:
"""Minimum available target temperature."""
return self._device.sys_info["max_control_temp"]
@property
def target_temperature(self) -> int:
"""Return target temperature."""
return self._device.sys_info["target_temperature"]
async def set_target_temperature(self, target: int):
"""Set target temperature."""
if (
target < self.minimum_target_temperature
or target > self.maximum_target_temperature
):
raise ValueError(
f"Invalid target temperature {target}, must be in range "
f"[{self.minimum_target_temperature},{self.maximum_target_temperature}]"
)
return await self.call("set_device_info", {"target_temp": target})
@property
def temperature_offset(self) -> int:
"""Return temperature offset."""
return self._device.sys_info["temp_offset"]
async def set_temperature_offset(self, offset: int):
"""Set temperature offset."""
if offset < -10 or offset > 10:
raise ValueError("Temperature offset must be [-10, 10]")
return await self.call("set_device_info", {"temp_offset": offset})

View File

@ -52,6 +52,7 @@ class SmartChildDevice(SmartDevice):
"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-fan": DeviceType.Fan,
"kasa.switch.outlet.sub-dimmer": DeviceType.Dimmer, "kasa.switch.outlet.sub-dimmer": DeviceType.Dimmer,
"subg.trv": DeviceType.Thermostat,
} }
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:

View File

@ -0,0 +1,34 @@
import pytest
from kasa.smart.modules import TemperatureSensor
from kasa.tests.device_fixtures import parametrize
temperature = parametrize(
"has temperature control",
component_filter="temperature_control",
protocol_filter={"SMART.CHILD"},
)
@temperature
@pytest.mark.parametrize(
"feature, type",
[
("target_temperature", int),
("temperature_offset", int),
],
)
async def test_temperature_control_features(dev, feature, type):
"""Test that features are registered and work as expected."""
temp_module: TemperatureSensor = dev.modules["TemperatureControl"]
prop = getattr(temp_module, feature)
assert isinstance(prop, type)
feat = temp_module._module_features[feature]
assert feat.value == prop
assert isinstance(feat.value, type)
await feat.set_value(10)
await dev.update()
assert feat.value == 10