Kasa Smart Plugs controlled with Python

So I just discovered the python-kasa library. If you own a Kasa power strip or Kasa smart plug, this will allow you to discover and control Kasa devices throughout your home. I’ve been playing with it all day and it’s way too much fun turning things on and off from my desk.

To get started, install python-kasa via pip:

$ pip install python-kasa

When that’s done, do a quick kasa discovery on your network. This will locate your devices and print it out in an easy to read format.

$ kasa discover

Example output:

$ kasa discover
== TP-LINK_Power Strip_XXXX - XXXX(US) ==
Host: 192.168.1.5
Port: 9999
Device state: True
Time:         2024-07-31 19:31:37 (tz: {'index': 6, 'err_code': 0}
Hardware:     2.0
Software:     1.0.0 Build 000000 Rel.000000
MAC (rssi):   XX:XX:XX:XX:XX:XX (-XX)
== Primary features ==
State (state): True

== Information ==
On since (on_since): 2024-07-31 08:30:03-07:00
Cloud connection (cloud_connection): True

== Configuration ==
LED (led): True

== Debug ==
RSSI (rssi): -50 dBm

== Children ==

        == Garage (Socket for XXXX(US)) ==
        == Primary features ==
        State (state): True

        == Information ==
        On since (on_since): 2024-07-31 18:38:44-07:00

        == Configuration ==

        == Debug ==

        == Guillotine (Socket for XXXX(US)) ==
        == Primary features ==
        State (state): True

        == Information ==
        On since (on_since): 2024-07-31 08:30:03-07:00

        == Configuration ==

Here, we’ll remember that the IP address is 192.186.1.5 and the Garage’s index is zero (the first on the list). We always count from zero in our world of electronics and programming.

The following example script grabs the list of devices plugged into a Kasa power strip. Instead of toggling the entire strip at once, we need to access and control the individual child elements directly. My Mario light is “Child 2,” so we address it with index numbers like [1] or [2].

import asyncio
from kasa import SmartStrip

async def main():
    strip = SmartStrip("IP_ADDRESS")

    await strip.update()

    for index, child in enumerate(strip.children):
        print(f"Child {index}: {child.alias} - State: {child.is_on}")

    light = strip.children[2]

    if light.is_on:
        await light.turn_off()
        print("Light turned off.")
    else:
        await light.turn_on()
        print("Light turned on.")

if __name__ == "__main__":
    asyncio.run(main())

What I ended up with, looks something like this:

import datetime
import asyncio
from kasa import SmartStrip, SmartPlug

current_time = datetime.datetime.now().time()
print(f'Current Time: {current_time}')

mario = ["IP_ADDRESS", "My Fancy Light, CHILD_INDEX]
copy_paste_to_add_more_devices = [["IP_ADDRESS", "My Fancy Light, CHILD_INDEX]

async def light_toggle(ip_address, name, index=None, desired_state=None):
    try:
        if index is not None:
            light_strip = SmartStrip(ip_address)
            await light_strip.update()
            light = light_strip.children[index]
        else:
            light = SmartPlug(ip_address)
            await light.update()

        #print(f'Device sys_info for {name}: {light.sys_info}')

        current_state = light.is_on
        if desired_state is not None:
            if desired_state == 'on' and not current_state:
                await light.turn_on()
                print(f'{name} turned on.')
            elif desired_state == 'off' and current_state:
                await light.turn_off()
                print(f'{name} turned off.')
        else:
            if current_state:
                await light.turn_off()
                print(f'{name} turned off.')
            else:
                await light.turn_on()
                print(f'{name} turned on.')
    except Exception as e:
        print(f'Error occurred while toggling light for {name}: {e}')

async def main():
    if current_time >= datetime.time(19, 0):
        await light_toggle(mario[0], mario[1], mario[2], 'on')
    else:
        await light_toggle(mario[0], mario[1], mario[2], 'off')

if __name__ == "__main__":
    asyncio.run(main())

Open the CronTab task scheduler with $ crontab -e. My Crontab is set to a 15 minute schedule - which is the example below. Otherwise, make a more convenient one from this AI generator.

*/15 * * * * python3 /home/poopypants/scripts/kasa_light_control.py

Instead of using the Kasa phone app for scheduling, your lights can easily be automated by any networked PC with Python installed. It can further be expaned with any trigger - like motion controls, geofencing, incoming messages, or even calendar events. It’s a simple project that keeps automation, privacy, data security, and user control in mind. Just make sure you set static IP addresses for your Kasa devices so the script continues working as expected.

Previous: Start a Minecraft server on Linux Next: Keychron Replacement Key