mirror of
https://github.com/python-kasa/python-kasa.git
synced 2024-12-22 11:13:34 +00:00
Update docs for the new module attributes has/get feature (#1301)
This commit is contained in:
parent
fe53cd7d9c
commit
cb4e28394d
@ -64,9 +64,9 @@ from __future__ import annotations
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from dataclasses import dataclass
|
||||
from typing import NamedTuple
|
||||
from typing import Annotated, NamedTuple
|
||||
|
||||
from ..module import Module
|
||||
from ..module import FeatureAttribute, Module
|
||||
|
||||
|
||||
@dataclass
|
||||
@ -129,7 +129,7 @@ class Light(Module, ABC):
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def hsv(self) -> HSV:
|
||||
def hsv(self) -> Annotated[HSV, FeatureAttribute()]:
|
||||
"""Return the current HSV state of the bulb.
|
||||
|
||||
:return: hue, saturation and value (degrees, %, %)
|
||||
@ -137,12 +137,12 @@ class Light(Module, ABC):
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def color_temp(self) -> int:
|
||||
def color_temp(self) -> Annotated[int, FeatureAttribute()]:
|
||||
"""Whether the bulb supports color temperature changes."""
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def brightness(self) -> int:
|
||||
def brightness(self) -> Annotated[int, FeatureAttribute()]:
|
||||
"""Return the current brightness in percentage."""
|
||||
|
||||
@abstractmethod
|
||||
@ -153,7 +153,7 @@ class Light(Module, ABC):
|
||||
value: int | None = None,
|
||||
*,
|
||||
transition: int | None = None,
|
||||
) -> dict:
|
||||
) -> Annotated[dict, FeatureAttribute()]:
|
||||
"""Set new HSV.
|
||||
|
||||
Note, transition is not supported and will be ignored.
|
||||
@ -167,7 +167,7 @@ class Light(Module, ABC):
|
||||
@abstractmethod
|
||||
async def set_color_temp(
|
||||
self, temp: int, *, brightness: int | None = None, transition: int | None = None
|
||||
) -> dict:
|
||||
) -> Annotated[dict, FeatureAttribute()]:
|
||||
"""Set the color temperature of the device in kelvin.
|
||||
|
||||
Note, transition is not supported and will be ignored.
|
||||
@ -179,7 +179,7 @@ class Light(Module, ABC):
|
||||
@abstractmethod
|
||||
async def set_brightness(
|
||||
self, brightness: int, *, transition: int | None = None
|
||||
) -> dict:
|
||||
) -> Annotated[dict, FeatureAttribute()]:
|
||||
"""Set the brightness in percentage.
|
||||
|
||||
Note, transition is not supported and will be ignored.
|
||||
|
@ -14,9 +14,17 @@ Light, AutoOff, Firmware etc.
|
||||
>>> print(dev.alias)
|
||||
Living Room Bulb
|
||||
|
||||
To see whether a device supports functionality check for the existence of the module:
|
||||
To see whether a device supports a group of functionality
|
||||
check for the existence of the module:
|
||||
|
||||
>>> if light := dev.modules.get("Light"):
|
||||
>>> print(light.brightness)
|
||||
100
|
||||
|
||||
To see whether a device supports specific functionality, you can check whether the
|
||||
module has that feature:
|
||||
|
||||
>>> if light.has_feature("hsv"):
|
||||
>>> print(light.hsv)
|
||||
HSV(hue=0, saturation=100, value=100)
|
||||
|
||||
@ -70,6 +78,9 @@ ModuleT = TypeVar("ModuleT", bound="Module")
|
||||
class FeatureAttribute:
|
||||
"""Class for annotating attributes bound to feature."""
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return "FeatureAttribute"
|
||||
|
||||
|
||||
class Module(ABC):
|
||||
"""Base class implemention for all modules.
|
||||
@ -147,6 +158,11 @@ class Module(ABC):
|
||||
self._module = module
|
||||
self._module_features: dict[str, Feature] = {}
|
||||
|
||||
@property
|
||||
def _all_features(self) -> dict[str, Feature]:
|
||||
"""Get the features for this module and any sub modules."""
|
||||
return self._module_features
|
||||
|
||||
def has_feature(self, attribute: str | property | Callable) -> bool:
|
||||
"""Return True if the module attribute feature is supported."""
|
||||
return bool(self.get_feature(attribute))
|
||||
@ -247,7 +263,7 @@ def _get_bound_feature(
|
||||
)
|
||||
|
||||
check = {attribute_name, attribute_callable}
|
||||
for feature in module._module_features.values():
|
||||
for feature in module._all_features.values():
|
||||
if (getter := feature.attribute_getter) and getter in check:
|
||||
return feature
|
||||
|
||||
|
@ -3,11 +3,13 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import asdict
|
||||
from typing import Annotated
|
||||
|
||||
from ...exceptions import KasaException
|
||||
from ...feature import Feature
|
||||
from ...interfaces.light import HSV, ColorTempRange, LightState
|
||||
from ...interfaces.light import Light as LightInterface
|
||||
from ...module import Module
|
||||
from ...module import FeatureAttribute, Module
|
||||
from ..smartmodule import SmartModule
|
||||
|
||||
|
||||
@ -16,6 +18,18 @@ class Light(SmartModule, LightInterface):
|
||||
|
||||
_light_state: LightState
|
||||
|
||||
@property
|
||||
def _all_features(self) -> dict[str, Feature]:
|
||||
"""Get the features for this module and any sub modules."""
|
||||
ret: dict[str, Feature] = {}
|
||||
if brightness := self._device.modules.get(Module.Brightness):
|
||||
ret.update(**brightness._module_features)
|
||||
if color := self._device.modules.get(Module.Color):
|
||||
ret.update(**color._module_features)
|
||||
if temp := self._device.modules.get(Module.ColorTemperature):
|
||||
ret.update(**temp._module_features)
|
||||
return ret
|
||||
|
||||
def query(self) -> dict:
|
||||
"""Query to execute during the update cycle."""
|
||||
return {}
|
||||
@ -47,7 +61,7 @@ class Light(SmartModule, LightInterface):
|
||||
return self._device.modules[Module.ColorTemperature].valid_temperature_range
|
||||
|
||||
@property
|
||||
def hsv(self) -> HSV:
|
||||
def hsv(self) -> Annotated[HSV, FeatureAttribute()]:
|
||||
"""Return the current HSV state of the bulb.
|
||||
|
||||
:return: hue, saturation and value (degrees, %, %)
|
||||
@ -58,7 +72,7 @@ class Light(SmartModule, LightInterface):
|
||||
return self._device.modules[Module.Color].hsv
|
||||
|
||||
@property
|
||||
def color_temp(self) -> int:
|
||||
def color_temp(self) -> Annotated[int, FeatureAttribute()]:
|
||||
"""Whether the bulb supports color temperature changes."""
|
||||
if not self.is_variable_color_temp:
|
||||
raise KasaException("Bulb does not support colortemp.")
|
||||
@ -66,7 +80,7 @@ class Light(SmartModule, LightInterface):
|
||||
return self._device.modules[Module.ColorTemperature].color_temp
|
||||
|
||||
@property
|
||||
def brightness(self) -> int:
|
||||
def brightness(self) -> Annotated[int, FeatureAttribute()]:
|
||||
"""Return the current brightness in percentage."""
|
||||
if not self.is_dimmable: # pragma: no cover
|
||||
raise KasaException("Bulb is not dimmable.")
|
||||
@ -80,7 +94,7 @@ class Light(SmartModule, LightInterface):
|
||||
value: int | None = None,
|
||||
*,
|
||||
transition: int | None = None,
|
||||
) -> dict:
|
||||
) -> Annotated[dict, FeatureAttribute()]:
|
||||
"""Set new HSV.
|
||||
|
||||
Note, transition is not supported and will be ignored.
|
||||
@ -97,7 +111,7 @@ class Light(SmartModule, LightInterface):
|
||||
|
||||
async def set_color_temp(
|
||||
self, temp: int, *, brightness: int | None = None, transition: int | None = None
|
||||
) -> dict:
|
||||
) -> Annotated[dict, FeatureAttribute()]:
|
||||
"""Set the color temperature of the device in kelvin.
|
||||
|
||||
Note, transition is not supported and will be ignored.
|
||||
@ -113,7 +127,7 @@ class Light(SmartModule, LightInterface):
|
||||
|
||||
async def set_brightness(
|
||||
self, brightness: int, *, transition: int | None = None
|
||||
) -> dict:
|
||||
) -> Annotated[dict, FeatureAttribute()]:
|
||||
"""Set the brightness in percentage.
|
||||
|
||||
Note, transition is not supported and will be ignored.
|
||||
|
@ -25,7 +25,13 @@ classifiers = [
|
||||
|
||||
[project.optional-dependencies]
|
||||
speedups = ["orjson>=3.9.1", "kasa-crypt>=0.2.0"]
|
||||
docs = ["sphinx~=6.2", "sphinx_rtd_theme~=2.0", "sphinxcontrib-programoutput~=0.0", "myst-parser", "docutils>=0.17"]
|
||||
docs = [
|
||||
"sphinx_rtd_theme~=2.0",
|
||||
"sphinxcontrib-programoutput~=0.0",
|
||||
"myst-parser",
|
||||
"docutils>=0.17",
|
||||
"sphinx>=7.4.7",
|
||||
]
|
||||
shell = ["ptpython", "rich"]
|
||||
|
||||
[project.urls]
|
||||
|
32
uv.lock
32
uv.lock
@ -381,11 +381,11 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "docutils"
|
||||
version = "0.19"
|
||||
version = "0.20.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/6b/5c/330ea8d383eb2ce973df34d1239b3b21e91cd8c865d21ff82902d952f91f/docutils-0.19.tar.gz", hash = "sha256:33995a6753c30b7f577febfc2c50411fec6aac7f7ffeb7c4cfe5991072dcf9e6", size = 2056383 }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/1f/53/a5da4f2c5739cf66290fac1431ee52aff6851c7c8ffd8264f13affd7bcdd/docutils-0.20.1.tar.gz", hash = "sha256:f08a4e276c3a1583a86dce3e34aba3fe04d02bba2dd51ed16106244e8a923e3b", size = 2058365 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/93/69/e391bd51bc08ed9141ecd899a0ddb61ab6465309f1eb470905c0c8868081/docutils-0.19-py3-none-any.whl", hash = "sha256:5e1de4d849fee02c63b040a4a3fd567f4ab104defd8a5511fbbc24a8a017efbc", size = 570472 },
|
||||
{ url = "https://files.pythonhosted.org/packages/26/87/f238c0670b94533ac0353a4e2a1a771a0cc73277b88bff23d3ae35a256c1/docutils-0.20.1-py3-none-any.whl", hash = "sha256:96f387a2c5562db4476f09f13bbab2192e764cac08ebbf3a34a95d9b1e4a59d6", size = 572666 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -556,14 +556,14 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "markdown-it-py"
|
||||
version = "2.2.0"
|
||||
version = "3.0.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "mdurl" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e4/c0/59bd6d0571986f72899288a95d9d6178d0eebd70b6650f1bb3f0da90f8f7/markdown-it-py-2.2.0.tar.gz", hash = "sha256:7c9a5e412688bc771c67432cbfebcdd686c93ce6484913dccf06cb5a0bea35a1", size = 67120 }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/bf/25/2d88e8feee8e055d015343f9b86e370a1ccbec546f2865c98397aaef24af/markdown_it_py-2.2.0-py3-none-any.whl", hash = "sha256:5a35f8d1870171d9acc47b99612dc146129b631baf04970128b568f190d0cc30", size = 84466 },
|
||||
{ url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -628,14 +628,14 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "mdit-py-plugins"
|
||||
version = "0.3.5"
|
||||
version = "0.4.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "markdown-it-py" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/49/e7/cc2720da8a32724b36d04c6dba5644154cdf883a1482b3bbb81959a642ed/mdit-py-plugins-0.3.5.tar.gz", hash = "sha256:eee0adc7195e5827e17e02d2a258a2ba159944a0748f59c5099a4a27f78fcf6a", size = 39871 }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/19/03/a2ecab526543b152300717cf232bb4bb8605b6edb946c845016fa9c9c9fd/mdit_py_plugins-0.4.2.tar.gz", hash = "sha256:5f2cd1fdb606ddf152d37ec30e46101a60512bc0e5fa1a7002c36647b09e26b5", size = 43542 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/fe/4c/a9b222f045f98775034d243198212cbea36d3524c3ee1e8ab8c0346d6953/mdit_py_plugins-0.3.5-py3-none-any.whl", hash = "sha256:ca9a0714ea59a24b2b044a1831f48d817dd0c817e84339f20e7889f392d77c4e", size = 52087 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a7/f7/7782a043553ee469c1ff49cfa1cdace2d6bf99a1f333cf38676b3ddf30da/mdit_py_plugins-0.4.2-py3-none-any.whl", hash = "sha256:0c673c3f889399a33b95e88d2f0d111b4447bdfea7f237dab2d488f459835636", size = 55316 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -740,7 +740,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "myst-parser"
|
||||
version = "1.0.0"
|
||||
version = "4.0.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "docutils" },
|
||||
@ -750,9 +750,9 @@ dependencies = [
|
||||
{ name = "pyyaml" },
|
||||
{ name = "sphinx" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/5f/69/fbddb50198c6b0901a981e72ae30f1b7769d2dfac88071f7df41c946d133/myst-parser-1.0.0.tar.gz", hash = "sha256:502845659313099542bd38a2ae62f01360e7dd4b1310f025dd014dfc0439cdae", size = 84224 }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/85/55/6d1741a1780e5e65038b74bce6689da15f620261c490c3511eb4c12bac4b/myst_parser-4.0.0.tar.gz", hash = "sha256:851c9dfb44e36e56d15d05e72f02b80da21a9e0d07cba96baf5e2d476bb91531", size = 93858 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/1c/1f/1621ef434ac5da26c30d31fcca6d588e3383344902941713640ba717fa87/myst_parser-1.0.0-py3-none-any.whl", hash = "sha256:69fb40a586c6fa68995e6521ac0a525793935db7e724ca9bac1d33be51be9a4c", size = 77312 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ca/b4/b036f8fdb667587bb37df29dc6644681dd78b7a2a6321a34684b79412b28/myst_parser-4.0.0-py3-none-any.whl", hash = "sha256:b9317997552424448c6096c2558872fdb6f81d3ecb3a40ce84a7518798f3f28d", size = 84563 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1143,7 +1143,7 @@ requires-dist = [
|
||||
{ name = "orjson", marker = "extra == 'speedups'", specifier = ">=3.9.1" },
|
||||
{ name = "ptpython", marker = "extra == 'shell'" },
|
||||
{ name = "rich", marker = "extra == 'shell'" },
|
||||
{ name = "sphinx", marker = "extra == 'docs'", specifier = "~=6.2" },
|
||||
{ name = "sphinx", marker = "extra == 'docs'", specifier = ">=7.4.7" },
|
||||
{ name = "sphinx-rtd-theme", marker = "extra == 'docs'", specifier = "~=2.0" },
|
||||
{ name = "sphinxcontrib-programoutput", marker = "extra == 'docs'", specifier = "~=0.0" },
|
||||
{ name = "tzdata", marker = "platform_system == 'Windows'", specifier = ">=2024.2" },
|
||||
@ -1287,7 +1287,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "sphinx"
|
||||
version = "6.2.1"
|
||||
version = "7.4.7"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "alabaster" },
|
||||
@ -1307,9 +1307,9 @@ dependencies = [
|
||||
{ name = "sphinxcontrib-qthelp" },
|
||||
{ name = "sphinxcontrib-serializinghtml" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/0f/6d/392defcc95ca48daf62aecb89550143e97a4651275e62a3d7755efe35a3a/Sphinx-6.2.1.tar.gz", hash = "sha256:6d56a34697bb749ffa0152feafc4b19836c755d90a7c59b72bc7dfd371b9cc6b", size = 6681092 }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/5b/be/50e50cb4f2eff47df05673d361095cafd95521d2a22521b920c67a372dcb/sphinx-7.4.7.tar.gz", hash = "sha256:242f92a7ea7e6c5b406fdc2615413890ba9f699114a9c09192d7dfead2ee9cfe", size = 8067911 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/5f/d8/45ba6097c39ba44d9f0e1462fb232e13ca4ddb5aea93a385dcfa964687da/sphinx-6.2.1-py3-none-any.whl", hash = "sha256:97787ff1fa3256a3eef9eda523a63dbf299f7b47e053cfcf684a1c2a8380c912", size = 3024615 },
|
||||
{ url = "https://files.pythonhosted.org/packages/0d/ef/153f6803c5d5f8917dbb7f7fcf6d34a871ede3296fa89c2c703f5f8a6c8e/sphinx-7.4.7-py3-none-any.whl", hash = "sha256:c2419e2135d11f1951cd994d6eb18a1835bd8fdd8429f9ca375dc1f3281bd239", size = 3401624 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
Loading…
Reference in New Issue
Block a user