Files
python-kasa/kasa/__init__.py
Teemu R. a68f2b3304
Some checks failed
CI / Perform Lint Checks (3.15) (push) Has been cancelled
CodeQL Checks / Analyze (python) (push) Has been cancelled
CI / Python 3.11 on macos-latest (push) Has been cancelled
CI / Python 3.12 on macos-latest (push) Has been cancelled
CI / Python 3.13 on macos-latest (push) Has been cancelled
CI / Python 3.14 on macos-latest (push) Has been cancelled
CI / Python 3.15 on macos-latest (push) Has been cancelled
CI / Python 3.11 on ubuntu-latest (push) Has been cancelled
CI / Python 3.12 on ubuntu-latest (push) Has been cancelled
CI / Python 3.13 on ubuntu-latest (push) Has been cancelled
CI / Python 3.14 on ubuntu-latest (push) Has been cancelled
CI / Python 3.15 on ubuntu-latest (push) Has been cancelled
CI / Python 3.11 on windows-latest (push) Has been cancelled
CI / Python 3.12 on windows-latest (push) Has been cancelled
CI / Python 3.13 on windows-latest (push) Has been cancelled
CI / Python 3.14 on windows-latest (push) Has been cancelled
CI / Python 3.15 on windows-latest (push) Has been cancelled
Stale / stale (push) Has been cancelled
Detect offline hub children and fail fast on writes to them (#1830)
Hubs keep reporting for children they have lost contact with, so callers
see stale values as if they were live. Trying to write to such devices
fail after timeouts with a generic error.
This PR exposes the offline/online information as reported by the hub,
and refuses writes to unreachable child devices without waiting for
device errors.

In details:
- Add new API: `Device.is_available`; hub children report unavailable
when the hub marks them offline or no longer lists them.
- New feature `available` for child devices to make it visible to the
CLI.
- Refuse writes to unavailable children with the new
`DeviceUnavailableError` instead of waiting for the hub to time out.
2026-11-01 18:18:03 +01:00

160 lines
4.8 KiB
Python
Executable File

"""Python interface for TP-Link's smart home devices.
All common, shared functionalities are available through `Device` class::
>>> from kasa import Discover
>>> x = await Discover.discover_single("192.168.1.1")
>>> print(x.model)
For device type specific actions `modules` and `features` should be used instead.
Module-specific errors are raised as `KasaException` and are expected
to be handled by the user of the library.
"""
from importlib.metadata import version
from typing import TYPE_CHECKING, Any
from warnings import warn
from kasa.credentials import Credentials
from kasa.device import Device
from kasa.device_type import DeviceType
from kasa.deviceconfig import (
DeviceConfig,
DeviceConnectionParameters,
DeviceEncryptionType,
DeviceFamily,
)
from kasa.discover import Discover
from kasa.emeterstatus import EmeterStatus
from kasa.exceptions import (
AuthenticationError,
DeviceError,
DeviceUnavailableError,
KasaException,
TimeoutError,
UnsupportedDeviceError,
)
from kasa.feature import Feature
from kasa.interfaces.light import HSV, ColorTempRange, Light, LightState
from kasa.interfaces.thermostat import Thermostat, ThermostatState
from kasa.module import Module
from kasa.protocols import BaseProtocol, IotProtocol, SmartCamProtocol, SmartProtocol
from kasa.protocols.iotprotocol import _deprecated_TPLinkSmartHomeProtocol # noqa: F401
from kasa.smartcam.modules.camera import StreamResolution
from kasa.transports import BaseTransport
__version__ = version("python-kasa")
__all__ = [
"Discover",
"BaseProtocol",
"BaseTransport",
"IotProtocol",
"SmartProtocol",
"SmartCamProtocol",
"LightState",
"TurnOnBehaviors",
"TurnOnBehavior",
"DeviceType",
"Feature",
"EmeterStatus",
"Device",
"Light",
"ColorTempRange",
"HSV",
"Plug",
"Module",
"KasaException",
"AuthenticationError",
"DeviceError",
"DeviceUnavailableError",
"UnsupportedDeviceError",
"TimeoutError",
"Credentials",
"DeviceConfig",
"DeviceConnectionParameters",
"DeviceEncryptionType",
"DeviceFamily",
"ThermostatState",
"Thermostat",
"StreamResolution",
]
from . import iot
from .iot.modules.lightpreset import IotLightPreset
deprecated_names = ["TPLinkSmartHomeProtocol"]
deprecated_smart_devices = {
"SmartDevice": iot.IotDevice,
"SmartPlug": iot.IotPlug,
"SmartBulb": iot.IotBulb,
"SmartLightStrip": iot.IotLightStrip,
"SmartStrip": iot.IotStrip,
"SmartDimmer": iot.IotDimmer,
"SmartBulbPreset": IotLightPreset,
}
deprecated_classes = {
"SmartDeviceException": KasaException,
"UnsupportedDeviceException": UnsupportedDeviceError,
"AuthenticationException": AuthenticationError,
"TimeoutException": TimeoutError,
"ConnectionType": DeviceConnectionParameters,
"EncryptType": DeviceEncryptionType,
"DeviceFamilyType": DeviceFamily,
}
if not TYPE_CHECKING:
def __getattr__(name: str) -> Any:
if name in deprecated_names:
warn(f"{name} is deprecated", DeprecationWarning, stacklevel=2)
return globals()[f"_deprecated_{name}"]
if name in deprecated_smart_devices:
new_class = deprecated_smart_devices[name]
package_name = ".".join(new_class.__module__.split(".")[:-1])
warn(
f"{name} is deprecated, use {new_class.__name__} from "
+ f"package {package_name} instead or use Discover.discover_single()"
+ " and Device.connect() to support new protocols",
DeprecationWarning,
stacklevel=2,
)
return new_class
if name in deprecated_classes:
new_class = deprecated_classes[name] # type: ignore[assignment]
msg = f"{name} is deprecated, use {new_class.__name__} instead"
warn(msg, DeprecationWarning, stacklevel=2)
return new_class
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
if TYPE_CHECKING:
SmartDevice = Device
SmartBulb = iot.IotBulb
SmartPlug = iot.IotPlug
SmartLightStrip = iot.IotLightStrip
SmartStrip = iot.IotStrip
SmartDimmer = iot.IotDimmer
SmartBulbPreset = IotLightPreset
SmartDeviceException = KasaException
UnsupportedDeviceException = UnsupportedDeviceError
AuthenticationException = AuthenticationError
TimeoutException = TimeoutError
ConnectionType = DeviceConnectionParameters
EncryptType = DeviceEncryptionType
DeviceFamilyType = DeviceFamily
# Instanstiate all classes so the type checkers catch abstract issues
from . import smart
smart.SmartDevice("127.0.0.1")
iot.IotDevice("127.0.0.1")
iot.IotPlug("127.0.0.1")
iot.IotBulb("127.0.0.1")
iot.IotLightStrip("127.0.0.1")
iot.IotStrip("127.0.0.1")
iot.IotDimmer("127.0.0.1")