Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions SUPPORTED.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ Some newer Kasa devices require authentication. These are marked with [^1] in th
- Hardware: 1.0 (US) / Firmware: 1.0.4[^1]
- Hardware: 1.0 (US) / Firmware: 1.0.5[^1]
- Hardware: 1.0 (US) / Firmware: 1.0.7[^1]
- Hardware: 1.0 (US) / Firmware: 1.0.9[^1]

### Bulbs

Expand Down
18 changes: 17 additions & 1 deletion devtools/helpers/smartrequests.py
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,22 @@ def get_on_off_gradually_info(
"get_on_off_gradually_info", params or SmartRequest.SmartRequestParams()
)

@staticmethod
def get_dimmer_calibration(
params: SmartRequestParams | None = None,
) -> list[SmartRequest]:
"""Get dimmer calibration."""
return [
# Not certain if the get_calibration is used anywhere...
SmartRequest(
"get_calibration", params or SmartRequest.SmartRequestParams()
),
# The brightness calibration is used, however.
SmartRequest(
"get_calibrate_brightness", params or SmartRequest.SmartRequestParams()
),
]

@staticmethod
def get_auto_light_info() -> SmartRequest:
"""Get auto light info."""
Expand Down Expand Up @@ -432,7 +448,7 @@ def get_component_requests(component_id, ver_code):
],
"control_child": [],
"homekit": [SmartRequest.get_raw_request("get_homekit_info")],
"dimmer_calibration": [],
"dimmer_calibration": SmartRequest.get_dimmer_calibration(),
"fan_control": [],
"overheat_protection": [],
# Vacuum components
Expand Down
3 changes: 3 additions & 0 deletions kasa/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,9 @@ class Module(ABC):
)
ContactSensor: Final[ModuleName[smart.ContactSensor]] = ModuleName("ContactSensor")
DeviceModule: Final[ModuleName[smart.DeviceModule]] = ModuleName("DeviceModule")
DimmerCalibration: Final[ModuleName[smart.DimmerCalibration]] = ModuleName(
"DimmerCalibration"
)
Firmware: Final[ModuleName[smart.Firmware]] = ModuleName("Firmware")
FrostProtection: Final[ModuleName[smart.FrostProtection]] = ModuleName(
"FrostProtection"
Expand Down
2 changes: 2 additions & 0 deletions kasa/smart/modules/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from .consumables import Consumables
from .contactsensor import ContactSensor
from .devicemodule import DeviceModule
from .dimmercalibration import DimmerCalibration
from .dustbin import Dustbin
from .energy import Energy
from .fan import Fan
Expand Down Expand Up @@ -49,6 +50,7 @@
"Time",
"Energy",
"DeviceModule",
"DimmerCalibration",
"ChildDevice",
"ChildLock",
"ChildSetup",
Expand Down
107 changes: 107 additions & 0 deletions kasa/smart/modules/dimmercalibration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
"""Implementation of the dimmer config module found in dimmers."""

from __future__ import annotations

import logging
from datetime import timedelta
from typing import Final

from ...exceptions import KasaException
from ...feature import Feature
from ..smartmodule import SmartModule

_LOGGER = logging.getLogger(__name__)


def _td_to_ms(td: timedelta) -> int:
"""
Convert timedelta to integer milliseconds.

Uses default float to integer rounding.
"""
return int(td / timedelta(milliseconds=1))


class DimmerCalibration(SmartModule):
"""Implements the dimmer config module."""

REQUIRED_COMPONENT = "dimmer_calibration"
QUERY_GETTER_NAME = "get_dimmer_calibration"

THRESHOLD_ABS_MIN: Final[int] = 0
THRESHOLD_ABS_MAX: Final[int] = 99

def _initialize_features(self) -> None:
"""Initialize features after the initial update."""
self._add_feature(
Feature(
device=self._device,
container=self,
id="dimmer_threshold_min",
name="Minimum dimming level",
icon="mdi:lightbulb-on-20",
attribute_getter="threshold_min",
attribute_setter="set_threshold_min",
range_getter=lambda: (self.THRESHOLD_ABS_MIN, self.THRESHOLD_ABS_MAX),
type=Feature.Type.Number,
category=Feature.Category.Config,
)
)
self._add_feature(
Feature(
device=self._device,
container=self,
id="dimmer_threshold_max",
name="Maximum dimming level",
icon="mdi:lightbulb-on-80",
attribute_getter="threshold_max",
attribute_setter="set_threshold_max",
range_getter=lambda: (self.THRESHOLD_ABS_MIN, self.THRESHOLD_ABS_MAX),
type=Feature.Type.Number,
category=Feature.Category.Config,
)
)

def query(self) -> dict:
"""Request Dimming configuration."""
if self.supported_version >= 1:
return {"get_calibrate_brightness": None}
return {}

@property
def threshold_min(self) -> int:
"""Return the minimum dimming level for this dimmer."""
return self.data["min_threshold"]

async def set_threshold_min(self, min: int) -> dict:
"""Set the minimum dimming level for this dimmer.

The value will depend on the luminaries connected to the dimmer.

:param min: The minimum dimming level, in the range 0-99.
"""
if min < self.THRESHOLD_ABS_MIN or min > self.THRESHOLD_ABS_MAX:
raise KasaException(
"Minimum dimming threshold is outside the supported range: "
f"{self.THRESHOLD_ABS_MIN}-{self.THRESHOLD_ABS_MAX}"
)
return await self.call("set_calibrate_brightness", {"min_threshold": min})

@property
def threshold_max(self) -> int:
"""Return the maximum dimming level for this dimmer."""
return self.data["max_threshold"]

async def set_threshold_max(self, max: int) -> dict:
"""Set the maximum dimming level for this dimmer.

The value will depend on the luminaries connected to the dimmer.

:param max: The minimum dimming level, in the range 0-99.
"""
if max < self.THRESHOLD_ABS_MIN or max > self.THRESHOLD_ABS_MAX:
raise KasaException(
"Minimum dimming threshold is outside the supported range: "
f"{self.THRESHOLD_ABS_MIN}-{self.THRESHOLD_ABS_MAX}"
)
return await self.call("set_calibrate_brightness", {"max_threshold": max})
Loading
Loading