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.
Summary
An HS300 power strip reached over KLAP is instantiated as
IotPluginstead ofIotStrip, 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
1.1.2 Build 241220 Rel.171333ConnectionRefusedon this firmware; tcp/80 serves KLAPIOT.SMARTPLUGSWITCH/KLAP/login_version: 2Observed
On the KLAP strips the parent
switchstaysunavailablewhile 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 onXorTransport:get_device_class_from_sys_info()is the only path that inspects the device's ownsysinfoand can therefore returnDeviceType.Strip->IotStrip. AnIotProtocolrunning overKlapTransportV2skips that branch and falls through toget_device_class_from_family(), where: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_QUERYworks over KLAP: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
IotProtocoltransports (e.g.LinkieTransportV2for IOT cameras) that I haven't considered.