""" python-kasa smart plug delay off, turn off plug a few hours after a user turns it on manually. intended for ebike charger, turn off after charging a few hours. User supplies: 1) plug name (alias). Use kasa CLI to provision. 2) off delay , in hours Operation: Mostly sleep loop Periodically wake, poll plug for on state if turned on, sleep for X delay upon wake, turn off plug Exceptions may happen if plug unpowered, so use Try. """ import platform import asyncio from time import sleep from kasa.smartdevice import SmartDeviceException from kasa import ( Discover, SmartPlug, ) # provisioned already, with alias my_alias = "Plug_8926_bike" # timer settings, sleep is in seconds off_delay_hours = 4 off_delay_sec = 60 * 60 * off_delay_hours sleep_poll_delay = 5 # poll plug periodically when off sleep_startup_poll = 60 # poll for valid plug upon startup # returns host IP from alias, no async def find_host_from_alias2(alias, target="255.255.255.255", timeout=1, attempts=3): """Discover a device identified by its alias.""" for attempt in range(1, attempts): found_devs = asyncio.run(Discover.discover(target=target, timeout=timeout)) for ip, dev in found_devs.items(): if dev.alias.lower() == alias.lower(): host = dev.host return host return None # can only asyncio.run once, from main(), so all plug comm in this loop, never returns async def plug_loop(plug): await plug.update() # always update once before sending stuff? await plug.turn_off() # default turn off when starting, in case restart. Don't leave on. print(plug.state_information) delayed_poweroff = False # use flag as we can't modify plug state if unpowered # main on/off-sleep loop # also manage exceptions if plug is unpowered. while True: try: await plug.update() except SmartDeviceException: print("kasa exception, plug unavailable.") # plug unpowered, can't read if delayed_poweroff: # plug unpowered, we're trying to turn it off try: await plug.turn_off() delayed_poweroff = False # success except SmartDeviceException: print("kasa exception, trying delayed plug off.") # plug unpowered, can't turn off elif plug.is_on: # user turned on, normal operation print('plug on, starting delay ', off_delay_sec, 'seconds.') # turned on, snooze a few hours await asyncio.sleep(off_delay_sec) print('turning plug off') try: await plug.turn_off() except SmartDeviceException: print("kasa exception, can't turn plug off.") # plug unpowered, can't turn off delayed_poweroff = True # turn off after plug comes back else: await asyncio.sleep(sleep_poll_delay) # snooze when off if __name__ == "__main__": # Windows async config: https://github.com/python-kasa/python-kasa/issues/315 if platform.system() == 'Windows': asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) # wait here until plug is available host = None while host is None: host = find_host_from_alias2(my_alias) if host is None: print("Plug ", my_alias, " not found") sleep(sleep_startup_poll) print(my_alias, " @ ", host) plug = SmartPlug(host) asyncio.run(plug_loop(plug))