Explore how to control your RGB lights using Python and TinyTuya with a fun GUI!
As the world of smart home technology expands, so does the excitement of creating personalized automation solutions. Recently, I dove into the TinyTuya library, which allows for seamless control of Tuya-based smart devices using Python. In this post, Iβll guide you through setting up TinyTuya and showcase a script I created to control RGB lights using a graphical user interface.
Setting Up TinyTuya
To get started, you need to install TinyTuya. You can easily do this with pip.
pip install tinytuyaMy Python Script: Rainbow and Pastel Light Control
import tkinter as tk
from tkinter import ttk
from PIL import Image, ImageTk
import tinytuya
import threading
import time
class RainbowPastelLightControlApp:
def __init__(self, root):
self.root = root
self.root.title("Rainbow and Pastel Light Control")
self.root.configure(bg="#F0E6FF") # Light lavender background
self.root.geometry("900x700") # Increased size to accommodate more buttons
self.setup_styles()
self.setup_lights()
self.create_ui()
def setup_styles(self):
self.style = ttk.Style(self.root)
self.style.theme_use('clam')
self.style.configure('TFrame', background='#F0E6FF')
self.style.configure('TLabel', background='#F0E6FF', foreground='#5D5D5D', font=('Helvetica', 12))
self.style.configure('TScale', background='#F0E6FF', troughcolor='#D4C5F9', sliderrelief='flat')
self.style.configure('TButton', background='#B49FDC', foreground='#5D5D5D', font=('Helvetica', 10, 'bold'))
self.style.map('TButton', background=[('active', '#A28ACB')])
def setup_lights(self):
self.lights = [
tinytuya.BulbDevice(
dev_id='eb0a0f9144fb36a2c9azuq',
address='192.168.1.3',
local_key="GchH'(YZt[{]E(|5",
version=3.3
),
tinytuya.BulbDevice(
dev_id='ebf7e63c5fdb0cbbe6zekv',
address='192.168.1.7',
local_key="#s]'vhfRpu]oM_$*",
version=3.3
)
]
self.light_states = {'is_on': False}
for light in self.lights:
light.set_version(3.3)
light.set_socketPersistent(True)
def create_ui(self):
main_frame = ttk.Frame(self.root, padding=20)
main_frame.pack(fill=tk.BOTH, expand=True)
left_frame = ttk.Frame(main_frame)
left_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(0, 10))
right_frame = ttk.Frame(main_frame)
right_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=(10, 0))
self.create_color_buttons(left_frame)
self.create_white_controls(right_frame)
self.create_toggle_button(right_frame)
self.create_rave_controls(right_frame)
def create_color_buttons(self, parent):
rainbow_colors = {
"Red": "#FF0000",
"Orange": "#FF7F00",
"Yellow": "#FFFF00",
"Green": "#00FF00",
"Blue": "#0000FF",
"Indigo": "#4B0082",
"Violet": "#8F00FF",
}
pastel_colors = {
"Pastel Lavender": "#B49FDC",
"Pastel Sky Blue": "#C5EBFE",
"Pastel Mint Green": "#A5F8CE",
"Pastel Lemon": "#FEFD97",
"Pastel Peach": "#FEC9A7",
"Pastel Pink": "#F197C0",
}
all_colors = {**rainbow_colors, **pastel_colors, "White": "#FFFFFF"}
for color_name, hex_color in all_colors.items():
button = tk.Button(parent, text=color_name, bg=hex_color, fg=self.get_contrasting_text_color(hex_color),
command=lambda c=self.hex_to_rgb(hex_color): self.set_lights_color(c),
font=('Helvetica', 12, 'bold'), relief=tk.FLAT, padx=10, pady=5,
borderwidth=0, highlightthickness=0)
button.pack(fill=tk.X, pady=5, ipady=5)
# Bind hover events
button.bind("<Enter>", lambda e, b=button: self.on_enter(e, b))
button.bind("<Leave>", lambda e, b=button: self.on_leave(e, b))
def create_white_controls(self, parent):
ttk.Label(parent, text="White Light Brightness").pack(pady=(0, 5))
self.white_brightness_slider = ttk.Scale(parent, from_=0, to=100, orient=tk.HORIZONTAL, command=self.set_white_brightness)
self.white_brightness_slider.set(100)
self.white_brightness_slider.pack(fill=tk.X, pady=(0, 15))
ttk.Label(parent, text="White Light Color Temperature").pack(pady=(0, 5))
self.color_temp_slider = ttk.Scale(parent, from_=0, to=100, orient=tk.HORIZONTAL, command=self.set_color_temperature)
self.color_temp_slider.set(0)
self.color_temp_slider.pack(fill=tk.X, pady=(0, 15))
def create_toggle_button(self, parent):
bulb_on_img = Image.open("/home/laura/Documents/code/light-bulb-hacking/bulbs/bulb-on.jpg")
bulb_off_img = Image.open("/home/laura/Documents/code/light-bulb-hacking/bulbs/bulb-off.jpeg")
bulb_on_img = bulb_on_img.resize((50, 50), Image.Resampling.LANCZOS)
bulb_off_img = bulb_off_img.resize((50, 50), Image.Resampling.LANCZOS)
self.bulb_on_img = ImageTk.PhotoImage(bulb_on_img)
self.bulb_off_img = ImageTk.PhotoImage(bulb_off_img)
self.toggle_button = ttk.Button(parent, image=self.bulb_off_img, command=self.toggle_lights)
self.toggle_button.pack(pady=15)
def create_rave_controls(self, parent):
ttk.Label(parent, text="Rave Mode BPM").pack(pady=(0, 5))
self.bpm_slider = ttk.Scale(parent, from_=60, to=180, orient=tk.HORIZONTAL)
self.bpm_slider.set(120)
self.bpm_slider.pack(fill=tk.X, pady=(0, 15))
bpm_frame = ttk.Frame(parent)
bpm_frame.pack(fill=tk.X, pady=(0, 15))
ttk.Label(bpm_frame, text="BPM:").pack(side=tk.LEFT)
self.bpm_entry = ttk.Entry(bpm_frame, width=5, font=('Helvetica', 12))
self.bpm_entry.insert(0, "120")
self.bpm_entry.pack(side=tk.LEFT, padx=(5, 0))
self.rave_button = ttk.Button(parent, text="Start Rave Mode", command=self.toggle_rave_mode)
self.rave_button.pack(fill=tk.X, pady=(0, 15))
def set_lights_color(self, color, brightness=100, colourtemp=0):
if color == (255, 255, 255):
for light in self.lights:
try:
light.set_white_percentage(brightness, colourtemp)
except Exception as e:
print(f"Error setting white color: {e}")
else:
r, g, b = color
for light in self.lights:
try:
light.set_colour(r, g, b)
except Exception as e:
print(f"Error setting color: {e}")
def toggle_lights(self):
if self.light_states['is_on']:
for light in self.lights:
try:
light.turn_off()
except Exception as e:
print(f"Error turning off light: {e}")
self.light_states['is_on'] = False
self.toggle_button.config(image=self.bulb_off_img)
else:
for light in self.lights:
try:
light.turn_on()
except Exception as e:
print(f"Error turning on light: {e}")
self.light_states['is_on'] = True
self.toggle_button.config(image=self.bulb_on_img)
def set_white_brightness(self, value):
brightness = int(float(value))
color_temp = self.color_temp_slider.get()
self.set_lights_color((255, 255, 255), brightness, color_temp)
def set_color_temperature(self, value):
color_temp = int(float(value))
brightness = self.white_brightness_slider.get()
self.set_lights_color((255, 255, 255), brightness, color_temp)
def toggle_rave_mode(self):
if not hasattr(self, 'rave_mode_active'):
self.rave_mode_active = False
self.rave_mode_active = not self.rave_mode_active
if self.rave_mode_active:
threading.Thread(target=self.rave_mode).start()
self.rave_button.config(text="Stop Rave Mode")
else:
self.rave_button.config(text="Start Rave Mode")
def rave_mode(self):
all_colors = [
(255, 0, 0), # Red
(255, 127, 0), # Orange
(255, 255, 0), # Yellow
(0, 255, 0), # Green
(0, 0, 255), # Blue
(75, 0, 130), # Indigo
(143, 0, 255), # Violet
(180, 159, 220), # Pastel Lavender
(197, 235, 254), # Pastel Sky Blue
(165, 248, 206), # Pastel Mint Green
(254, 253, 151), # Pastel Lemon
(254, 201, 167), # Pastel Peach
(241, 151, 192), # Pastel Pink
]
while self.rave_mode_active:
for color in all_colors:
self.set_lights_color(color)
bpm = self.bpm_slider.get()
time.sleep(60 / bpm)
@staticmethod
def hex_to_rgb(hex_color):
return tuple(int(hex_color.lstrip('#')[i:i+2], 16) for i in (0, 2, 4))
def on_enter(self, e, button):
button['background'] = self.lighten_color(button['background'])
def on_leave(self, e, button):
button['background'] = self.darken_color(button['background'])
@staticmethod
def lighten_color(color):
r, g, b = [int(color[i:i+2], 16) for i in (1, 3, 5)]
r = min(255, int(r * 1.2))
g = min(255, int(g * 1.2))
b = min(255, int(b * 1.2))
return f'#{r:02x}{g:02x}{b:02x}'
@staticmethod
def darken_color(color):
r, g, b = [int(color[i:i+2], 16) for i in (1, 3, 5)]
r = max(0, int(r * 0.8))
g = max(0, int(g * 0.8))
b = max(0, int(b * 0.8))
return f'#{r:02x}{g:02x}{b:02x}'
@staticmethod
def get_contrasting_text_color(bg_color):
r, g, b = [int(bg_color.lstrip('#')[i:i+2], 16) for i in (0, 2, 4)]
luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255
return '#000000' if luminance > 0.5 else '#FFFFFF'
if __name__ == "__main__":
root = tk.Tk()
app = RainbowPastelLightControlApp(root)
root.mainloop()Conclusion
With TinyTuya and Python, the possibilities for smart home automation are endless. This script serves as a solid foundation for controlling your lights with flair. Feel free to customize the interface, add more features, or integrate additional devices!

