TP-Link Kasa
Local control of TP-Link Kasa smart plugs and power strips (HS-series, KP-series) over the device's own LAN API. No Kasa cloud account and no hub sit in the middle — GEM talks straight to the device's IP address on port 9999, and no username or password is required.
This is an on/off power switch integration. A Kasa outlet either passes power or it doesn't; the driver does not dim, and it does not read the energy metering that some strips report. Use it to switch lights, appliances, and other loads, to power-cycle gear, or — wired to a momentary trigger — to pulse a gate or garage operator.
The standout feature is auto-discovery: when you add a multi-outlet Kasa device and enable it, GEM reads the strip's outlet list and creates one zone per outlet automatically, under the Power subsystem. You normally do not add zones by hand.
One Kasa device in GEM is one physical unit (a plug or a strip). On a multi-outlet strip — HS300, KP303, KP400, EP40 — each outlet becomes its own zone, so a 6-outlet HS300 gives you six independent on/off zones.
Prerequisites
- Pair the Kasa device in the Kasa mobile app first and put it on the same LAN the GEM controller can reach.
- Give the device a static IP or a DHCP reservation. The integration addresses the device by IP, so a moving lease takes it offline.
- Name each outlet in the Kasa app before you add it to GEM (e.g. "Patio Lights", "Christmas Tree"). GEM uses each outlet's Kasa alias as the new zone's label, so naming them first saves you renaming zones afterward.
- Confirm the unit still exposes its local LAN API. Some 2024-and-newer Kasa and Tapo firmware silently disables the legacy local API in favor of a cloud-only protocol — see Troubleshooting.
Setup
- Open Devices and add a new device. Pick the TP-Link Kasa driver.
- In Kasa IP Address enter the device's LAN IP (e.g.
192.168.1.50). No credentials are needed. - Enable the device. On connect, the driver reads the strip's outlet list and creates one zone per outlet under the Power subsystem, named after the outlet aliases you set in the Kasa app.
- Open Zones to confirm the new zones appeared. Each one is a Light Switch-style power control with on/off. Rename, re-home to a different subsystem, or assign them to UIs as you would any other zone.
The driver discovers outlets from the device's child-outlet list, which only multi-outlet strips report. A single-outlet plug (HS100/HS103/HS110, KP105/KP115, EP10, and similar) has no child list, so no zone is created and the plug can't be controlled through this driver. Use a multi-outlet Kasa device, or switch that single load through a different relay/plug integration.
How outlets become zones
Each auto-created zone carries an address equal to the outlet's Kasa child id — a long hexadecimal string the driver fills in for you. You don't type it; the address field's hint ("Kasa child outlet id (auto-populated on discovery)") reflects that it's populated during discovery.
The outlet list is read only at connect time. If you add or re-map outlets, or upgrade firmware, reload the device so the driver re-enumerates and binds any new outlets — see Troubleshooting.
Subsystem and control type
New zones land on the Power subsystem with a standard on/off power control. That's the right home for switched lights and appliances. The driver also suggests Lights and Garage as subsystems if a particular outlet fits better there:
- A switched light or appliance — leave it on Power (or move it to
Lights).
on/offenergize and de-energize the outlet. - A gate or garage operator wired to a Kasa outlet's trigger — move the zone
to the Garage (or Gates) subsystem and mark it momentary so
open/closebecome a pulse. See Momentary and garage outlets.
There is no dimmer control type for Kasa — these are on/off outlets. A level
command is treated as on/off (any level above 0 turns the outlet on, 0 turns
it off).
State polling
While the device is enabled, the driver polls each of its zones in turn and
writes the result back to the zone's state (on / off). Outlets are queried
about 500 ms apart so a multi-outlet strip isn't hit all at once, and the
whole sweep repeats on the device's Status Poll Interval (status_interval,
default 1000 ms). If a busy device or network struggles, raise
status_interval.
A zone can also mirror another zone's state instead of polling its own outlet:
set the zone's status_zone attribute to another zone's id and it follows that
zone's state. This is the same status-mirroring pattern used elsewhere in GEM
(for example, a relay zone that reports position from a separate sensor zone).
Power-cycling and momentary outlets
The driver turns two real-world actions into outlet sequences:
- Reboot (power-cycle) —
rebootturns the outlet off, waits, then turns it back on. The default gap is 5000 ms. Use it to hard-restart a modem, AP, or other load that's hung. Pass adelayarg (in ms) to change the off-time; raise it for loads sensitive to fast cycling. - Momentary pulse — a
pulseturns the outlet on, waits, then turns it back off. The default pulse is 1000 ms. This is what a gate or garage operator's trigger input expects.
Momentary and garage outlets
If a zone sits on the Garage subsystem (or the Garage subsystem itself is set
momentary) and that zone is marked momentary, the driver rewrites open
and close commands into a pulse automatically — one press fires the
operator and the outlet returns to off. To override the pulse length for a
specific command, pass a delay arg (ms); the per-zone Pulse Delay (delay)
attribute records the intended momentary pulse length for the outlet.
Because a switched outlet can't tell you whether the door actually opened, point
the outlet zone's status_zone attribute at a separate input zone wired to a
reed/limit switch so the zone reports real open/closed position.
Attributes
Add these in the Attribute editor for the device or zone; the labeled ones also have fields in the device/zone forms.
Device
| Attribute | Type | Required | Default | Description |
|---|---|---|---|---|
ip | string | yes | — | LAN IP of the Kasa plug or strip. Shown as Kasa IP Address. |
status_interval | int | no | 1000 | Status Poll Interval (ms) — how often the full outlet sweep repeats. |
Zone
| Attribute | Type | Required | Default | Description |
|---|---|---|---|---|
address | string | yes | auto | Kasa child outlet id. Auto-populated on discovery — you don't normally type it. |
delay | int | no | — | Pulse Delay (ms) — intended pulse length for a momentary/garage outlet. |
status_zone | int | no | — | Id of another zone whose state this zone should mirror instead of polling its own outlet. |
Commands
These are the commands the driver writes and keeps current, so they appear automatically on the device's Commands page and can be tested there.
| Command | Args | What it does |
|---|---|---|
on | address | Turn the outlet on. |
off | address | Turn the outlet off. |
level | level, address | On/off only — any level above 0 turns the outlet on, 0 turns it off. Kasa outlets don't dim. |
reboot | delay, address | Power-cycle: turn the outlet off, wait delay ms (default 5000), then on. |
get_state | address | Read the outlet's current state now and reconcile the zone. |
The rows above are rewritten by the driver every time the system starts. Add your own commands if you need them, but don't hand-edit the templates or args of these — your changes are overwritten on the next restart.
On a momentary zone on the Garage (or Gates) subsystem, open and close
are converted to a pulse (on → wait → off, default 1000 ms) before they
run — see Momentary and garage outlets.
Troubleshooting
| Symptom | Check |
|---|---|
Device never connects; log shows tplink kasa missing ip | The Kasa IP Address (ip) attribute is blank. Set it to the device's LAN IP. |
| Connect timeout on a known-good IP | The unit's firmware may have migrated to the cloud-only Tapo protocol and dropped the local LAN API. Test from the host with a Kasa LAN tool (e.g. kasa --host <ip>). If the local API is gone, downgrade the firmware or replace the unit. |
| No zones appeared after enabling | It's a single-outlet plug (no child-outlet list) — see the note under Setup — or the device never connected. Confirm the IP is reachable and that it's a multi-outlet strip. |
| Some outlets on a strip are missing | The outlet list is enumerated only at connect time. After firmware upgrades or outlet changes, reload the device so the driver re-enumerates the outlets. |
A command returns invalid address / invalid plug address | The zone's address doesn't match a discovered outlet. Reload the device to re-bind; don't hand-edit the auto-populated child id. |
Outlet cycles too fast for a sensitive load on reboot | Pass a larger delay (ms) on the reboot command, or raise it on the zone. |
Related documentation
- Devices — add the Kasa device and set its IP.
- Zones — the per-outlet zones the driver auto-creates.
- Subsystems — Power (default), Lights, or Garage/Gates; set momentary for a gate/garage outlet.
- Commands — where
on/off/reboot/level/get_stateappear and can be tested. - Relay semantics — how on/off and open/closed states map across subsystems.