Last active
January 16, 2024 19:54
-
-
Save swhitt/91f5402768261273e30e211e667ee006 to your computer and use it in GitHub Desktop.
Python script to light up the last LED on each string of Twinkly LED lights using the `xled` library.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
""" | |
This script was written to help me find the end light on each strand of my | |
Twinkly Christmas Lights. It's much easier to remove the lights from the tree | |
when you know where the end is. :) | |
Upon execution, the script automatically discovers all Twinkly devices on your | |
local network and illuminates the last LED of each strand with a random color. | |
Realtime mode on the devices has a duration of 60 seconds. The strands will | |
revert to the default lighting pattern once this has elapsed. | |
Make sure to `pip install xled` first. | |
""" | |
import xled | |
import struct | |
import io | |
import random | |
def make_custom_frame(num_leds, positions, colors): | |
""" | |
Creates a custom frame for a given number of LEDs. | |
Each LED is set to black (off) initially, and then specific LEDs | |
are set to the provided colors at the given positions. | |
:param num_leds: The total number of LEDs. | |
:param positions: A list of positions where LEDs should be turned on. | |
:param colors: A list of RGB color tuples corresponding to each position. | |
:return: A BytesIO object containing the binary data for the frame. | |
""" | |
frame = [b"\x00\x00\x00"] * num_leds # Initialize all LEDs to off (black) | |
for pos, color in zip(positions, colors): | |
if 0 <= pos < num_leds: | |
frame[pos] = struct.pack(">BBB", *color) | |
frame_data = io.BytesIO() | |
frame_data.write(b"".join(frame)) | |
frame_data.seek(0) | |
return frame_data | |
def random_color(): | |
""" | |
:return: A tuple representing a random choice of red, green, or blue. | |
""" | |
rgbl = [255, 0, 0] | |
random.shuffle(rgbl) | |
return tuple(rgbl) | |
def light_last_led_on_each_string(device): | |
""" | |
Lights up the last LED on each string of a given Twinkly device. | |
:param device: An xled Device object representing a Twinkly device. | |
""" | |
ctr = xled.ControlInterface(device.ip_address, device.hw_address) | |
ctr.set_mode("rt") # Set the device to real-time mode | |
num_leds = ctr.get_device_info()["number_of_led"] | |
led_config = ctr.get_led_config()["strings"] | |
positions_to_light = [ | |
light_string["first_led_id"] + light_string["length"] - 1 | |
for light_string in led_config | |
] | |
colors = [random_color() for _ in positions_to_light] | |
for i, (pos, color) in enumerate(zip(positions_to_light, colors)): | |
print(f"Lighting strand {i + 1} at index {pos} ({color})") | |
frame_data = make_custom_frame(num_leds, positions_to_light, colors) | |
ctr.set_rt_frame_socket(frame_data, 3) | |
if __name__ == "__main__": | |
timeout_seconds = 20 | |
try: | |
for device in xled.discover.xdiscover(timeout=timeout_seconds): | |
print(f"Found device at {device.ip_address} ({device.hw_address})!") | |
light_last_led_on_each_string(device) | |
except xled.exceptions.DiscoverTimeout: | |
print( | |
f"Discovery timed out after {timeout_seconds} seconds with no further devices found." | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment