2019-11-11 21:14:34 +00:00
|
|
|
"""Python library supporting TP-Link Smart Home devices.
|
2016-11-22 01:31:25 +00:00
|
|
|
|
|
|
|
The communication protocol was reverse engineered by Lubomir Stroetmann and
|
|
|
|
Tobias Esser in 'Reverse Engineering the TP-Link HS110':
|
|
|
|
https://www.softscheck.com/en/reverse-engineering-tp-link-hs110/
|
|
|
|
|
|
|
|
This library reuses codes and concepts of the TP-Link WiFi SmartPlug Client
|
|
|
|
at https://github.com/softScheck/tplink-smartplug, developed by Lubomir
|
|
|
|
Stroetmann which is licensed under the Apache License, Version 2.0.
|
|
|
|
|
|
|
|
You may obtain a copy of the license at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
"""
|
2024-04-16 18:21:20 +00:00
|
|
|
|
2024-04-17 13:39:24 +00:00
|
|
|
from __future__ import annotations
|
|
|
|
|
2020-03-16 13:52:40 +00:00
|
|
|
import functools
|
|
|
|
import inspect
|
|
|
|
import logging
|
2024-11-18 18:46:36 +00:00
|
|
|
from collections.abc import Callable, Mapping, Sequence
|
2024-10-08 07:16:51 +00:00
|
|
|
from datetime import datetime, timedelta, tzinfo
|
2024-11-18 18:46:36 +00:00
|
|
|
from typing import TYPE_CHECKING, Any, cast
|
2024-10-15 07:59:25 +00:00
|
|
|
from warnings import warn
|
2016-07-09 11:34:27 +00:00
|
|
|
|
2024-12-13 12:37:13 +00:00
|
|
|
from ..device import Device, DeviceInfo, WifiNetwork
|
2024-11-18 14:53:11 +00:00
|
|
|
from ..device_type import DeviceType
|
2024-02-04 15:20:08 +00:00
|
|
|
from ..deviceconfig import DeviceConfig
|
2024-02-21 15:52:55 +00:00
|
|
|
from ..exceptions import KasaException
|
2024-02-15 15:25:08 +00:00
|
|
|
from ..feature import Feature
|
2024-05-10 18:29:28 +00:00
|
|
|
from ..module import Module
|
|
|
|
from ..modulemapping import ModuleMapping, ModuleName
|
2024-11-13 17:50:21 +00:00
|
|
|
from ..protocols import BaseProtocol
|
2024-08-14 21:33:54 +00:00
|
|
|
from .iotmodule import IotModule, merge
|
2024-05-11 18:28:18 +00:00
|
|
|
from .modules import Emeter
|
2020-04-20 16:57:33 +00:00
|
|
|
|
2024-02-04 15:20:08 +00:00
|
|
|
_LOGGER = logging.getLogger(__name__)
|
2024-01-03 21:45:16 +00:00
|
|
|
|
2020-04-20 16:57:33 +00:00
|
|
|
|
2024-11-10 18:55:13 +00:00
|
|
|
def requires_update(f: Callable) -> Any:
|
2019-11-15 16:48:36 +00:00
|
|
|
"""Indicate that `update` should be called before accessing this method.""" # noqa: D202
|
|
|
|
if inspect.iscoroutinefunction(f):
|
2019-12-12 09:41:52 +00:00
|
|
|
|
2019-11-15 16:48:36 +00:00
|
|
|
@functools.wraps(f)
|
2024-11-10 18:55:13 +00:00
|
|
|
async def wrapped(*args: Any, **kwargs: Any) -> Any:
|
2019-11-15 16:48:36 +00:00
|
|
|
self = args[0]
|
2024-12-13 12:37:13 +00:00
|
|
|
if not self._last_update and (
|
2024-11-18 13:14:39 +00:00
|
|
|
self._sys_info is None or f.__name__ not in self._sys_info
|
|
|
|
):
|
2024-02-21 15:52:55 +00:00
|
|
|
raise KasaException("You need to await update() to access the data")
|
2019-11-15 16:48:36 +00:00
|
|
|
return await f(*args, **kwargs)
|
|
|
|
|
|
|
|
else:
|
2019-12-12 09:41:52 +00:00
|
|
|
|
2019-11-15 16:48:36 +00:00
|
|
|
@functools.wraps(f)
|
2024-11-10 18:55:13 +00:00
|
|
|
def wrapped(*args: Any, **kwargs: Any) -> Any:
|
2019-11-15 16:48:36 +00:00
|
|
|
self = args[0]
|
2024-12-13 12:37:13 +00:00
|
|
|
if not self._last_update and (
|
2024-11-18 13:14:39 +00:00
|
|
|
self._sys_info is None or f.__name__ not in self._sys_info
|
|
|
|
):
|
2024-02-21 15:52:55 +00:00
|
|
|
raise KasaException("You need to await update() to access the data")
|
2019-11-15 16:48:36 +00:00
|
|
|
return f(*args, **kwargs)
|
|
|
|
|
2024-11-10 18:55:13 +00:00
|
|
|
f.requires_update = True # type: ignore[attr-defined]
|
2019-11-15 16:48:36 +00:00
|
|
|
return wrapped
|
|
|
|
|
|
|
|
|
2023-10-07 19:18:47 +00:00
|
|
|
@functools.lru_cache
|
2024-04-17 13:39:24 +00:00
|
|
|
def _parse_features(features: str) -> set[str]:
|
2023-10-07 19:18:47 +00:00
|
|
|
"""Parse features string."""
|
|
|
|
return set(features.split(":"))
|
|
|
|
|
|
|
|
|
2024-12-06 23:06:58 +00:00
|
|
|
def _extract_sys_info(info: dict[str, Any]) -> dict[str, Any]:
|
|
|
|
"""Return the system info structure."""
|
|
|
|
sysinfo_default = info.get("system", {}).get("get_sysinfo", {})
|
|
|
|
sysinfo_nest = sysinfo_default.get("system", {})
|
|
|
|
|
|
|
|
if len(sysinfo_nest) > len(sysinfo_default) and isinstance(sysinfo_nest, dict):
|
|
|
|
return sysinfo_nest
|
|
|
|
return sysinfo_default
|
|
|
|
|
|
|
|
|
2024-02-04 15:20:08 +00:00
|
|
|
class IotDevice(Device):
|
2020-06-30 00:29:52 +00:00
|
|
|
"""Base class for all supported device types.
|
|
|
|
|
2023-10-29 22:15:42 +00:00
|
|
|
You don't usually want to initialize this class manually,
|
|
|
|
but either use :class:`Discover` class, or use one of the subclasses:
|
2020-06-30 00:29:52 +00:00
|
|
|
|
2024-02-04 15:20:08 +00:00
|
|
|
* :class:`IotPlug`
|
|
|
|
* :class:`IotBulb`
|
|
|
|
* :class:`IotStrip`
|
|
|
|
* :class:`IotDimmer`
|
|
|
|
* :class:`IotLightStrip`
|
2020-06-30 00:29:52 +00:00
|
|
|
|
|
|
|
To initialize, you have to await :func:`update()` at least once.
|
|
|
|
This will allow accessing the properties using the exposed properties.
|
|
|
|
|
|
|
|
All changes to the device are done using awaitable methods,
|
|
|
|
which will not change the cached values, but you must await update() separately.
|
|
|
|
|
2024-02-21 15:52:55 +00:00
|
|
|
Errors reported by the device are raised as
|
|
|
|
:class:`KasaException <kasa.exceptions.KasaException>`,
|
2020-06-30 00:29:52 +00:00
|
|
|
and should be handled by the user of the library.
|
|
|
|
|
|
|
|
Examples:
|
|
|
|
>>> import asyncio
|
2024-02-04 15:20:08 +00:00
|
|
|
>>> dev = IotDevice("127.0.0.1")
|
2020-06-30 00:29:52 +00:00
|
|
|
>>> asyncio.run(dev.update())
|
|
|
|
|
|
|
|
All devices provide several informational properties:
|
|
|
|
|
|
|
|
>>> dev.alias
|
2024-06-03 09:14:10 +00:00
|
|
|
Bedroom Lamp Plug
|
2020-06-30 00:29:52 +00:00
|
|
|
>>> dev.model
|
2024-12-13 12:37:13 +00:00
|
|
|
HS110
|
2020-06-30 00:29:52 +00:00
|
|
|
>>> dev.rssi
|
|
|
|
-71
|
|
|
|
>>> dev.mac
|
2024-01-20 13:20:08 +00:00
|
|
|
50:C7:BF:00:00:00
|
2020-06-30 00:29:52 +00:00
|
|
|
|
2022-07-17 17:19:09 +00:00
|
|
|
Some information can also be changed programmatically:
|
2020-06-30 00:29:52 +00:00
|
|
|
|
|
|
|
>>> asyncio.run(dev.set_alias("new alias"))
|
|
|
|
>>> asyncio.run(dev.set_mac("01:23:45:67:89:ab"))
|
|
|
|
>>> asyncio.run(dev.update())
|
|
|
|
>>> dev.alias
|
|
|
|
new alias
|
|
|
|
>>> dev.mac
|
|
|
|
01:23:45:67:89:ab
|
|
|
|
|
2023-10-29 22:15:42 +00:00
|
|
|
When initialized using discovery or using a subclass,
|
|
|
|
you can check the type of the device:
|
2020-06-30 00:29:52 +00:00
|
|
|
|
|
|
|
>>> dev.is_bulb
|
|
|
|
False
|
|
|
|
>>> dev.is_strip
|
|
|
|
False
|
|
|
|
>>> dev.is_plug
|
|
|
|
True
|
|
|
|
|
2023-10-29 22:15:42 +00:00
|
|
|
You can also get the hardware and software as a dict,
|
|
|
|
or access the full device response:
|
2020-06-30 00:29:52 +00:00
|
|
|
|
|
|
|
>>> dev.hw_info
|
|
|
|
{'sw_ver': '1.2.5 Build 171213 Rel.101523',
|
|
|
|
'hw_ver': '1.0',
|
|
|
|
'mac': '01:23:45:67:89:ab',
|
|
|
|
'type': 'IOT.SMARTPLUGSWITCH',
|
2024-01-20 13:20:08 +00:00
|
|
|
'hwId': '00000000000000000000000000000000',
|
2020-06-30 00:29:52 +00:00
|
|
|
'fwId': '00000000000000000000000000000000',
|
2024-01-20 13:20:08 +00:00
|
|
|
'oemId': '00000000000000000000000000000000',
|
2020-06-30 00:29:52 +00:00
|
|
|
'dev_name': 'Wi-Fi Smart Plug With Energy Monitoring'}
|
|
|
|
>>> dev.sys_info
|
|
|
|
|
|
|
|
All devices can be turned on and off:
|
|
|
|
|
|
|
|
>>> asyncio.run(dev.turn_off())
|
|
|
|
>>> asyncio.run(dev.turn_on())
|
|
|
|
>>> asyncio.run(dev.update())
|
|
|
|
>>> dev.is_on
|
|
|
|
True
|
|
|
|
|
2023-10-29 22:15:42 +00:00
|
|
|
Some devices provide energy consumption meter,
|
|
|
|
and regular update will already fetch some information:
|
2020-06-30 00:29:52 +00:00
|
|
|
|
|
|
|
>>> dev.has_emeter
|
|
|
|
True
|
|
|
|
>>> dev.emeter_realtime
|
2024-01-20 13:20:08 +00:00
|
|
|
<EmeterStatus power=0.928511 voltage=231.067823 current=0.014937 total=55.139>
|
2020-06-30 00:29:52 +00:00
|
|
|
>>> dev.emeter_today
|
|
|
|
>>> dev.emeter_this_month
|
|
|
|
|
2023-10-29 22:15:42 +00:00
|
|
|
You can also query the historical data (note that these needs to be awaited),
|
|
|
|
keyed with month/day:
|
2020-06-30 00:29:52 +00:00
|
|
|
|
|
|
|
>>> asyncio.run(dev.get_emeter_monthly(year=2016))
|
|
|
|
{11: 1.089, 12: 1.582}
|
|
|
|
>>> asyncio.run(dev.get_emeter_daily(year=2016, month=11))
|
|
|
|
{24: 0.026, 25: 0.109}
|
|
|
|
|
|
|
|
"""
|
2017-04-24 17:28:22 +00:00
|
|
|
|
2021-11-07 01:41:12 +00:00
|
|
|
emeter_type = "emeter"
|
2021-09-19 21:53:17 +00:00
|
|
|
|
2023-09-13 13:46:38 +00:00
|
|
|
def __init__(
|
|
|
|
self,
|
|
|
|
host: str,
|
|
|
|
*,
|
2024-04-17 13:39:24 +00:00
|
|
|
config: DeviceConfig | None = None,
|
|
|
|
protocol: BaseProtocol | None = None,
|
2023-09-13 13:46:38 +00:00
|
|
|
) -> None:
|
2024-02-04 15:20:08 +00:00
|
|
|
"""Create a new IotDevice instance."""
|
|
|
|
super().__init__(host=host, config=config, protocol=protocol)
|
2023-11-20 13:17:10 +00:00
|
|
|
|
2020-05-24 15:57:54 +00:00
|
|
|
self._sys_info: Any = None # TODO: this is here to avoid changing tests
|
2024-06-17 10:22:05 +00:00
|
|
|
self._supported_modules: dict[str | ModuleName[Module], IotModule] | None = None
|
2024-04-17 13:39:24 +00:00
|
|
|
self._legacy_features: set[str] = set()
|
|
|
|
self._children: Mapping[str, IotDevice] = {}
|
2024-05-10 18:29:28 +00:00
|
|
|
self._modules: dict[str | ModuleName[Module], IotModule] = {}
|
2024-10-02 14:04:16 +00:00
|
|
|
self._on_since: datetime | None = None
|
2023-12-29 19:17:15 +00:00
|
|
|
|
|
|
|
@property
|
2024-04-17 13:39:24 +00:00
|
|
|
def children(self) -> Sequence[IotDevice]:
|
2024-02-04 15:20:08 +00:00
|
|
|
"""Return list of children."""
|
2024-02-22 19:46:19 +00:00
|
|
|
return list(self._children.values())
|
2024-01-03 21:46:08 +00:00
|
|
|
|
2024-04-29 16:34:20 +00:00
|
|
|
@property
|
2024-06-17 10:22:05 +00:00
|
|
|
@requires_update
|
2024-05-10 18:29:28 +00:00
|
|
|
def modules(self) -> ModuleMapping[IotModule]:
|
2024-04-29 16:34:20 +00:00
|
|
|
"""Return the device modules."""
|
2024-05-10 18:29:28 +00:00
|
|
|
if TYPE_CHECKING:
|
2024-06-17 10:22:05 +00:00
|
|
|
return cast(ModuleMapping[IotModule], self._supported_modules)
|
|
|
|
return self._supported_modules
|
2024-04-29 16:34:20 +00:00
|
|
|
|
2024-11-10 18:55:13 +00:00
|
|
|
def add_module(self, name: str | ModuleName[Module], module: IotModule) -> None:
|
2021-11-07 01:41:12 +00:00
|
|
|
"""Register a module."""
|
2024-06-17 10:22:05 +00:00
|
|
|
if name in self._modules:
|
2024-08-30 14:13:14 +00:00
|
|
|
_LOGGER.debug("Module %s already registered, ignoring...", name)
|
2021-11-07 01:41:12 +00:00
|
|
|
return
|
|
|
|
|
|
|
|
_LOGGER.debug("Adding module %s", module)
|
2024-05-10 18:29:28 +00:00
|
|
|
self._modules[name] = module
|
2021-11-07 01:41:12 +00:00
|
|
|
|
2020-05-24 15:57:54 +00:00
|
|
|
def _create_request(
|
2024-11-10 18:55:13 +00:00
|
|
|
self,
|
|
|
|
target: str,
|
|
|
|
cmd: str,
|
|
|
|
arg: dict | None = None,
|
|
|
|
child_ids: list | None = None,
|
|
|
|
) -> dict:
|
2024-10-01 11:47:36 +00:00
|
|
|
if arg is None:
|
|
|
|
arg = {}
|
2024-04-17 13:39:24 +00:00
|
|
|
request: dict[str, Any] = {target: {cmd: arg}}
|
2020-05-24 15:57:54 +00:00
|
|
|
if child_ids is not None:
|
|
|
|
request = {"context": {"child_ids": child_ids}, target: {cmd: arg}}
|
|
|
|
|
|
|
|
return request
|
Read all data from the device, disable double-encoding, implement more APIs, refactor querying, update README (#11)
* Read from socket until no data available, disable double string encoding
HS110 sends sometimes datagrams in chunks especially for get_daystat,
this patch makes it to read until there is no more data to be read.
As json.dumps() does JSON encoding already, there's no need to str()
the year or month either.
* Add cli.py, a simple script to query devices for debugging purposes.
* allow easier importing with from pyHS100 import SmartPlug
* move cli.py to examples, add short usage into README.md
* Implement more available APIs, refactor querying code.
This commit adds access to new properties, both read & write, while keeping the old one (mostly) intact.
Querying is refactored to be done inside _query_helper() method,
which unwraps results automatically and rises SmartPlugException() in case of errors.
Errors are to be handled by clients.
New features:
* Setting device alias (plug.alias = "name")
* led read & write
* icon read (doesn't seem to return anything without cloud support at least), write API is not known, throws an exception currently
* time read (returns datetime), time write implemented, but not working even when no error is returned from the device
* timezone read
* mac read & write, writing is untested for now.
Properties for easier access:
* hw_info: return hw-specific elements from sysinfo
* on_since: pretty-printed from sysinfo
* location: latitude and longitued from sysinfo
* rssi: rssi from sysinfo
* Update README.md with examples of available features.
* Handle comments from mweinelt
* Refactor state handling, use booleans instead of strings
* Fix issues raised during the review.
Following issues are addressed by this commit:
* All API is more or less commented (including return types, exceptions, ..)
* Converted state to use
* Added properties is_on, is_off for those who don't want to check against strings.
* Handled most issues reported by pylint.
* Adjusted _query_helper() to strip off err_code from the result object.
* Fixed broken format() syntax for string formattings.
* Fix ci woes plus one typo.
* Do initialization after changing device properties, fix nits.
2016-12-12 09:13:45 +00:00
|
|
|
|
2021-09-23 22:24:44 +00:00
|
|
|
def _verify_emeter(self) -> None:
|
|
|
|
"""Raise an exception if there is no emeter."""
|
|
|
|
if not self.has_emeter:
|
2024-02-21 15:52:55 +00:00
|
|
|
raise KasaException("Device has no emeter")
|
2022-02-02 18:31:11 +00:00
|
|
|
if self.emeter_type not in self._last_update:
|
2024-02-21 15:52:55 +00:00
|
|
|
raise KasaException("update() required prior accessing emeter")
|
2021-09-23 22:24:44 +00:00
|
|
|
|
2019-11-11 16:21:23 +00:00
|
|
|
async def _query_helper(
|
2024-11-10 18:55:13 +00:00
|
|
|
self,
|
|
|
|
target: str,
|
|
|
|
cmd: str,
|
|
|
|
arg: dict | None = None,
|
|
|
|
child_ids: list | None = None,
|
|
|
|
) -> dict:
|
2020-05-27 14:55:18 +00:00
|
|
|
"""Query device, return results or raise an exception.
|
Read all data from the device, disable double-encoding, implement more APIs, refactor querying, update README (#11)
* Read from socket until no data available, disable double string encoding
HS110 sends sometimes datagrams in chunks especially for get_daystat,
this patch makes it to read until there is no more data to be read.
As json.dumps() does JSON encoding already, there's no need to str()
the year or month either.
* Add cli.py, a simple script to query devices for debugging purposes.
* allow easier importing with from pyHS100 import SmartPlug
* move cli.py to examples, add short usage into README.md
* Implement more available APIs, refactor querying code.
This commit adds access to new properties, both read & write, while keeping the old one (mostly) intact.
Querying is refactored to be done inside _query_helper() method,
which unwraps results automatically and rises SmartPlugException() in case of errors.
Errors are to be handled by clients.
New features:
* Setting device alias (plug.alias = "name")
* led read & write
* icon read (doesn't seem to return anything without cloud support at least), write API is not known, throws an exception currently
* time read (returns datetime), time write implemented, but not working even when no error is returned from the device
* timezone read
* mac read & write, writing is untested for now.
Properties for easier access:
* hw_info: return hw-specific elements from sysinfo
* on_since: pretty-printed from sysinfo
* location: latitude and longitued from sysinfo
* rssi: rssi from sysinfo
* Update README.md with examples of available features.
* Handle comments from mweinelt
* Refactor state handling, use booleans instead of strings
* Fix issues raised during the review.
Following issues are addressed by this commit:
* All API is more or less commented (including return types, exceptions, ..)
* Converted state to use
* Added properties is_on, is_off for those who don't want to check against strings.
* Handled most issues reported by pylint.
* Adjusted _query_helper() to strip off err_code from the result object.
* Fixed broken format() syntax for string formattings.
* Fix ci woes plus one typo.
* Do initialization after changing device properties, fix nits.
2016-12-12 09:13:45 +00:00
|
|
|
|
|
|
|
:param target: Target system {system, time, emeter, ..}
|
|
|
|
:param cmd: Command to execute
|
2020-05-27 14:55:18 +00:00
|
|
|
:param arg: payload dict to be send to the device
|
2020-05-24 15:57:54 +00:00
|
|
|
:param child_ids: ids of child devices
|
Read all data from the device, disable double-encoding, implement more APIs, refactor querying, update README (#11)
* Read from socket until no data available, disable double string encoding
HS110 sends sometimes datagrams in chunks especially for get_daystat,
this patch makes it to read until there is no more data to be read.
As json.dumps() does JSON encoding already, there's no need to str()
the year or month either.
* Add cli.py, a simple script to query devices for debugging purposes.
* allow easier importing with from pyHS100 import SmartPlug
* move cli.py to examples, add short usage into README.md
* Implement more available APIs, refactor querying code.
This commit adds access to new properties, both read & write, while keeping the old one (mostly) intact.
Querying is refactored to be done inside _query_helper() method,
which unwraps results automatically and rises SmartPlugException() in case of errors.
Errors are to be handled by clients.
New features:
* Setting device alias (plug.alias = "name")
* led read & write
* icon read (doesn't seem to return anything without cloud support at least), write API is not known, throws an exception currently
* time read (returns datetime), time write implemented, but not working even when no error is returned from the device
* timezone read
* mac read & write, writing is untested for now.
Properties for easier access:
* hw_info: return hw-specific elements from sysinfo
* on_since: pretty-printed from sysinfo
* location: latitude and longitued from sysinfo
* rssi: rssi from sysinfo
* Update README.md with examples of available features.
* Handle comments from mweinelt
* Refactor state handling, use booleans instead of strings
* Fix issues raised during the review.
Following issues are addressed by this commit:
* All API is more or less commented (including return types, exceptions, ..)
* Converted state to use
* Added properties is_on, is_off for those who don't want to check against strings.
* Handled most issues reported by pylint.
* Adjusted _query_helper() to strip off err_code from the result object.
* Fixed broken format() syntax for string formattings.
* Fix ci woes plus one typo.
* Do initialization after changing device properties, fix nits.
2016-12-12 09:13:45 +00:00
|
|
|
:return: Unwrapped result for the call.
|
|
|
|
"""
|
2020-05-24 15:57:54 +00:00
|
|
|
request = self._create_request(target, cmd, arg, child_ids)
|
API and tests cleanup (#151)
* Add new cli commands: raw_command and dump_discover
- raw_command can be used to execute raw commands with given parameters
* Useful for testing new calls before implementing them properly
- dump_discover can be used to dump the device discovery information (into a file)
* The discovery is extended to request more modules and methods from devices
* smartlife.iot.dimmer get_dimmer_parameters
* smartlife.iot.common.emeter get_realtime
* smartlife.iot.smartbulb.lightingservice get_light_state
* This is used to dump more information for proper tests, and will also allow better discovery in the future
This commit contains also some documentation updates and dropping click_datetime in favor of click's built-in datetime
* Docstring fixes
* Major API cleanup
Properties shall no more change the state of the device, this work in still in progress, the main goal being making the API more user-friendly and to make implementing new features simpler.
The newly deprecated functionality will remain working and will simply warn the user about deprecation.
Previously deprecated 'features' property and 'identify' method are now finally removed.
Deprecate and replace the following property setters:
* state with turn_on() and turn_off()
* hsv with set_hsv()
* color_temp with set_color_temp()
* brightness with set_brightness()
* led with set_led()
* alias with set_alias()
* mac with set_mac()
And getters:
* state with is_on and is_off
The {BULB,PLUG}_STATE_{ON,OFF} is simplified to STATE_ON and STATE_OFF, UNKNOWN state is removed.
These are now deprecated and will be removed in the future.
* is_on and is_off can be used to check for the state
* turn_on() and turn_off() for changing the device state.
Trying to use functionality not supported by the device will cause SmartDeviceExceptions instead of failing silently and/or returning None.
This includes, e.g., trying to set a color temperature on non-supported bulb.
ValueErrors are raised instead of SmartDeviceExceptions where appropriate (e.g. when trying to set an invalid hsv or brightness).
New enum type DeviceType is added to allow detecting device types without resorting to isinstance() calling. SmartDevice class' device_type property can be used to query the type. is_plug and is_bulb helpers are added.
* Cleanup tests and improve test coverage
* Make writing tests easier by sharing code for common implementations
* Instead of storing test data inside python files, dump-discover based information is used
* This will simplify adding new tests and remove code duplication
* fixtures are based on https://github.com/plasticrake/tplink-smarthome-simulator
* run black on newfakes
* Add HS300 tests and update SmartStrip API according to earlier changes, still WIP
* run black and avoid wildcard imports
* Black on conftest
* bump minimum required version to 3.5
* Rename fixture_tests to test_fixtures for autocollect
* fix typoed type to _type, black
* run black on several files with -79 to fix hound issues
* Fix broken merge on hue
* Fix tests (hue update, pass context to smartdevice), add is_strip property, disable emeter tests for HS300 until a solution for API is found.
* Fix old tests
* Run black on changed files
* Add real HS220 discovery, thanks to @poiyo
* add is_dimmable and is_variable_color_temp to smartdevice class, simplifies interfacing with homeassistant
* add KL120(US) fixture
* Add a simple query cache
This commit adds a simple query cache to speed up the process for users
requesting lots of different properties from the device, as done by the
cli tool as well as homeassistant.
The logic for caching is very simple:
1. A timestamp for last fetch for each module+command is stored alongside the response.
2. If the issued command starts with `get_` and the TTL has not expired, the cache result is returned.
3. Otherwise the cache for the whole corresponding module gets invalidated, the device will be queried and the result will be stored in the cache.
* add deprecation to tox.ini
* make tests pass again
* remove old tests, add flake8 to tox reqs
* run black against pyhs100 module, add it to precommit hooks, fix flake8 configuration to conform to black standards (https://ljvmiranda921.github.io/notebook/2018/06/21/precommits-using-black-and-flake8/)
* fix syntax
* cleanup conftest
2019-06-16 21:05:00 +00:00
|
|
|
|
Refactor & add unittests for almost all functionality, add tox for running tests on py27 and py35 (#17)
* Refactor & add unittests for almost all functionality, add tox for running tests on py27 and py35
This commit adds unit tests for current api functionality.
- currently no mocking, all tests are run on the device.
- the library is now compatible with python 2.7 and python 3.5, use tox for tests
- schema checks are done with voluptuous
refactoring:
- protocol is separated into its own file, smartplug adapted to receive protocol worker as parameter.
- cleaned up the initialization routine, initialization is done on use, not on creation of smartplug
- added model and features properties, identity kept for backwards compatibility
- no more storing of local variables outside _sys_info, paves a way to handle state changes sanely (without complete reinitialization)
* Fix CI warnings, remove unused leftover code
* Rename _initialize to _fetch_sysinfo, as that's what it does.
* examples.cli: fix identify call, prettyprint sysinfo, update readme which had false format for led setting
* Add tox-travis for automated testing.
2016-12-16 22:51:56 +00:00
|
|
|
try:
|
2024-02-04 15:20:08 +00:00
|
|
|
response = await self._raw_query(request=request)
|
Refactor & add unittests for almost all functionality, add tox for running tests on py27 and py35 (#17)
* Refactor & add unittests for almost all functionality, add tox for running tests on py27 and py35
This commit adds unit tests for current api functionality.
- currently no mocking, all tests are run on the device.
- the library is now compatible with python 2.7 and python 3.5, use tox for tests
- schema checks are done with voluptuous
refactoring:
- protocol is separated into its own file, smartplug adapted to receive protocol worker as parameter.
- cleaned up the initialization routine, initialization is done on use, not on creation of smartplug
- added model and features properties, identity kept for backwards compatibility
- no more storing of local variables outside _sys_info, paves a way to handle state changes sanely (without complete reinitialization)
* Fix CI warnings, remove unused leftover code
* Rename _initialize to _fetch_sysinfo, as that's what it does.
* examples.cli: fix identify call, prettyprint sysinfo, update readme which had false format for led setting
* Add tox-travis for automated testing.
2016-12-16 22:51:56 +00:00
|
|
|
except Exception as ex:
|
2024-02-21 15:52:55 +00:00
|
|
|
raise KasaException(f"Communication error on {target}:{cmd}") from ex
|
Refactor & add unittests for almost all functionality, add tox for running tests on py27 and py35 (#17)
* Refactor & add unittests for almost all functionality, add tox for running tests on py27 and py35
This commit adds unit tests for current api functionality.
- currently no mocking, all tests are run on the device.
- the library is now compatible with python 2.7 and python 3.5, use tox for tests
- schema checks are done with voluptuous
refactoring:
- protocol is separated into its own file, smartplug adapted to receive protocol worker as parameter.
- cleaned up the initialization routine, initialization is done on use, not on creation of smartplug
- added model and features properties, identity kept for backwards compatibility
- no more storing of local variables outside _sys_info, paves a way to handle state changes sanely (without complete reinitialization)
* Fix CI warnings, remove unused leftover code
* Rename _initialize to _fetch_sysinfo, as that's what it does.
* examples.cli: fix identify call, prettyprint sysinfo, update readme which had false format for led setting
* Add tox-travis for automated testing.
2016-12-16 22:51:56 +00:00
|
|
|
|
2017-01-07 22:45:47 +00:00
|
|
|
if target not in response:
|
2024-02-21 15:52:55 +00:00
|
|
|
raise KasaException(f"No required {target} in response: {response}")
|
2017-01-07 22:45:47 +00:00
|
|
|
|
Refactor & add unittests for almost all functionality, add tox for running tests on py27 and py35 (#17)
* Refactor & add unittests for almost all functionality, add tox for running tests on py27 and py35
This commit adds unit tests for current api functionality.
- currently no mocking, all tests are run on the device.
- the library is now compatible with python 2.7 and python 3.5, use tox for tests
- schema checks are done with voluptuous
refactoring:
- protocol is separated into its own file, smartplug adapted to receive protocol worker as parameter.
- cleaned up the initialization routine, initialization is done on use, not on creation of smartplug
- added model and features properties, identity kept for backwards compatibility
- no more storing of local variables outside _sys_info, paves a way to handle state changes sanely (without complete reinitialization)
* Fix CI warnings, remove unused leftover code
* Rename _initialize to _fetch_sysinfo, as that's what it does.
* examples.cli: fix identify call, prettyprint sysinfo, update readme which had false format for led setting
* Add tox-travis for automated testing.
2016-12-16 22:51:56 +00:00
|
|
|
result = response[target]
|
|
|
|
if "err_code" in result and result["err_code"] != 0:
|
2024-02-21 15:52:55 +00:00
|
|
|
raise KasaException(f"Error on {target}.{cmd}: {result}")
|
Read all data from the device, disable double-encoding, implement more APIs, refactor querying, update README (#11)
* Read from socket until no data available, disable double string encoding
HS110 sends sometimes datagrams in chunks especially for get_daystat,
this patch makes it to read until there is no more data to be read.
As json.dumps() does JSON encoding already, there's no need to str()
the year or month either.
* Add cli.py, a simple script to query devices for debugging purposes.
* allow easier importing with from pyHS100 import SmartPlug
* move cli.py to examples, add short usage into README.md
* Implement more available APIs, refactor querying code.
This commit adds access to new properties, both read & write, while keeping the old one (mostly) intact.
Querying is refactored to be done inside _query_helper() method,
which unwraps results automatically and rises SmartPlugException() in case of errors.
Errors are to be handled by clients.
New features:
* Setting device alias (plug.alias = "name")
* led read & write
* icon read (doesn't seem to return anything without cloud support at least), write API is not known, throws an exception currently
* time read (returns datetime), time write implemented, but not working even when no error is returned from the device
* timezone read
* mac read & write, writing is untested for now.
Properties for easier access:
* hw_info: return hw-specific elements from sysinfo
* on_since: pretty-printed from sysinfo
* location: latitude and longitued from sysinfo
* rssi: rssi from sysinfo
* Update README.md with examples of available features.
* Handle comments from mweinelt
* Refactor state handling, use booleans instead of strings
* Fix issues raised during the review.
Following issues are addressed by this commit:
* All API is more or less commented (including return types, exceptions, ..)
* Converted state to use
* Added properties is_on, is_off for those who don't want to check against strings.
* Handled most issues reported by pylint.
* Adjusted _query_helper() to strip off err_code from the result object.
* Fixed broken format() syntax for string formattings.
* Fix ci woes plus one typo.
* Do initialization after changing device properties, fix nits.
2016-12-12 09:13:45 +00:00
|
|
|
|
2018-10-18 17:57:44 +00:00
|
|
|
if cmd not in result:
|
2024-02-21 15:52:55 +00:00
|
|
|
raise KasaException(f"No command in response: {response}")
|
Refactor & add unittests for almost all functionality, add tox for running tests on py27 and py35 (#17)
* Refactor & add unittests for almost all functionality, add tox for running tests on py27 and py35
This commit adds unit tests for current api functionality.
- currently no mocking, all tests are run on the device.
- the library is now compatible with python 2.7 and python 3.5, use tox for tests
- schema checks are done with voluptuous
refactoring:
- protocol is separated into its own file, smartplug adapted to receive protocol worker as parameter.
- cleaned up the initialization routine, initialization is done on use, not on creation of smartplug
- added model and features properties, identity kept for backwards compatibility
- no more storing of local variables outside _sys_info, paves a way to handle state changes sanely (without complete reinitialization)
* Fix CI warnings, remove unused leftover code
* Rename _initialize to _fetch_sysinfo, as that's what it does.
* examples.cli: fix identify call, prettyprint sysinfo, update readme which had false format for led setting
* Add tox-travis for automated testing.
2016-12-16 22:51:56 +00:00
|
|
|
result = result[cmd]
|
2019-06-03 13:58:03 +00:00
|
|
|
if "err_code" in result and result["err_code"] != 0:
|
2024-02-21 15:52:55 +00:00
|
|
|
raise KasaException(f"Error on {target} {cmd}: {result}")
|
2019-06-03 13:58:03 +00:00
|
|
|
|
API and tests cleanup (#151)
* Add new cli commands: raw_command and dump_discover
- raw_command can be used to execute raw commands with given parameters
* Useful for testing new calls before implementing them properly
- dump_discover can be used to dump the device discovery information (into a file)
* The discovery is extended to request more modules and methods from devices
* smartlife.iot.dimmer get_dimmer_parameters
* smartlife.iot.common.emeter get_realtime
* smartlife.iot.smartbulb.lightingservice get_light_state
* This is used to dump more information for proper tests, and will also allow better discovery in the future
This commit contains also some documentation updates and dropping click_datetime in favor of click's built-in datetime
* Docstring fixes
* Major API cleanup
Properties shall no more change the state of the device, this work in still in progress, the main goal being making the API more user-friendly and to make implementing new features simpler.
The newly deprecated functionality will remain working and will simply warn the user about deprecation.
Previously deprecated 'features' property and 'identify' method are now finally removed.
Deprecate and replace the following property setters:
* state with turn_on() and turn_off()
* hsv with set_hsv()
* color_temp with set_color_temp()
* brightness with set_brightness()
* led with set_led()
* alias with set_alias()
* mac with set_mac()
And getters:
* state with is_on and is_off
The {BULB,PLUG}_STATE_{ON,OFF} is simplified to STATE_ON and STATE_OFF, UNKNOWN state is removed.
These are now deprecated and will be removed in the future.
* is_on and is_off can be used to check for the state
* turn_on() and turn_off() for changing the device state.
Trying to use functionality not supported by the device will cause SmartDeviceExceptions instead of failing silently and/or returning None.
This includes, e.g., trying to set a color temperature on non-supported bulb.
ValueErrors are raised instead of SmartDeviceExceptions where appropriate (e.g. when trying to set an invalid hsv or brightness).
New enum type DeviceType is added to allow detecting device types without resorting to isinstance() calling. SmartDevice class' device_type property can be used to query the type. is_plug and is_bulb helpers are added.
* Cleanup tests and improve test coverage
* Make writing tests easier by sharing code for common implementations
* Instead of storing test data inside python files, dump-discover based information is used
* This will simplify adding new tests and remove code duplication
* fixtures are based on https://github.com/plasticrake/tplink-smarthome-simulator
* run black on newfakes
* Add HS300 tests and update SmartStrip API according to earlier changes, still WIP
* run black and avoid wildcard imports
* Black on conftest
* bump minimum required version to 3.5
* Rename fixture_tests to test_fixtures for autocollect
* fix typoed type to _type, black
* run black on several files with -79 to fix hound issues
* Fix broken merge on hue
* Fix tests (hue update, pass context to smartdevice), add is_strip property, disable emeter tests for HS300 until a solution for API is found.
* Fix old tests
* Run black on changed files
* Add real HS220 discovery, thanks to @poiyo
* add is_dimmable and is_variable_color_temp to smartdevice class, simplifies interfacing with homeassistant
* add KL120(US) fixture
* Add a simple query cache
This commit adds a simple query cache to speed up the process for users
requesting lots of different properties from the device, as done by the
cli tool as well as homeassistant.
The logic for caching is very simple:
1. A timestamp for last fetch for each module+command is stored alongside the response.
2. If the issued command starts with `get_` and the TTL has not expired, the cache result is returned.
3. Otherwise the cache for the whole corresponding module gets invalidated, the device will be queried and the result will be stored in the cache.
* add deprecation to tox.ini
* make tests pass again
* remove old tests, add flake8 to tox reqs
* run black against pyhs100 module, add it to precommit hooks, fix flake8 configuration to conform to black standards (https://ljvmiranda921.github.io/notebook/2018/06/21/precommits-using-black-and-flake8/)
* fix syntax
* cleanup conftest
2019-06-16 21:05:00 +00:00
|
|
|
if "err_code" in result:
|
|
|
|
del result["err_code"]
|
Read all data from the device, disable double-encoding, implement more APIs, refactor querying, update README (#11)
* Read from socket until no data available, disable double string encoding
HS110 sends sometimes datagrams in chunks especially for get_daystat,
this patch makes it to read until there is no more data to be read.
As json.dumps() does JSON encoding already, there's no need to str()
the year or month either.
* Add cli.py, a simple script to query devices for debugging purposes.
* allow easier importing with from pyHS100 import SmartPlug
* move cli.py to examples, add short usage into README.md
* Implement more available APIs, refactor querying code.
This commit adds access to new properties, both read & write, while keeping the old one (mostly) intact.
Querying is refactored to be done inside _query_helper() method,
which unwraps results automatically and rises SmartPlugException() in case of errors.
Errors are to be handled by clients.
New features:
* Setting device alias (plug.alias = "name")
* led read & write
* icon read (doesn't seem to return anything without cloud support at least), write API is not known, throws an exception currently
* time read (returns datetime), time write implemented, but not working even when no error is returned from the device
* timezone read
* mac read & write, writing is untested for now.
Properties for easier access:
* hw_info: return hw-specific elements from sysinfo
* on_since: pretty-printed from sysinfo
* location: latitude and longitued from sysinfo
* rssi: rssi from sysinfo
* Update README.md with examples of available features.
* Handle comments from mweinelt
* Refactor state handling, use booleans instead of strings
* Fix issues raised during the review.
Following issues are addressed by this commit:
* All API is more or less commented (including return types, exceptions, ..)
* Converted state to use
* Added properties is_on, is_off for those who don't want to check against strings.
* Handled most issues reported by pylint.
* Adjusted _query_helper() to strip off err_code from the result object.
* Fixed broken format() syntax for string formattings.
* Fix ci woes plus one typo.
* Do initialization after changing device properties, fix nits.
2016-12-12 09:13:45 +00:00
|
|
|
|
|
|
|
return result
|
2016-07-09 11:34:27 +00:00
|
|
|
|
2021-09-23 22:24:44 +00:00
|
|
|
@property # type: ignore
|
|
|
|
@requires_update
|
2024-04-17 13:39:24 +00:00
|
|
|
def features(self) -> dict[str, Feature]:
|
2021-09-23 22:24:44 +00:00
|
|
|
"""Return a set of features that the device supports."""
|
2023-10-07 19:18:47 +00:00
|
|
|
return self._features
|
2021-09-23 22:24:44 +00:00
|
|
|
|
2020-04-12 14:00:15 +00:00
|
|
|
@property # type: ignore
|
|
|
|
@requires_update
|
2019-11-15 16:48:36 +00:00
|
|
|
def has_emeter(self) -> bool:
|
2020-05-27 14:55:18 +00:00
|
|
|
"""Return True if device has an energy meter."""
|
2024-02-15 15:25:08 +00:00
|
|
|
return "ENE" in self._legacy_features
|
2017-04-14 12:24:58 +00:00
|
|
|
|
2024-04-17 13:39:24 +00:00
|
|
|
async def get_sys_info(self) -> dict[str, Any]:
|
2020-05-27 14:55:18 +00:00
|
|
|
"""Retrieve system information."""
|
2019-11-11 16:21:23 +00:00
|
|
|
return await self._query_helper("system", "get_sysinfo")
|
2016-10-13 15:31:34 +00:00
|
|
|
|
2024-11-10 18:55:13 +00:00
|
|
|
async def update(self, update_children: bool = True) -> None:
|
2021-09-19 21:43:17 +00:00
|
|
|
"""Query the device to update the data.
|
2019-11-15 16:48:36 +00:00
|
|
|
|
2021-09-19 21:43:17 +00:00
|
|
|
Needed for properties that are decorated with `requires_update`.
|
2019-11-15 16:48:36 +00:00
|
|
|
"""
|
2020-05-24 15:57:54 +00:00
|
|
|
req = {}
|
|
|
|
req.update(self._create_request("system", "get_sysinfo"))
|
|
|
|
|
2021-09-19 21:43:17 +00:00
|
|
|
# If this is the initial update, check only for the sysinfo
|
|
|
|
# This is necessary as some devices crash on unexpected modules
|
|
|
|
# See #105, #120, #161
|
2024-12-13 12:37:13 +00:00
|
|
|
if not self._last_update:
|
2021-09-19 21:43:17 +00:00
|
|
|
_LOGGER.debug("Performing the initial update to obtain sysinfo")
|
2023-10-07 19:18:47 +00:00
|
|
|
response = await self.protocol.query(req)
|
|
|
|
self._last_update = response
|
2024-12-06 23:06:58 +00:00
|
|
|
self._set_sys_info(_extract_sys_info(response))
|
2021-09-19 21:43:17 +00:00
|
|
|
|
2024-05-13 16:34:44 +00:00
|
|
|
if not self._modules:
|
|
|
|
await self._initialize_modules()
|
|
|
|
|
2024-05-10 18:29:28 +00:00
|
|
|
await self._modular_update(req)
|
|
|
|
|
2024-12-06 23:06:58 +00:00
|
|
|
self._set_sys_info(_extract_sys_info(self._last_update))
|
2024-05-19 10:20:18 +00:00
|
|
|
for module in self._modules.values():
|
2024-10-08 07:16:51 +00:00
|
|
|
await module._post_update_hook()
|
2024-05-19 10:20:18 +00:00
|
|
|
|
2024-02-15 15:25:08 +00:00
|
|
|
if not self._features:
|
|
|
|
await self._initialize_features()
|
|
|
|
|
2024-11-10 18:55:13 +00:00
|
|
|
async def _initialize_modules(self) -> None:
|
2024-05-13 16:34:44 +00:00
|
|
|
"""Initialize modules not added in init."""
|
2024-06-17 10:22:05 +00:00
|
|
|
if self.has_emeter:
|
|
|
|
_LOGGER.debug(
|
|
|
|
"The device has emeter, querying its information along sysinfo"
|
|
|
|
)
|
|
|
|
self.add_module(Module.Energy, Emeter(self, self.emeter_type))
|
2024-05-13 16:34:44 +00:00
|
|
|
|
2024-11-10 18:55:13 +00:00
|
|
|
async def _initialize_features(self) -> None:
|
2024-05-22 14:52:00 +00:00
|
|
|
"""Initialize common features."""
|
|
|
|
self._add_feature(
|
|
|
|
Feature(
|
|
|
|
self,
|
|
|
|
id="state",
|
|
|
|
name="State",
|
|
|
|
attribute_getter="is_on",
|
|
|
|
attribute_setter="set_state",
|
|
|
|
type=Feature.Type.Switch,
|
|
|
|
category=Feature.Category.Primary,
|
|
|
|
)
|
|
|
|
)
|
2024-02-15 15:25:08 +00:00
|
|
|
self._add_feature(
|
|
|
|
Feature(
|
2024-04-23 17:20:12 +00:00
|
|
|
device=self,
|
2024-05-07 09:13:35 +00:00
|
|
|
id="rssi",
|
2024-04-23 17:20:12 +00:00
|
|
|
name="RSSI",
|
|
|
|
attribute_getter="rssi",
|
|
|
|
icon="mdi:signal",
|
2024-07-30 18:23:07 +00:00
|
|
|
unit_getter=lambda: "dBm",
|
2024-04-23 17:20:12 +00:00
|
|
|
category=Feature.Category.Debug,
|
2024-06-25 16:30:36 +00:00
|
|
|
type=Feature.Type.Sensor,
|
2024-02-15 15:25:08 +00:00
|
|
|
)
|
|
|
|
)
|
2024-06-14 21:04:20 +00:00
|
|
|
# iot strips calculate on_since from the children
|
|
|
|
if "on_time" in self._sys_info or self.device_type == Device.Type.Strip:
|
2024-02-15 15:25:08 +00:00
|
|
|
self._add_feature(
|
|
|
|
Feature(
|
|
|
|
device=self,
|
2024-05-07 09:13:35 +00:00
|
|
|
id="on_since",
|
2024-02-15 15:25:08 +00:00
|
|
|
name="On since",
|
|
|
|
attribute_getter="on_since",
|
|
|
|
icon="mdi:clock",
|
2024-06-21 16:42:43 +00:00
|
|
|
category=Feature.Category.Info,
|
2024-06-25 16:30:36 +00:00
|
|
|
type=Feature.Type.Sensor,
|
2024-02-15 15:25:08 +00:00
|
|
|
)
|
|
|
|
)
|
|
|
|
|
2024-07-24 13:47:38 +00:00
|
|
|
self._add_feature(
|
|
|
|
Feature(
|
|
|
|
device=self,
|
|
|
|
id="reboot",
|
|
|
|
name="Reboot",
|
|
|
|
attribute_setter="reboot",
|
|
|
|
icon="mdi:restart",
|
|
|
|
category=Feature.Category.Debug,
|
|
|
|
type=Feature.Type.Action,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
2024-11-10 18:55:13 +00:00
|
|
|
for module in self.modules.values():
|
2024-05-10 18:29:28 +00:00
|
|
|
module._initialize_features()
|
|
|
|
for module_feat in module._module_features.values():
|
|
|
|
self._add_feature(module_feat)
|
|
|
|
|
2022-04-05 16:16:36 +00:00
|
|
|
async def _modular_update(self, req: dict) -> None:
|
|
|
|
"""Execute an update query."""
|
2023-09-14 18:51:40 +00:00
|
|
|
request_list = []
|
|
|
|
est_response_size = 1024 if "system" in req else 0
|
2024-05-10 18:29:28 +00:00
|
|
|
for module in self._modules.values():
|
2022-01-29 17:15:59 +00:00
|
|
|
if not module.is_supported:
|
2024-08-23 09:56:33 +00:00
|
|
|
_LOGGER.debug("Module %s not supported, skipping", module)
|
2022-01-29 17:15:59 +00:00
|
|
|
continue
|
2023-09-14 18:51:40 +00:00
|
|
|
|
|
|
|
est_response_size += module.estimated_query_response_size
|
|
|
|
if est_response_size > self.max_device_response_size:
|
|
|
|
request_list.append(req)
|
|
|
|
req = {}
|
|
|
|
est_response_size = module.estimated_query_response_size
|
2023-05-18 15:04:24 +00:00
|
|
|
|
2021-11-07 01:41:12 +00:00
|
|
|
q = module.query()
|
|
|
|
_LOGGER.debug("Adding query for %s: %s", module, q)
|
2022-04-05 16:16:36 +00:00
|
|
|
req = merge(req, q)
|
2023-09-14 18:51:40 +00:00
|
|
|
request_list.append(req)
|
2021-09-19 21:43:17 +00:00
|
|
|
|
2023-09-14 18:51:40 +00:00
|
|
|
responses = [
|
|
|
|
await self.protocol.query(request) for request in request_list if request
|
|
|
|
]
|
|
|
|
|
2023-10-05 20:50:54 +00:00
|
|
|
# Preserve the last update and merge
|
|
|
|
# responses on top of it so we remember
|
|
|
|
# which modules are not supported, otherwise
|
|
|
|
# every other update will query for them
|
2024-04-17 13:39:24 +00:00
|
|
|
update: dict = self._last_update.copy() if self._last_update else {}
|
2023-09-14 18:51:40 +00:00
|
|
|
for response in responses:
|
2024-11-20 14:59:32 +00:00
|
|
|
for k, v in response.items():
|
|
|
|
# The same module could have results in different responses
|
|
|
|
# i.e. smartlife.iot.common.schedule for Usage and
|
|
|
|
# Schedule, so need to call update(**v) here. If a module is
|
|
|
|
# not supported the response
|
|
|
|
# {'err_code': -1, 'err_msg': 'module not support'}
|
|
|
|
# become top level key/values of the response so check for dict
|
|
|
|
if isinstance(v, dict):
|
|
|
|
update.setdefault(k, {}).update(**v)
|
2023-09-14 18:51:40 +00:00
|
|
|
self._last_update = update
|
2019-11-15 16:48:36 +00:00
|
|
|
|
2024-06-17 10:22:05 +00:00
|
|
|
# IOT modules are added as default but could be unsupported post first update
|
|
|
|
if self._supported_modules is None:
|
|
|
|
supported = {}
|
|
|
|
for module_name, module in self._modules.items():
|
|
|
|
if module.is_supported:
|
|
|
|
supported[module_name] = module
|
|
|
|
|
|
|
|
self._supported_modules = supported
|
|
|
|
|
2024-04-17 13:39:24 +00:00
|
|
|
def update_from_discover_info(self, info: dict[str, Any]) -> None:
|
2021-02-06 15:14:36 +00:00
|
|
|
"""Update state from info from the discover call."""
|
2023-12-04 18:50:05 +00:00
|
|
|
self._discovery_info = info
|
2023-11-20 13:17:10 +00:00
|
|
|
if "system" in info and (sys_info := info["system"].get("get_sysinfo")):
|
|
|
|
self._last_update = info
|
|
|
|
self._set_sys_info(sys_info)
|
|
|
|
else:
|
|
|
|
# This allows setting of some info properties directly
|
|
|
|
# from partial discovery info that will then be found
|
|
|
|
# by the requires_update decorator
|
2024-12-13 12:37:13 +00:00
|
|
|
discovery_model = info["device_model"]
|
|
|
|
no_region_model, _, _ = discovery_model.partition("(")
|
|
|
|
self._set_sys_info({**info, "model": no_region_model})
|
2023-10-07 19:18:47 +00:00
|
|
|
|
2024-04-17 13:39:24 +00:00
|
|
|
def _set_sys_info(self, sys_info: dict[str, Any]) -> None:
|
2023-10-07 19:18:47 +00:00
|
|
|
"""Set sys_info."""
|
|
|
|
self._sys_info = sys_info
|
|
|
|
if features := sys_info.get("feature"):
|
2024-02-15 15:25:08 +00:00
|
|
|
self._legacy_features = _parse_features(features)
|
2021-02-06 15:14:36 +00:00
|
|
|
|
2019-12-12 09:46:40 +00:00
|
|
|
@property # type: ignore
|
2019-11-15 16:48:36 +00:00
|
|
|
@requires_update
|
2024-04-17 13:39:24 +00:00
|
|
|
def sys_info(self) -> dict[str, Any]:
|
2023-11-20 13:17:10 +00:00
|
|
|
"""
|
|
|
|
Return system information.
|
|
|
|
|
|
|
|
Do not call this function from within the SmartDevice
|
|
|
|
class itself as @requires_update will be affected for other properties.
|
|
|
|
"""
|
2020-05-24 15:57:54 +00:00
|
|
|
return self._sys_info # type: ignore
|
2019-11-15 16:48:36 +00:00
|
|
|
|
2024-11-11 17:41:31 +00:00
|
|
|
@property
|
|
|
|
@requires_update
|
2024-12-13 12:37:13 +00:00
|
|
|
def model(self) -> str:
|
|
|
|
"""Returns the device model."""
|
|
|
|
if self._last_update:
|
|
|
|
return self.device_info.short_name
|
|
|
|
return self._sys_info["model"]
|
2024-11-11 17:41:31 +00:00
|
|
|
|
2019-12-12 09:46:40 +00:00
|
|
|
@property # type: ignore
|
2024-04-17 13:39:24 +00:00
|
|
|
def alias(self) -> str | None:
|
2020-05-27 14:55:18 +00:00
|
|
|
"""Return device name (alias)."""
|
2023-11-20 13:17:10 +00:00
|
|
|
sys_info = self._sys_info
|
2024-01-11 15:12:02 +00:00
|
|
|
return sys_info.get("alias") if sys_info else None
|
API and tests cleanup (#151)
* Add new cli commands: raw_command and dump_discover
- raw_command can be used to execute raw commands with given parameters
* Useful for testing new calls before implementing them properly
- dump_discover can be used to dump the device discovery information (into a file)
* The discovery is extended to request more modules and methods from devices
* smartlife.iot.dimmer get_dimmer_parameters
* smartlife.iot.common.emeter get_realtime
* smartlife.iot.smartbulb.lightingservice get_light_state
* This is used to dump more information for proper tests, and will also allow better discovery in the future
This commit contains also some documentation updates and dropping click_datetime in favor of click's built-in datetime
* Docstring fixes
* Major API cleanup
Properties shall no more change the state of the device, this work in still in progress, the main goal being making the API more user-friendly and to make implementing new features simpler.
The newly deprecated functionality will remain working and will simply warn the user about deprecation.
Previously deprecated 'features' property and 'identify' method are now finally removed.
Deprecate and replace the following property setters:
* state with turn_on() and turn_off()
* hsv with set_hsv()
* color_temp with set_color_temp()
* brightness with set_brightness()
* led with set_led()
* alias with set_alias()
* mac with set_mac()
And getters:
* state with is_on and is_off
The {BULB,PLUG}_STATE_{ON,OFF} is simplified to STATE_ON and STATE_OFF, UNKNOWN state is removed.
These are now deprecated and will be removed in the future.
* is_on and is_off can be used to check for the state
* turn_on() and turn_off() for changing the device state.
Trying to use functionality not supported by the device will cause SmartDeviceExceptions instead of failing silently and/or returning None.
This includes, e.g., trying to set a color temperature on non-supported bulb.
ValueErrors are raised instead of SmartDeviceExceptions where appropriate (e.g. when trying to set an invalid hsv or brightness).
New enum type DeviceType is added to allow detecting device types without resorting to isinstance() calling. SmartDevice class' device_type property can be used to query the type. is_plug and is_bulb helpers are added.
* Cleanup tests and improve test coverage
* Make writing tests easier by sharing code for common implementations
* Instead of storing test data inside python files, dump-discover based information is used
* This will simplify adding new tests and remove code duplication
* fixtures are based on https://github.com/plasticrake/tplink-smarthome-simulator
* run black on newfakes
* Add HS300 tests and update SmartStrip API according to earlier changes, still WIP
* run black and avoid wildcard imports
* Black on conftest
* bump minimum required version to 3.5
* Rename fixture_tests to test_fixtures for autocollect
* fix typoed type to _type, black
* run black on several files with -79 to fix hound issues
* Fix broken merge on hue
* Fix tests (hue update, pass context to smartdevice), add is_strip property, disable emeter tests for HS300 until a solution for API is found.
* Fix old tests
* Run black on changed files
* Add real HS220 discovery, thanks to @poiyo
* add is_dimmable and is_variable_color_temp to smartdevice class, simplifies interfacing with homeassistant
* add KL120(US) fixture
* Add a simple query cache
This commit adds a simple query cache to speed up the process for users
requesting lots of different properties from the device, as done by the
cli tool as well as homeassistant.
The logic for caching is very simple:
1. A timestamp for last fetch for each module+command is stored alongside the response.
2. If the issued command starts with `get_` and the TTL has not expired, the cache result is returned.
3. Otherwise the cache for the whole corresponding module gets invalidated, the device will be queried and the result will be stored in the cache.
* add deprecation to tox.ini
* make tests pass again
* remove old tests, add flake8 to tox reqs
* run black against pyhs100 module, add it to precommit hooks, fix flake8 configuration to conform to black standards (https://ljvmiranda921.github.io/notebook/2018/06/21/precommits-using-black-and-flake8/)
* fix syntax
* cleanup conftest
2019-06-16 21:05:00 +00:00
|
|
|
|
2024-11-10 18:55:13 +00:00
|
|
|
async def set_alias(self, alias: str) -> dict:
|
2020-05-27 14:55:18 +00:00
|
|
|
"""Set the device name (alias)."""
|
2020-05-24 15:57:54 +00:00
|
|
|
return await self._query_helper("system", "set_dev_alias", {"alias": alias})
|
Read all data from the device, disable double-encoding, implement more APIs, refactor querying, update README (#11)
* Read from socket until no data available, disable double string encoding
HS110 sends sometimes datagrams in chunks especially for get_daystat,
this patch makes it to read until there is no more data to be read.
As json.dumps() does JSON encoding already, there's no need to str()
the year or month either.
* Add cli.py, a simple script to query devices for debugging purposes.
* allow easier importing with from pyHS100 import SmartPlug
* move cli.py to examples, add short usage into README.md
* Implement more available APIs, refactor querying code.
This commit adds access to new properties, both read & write, while keeping the old one (mostly) intact.
Querying is refactored to be done inside _query_helper() method,
which unwraps results automatically and rises SmartPlugException() in case of errors.
Errors are to be handled by clients.
New features:
* Setting device alias (plug.alias = "name")
* led read & write
* icon read (doesn't seem to return anything without cloud support at least), write API is not known, throws an exception currently
* time read (returns datetime), time write implemented, but not working even when no error is returned from the device
* timezone read
* mac read & write, writing is untested for now.
Properties for easier access:
* hw_info: return hw-specific elements from sysinfo
* on_since: pretty-printed from sysinfo
* location: latitude and longitued from sysinfo
* rssi: rssi from sysinfo
* Update README.md with examples of available features.
* Handle comments from mweinelt
* Refactor state handling, use booleans instead of strings
* Fix issues raised during the review.
Following issues are addressed by this commit:
* All API is more or less commented (including return types, exceptions, ..)
* Converted state to use
* Added properties is_on, is_off for those who don't want to check against strings.
* Handled most issues reported by pylint.
* Adjusted _query_helper() to strip off err_code from the result object.
* Fixed broken format() syntax for string formattings.
* Fix ci woes plus one typo.
* Do initialization after changing device properties, fix nits.
2016-12-12 09:13:45 +00:00
|
|
|
|
2024-04-29 16:34:20 +00:00
|
|
|
@property
|
2021-11-07 01:41:12 +00:00
|
|
|
@requires_update
|
|
|
|
def time(self) -> datetime:
|
|
|
|
"""Return current time from the device."""
|
2024-10-15 07:59:25 +00:00
|
|
|
return self.modules[Module.Time].time
|
2021-11-07 01:41:12 +00:00
|
|
|
|
2024-04-29 16:34:20 +00:00
|
|
|
@property
|
2021-11-07 01:41:12 +00:00
|
|
|
@requires_update
|
2024-10-08 07:16:51 +00:00
|
|
|
def timezone(self) -> tzinfo:
|
2021-11-07 01:41:12 +00:00
|
|
|
"""Return the current timezone."""
|
2024-10-15 07:59:25 +00:00
|
|
|
return self.modules[Module.Time].timezone
|
2021-11-07 01:41:12 +00:00
|
|
|
|
2024-10-15 07:59:25 +00:00
|
|
|
async def get_time(self) -> datetime:
|
2020-05-27 14:55:18 +00:00
|
|
|
"""Return current time from the device, if available."""
|
2024-10-15 07:59:25 +00:00
|
|
|
msg = "Use `time` property instead, this call will be removed in the future."
|
2024-10-31 13:12:17 +00:00
|
|
|
warn(msg, DeprecationWarning, stacklevel=2)
|
2024-10-15 07:59:25 +00:00
|
|
|
return self.time
|
Read all data from the device, disable double-encoding, implement more APIs, refactor querying, update README (#11)
* Read from socket until no data available, disable double string encoding
HS110 sends sometimes datagrams in chunks especially for get_daystat,
this patch makes it to read until there is no more data to be read.
As json.dumps() does JSON encoding already, there's no need to str()
the year or month either.
* Add cli.py, a simple script to query devices for debugging purposes.
* allow easier importing with from pyHS100 import SmartPlug
* move cli.py to examples, add short usage into README.md
* Implement more available APIs, refactor querying code.
This commit adds access to new properties, both read & write, while keeping the old one (mostly) intact.
Querying is refactored to be done inside _query_helper() method,
which unwraps results automatically and rises SmartPlugException() in case of errors.
Errors are to be handled by clients.
New features:
* Setting device alias (plug.alias = "name")
* led read & write
* icon read (doesn't seem to return anything without cloud support at least), write API is not known, throws an exception currently
* time read (returns datetime), time write implemented, but not working even when no error is returned from the device
* timezone read
* mac read & write, writing is untested for now.
Properties for easier access:
* hw_info: return hw-specific elements from sysinfo
* on_since: pretty-printed from sysinfo
* location: latitude and longitued from sysinfo
* rssi: rssi from sysinfo
* Update README.md with examples of available features.
* Handle comments from mweinelt
* Refactor state handling, use booleans instead of strings
* Fix issues raised during the review.
Following issues are addressed by this commit:
* All API is more or less commented (including return types, exceptions, ..)
* Converted state to use
* Added properties is_on, is_off for those who don't want to check against strings.
* Handled most issues reported by pylint.
* Adjusted _query_helper() to strip off err_code from the result object.
* Fixed broken format() syntax for string formattings.
* Fix ci woes plus one typo.
* Do initialization after changing device properties, fix nits.
2016-12-12 09:13:45 +00:00
|
|
|
|
2024-10-15 07:59:25 +00:00
|
|
|
async def get_timezone(self) -> tzinfo:
|
2020-05-27 14:55:18 +00:00
|
|
|
"""Return timezone information."""
|
2024-10-15 07:59:25 +00:00
|
|
|
msg = (
|
2022-01-29 19:36:08 +00:00
|
|
|
"Use `timezone` property instead, this call will be removed in the future."
|
|
|
|
)
|
2024-10-31 13:12:17 +00:00
|
|
|
warn(msg, DeprecationWarning, stacklevel=2)
|
2024-10-15 07:59:25 +00:00
|
|
|
return self.timezone
|
Read all data from the device, disable double-encoding, implement more APIs, refactor querying, update README (#11)
* Read from socket until no data available, disable double string encoding
HS110 sends sometimes datagrams in chunks especially for get_daystat,
this patch makes it to read until there is no more data to be read.
As json.dumps() does JSON encoding already, there's no need to str()
the year or month either.
* Add cli.py, a simple script to query devices for debugging purposes.
* allow easier importing with from pyHS100 import SmartPlug
* move cli.py to examples, add short usage into README.md
* Implement more available APIs, refactor querying code.
This commit adds access to new properties, both read & write, while keeping the old one (mostly) intact.
Querying is refactored to be done inside _query_helper() method,
which unwraps results automatically and rises SmartPlugException() in case of errors.
Errors are to be handled by clients.
New features:
* Setting device alias (plug.alias = "name")
* led read & write
* icon read (doesn't seem to return anything without cloud support at least), write API is not known, throws an exception currently
* time read (returns datetime), time write implemented, but not working even when no error is returned from the device
* timezone read
* mac read & write, writing is untested for now.
Properties for easier access:
* hw_info: return hw-specific elements from sysinfo
* on_since: pretty-printed from sysinfo
* location: latitude and longitued from sysinfo
* rssi: rssi from sysinfo
* Update README.md with examples of available features.
* Handle comments from mweinelt
* Refactor state handling, use booleans instead of strings
* Fix issues raised during the review.
Following issues are addressed by this commit:
* All API is more or less commented (including return types, exceptions, ..)
* Converted state to use
* Added properties is_on, is_off for those who don't want to check against strings.
* Handled most issues reported by pylint.
* Adjusted _query_helper() to strip off err_code from the result object.
* Fixed broken format() syntax for string formattings.
* Fix ci woes plus one typo.
* Do initialization after changing device properties, fix nits.
2016-12-12 09:13:45 +00:00
|
|
|
|
2019-12-12 09:46:40 +00:00
|
|
|
@property # type: ignore
|
2019-11-15 16:48:36 +00:00
|
|
|
@requires_update
|
2024-04-17 13:39:24 +00:00
|
|
|
def hw_info(self) -> dict:
|
API and tests cleanup (#151)
* Add new cli commands: raw_command and dump_discover
- raw_command can be used to execute raw commands with given parameters
* Useful for testing new calls before implementing them properly
- dump_discover can be used to dump the device discovery information (into a file)
* The discovery is extended to request more modules and methods from devices
* smartlife.iot.dimmer get_dimmer_parameters
* smartlife.iot.common.emeter get_realtime
* smartlife.iot.smartbulb.lightingservice get_light_state
* This is used to dump more information for proper tests, and will also allow better discovery in the future
This commit contains also some documentation updates and dropping click_datetime in favor of click's built-in datetime
* Docstring fixes
* Major API cleanup
Properties shall no more change the state of the device, this work in still in progress, the main goal being making the API more user-friendly and to make implementing new features simpler.
The newly deprecated functionality will remain working and will simply warn the user about deprecation.
Previously deprecated 'features' property and 'identify' method are now finally removed.
Deprecate and replace the following property setters:
* state with turn_on() and turn_off()
* hsv with set_hsv()
* color_temp with set_color_temp()
* brightness with set_brightness()
* led with set_led()
* alias with set_alias()
* mac with set_mac()
And getters:
* state with is_on and is_off
The {BULB,PLUG}_STATE_{ON,OFF} is simplified to STATE_ON and STATE_OFF, UNKNOWN state is removed.
These are now deprecated and will be removed in the future.
* is_on and is_off can be used to check for the state
* turn_on() and turn_off() for changing the device state.
Trying to use functionality not supported by the device will cause SmartDeviceExceptions instead of failing silently and/or returning None.
This includes, e.g., trying to set a color temperature on non-supported bulb.
ValueErrors are raised instead of SmartDeviceExceptions where appropriate (e.g. when trying to set an invalid hsv or brightness).
New enum type DeviceType is added to allow detecting device types without resorting to isinstance() calling. SmartDevice class' device_type property can be used to query the type. is_plug and is_bulb helpers are added.
* Cleanup tests and improve test coverage
* Make writing tests easier by sharing code for common implementations
* Instead of storing test data inside python files, dump-discover based information is used
* This will simplify adding new tests and remove code duplication
* fixtures are based on https://github.com/plasticrake/tplink-smarthome-simulator
* run black on newfakes
* Add HS300 tests and update SmartStrip API according to earlier changes, still WIP
* run black and avoid wildcard imports
* Black on conftest
* bump minimum required version to 3.5
* Rename fixture_tests to test_fixtures for autocollect
* fix typoed type to _type, black
* run black on several files with -79 to fix hound issues
* Fix broken merge on hue
* Fix tests (hue update, pass context to smartdevice), add is_strip property, disable emeter tests for HS300 until a solution for API is found.
* Fix old tests
* Run black on changed files
* Add real HS220 discovery, thanks to @poiyo
* add is_dimmable and is_variable_color_temp to smartdevice class, simplifies interfacing with homeassistant
* add KL120(US) fixture
* Add a simple query cache
This commit adds a simple query cache to speed up the process for users
requesting lots of different properties from the device, as done by the
cli tool as well as homeassistant.
The logic for caching is very simple:
1. A timestamp for last fetch for each module+command is stored alongside the response.
2. If the issued command starts with `get_` and the TTL has not expired, the cache result is returned.
3. Otherwise the cache for the whole corresponding module gets invalidated, the device will be queried and the result will be stored in the cache.
* add deprecation to tox.ini
* make tests pass again
* remove old tests, add flake8 to tox reqs
* run black against pyhs100 module, add it to precommit hooks, fix flake8 configuration to conform to black standards (https://ljvmiranda921.github.io/notebook/2018/06/21/precommits-using-black-and-flake8/)
* fix syntax
* cleanup conftest
2019-06-16 21:05:00 +00:00
|
|
|
"""Return hardware information.
|
Read all data from the device, disable double-encoding, implement more APIs, refactor querying, update README (#11)
* Read from socket until no data available, disable double string encoding
HS110 sends sometimes datagrams in chunks especially for get_daystat,
this patch makes it to read until there is no more data to be read.
As json.dumps() does JSON encoding already, there's no need to str()
the year or month either.
* Add cli.py, a simple script to query devices for debugging purposes.
* allow easier importing with from pyHS100 import SmartPlug
* move cli.py to examples, add short usage into README.md
* Implement more available APIs, refactor querying code.
This commit adds access to new properties, both read & write, while keeping the old one (mostly) intact.
Querying is refactored to be done inside _query_helper() method,
which unwraps results automatically and rises SmartPlugException() in case of errors.
Errors are to be handled by clients.
New features:
* Setting device alias (plug.alias = "name")
* led read & write
* icon read (doesn't seem to return anything without cloud support at least), write API is not known, throws an exception currently
* time read (returns datetime), time write implemented, but not working even when no error is returned from the device
* timezone read
* mac read & write, writing is untested for now.
Properties for easier access:
* hw_info: return hw-specific elements from sysinfo
* on_since: pretty-printed from sysinfo
* location: latitude and longitued from sysinfo
* rssi: rssi from sysinfo
* Update README.md with examples of available features.
* Handle comments from mweinelt
* Refactor state handling, use booleans instead of strings
* Fix issues raised during the review.
Following issues are addressed by this commit:
* All API is more or less commented (including return types, exceptions, ..)
* Converted state to use
* Added properties is_on, is_off for those who don't want to check against strings.
* Handled most issues reported by pylint.
* Adjusted _query_helper() to strip off err_code from the result object.
* Fixed broken format() syntax for string formattings.
* Fix ci woes plus one typo.
* Do initialization after changing device properties, fix nits.
2016-12-12 09:13:45 +00:00
|
|
|
|
2020-05-27 14:55:18 +00:00
|
|
|
This returns just a selection of sysinfo keys that are related to hardware.
|
Read all data from the device, disable double-encoding, implement more APIs, refactor querying, update README (#11)
* Read from socket until no data available, disable double string encoding
HS110 sends sometimes datagrams in chunks especially for get_daystat,
this patch makes it to read until there is no more data to be read.
As json.dumps() does JSON encoding already, there's no need to str()
the year or month either.
* Add cli.py, a simple script to query devices for debugging purposes.
* allow easier importing with from pyHS100 import SmartPlug
* move cli.py to examples, add short usage into README.md
* Implement more available APIs, refactor querying code.
This commit adds access to new properties, both read & write, while keeping the old one (mostly) intact.
Querying is refactored to be done inside _query_helper() method,
which unwraps results automatically and rises SmartPlugException() in case of errors.
Errors are to be handled by clients.
New features:
* Setting device alias (plug.alias = "name")
* led read & write
* icon read (doesn't seem to return anything without cloud support at least), write API is not known, throws an exception currently
* time read (returns datetime), time write implemented, but not working even when no error is returned from the device
* timezone read
* mac read & write, writing is untested for now.
Properties for easier access:
* hw_info: return hw-specific elements from sysinfo
* on_since: pretty-printed from sysinfo
* location: latitude and longitued from sysinfo
* rssi: rssi from sysinfo
* Update README.md with examples of available features.
* Handle comments from mweinelt
* Refactor state handling, use booleans instead of strings
* Fix issues raised during the review.
Following issues are addressed by this commit:
* All API is more or less commented (including return types, exceptions, ..)
* Converted state to use
* Added properties is_on, is_off for those who don't want to check against strings.
* Handled most issues reported by pylint.
* Adjusted _query_helper() to strip off err_code from the result object.
* Fixed broken format() syntax for string formattings.
* Fix ci woes plus one typo.
* Do initialization after changing device properties, fix nits.
2016-12-12 09:13:45 +00:00
|
|
|
"""
|
API and tests cleanup (#151)
* Add new cli commands: raw_command and dump_discover
- raw_command can be used to execute raw commands with given parameters
* Useful for testing new calls before implementing them properly
- dump_discover can be used to dump the device discovery information (into a file)
* The discovery is extended to request more modules and methods from devices
* smartlife.iot.dimmer get_dimmer_parameters
* smartlife.iot.common.emeter get_realtime
* smartlife.iot.smartbulb.lightingservice get_light_state
* This is used to dump more information for proper tests, and will also allow better discovery in the future
This commit contains also some documentation updates and dropping click_datetime in favor of click's built-in datetime
* Docstring fixes
* Major API cleanup
Properties shall no more change the state of the device, this work in still in progress, the main goal being making the API more user-friendly and to make implementing new features simpler.
The newly deprecated functionality will remain working and will simply warn the user about deprecation.
Previously deprecated 'features' property and 'identify' method are now finally removed.
Deprecate and replace the following property setters:
* state with turn_on() and turn_off()
* hsv with set_hsv()
* color_temp with set_color_temp()
* brightness with set_brightness()
* led with set_led()
* alias with set_alias()
* mac with set_mac()
And getters:
* state with is_on and is_off
The {BULB,PLUG}_STATE_{ON,OFF} is simplified to STATE_ON and STATE_OFF, UNKNOWN state is removed.
These are now deprecated and will be removed in the future.
* is_on and is_off can be used to check for the state
* turn_on() and turn_off() for changing the device state.
Trying to use functionality not supported by the device will cause SmartDeviceExceptions instead of failing silently and/or returning None.
This includes, e.g., trying to set a color temperature on non-supported bulb.
ValueErrors are raised instead of SmartDeviceExceptions where appropriate (e.g. when trying to set an invalid hsv or brightness).
New enum type DeviceType is added to allow detecting device types without resorting to isinstance() calling. SmartDevice class' device_type property can be used to query the type. is_plug and is_bulb helpers are added.
* Cleanup tests and improve test coverage
* Make writing tests easier by sharing code for common implementations
* Instead of storing test data inside python files, dump-discover based information is used
* This will simplify adding new tests and remove code duplication
* fixtures are based on https://github.com/plasticrake/tplink-smarthome-simulator
* run black on newfakes
* Add HS300 tests and update SmartStrip API according to earlier changes, still WIP
* run black and avoid wildcard imports
* Black on conftest
* bump minimum required version to 3.5
* Rename fixture_tests to test_fixtures for autocollect
* fix typoed type to _type, black
* run black on several files with -79 to fix hound issues
* Fix broken merge on hue
* Fix tests (hue update, pass context to smartdevice), add is_strip property, disable emeter tests for HS300 until a solution for API is found.
* Fix old tests
* Run black on changed files
* Add real HS220 discovery, thanks to @poiyo
* add is_dimmable and is_variable_color_temp to smartdevice class, simplifies interfacing with homeassistant
* add KL120(US) fixture
* Add a simple query cache
This commit adds a simple query cache to speed up the process for users
requesting lots of different properties from the device, as done by the
cli tool as well as homeassistant.
The logic for caching is very simple:
1. A timestamp for last fetch for each module+command is stored alongside the response.
2. If the issued command starts with `get_` and the TTL has not expired, the cache result is returned.
3. Otherwise the cache for the whole corresponding module gets invalidated, the device will be queried and the result will be stored in the cache.
* add deprecation to tox.ini
* make tests pass again
* remove old tests, add flake8 to tox reqs
* run black against pyhs100 module, add it to precommit hooks, fix flake8 configuration to conform to black standards (https://ljvmiranda921.github.io/notebook/2018/06/21/precommits-using-black-and-flake8/)
* fix syntax
* cleanup conftest
2019-06-16 21:05:00 +00:00
|
|
|
keys = [
|
|
|
|
"sw_ver",
|
|
|
|
"hw_ver",
|
|
|
|
"mac",
|
|
|
|
"mic_mac",
|
|
|
|
"type",
|
|
|
|
"mic_type",
|
|
|
|
"hwId",
|
|
|
|
"fwId",
|
|
|
|
"oemId",
|
|
|
|
"dev_name",
|
|
|
|
]
|
2023-11-20 13:17:10 +00:00
|
|
|
sys_info = self._sys_info
|
2019-11-11 16:21:23 +00:00
|
|
|
return {key: sys_info[key] for key in keys if key in sys_info}
|
Read all data from the device, disable double-encoding, implement more APIs, refactor querying, update README (#11)
* Read from socket until no data available, disable double string encoding
HS110 sends sometimes datagrams in chunks especially for get_daystat,
this patch makes it to read until there is no more data to be read.
As json.dumps() does JSON encoding already, there's no need to str()
the year or month either.
* Add cli.py, a simple script to query devices for debugging purposes.
* allow easier importing with from pyHS100 import SmartPlug
* move cli.py to examples, add short usage into README.md
* Implement more available APIs, refactor querying code.
This commit adds access to new properties, both read & write, while keeping the old one (mostly) intact.
Querying is refactored to be done inside _query_helper() method,
which unwraps results automatically and rises SmartPlugException() in case of errors.
Errors are to be handled by clients.
New features:
* Setting device alias (plug.alias = "name")
* led read & write
* icon read (doesn't seem to return anything without cloud support at least), write API is not known, throws an exception currently
* time read (returns datetime), time write implemented, but not working even when no error is returned from the device
* timezone read
* mac read & write, writing is untested for now.
Properties for easier access:
* hw_info: return hw-specific elements from sysinfo
* on_since: pretty-printed from sysinfo
* location: latitude and longitued from sysinfo
* rssi: rssi from sysinfo
* Update README.md with examples of available features.
* Handle comments from mweinelt
* Refactor state handling, use booleans instead of strings
* Fix issues raised during the review.
Following issues are addressed by this commit:
* All API is more or less commented (including return types, exceptions, ..)
* Converted state to use
* Added properties is_on, is_off for those who don't want to check against strings.
* Handled most issues reported by pylint.
* Adjusted _query_helper() to strip off err_code from the result object.
* Fixed broken format() syntax for string formattings.
* Fix ci woes plus one typo.
* Do initialization after changing device properties, fix nits.
2016-12-12 09:13:45 +00:00
|
|
|
|
2019-12-12 09:46:40 +00:00
|
|
|
@property # type: ignore
|
2019-11-15 16:48:36 +00:00
|
|
|
@requires_update
|
2024-04-17 13:39:24 +00:00
|
|
|
def location(self) -> dict:
|
2020-05-27 14:55:18 +00:00
|
|
|
"""Return geographical location."""
|
2023-11-20 13:17:10 +00:00
|
|
|
sys_info = self._sys_info
|
API and tests cleanup (#151)
* Add new cli commands: raw_command and dump_discover
- raw_command can be used to execute raw commands with given parameters
* Useful for testing new calls before implementing them properly
- dump_discover can be used to dump the device discovery information (into a file)
* The discovery is extended to request more modules and methods from devices
* smartlife.iot.dimmer get_dimmer_parameters
* smartlife.iot.common.emeter get_realtime
* smartlife.iot.smartbulb.lightingservice get_light_state
* This is used to dump more information for proper tests, and will also allow better discovery in the future
This commit contains also some documentation updates and dropping click_datetime in favor of click's built-in datetime
* Docstring fixes
* Major API cleanup
Properties shall no more change the state of the device, this work in still in progress, the main goal being making the API more user-friendly and to make implementing new features simpler.
The newly deprecated functionality will remain working and will simply warn the user about deprecation.
Previously deprecated 'features' property and 'identify' method are now finally removed.
Deprecate and replace the following property setters:
* state with turn_on() and turn_off()
* hsv with set_hsv()
* color_temp with set_color_temp()
* brightness with set_brightness()
* led with set_led()
* alias with set_alias()
* mac with set_mac()
And getters:
* state with is_on and is_off
The {BULB,PLUG}_STATE_{ON,OFF} is simplified to STATE_ON and STATE_OFF, UNKNOWN state is removed.
These are now deprecated and will be removed in the future.
* is_on and is_off can be used to check for the state
* turn_on() and turn_off() for changing the device state.
Trying to use functionality not supported by the device will cause SmartDeviceExceptions instead of failing silently and/or returning None.
This includes, e.g., trying to set a color temperature on non-supported bulb.
ValueErrors are raised instead of SmartDeviceExceptions where appropriate (e.g. when trying to set an invalid hsv or brightness).
New enum type DeviceType is added to allow detecting device types without resorting to isinstance() calling. SmartDevice class' device_type property can be used to query the type. is_plug and is_bulb helpers are added.
* Cleanup tests and improve test coverage
* Make writing tests easier by sharing code for common implementations
* Instead of storing test data inside python files, dump-discover based information is used
* This will simplify adding new tests and remove code duplication
* fixtures are based on https://github.com/plasticrake/tplink-smarthome-simulator
* run black on newfakes
* Add HS300 tests and update SmartStrip API according to earlier changes, still WIP
* run black and avoid wildcard imports
* Black on conftest
* bump minimum required version to 3.5
* Rename fixture_tests to test_fixtures for autocollect
* fix typoed type to _type, black
* run black on several files with -79 to fix hound issues
* Fix broken merge on hue
* Fix tests (hue update, pass context to smartdevice), add is_strip property, disable emeter tests for HS300 until a solution for API is found.
* Fix old tests
* Run black on changed files
* Add real HS220 discovery, thanks to @poiyo
* add is_dimmable and is_variable_color_temp to smartdevice class, simplifies interfacing with homeassistant
* add KL120(US) fixture
* Add a simple query cache
This commit adds a simple query cache to speed up the process for users
requesting lots of different properties from the device, as done by the
cli tool as well as homeassistant.
The logic for caching is very simple:
1. A timestamp for last fetch for each module+command is stored alongside the response.
2. If the issued command starts with `get_` and the TTL has not expired, the cache result is returned.
3. Otherwise the cache for the whole corresponding module gets invalidated, the device will be queried and the result will be stored in the cache.
* add deprecation to tox.ini
* make tests pass again
* remove old tests, add flake8 to tox reqs
* run black against pyhs100 module, add it to precommit hooks, fix flake8 configuration to conform to black standards (https://ljvmiranda921.github.io/notebook/2018/06/21/precommits-using-black-and-flake8/)
* fix syntax
* cleanup conftest
2019-06-16 21:05:00 +00:00
|
|
|
loc = {"latitude": None, "longitude": None}
|
2017-04-26 16:43:50 +00:00
|
|
|
|
2019-11-15 16:48:36 +00:00
|
|
|
if "latitude" in sys_info and "longitude" in sys_info:
|
|
|
|
loc["latitude"] = sys_info["latitude"]
|
|
|
|
loc["longitude"] = sys_info["longitude"]
|
|
|
|
elif "latitude_i" in sys_info and "longitude_i" in sys_info:
|
2021-09-24 21:25:43 +00:00
|
|
|
loc["latitude"] = sys_info["latitude_i"] / 10000
|
|
|
|
loc["longitude"] = sys_info["longitude_i"] / 10000
|
2017-04-26 16:43:50 +00:00
|
|
|
else:
|
2022-01-29 19:36:08 +00:00
|
|
|
_LOGGER.debug("Unsupported device location.")
|
2017-04-26 16:43:50 +00:00
|
|
|
|
|
|
|
return loc
|
Read all data from the device, disable double-encoding, implement more APIs, refactor querying, update README (#11)
* Read from socket until no data available, disable double string encoding
HS110 sends sometimes datagrams in chunks especially for get_daystat,
this patch makes it to read until there is no more data to be read.
As json.dumps() does JSON encoding already, there's no need to str()
the year or month either.
* Add cli.py, a simple script to query devices for debugging purposes.
* allow easier importing with from pyHS100 import SmartPlug
* move cli.py to examples, add short usage into README.md
* Implement more available APIs, refactor querying code.
This commit adds access to new properties, both read & write, while keeping the old one (mostly) intact.
Querying is refactored to be done inside _query_helper() method,
which unwraps results automatically and rises SmartPlugException() in case of errors.
Errors are to be handled by clients.
New features:
* Setting device alias (plug.alias = "name")
* led read & write
* icon read (doesn't seem to return anything without cloud support at least), write API is not known, throws an exception currently
* time read (returns datetime), time write implemented, but not working even when no error is returned from the device
* timezone read
* mac read & write, writing is untested for now.
Properties for easier access:
* hw_info: return hw-specific elements from sysinfo
* on_since: pretty-printed from sysinfo
* location: latitude and longitued from sysinfo
* rssi: rssi from sysinfo
* Update README.md with examples of available features.
* Handle comments from mweinelt
* Refactor state handling, use booleans instead of strings
* Fix issues raised during the review.
Following issues are addressed by this commit:
* All API is more or less commented (including return types, exceptions, ..)
* Converted state to use
* Added properties is_on, is_off for those who don't want to check against strings.
* Handled most issues reported by pylint.
* Adjusted _query_helper() to strip off err_code from the result object.
* Fixed broken format() syntax for string formattings.
* Fix ci woes plus one typo.
* Do initialization after changing device properties, fix nits.
2016-12-12 09:13:45 +00:00
|
|
|
|
2019-12-12 09:46:40 +00:00
|
|
|
@property # type: ignore
|
2019-11-15 16:48:36 +00:00
|
|
|
@requires_update
|
2024-04-17 13:39:24 +00:00
|
|
|
def rssi(self) -> int | None:
|
2022-07-17 17:19:09 +00:00
|
|
|
"""Return WiFi signal strength (rssi)."""
|
2023-11-20 13:17:10 +00:00
|
|
|
rssi = self._sys_info.get("rssi")
|
2021-09-23 22:24:44 +00:00
|
|
|
return None if rssi is None else int(rssi)
|
Read all data from the device, disable double-encoding, implement more APIs, refactor querying, update README (#11)
* Read from socket until no data available, disable double string encoding
HS110 sends sometimes datagrams in chunks especially for get_daystat,
this patch makes it to read until there is no more data to be read.
As json.dumps() does JSON encoding already, there's no need to str()
the year or month either.
* Add cli.py, a simple script to query devices for debugging purposes.
* allow easier importing with from pyHS100 import SmartPlug
* move cli.py to examples, add short usage into README.md
* Implement more available APIs, refactor querying code.
This commit adds access to new properties, both read & write, while keeping the old one (mostly) intact.
Querying is refactored to be done inside _query_helper() method,
which unwraps results automatically and rises SmartPlugException() in case of errors.
Errors are to be handled by clients.
New features:
* Setting device alias (plug.alias = "name")
* led read & write
* icon read (doesn't seem to return anything without cloud support at least), write API is not known, throws an exception currently
* time read (returns datetime), time write implemented, but not working even when no error is returned from the device
* timezone read
* mac read & write, writing is untested for now.
Properties for easier access:
* hw_info: return hw-specific elements from sysinfo
* on_since: pretty-printed from sysinfo
* location: latitude and longitued from sysinfo
* rssi: rssi from sysinfo
* Update README.md with examples of available features.
* Handle comments from mweinelt
* Refactor state handling, use booleans instead of strings
* Fix issues raised during the review.
Following issues are addressed by this commit:
* All API is more or less commented (including return types, exceptions, ..)
* Converted state to use
* Added properties is_on, is_off for those who don't want to check against strings.
* Handled most issues reported by pylint.
* Adjusted _query_helper() to strip off err_code from the result object.
* Fixed broken format() syntax for string formattings.
* Fix ci woes plus one typo.
* Do initialization after changing device properties, fix nits.
2016-12-12 09:13:45 +00:00
|
|
|
|
2019-12-12 09:46:40 +00:00
|
|
|
@property # type: ignore
|
2019-11-15 16:48:36 +00:00
|
|
|
@requires_update
|
|
|
|
def mac(self) -> str:
|
API and tests cleanup (#151)
* Add new cli commands: raw_command and dump_discover
- raw_command can be used to execute raw commands with given parameters
* Useful for testing new calls before implementing them properly
- dump_discover can be used to dump the device discovery information (into a file)
* The discovery is extended to request more modules and methods from devices
* smartlife.iot.dimmer get_dimmer_parameters
* smartlife.iot.common.emeter get_realtime
* smartlife.iot.smartbulb.lightingservice get_light_state
* This is used to dump more information for proper tests, and will also allow better discovery in the future
This commit contains also some documentation updates and dropping click_datetime in favor of click's built-in datetime
* Docstring fixes
* Major API cleanup
Properties shall no more change the state of the device, this work in still in progress, the main goal being making the API more user-friendly and to make implementing new features simpler.
The newly deprecated functionality will remain working and will simply warn the user about deprecation.
Previously deprecated 'features' property and 'identify' method are now finally removed.
Deprecate and replace the following property setters:
* state with turn_on() and turn_off()
* hsv with set_hsv()
* color_temp with set_color_temp()
* brightness with set_brightness()
* led with set_led()
* alias with set_alias()
* mac with set_mac()
And getters:
* state with is_on and is_off
The {BULB,PLUG}_STATE_{ON,OFF} is simplified to STATE_ON and STATE_OFF, UNKNOWN state is removed.
These are now deprecated and will be removed in the future.
* is_on and is_off can be used to check for the state
* turn_on() and turn_off() for changing the device state.
Trying to use functionality not supported by the device will cause SmartDeviceExceptions instead of failing silently and/or returning None.
This includes, e.g., trying to set a color temperature on non-supported bulb.
ValueErrors are raised instead of SmartDeviceExceptions where appropriate (e.g. when trying to set an invalid hsv or brightness).
New enum type DeviceType is added to allow detecting device types without resorting to isinstance() calling. SmartDevice class' device_type property can be used to query the type. is_plug and is_bulb helpers are added.
* Cleanup tests and improve test coverage
* Make writing tests easier by sharing code for common implementations
* Instead of storing test data inside python files, dump-discover based information is used
* This will simplify adding new tests and remove code duplication
* fixtures are based on https://github.com/plasticrake/tplink-smarthome-simulator
* run black on newfakes
* Add HS300 tests and update SmartStrip API according to earlier changes, still WIP
* run black and avoid wildcard imports
* Black on conftest
* bump minimum required version to 3.5
* Rename fixture_tests to test_fixtures for autocollect
* fix typoed type to _type, black
* run black on several files with -79 to fix hound issues
* Fix broken merge on hue
* Fix tests (hue update, pass context to smartdevice), add is_strip property, disable emeter tests for HS300 until a solution for API is found.
* Fix old tests
* Run black on changed files
* Add real HS220 discovery, thanks to @poiyo
* add is_dimmable and is_variable_color_temp to smartdevice class, simplifies interfacing with homeassistant
* add KL120(US) fixture
* Add a simple query cache
This commit adds a simple query cache to speed up the process for users
requesting lots of different properties from the device, as done by the
cli tool as well as homeassistant.
The logic for caching is very simple:
1. A timestamp for last fetch for each module+command is stored alongside the response.
2. If the issued command starts with `get_` and the TTL has not expired, the cache result is returned.
3. Otherwise the cache for the whole corresponding module gets invalidated, the device will be queried and the result will be stored in the cache.
* add deprecation to tox.ini
* make tests pass again
* remove old tests, add flake8 to tox reqs
* run black against pyhs100 module, add it to precommit hooks, fix flake8 configuration to conform to black standards (https://ljvmiranda921.github.io/notebook/2018/06/21/precommits-using-black-and-flake8/)
* fix syntax
* cleanup conftest
2019-06-16 21:05:00 +00:00
|
|
|
"""Return mac address.
|
Read all data from the device, disable double-encoding, implement more APIs, refactor querying, update README (#11)
* Read from socket until no data available, disable double string encoding
HS110 sends sometimes datagrams in chunks especially for get_daystat,
this patch makes it to read until there is no more data to be read.
As json.dumps() does JSON encoding already, there's no need to str()
the year or month either.
* Add cli.py, a simple script to query devices for debugging purposes.
* allow easier importing with from pyHS100 import SmartPlug
* move cli.py to examples, add short usage into README.md
* Implement more available APIs, refactor querying code.
This commit adds access to new properties, both read & write, while keeping the old one (mostly) intact.
Querying is refactored to be done inside _query_helper() method,
which unwraps results automatically and rises SmartPlugException() in case of errors.
Errors are to be handled by clients.
New features:
* Setting device alias (plug.alias = "name")
* led read & write
* icon read (doesn't seem to return anything without cloud support at least), write API is not known, throws an exception currently
* time read (returns datetime), time write implemented, but not working even when no error is returned from the device
* timezone read
* mac read & write, writing is untested for now.
Properties for easier access:
* hw_info: return hw-specific elements from sysinfo
* on_since: pretty-printed from sysinfo
* location: latitude and longitued from sysinfo
* rssi: rssi from sysinfo
* Update README.md with examples of available features.
* Handle comments from mweinelt
* Refactor state handling, use booleans instead of strings
* Fix issues raised during the review.
Following issues are addressed by this commit:
* All API is more or less commented (including return types, exceptions, ..)
* Converted state to use
* Added properties is_on, is_off for those who don't want to check against strings.
* Handled most issues reported by pylint.
* Adjusted _query_helper() to strip off err_code from the result object.
* Fixed broken format() syntax for string formattings.
* Fix ci woes plus one typo.
* Do initialization after changing device properties, fix nits.
2016-12-12 09:13:45 +00:00
|
|
|
|
|
|
|
:return: mac address in hexadecimal with colons, e.g. 01:23:45:67:89:ab
|
|
|
|
"""
|
2023-11-20 13:17:10 +00:00
|
|
|
sys_info = self._sys_info
|
2021-05-12 13:07:53 +00:00
|
|
|
mac = sys_info.get("mac", sys_info.get("mic_mac"))
|
|
|
|
if not mac:
|
2024-02-21 15:52:55 +00:00
|
|
|
raise KasaException(
|
2021-05-12 13:07:53 +00:00
|
|
|
"Unknown mac, please submit a bug report with sys_info output."
|
2019-11-11 18:16:55 +00:00
|
|
|
)
|
2023-11-20 13:17:10 +00:00
|
|
|
mac = mac.replace("-", ":")
|
|
|
|
# Format a mac that has no colons (usually from mic_mac field)
|
2021-05-12 13:07:53 +00:00
|
|
|
if ":" not in mac:
|
|
|
|
mac = ":".join(format(s, "02x") for s in bytes.fromhex(mac))
|
|
|
|
|
|
|
|
return mac
|
Read all data from the device, disable double-encoding, implement more APIs, refactor querying, update README (#11)
* Read from socket until no data available, disable double string encoding
HS110 sends sometimes datagrams in chunks especially for get_daystat,
this patch makes it to read until there is no more data to be read.
As json.dumps() does JSON encoding already, there's no need to str()
the year or month either.
* Add cli.py, a simple script to query devices for debugging purposes.
* allow easier importing with from pyHS100 import SmartPlug
* move cli.py to examples, add short usage into README.md
* Implement more available APIs, refactor querying code.
This commit adds access to new properties, both read & write, while keeping the old one (mostly) intact.
Querying is refactored to be done inside _query_helper() method,
which unwraps results automatically and rises SmartPlugException() in case of errors.
Errors are to be handled by clients.
New features:
* Setting device alias (plug.alias = "name")
* led read & write
* icon read (doesn't seem to return anything without cloud support at least), write API is not known, throws an exception currently
* time read (returns datetime), time write implemented, but not working even when no error is returned from the device
* timezone read
* mac read & write, writing is untested for now.
Properties for easier access:
* hw_info: return hw-specific elements from sysinfo
* on_since: pretty-printed from sysinfo
* location: latitude and longitued from sysinfo
* rssi: rssi from sysinfo
* Update README.md with examples of available features.
* Handle comments from mweinelt
* Refactor state handling, use booleans instead of strings
* Fix issues raised during the review.
Following issues are addressed by this commit:
* All API is more or less commented (including return types, exceptions, ..)
* Converted state to use
* Added properties is_on, is_off for those who don't want to check against strings.
* Handled most issues reported by pylint.
* Adjusted _query_helper() to strip off err_code from the result object.
* Fixed broken format() syntax for string formattings.
* Fix ci woes plus one typo.
* Do initialization after changing device properties, fix nits.
2016-12-12 09:13:45 +00:00
|
|
|
|
2024-11-10 18:55:13 +00:00
|
|
|
async def set_mac(self, mac: str) -> dict:
|
API and tests cleanup (#151)
* Add new cli commands: raw_command and dump_discover
- raw_command can be used to execute raw commands with given parameters
* Useful for testing new calls before implementing them properly
- dump_discover can be used to dump the device discovery information (into a file)
* The discovery is extended to request more modules and methods from devices
* smartlife.iot.dimmer get_dimmer_parameters
* smartlife.iot.common.emeter get_realtime
* smartlife.iot.smartbulb.lightingservice get_light_state
* This is used to dump more information for proper tests, and will also allow better discovery in the future
This commit contains also some documentation updates and dropping click_datetime in favor of click's built-in datetime
* Docstring fixes
* Major API cleanup
Properties shall no more change the state of the device, this work in still in progress, the main goal being making the API more user-friendly and to make implementing new features simpler.
The newly deprecated functionality will remain working and will simply warn the user about deprecation.
Previously deprecated 'features' property and 'identify' method are now finally removed.
Deprecate and replace the following property setters:
* state with turn_on() and turn_off()
* hsv with set_hsv()
* color_temp with set_color_temp()
* brightness with set_brightness()
* led with set_led()
* alias with set_alias()
* mac with set_mac()
And getters:
* state with is_on and is_off
The {BULB,PLUG}_STATE_{ON,OFF} is simplified to STATE_ON and STATE_OFF, UNKNOWN state is removed.
These are now deprecated and will be removed in the future.
* is_on and is_off can be used to check for the state
* turn_on() and turn_off() for changing the device state.
Trying to use functionality not supported by the device will cause SmartDeviceExceptions instead of failing silently and/or returning None.
This includes, e.g., trying to set a color temperature on non-supported bulb.
ValueErrors are raised instead of SmartDeviceExceptions where appropriate (e.g. when trying to set an invalid hsv or brightness).
New enum type DeviceType is added to allow detecting device types without resorting to isinstance() calling. SmartDevice class' device_type property can be used to query the type. is_plug and is_bulb helpers are added.
* Cleanup tests and improve test coverage
* Make writing tests easier by sharing code for common implementations
* Instead of storing test data inside python files, dump-discover based information is used
* This will simplify adding new tests and remove code duplication
* fixtures are based on https://github.com/plasticrake/tplink-smarthome-simulator
* run black on newfakes
* Add HS300 tests and update SmartStrip API according to earlier changes, still WIP
* run black and avoid wildcard imports
* Black on conftest
* bump minimum required version to 3.5
* Rename fixture_tests to test_fixtures for autocollect
* fix typoed type to _type, black
* run black on several files with -79 to fix hound issues
* Fix broken merge on hue
* Fix tests (hue update, pass context to smartdevice), add is_strip property, disable emeter tests for HS300 until a solution for API is found.
* Fix old tests
* Run black on changed files
* Add real HS220 discovery, thanks to @poiyo
* add is_dimmable and is_variable_color_temp to smartdevice class, simplifies interfacing with homeassistant
* add KL120(US) fixture
* Add a simple query cache
This commit adds a simple query cache to speed up the process for users
requesting lots of different properties from the device, as done by the
cli tool as well as homeassistant.
The logic for caching is very simple:
1. A timestamp for last fetch for each module+command is stored alongside the response.
2. If the issued command starts with `get_` and the TTL has not expired, the cache result is returned.
3. Otherwise the cache for the whole corresponding module gets invalidated, the device will be queried and the result will be stored in the cache.
* add deprecation to tox.ini
* make tests pass again
* remove old tests, add flake8 to tox reqs
* run black against pyhs100 module, add it to precommit hooks, fix flake8 configuration to conform to black standards (https://ljvmiranda921.github.io/notebook/2018/06/21/precommits-using-black-and-flake8/)
* fix syntax
* cleanup conftest
2019-06-16 21:05:00 +00:00
|
|
|
"""Set the mac address.
|
Read all data from the device, disable double-encoding, implement more APIs, refactor querying, update README (#11)
* Read from socket until no data available, disable double string encoding
HS110 sends sometimes datagrams in chunks especially for get_daystat,
this patch makes it to read until there is no more data to be read.
As json.dumps() does JSON encoding already, there's no need to str()
the year or month either.
* Add cli.py, a simple script to query devices for debugging purposes.
* allow easier importing with from pyHS100 import SmartPlug
* move cli.py to examples, add short usage into README.md
* Implement more available APIs, refactor querying code.
This commit adds access to new properties, both read & write, while keeping the old one (mostly) intact.
Querying is refactored to be done inside _query_helper() method,
which unwraps results automatically and rises SmartPlugException() in case of errors.
Errors are to be handled by clients.
New features:
* Setting device alias (plug.alias = "name")
* led read & write
* icon read (doesn't seem to return anything without cloud support at least), write API is not known, throws an exception currently
* time read (returns datetime), time write implemented, but not working even when no error is returned from the device
* timezone read
* mac read & write, writing is untested for now.
Properties for easier access:
* hw_info: return hw-specific elements from sysinfo
* on_since: pretty-printed from sysinfo
* location: latitude and longitued from sysinfo
* rssi: rssi from sysinfo
* Update README.md with examples of available features.
* Handle comments from mweinelt
* Refactor state handling, use booleans instead of strings
* Fix issues raised during the review.
Following issues are addressed by this commit:
* All API is more or less commented (including return types, exceptions, ..)
* Converted state to use
* Added properties is_on, is_off for those who don't want to check against strings.
* Handled most issues reported by pylint.
* Adjusted _query_helper() to strip off err_code from the result object.
* Fixed broken format() syntax for string formattings.
* Fix ci woes plus one typo.
* Do initialization after changing device properties, fix nits.
2016-12-12 09:13:45 +00:00
|
|
|
|
|
|
|
:param str mac: mac in hexadecimal with colons, e.g. 01:23:45:67:89:ab
|
|
|
|
"""
|
2020-05-24 15:57:54 +00:00
|
|
|
return await self._query_helper("system", "set_mac_addr", {"mac": mac})
|
2017-01-17 13:38:23 +00:00
|
|
|
|
2020-06-30 00:29:52 +00:00
|
|
|
async def reboot(self, delay: int = 1) -> None:
|
API and tests cleanup (#151)
* Add new cli commands: raw_command and dump_discover
- raw_command can be used to execute raw commands with given parameters
* Useful for testing new calls before implementing them properly
- dump_discover can be used to dump the device discovery information (into a file)
* The discovery is extended to request more modules and methods from devices
* smartlife.iot.dimmer get_dimmer_parameters
* smartlife.iot.common.emeter get_realtime
* smartlife.iot.smartbulb.lightingservice get_light_state
* This is used to dump more information for proper tests, and will also allow better discovery in the future
This commit contains also some documentation updates and dropping click_datetime in favor of click's built-in datetime
* Docstring fixes
* Major API cleanup
Properties shall no more change the state of the device, this work in still in progress, the main goal being making the API more user-friendly and to make implementing new features simpler.
The newly deprecated functionality will remain working and will simply warn the user about deprecation.
Previously deprecated 'features' property and 'identify' method are now finally removed.
Deprecate and replace the following property setters:
* state with turn_on() and turn_off()
* hsv with set_hsv()
* color_temp with set_color_temp()
* brightness with set_brightness()
* led with set_led()
* alias with set_alias()
* mac with set_mac()
And getters:
* state with is_on and is_off
The {BULB,PLUG}_STATE_{ON,OFF} is simplified to STATE_ON and STATE_OFF, UNKNOWN state is removed.
These are now deprecated and will be removed in the future.
* is_on and is_off can be used to check for the state
* turn_on() and turn_off() for changing the device state.
Trying to use functionality not supported by the device will cause SmartDeviceExceptions instead of failing silently and/or returning None.
This includes, e.g., trying to set a color temperature on non-supported bulb.
ValueErrors are raised instead of SmartDeviceExceptions where appropriate (e.g. when trying to set an invalid hsv or brightness).
New enum type DeviceType is added to allow detecting device types without resorting to isinstance() calling. SmartDevice class' device_type property can be used to query the type. is_plug and is_bulb helpers are added.
* Cleanup tests and improve test coverage
* Make writing tests easier by sharing code for common implementations
* Instead of storing test data inside python files, dump-discover based information is used
* This will simplify adding new tests and remove code duplication
* fixtures are based on https://github.com/plasticrake/tplink-smarthome-simulator
* run black on newfakes
* Add HS300 tests and update SmartStrip API according to earlier changes, still WIP
* run black and avoid wildcard imports
* Black on conftest
* bump minimum required version to 3.5
* Rename fixture_tests to test_fixtures for autocollect
* fix typoed type to _type, black
* run black on several files with -79 to fix hound issues
* Fix broken merge on hue
* Fix tests (hue update, pass context to smartdevice), add is_strip property, disable emeter tests for HS300 until a solution for API is found.
* Fix old tests
* Run black on changed files
* Add real HS220 discovery, thanks to @poiyo
* add is_dimmable and is_variable_color_temp to smartdevice class, simplifies interfacing with homeassistant
* add KL120(US) fixture
* Add a simple query cache
This commit adds a simple query cache to speed up the process for users
requesting lots of different properties from the device, as done by the
cli tool as well as homeassistant.
The logic for caching is very simple:
1. A timestamp for last fetch for each module+command is stored alongside the response.
2. If the issued command starts with `get_` and the TTL has not expired, the cache result is returned.
3. Otherwise the cache for the whole corresponding module gets invalidated, the device will be queried and the result will be stored in the cache.
* add deprecation to tox.ini
* make tests pass again
* remove old tests, add flake8 to tox reqs
* run black against pyhs100 module, add it to precommit hooks, fix flake8 configuration to conform to black standards (https://ljvmiranda921.github.io/notebook/2018/06/21/precommits-using-black-and-flake8/)
* fix syntax
* cleanup conftest
2019-06-16 21:05:00 +00:00
|
|
|
"""Reboot the device.
|
|
|
|
|
|
|
|
Note that giving a delay of zero causes this to block,
|
|
|
|
as the device reboots immediately without responding to the call.
|
2018-09-08 14:11:58 +00:00
|
|
|
"""
|
2019-11-11 16:21:23 +00:00
|
|
|
await self._query_helper("system", "reboot", {"delay": delay})
|
2018-09-08 14:11:58 +00:00
|
|
|
|
2024-09-27 16:42:22 +00:00
|
|
|
async def factory_reset(self) -> None:
|
|
|
|
"""Reset device back to factory settings.
|
|
|
|
|
|
|
|
Note, this does not downgrade the firmware.
|
|
|
|
"""
|
|
|
|
await self._query_helper("system", "reset")
|
|
|
|
|
2024-04-17 13:39:24 +00:00
|
|
|
async def turn_off(self, **kwargs) -> dict:
|
API and tests cleanup (#151)
* Add new cli commands: raw_command and dump_discover
- raw_command can be used to execute raw commands with given parameters
* Useful for testing new calls before implementing them properly
- dump_discover can be used to dump the device discovery information (into a file)
* The discovery is extended to request more modules and methods from devices
* smartlife.iot.dimmer get_dimmer_parameters
* smartlife.iot.common.emeter get_realtime
* smartlife.iot.smartbulb.lightingservice get_light_state
* This is used to dump more information for proper tests, and will also allow better discovery in the future
This commit contains also some documentation updates and dropping click_datetime in favor of click's built-in datetime
* Docstring fixes
* Major API cleanup
Properties shall no more change the state of the device, this work in still in progress, the main goal being making the API more user-friendly and to make implementing new features simpler.
The newly deprecated functionality will remain working and will simply warn the user about deprecation.
Previously deprecated 'features' property and 'identify' method are now finally removed.
Deprecate and replace the following property setters:
* state with turn_on() and turn_off()
* hsv with set_hsv()
* color_temp with set_color_temp()
* brightness with set_brightness()
* led with set_led()
* alias with set_alias()
* mac with set_mac()
And getters:
* state with is_on and is_off
The {BULB,PLUG}_STATE_{ON,OFF} is simplified to STATE_ON and STATE_OFF, UNKNOWN state is removed.
These are now deprecated and will be removed in the future.
* is_on and is_off can be used to check for the state
* turn_on() and turn_off() for changing the device state.
Trying to use functionality not supported by the device will cause SmartDeviceExceptions instead of failing silently and/or returning None.
This includes, e.g., trying to set a color temperature on non-supported bulb.
ValueErrors are raised instead of SmartDeviceExceptions where appropriate (e.g. when trying to set an invalid hsv or brightness).
New enum type DeviceType is added to allow detecting device types without resorting to isinstance() calling. SmartDevice class' device_type property can be used to query the type. is_plug and is_bulb helpers are added.
* Cleanup tests and improve test coverage
* Make writing tests easier by sharing code for common implementations
* Instead of storing test data inside python files, dump-discover based information is used
* This will simplify adding new tests and remove code duplication
* fixtures are based on https://github.com/plasticrake/tplink-smarthome-simulator
* run black on newfakes
* Add HS300 tests and update SmartStrip API according to earlier changes, still WIP
* run black and avoid wildcard imports
* Black on conftest
* bump minimum required version to 3.5
* Rename fixture_tests to test_fixtures for autocollect
* fix typoed type to _type, black
* run black on several files with -79 to fix hound issues
* Fix broken merge on hue
* Fix tests (hue update, pass context to smartdevice), add is_strip property, disable emeter tests for HS300 until a solution for API is found.
* Fix old tests
* Run black on changed files
* Add real HS220 discovery, thanks to @poiyo
* add is_dimmable and is_variable_color_temp to smartdevice class, simplifies interfacing with homeassistant
* add KL120(US) fixture
* Add a simple query cache
This commit adds a simple query cache to speed up the process for users
requesting lots of different properties from the device, as done by the
cli tool as well as homeassistant.
The logic for caching is very simple:
1. A timestamp for last fetch for each module+command is stored alongside the response.
2. If the issued command starts with `get_` and the TTL has not expired, the cache result is returned.
3. Otherwise the cache for the whole corresponding module gets invalidated, the device will be queried and the result will be stored in the cache.
* add deprecation to tox.ini
* make tests pass again
* remove old tests, add flake8 to tox reqs
* run black against pyhs100 module, add it to precommit hooks, fix flake8 configuration to conform to black standards (https://ljvmiranda921.github.io/notebook/2018/06/21/precommits-using-black-and-flake8/)
* fix syntax
* cleanup conftest
2019-06-16 21:05:00 +00:00
|
|
|
"""Turn off the device."""
|
2017-04-24 17:28:22 +00:00
|
|
|
raise NotImplementedError("Device subclass needs to implement this.")
|
|
|
|
|
2024-11-10 18:55:13 +00:00
|
|
|
async def turn_on(self, **kwargs) -> dict:
|
API and tests cleanup (#151)
* Add new cli commands: raw_command and dump_discover
- raw_command can be used to execute raw commands with given parameters
* Useful for testing new calls before implementing them properly
- dump_discover can be used to dump the device discovery information (into a file)
* The discovery is extended to request more modules and methods from devices
* smartlife.iot.dimmer get_dimmer_parameters
* smartlife.iot.common.emeter get_realtime
* smartlife.iot.smartbulb.lightingservice get_light_state
* This is used to dump more information for proper tests, and will also allow better discovery in the future
This commit contains also some documentation updates and dropping click_datetime in favor of click's built-in datetime
* Docstring fixes
* Major API cleanup
Properties shall no more change the state of the device, this work in still in progress, the main goal being making the API more user-friendly and to make implementing new features simpler.
The newly deprecated functionality will remain working and will simply warn the user about deprecation.
Previously deprecated 'features' property and 'identify' method are now finally removed.
Deprecate and replace the following property setters:
* state with turn_on() and turn_off()
* hsv with set_hsv()
* color_temp with set_color_temp()
* brightness with set_brightness()
* led with set_led()
* alias with set_alias()
* mac with set_mac()
And getters:
* state with is_on and is_off
The {BULB,PLUG}_STATE_{ON,OFF} is simplified to STATE_ON and STATE_OFF, UNKNOWN state is removed.
These are now deprecated and will be removed in the future.
* is_on and is_off can be used to check for the state
* turn_on() and turn_off() for changing the device state.
Trying to use functionality not supported by the device will cause SmartDeviceExceptions instead of failing silently and/or returning None.
This includes, e.g., trying to set a color temperature on non-supported bulb.
ValueErrors are raised instead of SmartDeviceExceptions where appropriate (e.g. when trying to set an invalid hsv or brightness).
New enum type DeviceType is added to allow detecting device types without resorting to isinstance() calling. SmartDevice class' device_type property can be used to query the type. is_plug and is_bulb helpers are added.
* Cleanup tests and improve test coverage
* Make writing tests easier by sharing code for common implementations
* Instead of storing test data inside python files, dump-discover based information is used
* This will simplify adding new tests and remove code duplication
* fixtures are based on https://github.com/plasticrake/tplink-smarthome-simulator
* run black on newfakes
* Add HS300 tests and update SmartStrip API according to earlier changes, still WIP
* run black and avoid wildcard imports
* Black on conftest
* bump minimum required version to 3.5
* Rename fixture_tests to test_fixtures for autocollect
* fix typoed type to _type, black
* run black on several files with -79 to fix hound issues
* Fix broken merge on hue
* Fix tests (hue update, pass context to smartdevice), add is_strip property, disable emeter tests for HS300 until a solution for API is found.
* Fix old tests
* Run black on changed files
* Add real HS220 discovery, thanks to @poiyo
* add is_dimmable and is_variable_color_temp to smartdevice class, simplifies interfacing with homeassistant
* add KL120(US) fixture
* Add a simple query cache
This commit adds a simple query cache to speed up the process for users
requesting lots of different properties from the device, as done by the
cli tool as well as homeassistant.
The logic for caching is very simple:
1. A timestamp for last fetch for each module+command is stored alongside the response.
2. If the issued command starts with `get_` and the TTL has not expired, the cache result is returned.
3. Otherwise the cache for the whole corresponding module gets invalidated, the device will be queried and the result will be stored in the cache.
* add deprecation to tox.ini
* make tests pass again
* remove old tests, add flake8 to tox reqs
* run black against pyhs100 module, add it to precommit hooks, fix flake8 configuration to conform to black standards (https://ljvmiranda921.github.io/notebook/2018/06/21/precommits-using-black-and-flake8/)
* fix syntax
* cleanup conftest
2019-06-16 21:05:00 +00:00
|
|
|
"""Turn device on."""
|
2017-04-24 17:28:22 +00:00
|
|
|
raise NotImplementedError("Device subclass needs to implement this.")
|
|
|
|
|
2019-12-12 09:46:40 +00:00
|
|
|
@property # type: ignore
|
2019-11-15 16:48:36 +00:00
|
|
|
@requires_update
|
|
|
|
def is_on(self) -> bool:
|
2020-05-27 14:55:18 +00:00
|
|
|
"""Return True if the device is on."""
|
2017-04-24 17:28:22 +00:00
|
|
|
raise NotImplementedError("Device subclass needs to implement this.")
|
|
|
|
|
2024-11-10 18:55:13 +00:00
|
|
|
async def set_state(self, on: bool) -> dict:
|
2024-05-22 14:52:00 +00:00
|
|
|
"""Set the device state."""
|
|
|
|
if on:
|
|
|
|
return await self.turn_on()
|
|
|
|
else:
|
|
|
|
return await self.turn_off()
|
|
|
|
|
2020-04-24 14:47:57 +00:00
|
|
|
@property # type: ignore
|
|
|
|
@requires_update
|
2024-04-17 13:39:24 +00:00
|
|
|
def on_since(self) -> datetime | None:
|
2024-10-02 14:04:16 +00:00
|
|
|
"""Return the time that the device was turned on or None if turned off.
|
2020-04-24 14:47:57 +00:00
|
|
|
|
2024-10-02 14:04:16 +00:00
|
|
|
This returns a cached value if the device reported value difference is under
|
|
|
|
five seconds to avoid device-caused jitter.
|
|
|
|
"""
|
|
|
|
if self.is_off or "on_time" not in self._sys_info:
|
|
|
|
self._on_since = None
|
2020-04-24 14:47:57 +00:00
|
|
|
return None
|
|
|
|
|
2023-11-20 13:17:10 +00:00
|
|
|
on_time = self._sys_info["on_time"]
|
2020-04-24 14:47:57 +00:00
|
|
|
|
2024-10-08 07:16:51 +00:00
|
|
|
on_since = self.time - timedelta(seconds=on_time)
|
2024-10-02 14:04:16 +00:00
|
|
|
if not self._on_since or timedelta(
|
|
|
|
seconds=0
|
|
|
|
) < on_since - self._on_since > timedelta(seconds=5):
|
|
|
|
self._on_since = on_since
|
|
|
|
return self._on_since
|
2020-04-24 14:47:57 +00:00
|
|
|
|
2020-01-12 23:17:45 +00:00
|
|
|
@property # type: ignore
|
|
|
|
@requires_update
|
|
|
|
def device_id(self) -> str:
|
|
|
|
"""Return unique ID for the device.
|
|
|
|
|
2021-09-19 21:45:48 +00:00
|
|
|
If not overridden, this is the MAC address of the device.
|
|
|
|
Individual sockets on strips will override this.
|
2020-01-12 23:17:45 +00:00
|
|
|
"""
|
|
|
|
return self.mac
|
|
|
|
|
2024-04-17 13:39:24 +00:00
|
|
|
async def wifi_scan(self) -> list[WifiNetwork]: # noqa: D202
|
2020-04-20 16:57:33 +00:00
|
|
|
"""Scan for available wifi networks."""
|
2020-04-24 14:57:04 +00:00
|
|
|
|
2024-11-10 18:55:13 +00:00
|
|
|
async def _scan(target: str) -> dict:
|
2020-04-24 14:57:04 +00:00
|
|
|
return await self._query_helper(target, "get_scaninfo", {"refresh": 1})
|
|
|
|
|
|
|
|
try:
|
|
|
|
info = await _scan("netif")
|
2024-02-21 15:52:55 +00:00
|
|
|
except KasaException as ex:
|
2020-04-24 14:57:04 +00:00
|
|
|
_LOGGER.debug(
|
|
|
|
"Unable to scan using 'netif', retrying with 'softaponboarding': %s", ex
|
|
|
|
)
|
|
|
|
info = await _scan("smartlife.iot.common.softaponboarding")
|
|
|
|
|
2020-04-20 16:57:33 +00:00
|
|
|
if "ap_list" not in info:
|
2024-11-10 18:55:13 +00:00
|
|
|
raise KasaException(f"Invalid response for wifi scan: {info}")
|
2020-04-20 16:57:33 +00:00
|
|
|
|
|
|
|
return [WifiNetwork(**x) for x in info["ap_list"]]
|
|
|
|
|
2024-11-10 18:55:13 +00:00
|
|
|
async def wifi_join(self, ssid: str, password: str, keytype: str = "3") -> dict: # noqa: D202
|
2020-04-20 16:57:33 +00:00
|
|
|
"""Join the given wifi network.
|
|
|
|
|
|
|
|
If joining the network fails, the device will return to AP mode after a while.
|
|
|
|
"""
|
2020-04-24 14:57:04 +00:00
|
|
|
|
2024-11-10 18:55:13 +00:00
|
|
|
async def _join(target: str, payload: dict) -> dict:
|
2020-04-24 14:57:04 +00:00
|
|
|
return await self._query_helper(target, "set_stainfo", payload)
|
|
|
|
|
2024-01-03 21:45:16 +00:00
|
|
|
payload = {"ssid": ssid, "password": password, "key_type": int(keytype)}
|
2020-04-24 14:57:04 +00:00
|
|
|
try:
|
|
|
|
return await _join("netif", payload)
|
2024-02-21 15:52:55 +00:00
|
|
|
except KasaException as ex:
|
2020-04-24 14:57:04 +00:00
|
|
|
_LOGGER.debug(
|
|
|
|
"Unable to join using 'netif', retrying with 'softaponboarding': %s", ex
|
|
|
|
)
|
|
|
|
return await _join("smartlife.iot.common.softaponboarding", payload)
|
2020-04-20 16:57:33 +00:00
|
|
|
|
API and tests cleanup (#151)
* Add new cli commands: raw_command and dump_discover
- raw_command can be used to execute raw commands with given parameters
* Useful for testing new calls before implementing them properly
- dump_discover can be used to dump the device discovery information (into a file)
* The discovery is extended to request more modules and methods from devices
* smartlife.iot.dimmer get_dimmer_parameters
* smartlife.iot.common.emeter get_realtime
* smartlife.iot.smartbulb.lightingservice get_light_state
* This is used to dump more information for proper tests, and will also allow better discovery in the future
This commit contains also some documentation updates and dropping click_datetime in favor of click's built-in datetime
* Docstring fixes
* Major API cleanup
Properties shall no more change the state of the device, this work in still in progress, the main goal being making the API more user-friendly and to make implementing new features simpler.
The newly deprecated functionality will remain working and will simply warn the user about deprecation.
Previously deprecated 'features' property and 'identify' method are now finally removed.
Deprecate and replace the following property setters:
* state with turn_on() and turn_off()
* hsv with set_hsv()
* color_temp with set_color_temp()
* brightness with set_brightness()
* led with set_led()
* alias with set_alias()
* mac with set_mac()
And getters:
* state with is_on and is_off
The {BULB,PLUG}_STATE_{ON,OFF} is simplified to STATE_ON and STATE_OFF, UNKNOWN state is removed.
These are now deprecated and will be removed in the future.
* is_on and is_off can be used to check for the state
* turn_on() and turn_off() for changing the device state.
Trying to use functionality not supported by the device will cause SmartDeviceExceptions instead of failing silently and/or returning None.
This includes, e.g., trying to set a color temperature on non-supported bulb.
ValueErrors are raised instead of SmartDeviceExceptions where appropriate (e.g. when trying to set an invalid hsv or brightness).
New enum type DeviceType is added to allow detecting device types without resorting to isinstance() calling. SmartDevice class' device_type property can be used to query the type. is_plug and is_bulb helpers are added.
* Cleanup tests and improve test coverage
* Make writing tests easier by sharing code for common implementations
* Instead of storing test data inside python files, dump-discover based information is used
* This will simplify adding new tests and remove code duplication
* fixtures are based on https://github.com/plasticrake/tplink-smarthome-simulator
* run black on newfakes
* Add HS300 tests and update SmartStrip API according to earlier changes, still WIP
* run black and avoid wildcard imports
* Black on conftest
* bump minimum required version to 3.5
* Rename fixture_tests to test_fixtures for autocollect
* fix typoed type to _type, black
* run black on several files with -79 to fix hound issues
* Fix broken merge on hue
* Fix tests (hue update, pass context to smartdevice), add is_strip property, disable emeter tests for HS300 until a solution for API is found.
* Fix old tests
* Run black on changed files
* Add real HS220 discovery, thanks to @poiyo
* add is_dimmable and is_variable_color_temp to smartdevice class, simplifies interfacing with homeassistant
* add KL120(US) fixture
* Add a simple query cache
This commit adds a simple query cache to speed up the process for users
requesting lots of different properties from the device, as done by the
cli tool as well as homeassistant.
The logic for caching is very simple:
1. A timestamp for last fetch for each module+command is stored alongside the response.
2. If the issued command starts with `get_` and the TTL has not expired, the cache result is returned.
3. Otherwise the cache for the whole corresponding module gets invalidated, the device will be queried and the result will be stored in the cache.
* add deprecation to tox.ini
* make tests pass again
* remove old tests, add flake8 to tox reqs
* run black against pyhs100 module, add it to precommit hooks, fix flake8 configuration to conform to black standards (https://ljvmiranda921.github.io/notebook/2018/06/21/precommits-using-black-and-flake8/)
* fix syntax
* cleanup conftest
2019-06-16 21:05:00 +00:00
|
|
|
@property
|
2023-09-14 18:51:40 +00:00
|
|
|
def max_device_response_size(self) -> int:
|
|
|
|
"""Returns the maximum response size the device can safely construct."""
|
|
|
|
return 16 * 1024
|
|
|
|
|
2022-02-07 08:13:47 +00:00
|
|
|
@property
|
|
|
|
def internal_state(self) -> Any:
|
|
|
|
"""Return the internal state of the instance.
|
|
|
|
|
|
|
|
The returned object contains the raw results from the last update call.
|
|
|
|
This should only be used for debugging purposes.
|
|
|
|
"""
|
2023-12-29 19:17:15 +00:00
|
|
|
return self._last_update or self._discovery_info
|
2024-11-18 14:53:11 +00:00
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def _get_device_type_from_sys_info(info: dict[str, Any]) -> DeviceType:
|
|
|
|
"""Find SmartDevice subclass for device described by passed data."""
|
2024-12-06 23:06:58 +00:00
|
|
|
if "system" in info.get("system", {}).get("get_sysinfo", {}):
|
|
|
|
return DeviceType.Camera
|
|
|
|
|
2024-11-18 14:53:11 +00:00
|
|
|
if "system" not in info or "get_sysinfo" not in info["system"]:
|
|
|
|
raise KasaException("No 'system' or 'get_sysinfo' in response")
|
|
|
|
|
2024-12-06 23:06:58 +00:00
|
|
|
sysinfo: dict[str, Any] = _extract_sys_info(info)
|
2024-11-18 14:53:11 +00:00
|
|
|
type_: str | None = sysinfo.get("type", sysinfo.get("mic_type"))
|
|
|
|
if type_ is None:
|
|
|
|
raise KasaException("Unable to find the device type field!")
|
|
|
|
|
|
|
|
if "dev_name" in sysinfo and "Dimmer" in sysinfo["dev_name"]:
|
|
|
|
return DeviceType.Dimmer
|
|
|
|
|
|
|
|
if "smartplug" in type_.lower():
|
|
|
|
if "children" in sysinfo:
|
|
|
|
return DeviceType.Strip
|
|
|
|
if (dev_name := sysinfo.get("dev_name")) and "light" in dev_name.lower():
|
|
|
|
return DeviceType.WallSwitch
|
|
|
|
return DeviceType.Plug
|
|
|
|
|
|
|
|
if "smartbulb" in type_.lower():
|
|
|
|
if "length" in sysinfo: # strips have length
|
|
|
|
return DeviceType.LightStrip
|
|
|
|
|
|
|
|
return DeviceType.Bulb
|
2024-12-06 23:06:58 +00:00
|
|
|
|
2024-11-18 14:53:11 +00:00
|
|
|
_LOGGER.warning("Unknown device type %s, falling back to plug", type_)
|
|
|
|
return DeviceType.Plug
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def _get_device_info(
|
|
|
|
info: dict[str, Any], discovery_info: dict[str, Any] | None
|
2024-12-13 12:37:13 +00:00
|
|
|
) -> DeviceInfo:
|
2024-11-18 14:53:11 +00:00
|
|
|
"""Get model information for a device."""
|
2024-12-06 23:06:58 +00:00
|
|
|
sys_info = _extract_sys_info(info)
|
2024-11-18 14:53:11 +00:00
|
|
|
|
|
|
|
# Get model and region info
|
|
|
|
region = None
|
|
|
|
device_model = sys_info["model"]
|
|
|
|
long_name, _, region = device_model.partition("(")
|
|
|
|
if region: # All iot devices have region but just in case
|
|
|
|
region = region.replace(")", "")
|
|
|
|
|
|
|
|
# Get other info
|
|
|
|
device_family = sys_info.get("type", sys_info.get("mic_type"))
|
|
|
|
device_type = IotDevice._get_device_type_from_sys_info(info)
|
|
|
|
fw_version_full = sys_info["sw_ver"]
|
|
|
|
firmware_version, firmware_build = fw_version_full.split(" ", maxsplit=1)
|
|
|
|
auth = bool(discovery_info and ("mgt_encrypt_schm" in discovery_info))
|
|
|
|
|
2024-12-13 12:37:13 +00:00
|
|
|
return DeviceInfo(
|
2024-11-18 14:53:11 +00:00
|
|
|
short_name=long_name,
|
|
|
|
long_name=long_name,
|
|
|
|
brand="kasa",
|
|
|
|
device_family=device_family,
|
|
|
|
device_type=device_type,
|
|
|
|
hardware_version=sys_info["hw_ver"],
|
|
|
|
firmware_version=firmware_version,
|
|
|
|
firmware_build=firmware_build,
|
|
|
|
requires_auth=auth,
|
|
|
|
region=region,
|
|
|
|
)
|