This PR adds fixture files for:
- KH100 (UK), hardware 1.0, firmware 1.7.5
- KE100 (UK), hardware 1.0, firmware 2.15.0
- KE110 (EU), hardware 1.0, firmware 1.4.0
The fixtures were collected from physical devices and anonymized for use
in development and testing.
The KE110 fixture is a new device model and has been added to the
appropriate fixture category.
This also improves test suite by adding TRV state simulation to allow tests pass on fixtures that report a non-heating state.
---------
Co-authored-by: Teemu Rytilahti <tpr@iki.fi>
This PR adds update_admin_password in the library and CLI for SMARTCAM
devices. It also updates the update_credentials function to correctly
handle the local_account for local device authentication. It updates all
of the required tests and code coverage as well.
Add type annotations (parameter types and `-> None` return types) to all
top-level test functions across 22 test files. This enables mypy to
check test function bodies, catching type errors that were previously
hidden.
_execute_multiple_query only disables batching on JSON_DECODE_FAIL_ERROR / INTERNAL_UNKNOWN_ERROR (#1025). This change applies the same pattern to a TimeoutError raised while sending a batch: set the batch size to 1 and raise _RetryableError.
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Bulbs and light strips can fade smoothly when their brightness or colour
changes, instead of snapping straight to the new value. The Tapo app
exposes this as "Smooth Transition". python-kasa cannot read or change
it today, so the setting is missing from Home Assistant even though the
device supports it.
This adds support for this feature as a switch:
- read `change_state` in `_post_update_hook`
- add a `change_transition` property and `set_change_transition()`
- register a `smooth_transition_change` switch feature when the device
reports the flag
A light strip can be cut shorter than it ships, and the device has to be
told how much of it is actually there so effects and colours span the
right distance. The Tapo app exposes this as "Light Strip Length".
python-kasa has no way to read or change it, so the setting is missing
from Home Assistant.
This change exposes a new feature backed by `segment` component:
- add a `Segment` module: `REQUIRED_COMPONENT = "segment"`,
`QUERY_GETTER_NAME = "get_device_segment"`
- expose one Number feature, `strip_segments`, range 0-50,
`Category.Config`
- add a `segments` property and `set_segments()`, which writes
`set_device_segment`
A new or factory-reset iot device can report a clock far in the past,
e.g. year 2000, which causes a failure in timezone autodetection
logic as the difference can be in years.
This causes `update()` to fail, so every cli command fails, including
`wifi scan` and `wifi join`, which are the commands needed to provision
the device.
To fix this, this changes the logic fall back to UTC if the device clock is not set,
detected by checking if the timedelta is too large.
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Drop the `XorTransport` guard and always query sysinfo for `IotProtocol`
connections, so `get_device_class_from_sys_info` sees `dev_name`
(\"Wi-Fi Smart Dimmer\", etc.) and picks the right class to initialize.
This is necessary, as some IOT-speaking devices have moved to use KLAP
instead of XOR as their transport, which in turn caused devices like dimmers
to be detected incorrectly as plugs.
---------
Co-authored-by: bluntman001 <noreply@github.com>
Co-authored-by: Teemu Rytilahti <tpr@iki.fi>
A `credentials_hash` is specific to the transport that produced it, but
nothing checked that the hash a transport was handed was its own.
This matters because a device can change its encryption type without the
credentials changing. For example, toggling **Third-Party Compatibility** in the Tapo
app moves a plug or strip between TPAP and KLAP, and firmware updates are known to introduce new transports.
This introduces a per-transport check that the hash has the shape that transport produces, and treats a foreign hash as absent rather than as a bad password. This makes the failure honest but still ends in a reauth.
This also changes how credentials are read back out when the hash is one of the plaintext forms and lets the transport derive its own, so the change of encryption type needs no reauth in all cases.
Some cameras (C220 and C100 on the 1.4.4 line in those reports) drop the
session about every ten minutes and answer the next `securePassthrough`
with HTTP 401 and `-40421` in the body.
With this change a 401 on passthrough marks the transport as needing a
handshake and raises `_RetryableError`, so the protocol logs in again
and retries the same request.
---------
Co-authored-by: freeKC <7538438+freeKC@users.noreply.github.com>
Some IOT-family devices keep the legacy IOT command set after a firmware
update but upgrade their KLAP authentication to login version 2 (sha256 hashing).
This change selects `KlapTransportV2` for `IOT.KLAP` devices that advertise
`login_version >= 2`, matching the transport already used for `SMART.KLAP`.
Devices with login version 1 (or unset) are unchanged and continue to use the v1 `KlapTransport`.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
The C460 on firmware 1.2.2 answers the UDP discovery with
`mgt_encrypt_schm: {"is_support_https": true}` and `encrypt_type:
["3"]`, but no `encrypt_type` inside the scheme and no `encrypt_info`.
`_get_connection_parameters` only looks at those two places for the
encryption type, so the camera ends up as "Unsupported device ... with
no encryption type" even though it works with the same AES login as
every other camera.
This uses the top level list as a last resort: when nothing else names
the encryption type, the device supports https and the list says 3,
treat it as AES.
Co-authored-by: freeKC <7538438+freeKC@users.noreply.github.com>
Add a `LastAlertDetection` module for SMARTCAM devices exposing `getLastAlarmInfo` as two `Info` sensor features:
- `last_alert_timestamp` — tz-aware `datetime` of the last alert, `None` when the device has never detected anything (`last_alarm_time` is `""` or `"0"` on such devices, and an unexpected value is also mapped to `None`
- `last_alert_type` — `LastAlertType` enum (`Motion`; `Unknown` fallback with a one-time warning per unknown raw value), `None` when unset.
Iot firmware timezone index 18 corresponds to "(UTC-05:00) Eastern Time (US & Canada)", which observes daylight saving time.
Previously, this was mapped to a fixed UTC-5 (EST= causing one hour off for New York local times.
Co-authored-by: monteccarlos <vld@noise.xyz>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Adds a device fixture for the **Tapo P125M** on firmware **1.4.4 Build
260521 Rel.100818** (US).
The existing P125M fixture covers firmware 1.1.0 (Build 231009).
This newer firmware:
- reports **matter component v3** (vs v2 in the old fixture)
- adds the **`get_matter_setup_info`** module (Matter setup
code/payload)
---------
Co-authored-by: Harry Pehkonen <harrypehkonen@users.noreply.github.com>
Adds the `KL420` series to `TPLINK_KELVIN` to support wider color temperature ranges (2500-9000K as supported by the device).
While older firmware (1.0.2) reported is_variable_color_temp: 0, firmware 1.0.7 enables variable color temperature support.
Add ECDHE-RSA-AES256-GCM-SHA384 alongside the existing ECDHE-RSA-AES128-GCM-SHA256 suite for newer Tapo firmware, while keeping all legacy suites for backward compatibility.
Tested with Tapo TC70, C530WS, and C210 cameras after firmware updates produced TLS handshake failures.
- add the KL400L5 color temperature range used by newer firmware
- add a KL400L5(US) 1.0.11 fixture that reports variable color
temperature support
- categorize that fixture as variable color temperature while keeping
older KL400L5 fixtures non-variable
- The Tapo app exposes 2500K-9000K for these KL400L5 strips, so this PR
uses that app-visible range.
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add support for S515D and other Energy Monitoring V2 devices that report current power via get_energy_usage instead of get_current_power, with fixture coverage and fallback tests.
Update SmartCam time handling to apply timezone-only changes with explicit warnings, while keeping common time and CLI tests focused on devices that support setting clock time and adding SmartCam-specific coverage for the special behavior.
type annotations and parameter type annotations to
all test functions and helper functions across the 5 transport test
files. This enables mypy to check function bodies, catching type errors
that were previously hidden.
Add type annotations (`-> None` return types and parameter types) to
test files in the `tests/cli/`, `tests/protocols/`, and
`tests/smartcam/` directories. This enables mypy to check test function
bodies, catching type errors that were previously hidden.
Comprehensive docstring cleanup across the repository — fix Sphinx
cross-references, Python REPL continuation syntax, stale class names,
and modernize the IotBulb docstring to use the
`Module.Light`/`Module.LightPreset` interface.
`ENETUNREACH` is semantically equivalent to `EHOSTUNREACH` (which is
already in `_NO_RETRY_ERRORS`) — both indicate the destination cannot be
reached at the network layer. Retrying immediately will not resolve the
condition.
In containerized environments (e.g., Home Assistant running in Docker
with bridge networking), a single ICMP "Network unreachable" response
can temporarily poison the container's routing cache. The unnecessary
retries reinforce the poisoned cache entry, causing cascading failures
for other devices on the same subnet.
Updates to get_default_credentials and DEFAULT_CREDENTIALS for handling
a new default password for encryption_type 3 in TAPOCAMERA devices that
use encryption_type 3.
This adds support for devices like TC40.
We do not, by design, add Time module for hub's children.
This has a side-effect that we need to fallback to the parent's time
module to allow presenting the correct timestamp for the last alert.
This fixes the time handling of the child devices for iotstrip to
pull from the parent device time module instead of having each child
with its own time module.
The `low_battery` state needs to be discarded before checking for an empty
list of states to avoid incorrect reporting when the battery is low and
there is no specific state set.