Skip to content

IOT devices connected over KLAP are always built as IotPlug, never IotStrip — HS300 loses all child sockets #1748

Description

@luxardolabs

Summary

An HS300 power strip reached over KLAP is instantiated as IotPlug instead of IotStrip, so it exposes no child sockets — no per-outlet switching and no per-outlet emeter. The same strip on XOR firmware works correctly.

This is separate from, and in addition to, the authentication issue in #1604 / PR #1731. With #1731 applied, the device authenticates but is still childless.

Environment

  • Device: HS300(US) hardware 2.0, firmware 1.1.2 Build 241220 Rel.171333
  • python-kasa 0.10.2
  • Home Assistant 2026.7.4 (also reproducible outside HA)
  • tcp/9999 is ConnectionRefused on this firmware; tcp/80 serves KLAP
  • Connection parameters recorded for the device: IOT.SMARTPLUGSWITCH / KLAP / login_version: 2

Observed

strip transport entities outlet switches
HS300 on old firmware XOR 18 6
HS300 on new firmware KLAP 11 0

On the KLAP strips the parent switch stays unavailable while strip-level sensors report real values (voltage, consumption), so communication is fine — only the child devices are missing.

Cause

kasa/device_factory.py::_connect() gates sysinfo-based class detection on XorTransport:

if isinstance(protocol, IotProtocol) and isinstance(
    protocol._transport, XorTransport
):
    info = await protocol.query(GET_SYSINFO_QUERY)
    device_class = get_device_class_from_sys_info(info)   # only path that can return IotStrip
    ...
elif device_class := get_device_class_from_family(
    config.connection_type.device_family.value, https=config.connection_type.https
):

get_device_class_from_sys_info() is the only path that inspects the device's own sysinfo and can therefore return DeviceType.Strip -> IotStrip. An IotProtocol running over KlapTransportV2 skips that branch and falls through to get_device_class_from_family(), where:

"IOT.SMARTPLUGSWITCH": IotPlug,

is unconditional and has no concept of children. So any IoT device that reaches over KLAP loses its children, regardless of what it actually is.

Suggested fix

Drop the transport check — GET_SYSINFO_QUERY works over KLAP:

if isinstance(protocol, IotProtocol):
    info = await protocol.query(GET_SYSINFO_QUERY)
    device_class = get_device_class_from_sys_info(info)
    ...

Verified

Applied to three HS300s on KLAP firmware. All three went from 11 entities / 0 outlets to 18 entities with 6 switchable outlets and per-outlet emeter. No regression on XOR devices, which already take that branch.

Happy to open a PR if the approach looks right — there may be a preferred way to handle non-IoT IotProtocol transports (e.g. LinkieTransportV2 for IOT cameras) that I haven't considered.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions