import asyncio import argparse import random from kasa import SmartBulb color_interval = 5 # seconds fade_time = 10 # seconds # Set up global variables colors = [ (205, 92, 92), # IndianRed (240, 128, 128), # LightCoral (255, 160, 122), # LightSalmon (255, 127, 80), # Coral (255, 99, 71), # Tomato (255, 140, 0), # DarkOrange (255, 165, 0), # Orange (255, 215, 0), # Gold (218, 165, 32), # Goldenrod (184, 134, 11), # DarkGoldenrod (154, 205, 50), # YellowGreen (85, 107, 47), # DarkOliveGreen (60, 179, 113), # MediumSeaGreen (46, 139, 87), # SeaGreen (32, 178, 170), # LightSeaGreen (0, 128, 128), # Teal (72, 61, 139), # DarkSlateBlue (106, 90, 205), # SlateBlue (123, 104, 238), # MediumSlateBlue (139, 0, 139), # DarkMagenta (148, 0, 211), # DarkViolet (153, 50, 204), # DarkOrchid (186, 85, 211), # MediumOrchid (216, 191, 216), # Thistle (221, 160, 221), # Plum (238, 130, 238), # Violet (255, 105, 180), # HotPink (255, 182, 193), # LightPink (255, 192, 203), # Pink (255, 228, 196), # Bisque ] color_patterns = { 'default': [ (205, 92, 92), # IndianRed (240, 128, 128), # LightCoral (255, 160, 122), # LightSalmon (255, 127, 80), # Coral (255, 99, 71), # Tomato (255, 140, 0), # DarkOrange (255, 165, 0), # Orange (255, 215, 0), # Gold (218, 165, 32), # Goldenrod (184, 134, 11), # DarkGoldenrod (154, 205, 50), # YellowGreen (85, 107, 47), # DarkOliveGreen (60, 179, 113), # MediumSeaGreen (46, 139, 87), # SeaGreen (32, 178, 170), # LightSeaGreen (0, 128, 128), # Teal (72, 61, 139), # DarkSlateBlue (106, 90, 205), # SlateBlue (123, 104, 238), # MediumSlateBlue (139, 0, 139), # DarkMagenta (148, 0, 211), # DarkViolet (153, 50, 204), # DarkOrchid (186, 85, 211), # MediumOrchid (216, 191, 216), # Thistle (221, 160, 221), # Plum (238, 130, 238), # Violet (255, 105, 180), # HotPink (255, 182, 193), # LightPink (255, 192, 203), # Pink (255, 228, 196), # Bisque ], 'ocean': [ (0, 204, 255), (153, 204, 255), (204, 229, 255), (204, 255, 242), (204, 255, 204), (255, 255, 204), (255, 230, 204), (255, 204, 204), (255, 204, 255), (204, 204, 255), ], 'sunset': [ (255, 61, 0), (255, 112, 0), (255, 179, 0), (255, 217, 0), (255, 242, 0), (255, 255, 0), (255, 255, 102), (255, 255, 178), (255, 255, 229), (255, 255, 255), ], 'pastel': [ (236, 192, 255), (191, 199, 255), (165, 238, 255), (191, 255, 234), (212, 255, 191), (255, 246, 191), (255, 208, 191), (255, 191, 227), (229, 191, 255), (191, 204, 255), ], 'cool': [ (0, 128, 128), (0, 139, 139), (0, 191, 255), (30, 144, 255), (70, 130, 180), (100, 149, 237), (106, 90, 205) ], 'warm': [ (255, 36, 0), (255, 109, 0), (255, 182, 0), (255, 255, 0), (255, 218, 0), (255, 182, 0), (255, 109, 0) ], 'aurora': [ (255, 0, 0), (255, 165, 0), (255, 255, 0), (0, 128, 0), (0, 0, 255), (75, 0, 130), (238, 130, 238) ], 'candy cane': [(255, 0, 0), (255, 255, 255)], 'ocean': [(0, 25, 255), (0, 255, 255), (255, 255, 255)], 'colorwaves': [(255, 0, 0), (255, 255, 0), (0, 255, 0), (0, 255, 255), (0, 0, 255), (255, 0, 255)], 'tropical': [(255, 51, 0), (255, 255, 0), (0, 255, 51), (0, 255, 255)], 'random cycle': [(255, 0, 0), (255, 255, 0), (0, 255, 0), (0, 255, 255), (0, 0, 255), (255, 0, 255)], 'christmas': [(255, 0, 0), (0, 255, 0)], 'rainbow': [(148, 0, 211), (75, 0, 130), (0, 0, 255), (0, 255, 0), (255, 255, 0), (255, 127, 0), (255, 0, 0)], 'fire': [(255, 25, 0), (255, 100, 0), (255, 200, 0), (255, 255, 0), (255, 255, 255)], 'cloud': [(255, 255, 255), (200, 200, 200), (150, 150, 150), (100, 100, 100), (50, 50, 50), (0, 0, 0)], 'blue': [(0, 0, 255), (0, 70, 255), (0, 140, 255), (0, 210, 255), (0, 255, 255), (0, 255, 210), (0, 255, 140), (0, 255, 70)], 'green': [(0, 255, 0), (70, 255, 0), (140, 255, 0), (210, 255, 0), (255, 255, 0), (255, 210, 0), (255, 140, 0), (255, 70, 0)], 'purple': [(128, 0, 128), (148, 0, 211), (153, 50, 204), (186, 85, 211), (218, 112, 214), (153, 50, 204), (123, 104, 238), (106, 90, 205)], 'pink': [(255, 0, 0), (255, 105, 180), (255, 192, 203), (255, 182, 193), (255, 228, 196), (255, 174, 185), (255, 20, 147), (219, 112, 147)], 'orange': [(255, 165, 0), (255, 193, 37), (255, 215, 0), (255, 216, 0), (255, 223, 0), (255, 191, 0), (255, 140, 0), (255, 69, 0)], 'purplefire': [ (55, 0, 85), (103, 5, 142), (157, 17, 204), (36, 41, 204), (1, 3, 34), (63, 38, 72), (230, 230, 255), (163, 181, 255), (75, 0, 130), (0, 255, 255), (66, 66, 111), (0, 0, 55) ], 'AprilNight': [(50, 13, 61), (5, 17, 56), (7, 62, 146), (0, 205, 255)], 'Aurora2': [(0, 0, 0), (255, 255, 255), (0, 255, 254), (127, 0, 255), (255, 0, 0), (255, 255, 0)], 'LiteLight': [(0, 0, 0), (255, 255, 255), (255, 121, 121), (255, 207, 144), (231, 255, 144), (144, 255, 154), (144, 255, 231), (147, 175, 255), (207, 160, 255), (255, 146, 255)], 'cyane': [(0, 255, 255), (0, 255, 127), (0, 127, 255), (0, 0, 255), (127, 0, 255), (255, 0, 255)], 'light_pink': [(255, 204, 204), (255, 153, 153), (255, 102, 102), (255, 51, 51), (255, 0, 0), (204, 0, 0)], 'tiamat': [(255, 247, 225), (255, 204, 204), (204, 204, 255), (204, 204, 0), (255, 153, 204), (204, 255, 204)] } colors = color_patterns['default'] parser = argparse.ArgumentParser() parser.add_argument("-l", action='store_true', help="list the available color patterns") parser.add_argument("-p", help="specify the color pattern to use") parser.add_argument('-i', type=int, nargs=1, help="specify the interval between colors. set to 0 to always be fadin'") parser.add_argument('-f', type=int, nargs=1, help="specify the time to fade between colors. set to 0 to instantly transition.") parser.add_argument('ip', help="specify the ip address") args = parser.parse_args() print(f'args: {args}') if args.p: try: colors = color_patterns.get(args.p, colors) print(f'color pattern {args.p} color lineup: {colors}') except: print("not a valid pattern") if args.l: print('available patterns:') for pattern in color_patterns: print(pattern) print('use -p to choose one') exit() if args.i: color_interval=args.i[0] if args.f: fade_time=args.f[0] if args.ip: bulbip=args.ip def get_random_color(colors): return random.choice(colors) async def main(): bulb = SmartBulb(bulbip) await bulb.update() # set initial color to red await bulb.set_hsv(*rgb_to_hsv(*colors[0], 0.9)) i = 0 while True: # wait for color interval await asyncio.sleep(color_interval) # shift color to the next one in the list i = (i + 1) % len(colors) # set the bulb to the next color in the list with 90% saturation await transition_color(bulb, colors[i]) async def transition_color(bulb, target_color): current_color = await get_current_color(bulb) start_hsv = current_color #start_hsv = rgb_to_hsv(*current_color) target_hsv = rgb_to_hsv(*target_color) steps = int(fade_time * 10) try: for i in range(steps + 1): ratio = i / steps # interpolate HSV values hsv = tuple(int(start_hsv[j] + ratio * (target_hsv[j] - start_hsv[j])) for j in range(3)) # clip brightness value to valid range hsv = (hsv[0], hsv[1], min(max(hsv[2], 0), 100)) # set bulb color #print(f'delay: {fade_time}') #print(f'start color: {start_hsv}- target: {target_hsv} - setting bulb to: {hsv}') await bulb.set_hsv(*hsv) await asyncio.sleep(fade_time/steps) except: await bulb.set_hsv(*target_hsv) # wait for fade step async def get_current_color(bulb): await bulb.update() return bulb.hsv def rgb_to_hsv(r, g, b, s=1): # convert RGB color to HSV color with specified saturation max_val = max(r, g, b) min_val = min(r, g, b) delta = max_val - min_val v = max_val / 255 if max_val == 0: s = 0 else: s = delta / max_val if delta == 0: h = 0 elif max_val == r: h = ((g - b) / delta) % 6 elif max_val == g: h = (b - r) / delta + 2 else: h = (r - g) / delta + 4 h = int(h * 60) s = int(s * 100) v = min(max(v, 0), 1) # clip v value to valid range v = int(v * 100) return (h, s, v) asyncio.run(main())