mirror of
https://github.com/python-kasa/python-kasa.git
synced 2024-12-22 19:23:34 +00:00
59e5073509
Some checks are pending
CI / Perform linting checks (3.13) (push) Waiting to run
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, macos-latest, 3.11) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, macos-latest, 3.12) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, macos-latest, 3.13) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, ubuntu-latest, 3.11) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, ubuntu-latest, 3.12) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, ubuntu-latest, 3.13) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, windows-latest, 3.11) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, windows-latest, 3.12) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, windows-latest, 3.13) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (true, ubuntu-latest, 3.11) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (true, ubuntu-latest, 3.12) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (true, ubuntu-latest, 3.13) (push) Blocked by required conditions
CodeQL checks / Analyze (python) (push) Waiting to run
Co-authored-by: Teemu R. <tpr@iki.fi>
143 lines
8.0 KiB
Markdown
143 lines
8.0 KiB
Markdown
|
|
# Topics
|
|
|
|
```{contents} Contents
|
|
:local:
|
|
```
|
|
|
|
These topics aim to provide some details on the design and internals of this library.
|
|
You might be interested in this if you want to improve this library,
|
|
or if you are just looking to access some information that is not currently exposed.
|
|
|
|
(topics-initialization)=
|
|
## Initialization
|
|
|
|
Use {func}`~kasa.Discover.discover` to perform udp-based broadcast discovery on the network.
|
|
This will return you a list of device instances based on the discovery replies.
|
|
|
|
If the device's host is already known, you can use to construct a device instance with
|
|
{meth}`~kasa.Device.connect()`.
|
|
|
|
The {meth}`~kasa.Device.connect()` also enables support for connecting to new
|
|
KASA SMART protocol and TAPO devices directly using the parameter {class}`~kasa.DeviceConfig`.
|
|
Simply serialize the {attr}`~kasa.Device.config` property via {meth}`~kasa.DeviceConfig.to_dict()`
|
|
and then deserialize it later with {func}`~kasa.DeviceConfig.from_dict()`
|
|
and then pass it into {meth}`~kasa.Device.connect()`.
|
|
|
|
|
|
(topics-discovery)=
|
|
## Discovery
|
|
|
|
Discovery works by sending broadcast UDP packets to two known TP-link discovery ports, 9999 and 20002.
|
|
Port 9999 is used for legacy devices that do not use strong encryption and 20002 is for newer devices that use different
|
|
levels of encryption.
|
|
If a device uses port 20002 for discovery you will obtain some basic information from the device via discovery, but you
|
|
will need to await {func}`Device.update() <kasa.Device.update()>` to get full device information.
|
|
Credentials will most likely be required for port 20002 devices although if the device has never been connected to the tplink
|
|
cloud it may work without credentials.
|
|
|
|
To query or update the device requires authentication via {class}`Credentials <kasa.Credentials>` and if this is invalid or not provided it
|
|
will raise an {class}`AuthenticationException <kasa.AuthenticationException>`.
|
|
|
|
If discovery encounters an unsupported device when calling via {meth}`Discover.discover_single() <kasa.Discover.discover_single>`
|
|
it will raise a {class}`UnsupportedDeviceException <kasa.UnsupportedDeviceException>`.
|
|
If discovery encounters a device when calling {func}`Discover.discover() <kasa.Discover.discover>`,
|
|
you can provide a callback to the ``on_unsupported`` parameter
|
|
to handle these.
|
|
|
|
(topics-deviceconfig)=
|
|
## DeviceConfig
|
|
|
|
The {class}`DeviceConfig` class can be used to initialise devices with parameters to allow them to be connected to without using
|
|
discovery.
|
|
This is required for newer KASA and TAPO devices that use different protocols for communication and will not respond
|
|
on port 9999 but instead use different encryption protocols over http port 80.
|
|
Currently there are three known types of encryption for TP-Link devices and two different protocols.
|
|
Devices with automatic firmware updates enabled may update to newer versions of the encryption without separate notice,
|
|
so discovery can be helpful to determine the correct config.
|
|
|
|
To connect directly pass a {class}`DeviceConfig` object to {meth}`Device.connect()`.
|
|
|
|
A {class}`DeviceConfig` can be constucted manually if you know the {attr}`DeviceConfig.connection_type` values for the device or
|
|
alternatively the config can be retrieved from {attr}`Device.config` post discovery and then re-used.
|
|
|
|
(topics-update-cycle)=
|
|
## Update Cycle
|
|
|
|
When {meth}`~kasa.Device.update()` is called,
|
|
the library constructs a query to send to the device based on :ref:`supported modules <modules>`.
|
|
Internally, each module defines {meth}`~kasa.modules.Module.query()` to describe what they want query during the update.
|
|
|
|
The returned data is cached internally to avoid I/O on property accesses.
|
|
All properties defined both in the device class and in the module classes follow this principle.
|
|
|
|
While the properties are designed to provide a nice API to use for common use cases,
|
|
you may sometimes want to access the raw, cached data as returned by the device.
|
|
This can be done using the {attr}`~kasa.Device.internal_state` property.
|
|
|
|
|
|
(topics-modules-and-features)=
|
|
## Modules and Features
|
|
|
|
The functionality provided by all {class}`~kasa.Device` instances is (mostly) done inside separate modules.
|
|
While the device class provides easy access for most device related attributes,
|
|
for components like `light` and `camera` you can access the module through {attr}`kasa.Device.modules`.
|
|
The module names are handily available as constants on {class}`~kasa.Module` and will return type aware values from the collection.
|
|
|
|
Features represent individual pieces of functionality within a module like brightness, hsv and temperature within a light module.
|
|
They allow for instrospection and can be accessed through {attr}`kasa.Device.features`.
|
|
Attributes can be accessed via a `Feature` or a module attribute depending on the use case.
|
|
Modules tend to provide richer functionality but using the features does not require an understanding of the module api.
|
|
|
|
:::{include} featureattributes.md
|
|
:::
|
|
|
|
(topics-protocols-and-transports)=
|
|
## Protocols and Transports
|
|
|
|
The library supports two different TP-Link protocols, ``IOT`` and ``SMART``.
|
|
``IOT`` is the original Kasa protocol and ``SMART`` is the newer protocol supported by TAPO devices and newer KASA devices.
|
|
The original protocol has a ``target``, ``command``, ``args`` interface whereas the new protocol uses a different set of
|
|
commands and has a ``method``, ``parameters`` interface.
|
|
Confusingly TP-Link originally called the Kasa line "Kasa Smart" and hence this library used "Smart" in a lot of the
|
|
module and class names but actually they were built to work with the ``IOT`` protocol.
|
|
|
|
In 2021 TP-Link started updating the underlying communication transport used by Kasa devices to make them more secure.
|
|
It switched from a TCP connection with static XOR type of encryption to a transport called ``KLAP`` which communicates
|
|
over http and uses handshakes to negotiate a dynamic encryption cipher.
|
|
This automatic update was put on hold and only seemed to affect UK HS100 models.
|
|
|
|
In 2023 TP-Link started updating the underlying communication transport used by Tapo devices to make them more secure.
|
|
It switched from AES encryption via public key exchange to use ``KLAP`` encryption and negotiation due to concerns
|
|
around impersonation with AES.
|
|
The encryption cipher is the same as for Kasa KLAP but the handshake seeds are slightly different.
|
|
Also in 2023 TP-Link started releasing newer Kasa branded devices using the ``SMART`` protocol.
|
|
This appears to be driven by hardware version rather than firmware.
|
|
|
|
|
|
In order to support these different configurations the library migrated from a single protocol class ``TPLinkSmartHomeProtocol``
|
|
to support pluggable transports and protocols.
|
|
The classes providing this functionality are:
|
|
|
|
- {class}`BaseProtocol <kasa.protocols.BaseProtocol>`
|
|
- {class}`IotProtocol <kasa.protocols.IotProtocol>`
|
|
- {class}`SmartProtocol <kasa.protocols.SmartProtocol>`
|
|
|
|
- {class}`BaseTransport <kasa.transports.BaseTransport>`
|
|
- {class}`XorTransport <kasa.transports.XorTransport>`
|
|
- {class}`AesTransport <kasa.transports.AesTransport>`
|
|
- {class}`KlapTransport <kasa.transports.KlapTransport>`
|
|
- {class}`KlapTransportV2 <kasa.transports.KlapTransportV2>`
|
|
|
|
(topics-errors-and-exceptions)=
|
|
## Errors and Exceptions
|
|
|
|
The base exception for all library errors is {class}`KasaException <kasa.exceptions.KasaException>`.
|
|
|
|
- If the device returns an error the library raises a {class}`DeviceError <kasa.exceptions.DeviceError>` which will usually contain an ``error_code`` with the detail.
|
|
- If the device fails to authenticate the library raises an {class}`AuthenticationError <kasa.exceptions.AuthenticationError>` which is derived
|
|
from {class}`DeviceError <kasa.exceptions.DeviceError>` and could contain an ``error_code`` depending on the type of failure.
|
|
- If the library encounters and unsupported deviceit raises an {class}`UnsupportedDeviceError <kasa.exceptions.UnsupportedDeviceError>`.
|
|
- If the device fails to respond within a timeout the library raises a {class}`TimeoutError <kasa.exceptions.TimeoutError>`.
|
|
- All other failures will raise the base {class}`KasaException <kasa.exceptions.KasaException>` class.
|