Files
lighting-controller/src/main.py
jimmy 8d0c9edf5d ws: adopt nested {'0': {...}} payloads
midi: bind patterns to notes 36+; beat triggers selected pattern; include n index; CC map: 30=R 31=G 32=B 33=brightness 34=n1 35=n2 36=delay; send n1/n2 raw 0-127

gui: show n1 and n2 in status
2025-09-17 20:22:11 +12:00

124 lines
4.6 KiB
Python

import asyncio
import tkinter as tk
from tkinter import ttk, messagebox # Import messagebox for confirmations
import json
from async_tkinter_loop import async_handler, async_mainloop
from networking import WebSocketClient
import color_utils
import time
from midi import MidiHandler # Import MidiHandler
# Dark theme colors (unchanged)
bg_color = "#2e2e2e"
fg_color = "white"
trough_color_red = "#4a0000"
trough_color_green = "#004a00"
trough_color_blue = "#00004a"
trough_color_brightness = "#4a4a4a"
trough_color_delay = "#4a4a4a"
active_bg_color = "#4a4a4a"
highlight_pattern_color = "#6a5acd"
# New color for active color in palette
active_palette_color_border = "#FFD700" # Gold color
class App:
def __init__(self) -> None:
self.root = tk.Tk()
# self.root.attributes("-fullscreen", True)
self.root.configure(bg=bg_color)
# --- WebSocketClient ---
self.websocket_client = WebSocketClient("ws://192.168.4.1:80/ws")
self.root.after(100, async_handler(self.websocket_client.connect))
# --- MIDI Handler ---
MIDI_PORT_INDEX = 1 # Adjust as needed
WEBSOCKET_SERVER_URI = "ws://192.168.4.1:80/ws"
self.midi_handler = MidiHandler(MIDI_PORT_INDEX, WEBSOCKET_SERVER_URI)
self.midi_task: asyncio.Task | None = None
# Start MIDI in background
self.root.after(0, async_handler(self.start_midi))
# Configure ttk style (unchanged)
style = ttk.Style()
style.theme_use("alt")
style.configure(".", background=bg_color, foreground=fg_color, font=("Arial", 14))
style.configure("TNotebook", background=bg_color, borderwidth=0)
style.configure(
"TNotebook.Tab", background=bg_color, foreground=fg_color, font=("Arial", 30), padding=[10, 5]
)
# --- Status Frame ---
status_frame = ttk.Frame(self.root)
status_frame.pack(padx=16, pady=16, fill="x")
self.lbl_delay = ttk.Label(status_frame, text="Delay: -")
self.lbl_brightness = ttk.Label(status_frame, text="Brightness: -")
self.lbl_r = ttk.Label(status_frame, text="R: -")
self.lbl_g = ttk.Label(status_frame, text="G: -")
self.lbl_b = ttk.Label(status_frame, text="B: -")
self.lbl_pattern = ttk.Label(status_frame, text="Pattern: -")
self.lbl_n1 = ttk.Label(status_frame, text="n1: -")
self.lbl_n2 = ttk.Label(status_frame, text="n2: -")
self.lbl_bpm = ttk.Label(status_frame, text="BPM: -")
for w in (self.lbl_delay, self.lbl_brightness, self.lbl_r, self.lbl_g, self.lbl_b, self.lbl_pattern, self.lbl_n1, self.lbl_n2, self.lbl_bpm):
w.pack(anchor="w")
# schedule periodic UI updates
self.root.after(200, self.update_status_labels)
self.root.protocol("WM_DELETE_WINDOW", self.on_closing)
async_mainloop(self.root)
@async_handler
async def start_midi(self):
# Launch MidiHandler.run() as a background task
if self.midi_task is None or self.midi_task.done():
self.midi_task = asyncio.create_task(self.midi_handler.run())
def on_closing(self):
print("Closing application...")
if self.midi_task and not self.midi_task.done():
self.midi_task.cancel()
asyncio.create_task(self.websocket_client.close())
self.root.destroy()
# --- Asynchronous Update Functions ---
@async_handler
async def update_rgb(self, tab):
pass
def update_status_labels(self):
# Pull values from midi_handler
delay = self.midi_handler.delay
brightness = self.midi_handler.brightness
r = getattr(self.midi_handler, 'color_r', 0)
g = getattr(self.midi_handler, 'color_g', 0)
b = getattr(self.midi_handler, 'color_b', 0)
pattern = getattr(self.midi_handler, 'current_pattern', '') or '-'
n1 = getattr(self.midi_handler, 'n1', '-')
n2 = getattr(self.midi_handler, 'n2', '-')
bpm = getattr(self.midi_handler, 'current_bpm', None)
bpm_text = f"{bpm:.2f}" if isinstance(bpm, (float, int)) else "-"
self.lbl_delay.config(text=f"Delay: {delay}")
self.lbl_brightness.config(text=f"Brightness: {brightness}")
self.lbl_r.config(text=f"R: {r}")
self.lbl_g.config(text=f"G: {g}")
self.lbl_b.config(text=f"B: {b}")
self.lbl_pattern.config(text=f"Pattern: {pattern}")
self.lbl_n1.config(text=f"n1: {n1}")
self.lbl_n2.config(text=f"n2: {n2}")
self.lbl_bpm.config(text=f"BPM: {bpm_text}")
# reschedule
self.root.after(200, self.update_status_labels)
if __name__ == "__main__":
app = App()