From 9cf1855b51734f5469e594497d2feec288610f48 Mon Sep 17 00:00:00 2001 From: Jimmy Date: Sat, 4 Oct 2025 10:02:47 +1300 Subject: [PATCH] UI: Add rate limiting to brightness control - Add 100ms minimum interval between brightness updates to backend - Keep UI responsive with immediate local brightness updates - Prevent backend overload from rapid MIDI CC33 changes - Add time import for rate limiting functionality - Add debug output to show when updates are sent vs rate limited - Improve smoothness and reduce network traffic - Protect lighting controller from too many rapid parameter changes --- src/ui_client.py | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/ui_client.py b/src/ui_client.py index fc3e50c..d21c742 100644 --- a/src/ui_client.py +++ b/src/ui_client.py @@ -18,6 +18,7 @@ import atexit import signal import urllib.request import urllib.error +import time # Single instance locker to prevent multiple UI processes class SingleInstanceLocker: @@ -169,6 +170,10 @@ class MidiController: self.knob7 = 0 self.knob8 = 0 self.beat_sending_enabled = True + + # Rate limiting for brightness control + self.last_brightness_update = 0 + self.brightness_update_interval = 0.1 # 100ms minimum between updates # Color palette selection state (two selected indices, alternating target) self.selected_indices = [0, 1] self.next_selected_target = 0 # 0 selects color1 next, 1 selects color2 next @@ -313,9 +318,20 @@ class MidiController: logging.info(f"MIDI CC {control}: {value}") if control == 33: # Brightness (0-100) - self.brightness = round((value / 127) * 100) - if callable(self.on_parameters_change): - self.on_parameters_change({"brightness": self.brightness}) + current_time = time.time() + new_brightness = round((value / 127) * 100) + + # Rate limiting: only update if enough time has passed + if current_time - self.last_brightness_update >= self.brightness_update_interval: + self.brightness = new_brightness + self.last_brightness_update = current_time + print(f"Brightness changed to: {self.brightness}") + if callable(self.on_parameters_change): + self.on_parameters_change({"brightness": self.brightness}) + else: + # Still update the local value for UI display, but don't send to backend + self.brightness = new_brightness + print(f"Brightness updated locally to: {self.brightness} (rate limited)") elif control == 34: # n1 (0-255) self.n1 = int(value) if callable(self.on_parameters_change): @@ -1027,12 +1043,15 @@ class UIClient: async def persist_parameters(self, params: dict): """POST parameter changes via REST to /api/parameters.""" try: + print(f"Sending parameters to backend: {params}") data = json.dumps(params).encode('utf-8') req = urllib.request.Request(f"{HTTP_BASE}/api/parameters", data=data, method='POST', headers={'Content-Type': 'application/json'}) with urllib.request.urlopen(req, timeout=2.0) as resp: _ = resp.read() + print(f"Successfully sent parameters: {params}") except Exception as e: logging.debug(f"Failed to persist parameters (REST): {e}") + print(f"Failed to send parameters: {e}") def on_closing(self): """Handle application closing."""