diff --git a/src/main.py b/src/main.py index 43f0ca6..8651e25 100644 --- a/src/main.py +++ b/src/main.py @@ -94,6 +94,42 @@ class App: self.root.protocol("WM_DELETE_WINDOW", self.on_closing) async_mainloop(self.root) + + def get_pattern_settings(self, tab_name, pattern_name): + """Get pattern-specific settings (colors, delay, n params). Returns defaults if not found.""" + light_settings = self.settings["lights"][tab_name]["settings"] + if "patterns" not in light_settings: + light_settings["patterns"] = {} + if pattern_name not in light_settings["patterns"]: + light_settings["patterns"][pattern_name] = {} + + pattern_settings = light_settings["patterns"][pattern_name] + return { + "colors": pattern_settings.get("colors", ["#000000"]), + "delay": pattern_settings.get("delay", 100), + "n1": pattern_settings.get("n1", 10), + "n2": pattern_settings.get("n2", 10), + "n3": pattern_settings.get("n3", 10), + "n4": pattern_settings.get("n4", 10), + } + + def save_pattern_settings(self, tab_name, pattern_name, colors=None, delay=None, n_params=None): + """Save pattern-specific settings.""" + light_settings = self.settings["lights"][tab_name]["settings"] + if "patterns" not in light_settings: + light_settings["patterns"] = {} + if pattern_name not in light_settings["patterns"]: + light_settings["patterns"][pattern_name] = {} + + pattern_settings = light_settings["patterns"][pattern_name] + if colors is not None: + pattern_settings["colors"] = colors + if delay is not None: + pattern_settings["delay"] = delay + if n_params is not None: + for i in range(1, 5): + if f"n{i}" in n_params: + pattern_settings[f"n{i}"] = n_params[f"n{i}"] def on_closing(self): print("Closing application...") @@ -115,18 +151,19 @@ class App: slider_length = 600 slider_width = 50 - # Extract initial color, brightness, and delay - initial_colors = initial_settings.get("colors", ["#000000"]) - initial_hex_color = initial_colors[0] if initial_colors else "#000000" - initial_brightness = initial_settings.get("brightness", 127) - initial_delay = initial_settings.get("delay", 0) + # Get initial pattern and load pattern-specific settings initial_pattern = initial_settings.get("pattern", "on") - initial_n1 = initial_settings.get("n1", 10) - initial_n2 = initial_settings.get("n2", 10) - initial_n3 = initial_settings.get("n3", 10) - initial_n4 = initial_settings.get("n4", 10) - initial_n5 = initial_settings.get("n5", 10) - initial_n6 = initial_settings.get("n6", 10) + initial_brightness = initial_settings.get("brightness", 127) # Global brightness + pattern_settings = self.get_pattern_settings(tab_name, initial_pattern) + + # Extract initial color, delay, and n params from pattern-specific settings + initial_colors = pattern_settings["colors"] + initial_hex_color = initial_colors[0] if initial_colors else "#000000" + initial_delay = pattern_settings["delay"] + initial_n1 = pattern_settings["n1"] + initial_n2 = pattern_settings["n2"] + initial_n3 = pattern_settings["n3"] + initial_n4 = pattern_settings["n4"] initial_r, initial_g, initial_b = color_utils.hex_to_rgb(initial_hex_color) @@ -241,11 +278,11 @@ class App: "buttonbackground": active_bg_color, } - # Create n1-n6 inputs in a grid with arrows on both sides + # Create n1-n4 inputs in a grid with arrows on both sides n_inputs = {} - for i in range(1, 7): + for i in range(1, 5): n_frame = tk.Frame(n_inputs_inner_frame, bg=bg_color) - n_frame.grid(row=(i-1)//3, column=(i-1)%3, padx=10, pady=10) + n_frame.grid(row=(i-1)//2, column=(i-1)%2, padx=10, pady=10) tk.Label(n_frame, text=f"n{i}", font=("Arial", 20), bg=bg_color, fg=fg_color).pack(pady=(0, 5)) @@ -340,10 +377,6 @@ class App: "n3_var": n_inputs["n3_var"], "n4": n_inputs["n4"], "n4_var": n_inputs["n4_var"], - "n5": n_inputs["n5"], - "n5_var": n_inputs["n5_var"], - "n6": n_inputs["n6"], - "n6_var": n_inputs["n6_var"], "selected_color_index": 0, # Default to the first color } tab.colors_in_palette = initial_colors.copy() # Store the list of hex colors for this tab @@ -571,7 +604,7 @@ class App: # If not in 'transition' mode, but a color is selected, update sliders to that. # Or, if this is a fresh load/tab change, ensure it's the first color. # This ensures the sliders consistently show the color that will be sent - # for 'on'/'blink' based on the palette's first entry. + # for 'on' based on the palette's first entry. hex_color = tab.colors_in_palette[tab.widgets["selected_color_index"]] r, g, b = color_utils.hex_to_rgb(hex_color) tab.widgets["red_slider"].set(r) @@ -608,8 +641,12 @@ class App: # In a full reload, create_tabs ensures it is. return + # Get current pattern and load pattern-specific settings + current_pattern = initial_settings.get("pattern", "on") + pattern_settings = self.get_pattern_settings(selected_tab_name, current_pattern) + # Update the local colors_in_palette list for the tab - current_tab_widget.colors_in_palette = initial_settings.get("colors", ["#000000"]).copy() + current_tab_widget.colors_in_palette = pattern_settings["colors"].copy() current_tab_widget.widgets["selected_color_index"] = 0 # Default to first color # Refresh the color palette display and select the first color @@ -621,19 +658,19 @@ class App: current_tab_widget.widgets["green_slider"].set(0) current_tab_widget.widgets["blue_slider"].set(0) - # Update brightness and delay sliders + # Update brightness (global) and delay (pattern-specific) sliders current_tab_widget.widgets["brightness_slider"].set(initial_settings.get("brightness", 127)) # Convert delay to slider position using logarithmic scale - initial_delay = initial_settings.get("delay", 0) + initial_delay = pattern_settings["delay"] initial_delay_slider_pos = delay_to_slider(initial_delay) current_tab_widget.widgets["delay_slider"].set(initial_delay_slider_pos) # Update the delay value label to show the actual delay value if "delay_value_label" in current_tab_widget.widgets: current_tab_widget.widgets["delay_value_label"].config(text=f"{initial_delay}") - # Update n parameter inputs - for i in range(1, 7): - current_tab_widget.widgets[f"n{i}_var"].set(str(initial_settings.get(f"n{i}", 10))) + # Update n parameter inputs (pattern-specific) + for i in range(1, 5): + current_tab_widget.widgets[f"n{i}_var"].set(str(pattern_settings[f"n{i}"])) # Highlight the active pattern button initial_pattern = initial_settings.get("pattern", "on") @@ -761,7 +798,7 @@ class App: if current_pattern == "transition": colors_to_send = tab.colors_in_palette.copy() - elif current_pattern in ["on", "blink", "theater_chase", "flicker"]: # Add other patterns that use a single color + elif current_pattern in ["on", "theater_chase", "flicker"]: # Add other patterns that use a single color if tab.colors_in_palette: # For non-transition patterns, the device typically uses only the first color. # However, if a user picks a color from the palette, we want THAT color to be the one @@ -788,19 +825,23 @@ class App: "n2": int(tab.widgets["n2_var"].get()), "n3": int(tab.widgets["n3_var"].get()), "n4": int(tab.widgets["n4_var"].get()), - "n5": int(tab.widgets["n5_var"].get()), - "n6": int(tab.widgets["n6_var"].get()), }, } - # Update the settings object with the new color list (and potentially other synced values) - self.settings["lights"][selected_server]["settings"]["colors"] = tab.colors_in_palette.copy() - self.settings["lights"][selected_server]["settings"]["brightness"] = tab.widgets["brightness_slider"].get() - # Convert slider position to actual delay using logarithmic scale + # Update the settings object - save pattern-specific settings + current_pattern = self.settings["lights"][selected_server]["settings"].get("pattern", "on") slider_value = tab.widgets["delay_slider"].get() - self.settings["lights"][selected_server]["settings"]["delay"] = slider_to_delay(slider_value) - for i in range(1, 7): - self.settings["lights"][selected_server]["settings"][f"n{i}"] = int(tab.widgets[f"n{i}_var"].get()) + delay = slider_to_delay(slider_value) + n_params = {f"n{i}": int(tab.widgets[f"n{i}_var"].get()) for i in range(1, 5)} + + # Save pattern-specific settings + self.save_pattern_settings(selected_server, current_pattern, + colors=tab.colors_in_palette.copy(), + delay=delay, + n_params=n_params) + + # Brightness is global + self.settings["lights"][selected_server]["settings"]["brightness"] = tab.widgets["brightness_slider"].get() self.settings.save() await self.websocket_client.send_data(payload) @@ -851,8 +892,9 @@ class App: "delay": delay, }, } - # Update the settings object with the new delay - self.settings["lights"][selected_server]["settings"]["delay"] = delay + # Update pattern-specific delay setting + current_pattern = self.settings["lights"][selected_server]["settings"].get("pattern", "on") + self.save_pattern_settings(selected_server, current_pattern, delay=delay) self.settings.save() await self.websocket_client.send_data(payload) print(f"Sent delay payload: {payload}") @@ -863,7 +905,7 @@ class App: async def update_n_params(self, tab): try: n_params = {} - for i in range(1, 7): + for i in range(1, 5): n_var = tab.widgets[f"n{i}_var"] n_params[f"n{i}"] = int(n_var.get()) @@ -876,9 +918,9 @@ class App: "names": names, "settings": n_params, } - # Update the settings object with the new n params - for key, value in n_params.items(): - self.settings["lights"][selected_server]["settings"][key] = value + # Update pattern-specific n params + current_pattern = self.settings["lights"][selected_server]["settings"].get("pattern", "on") + self.save_pattern_settings(selected_server, current_pattern, n_params=n_params) self.settings.save() await self.websocket_client.send_data(payload) print(f"Sent n params payload: {payload}") @@ -901,31 +943,53 @@ class App: return current_settings_for_tab = self.settings["lights"][tab_name]["settings"] + + # Save current pattern's settings before switching + old_pattern = current_settings_for_tab.get("pattern", "on") + current_delay = slider_to_delay(current_tab_widget.widgets["delay_slider"].get()) + current_n_params = {f"n{i}": int(current_tab_widget.widgets[f"n{i}_var"].get()) for i in range(1, 5)} + self.save_pattern_settings(tab_name, old_pattern, + colors=current_tab_widget.colors_in_palette.copy(), + delay=current_delay, + n_params=current_n_params) + + # Load the new pattern's settings + new_pattern_settings = self.get_pattern_settings(tab_name, pattern_name) + + # Update UI with new pattern's settings + current_tab_widget.colors_in_palette = new_pattern_settings["colors"].copy() + self.refresh_color_palette_display(current_tab_widget) + + # Update delay slider + delay_slider_pos = delay_to_slider(new_pattern_settings["delay"]) + current_tab_widget.widgets["delay_slider"].set(delay_slider_pos) + if "delay_value_label" in current_tab_widget.widgets: + current_tab_widget.widgets["delay_value_label"].config(text=f"{new_pattern_settings['delay']}") + + # Update n params + for i in range(1, 5): + current_tab_widget.widgets[f"n{i}_var"].set(str(new_pattern_settings[f"n{i}"])) + + # Update RGB sliders to first color + if current_tab_widget.colors_in_palette: + hex_color = current_tab_widget.colors_in_palette[0] + r, g, b = color_utils.hex_to_rgb(hex_color) + current_tab_widget.widgets["red_slider"].set(r) + current_tab_widget.widgets["green_slider"].set(g) + current_tab_widget.widgets["blue_slider"].set(b) + current_tab_widget.widgets["selected_color_index"] = 0 + self._highlight_selected_color_swatch(current_tab_widget) payload_settings = { "pattern": pattern_name, - "brightness": current_settings_for_tab.get("brightness", 127), - "delay": slider_to_delay(current_tab_widget.widgets["delay_slider"].get()), # Convert from logarithmic slider + "brightness": current_settings_for_tab.get("brightness", 127), # Global brightness + "delay": new_pattern_settings["delay"], + "colors": new_pattern_settings["colors"], } # Include n parameters - for i in range(1, 7): - payload_settings[f"n{i}"] = int(current_tab_widget.widgets[f"n{i}_var"].get()) - - # Determine colors to send based on the *newly selected* pattern - if pattern_name == "transition": - payload_settings["colors"] = current_tab_widget.colors_in_palette.copy() - elif pattern_name in ["on", "blink"]: # Add other patterns that use a single color here - # When switching TO 'on' or 'blink', ensure the color sent is the one - # currently displayed on the sliders (which reflects the selected palette color). - r = current_tab_widget.widgets["red_slider"].get() - g = current_tab_widget.widgets["green_slider"].get() - b = current_tab_widget.widgets["blue_slider"].get() - hex_color_from_sliders = f"#{r:02x}{g:02x}{b:02x}" - payload_settings["colors"] = [hex_color_from_sliders] - else: - # For other patterns, send the full palette, device might ignore or use default - payload_settings["colors"] = current_tab_widget.colors_in_palette.copy() + for i in range(1, 5): + payload_settings[f"n{i}"] = new_pattern_settings[f"n{i}"] payload = { "save": True, @@ -933,12 +997,8 @@ class App: "settings": payload_settings, } - # Update the settings object with the new pattern and current colors/brightness/delay + # Update the current pattern self.settings["lights"][tab_name]["settings"]["pattern"] = pattern_name - # Always save the full current palette state in settings. - self.settings["lights"][tab_name]["settings"]["colors"] = current_tab_widget.colors_in_palette.copy() - for i in range(1, 7): - self.settings["lights"][tab_name]["settings"][f"n{i}"] = int(current_tab_widget.widgets[f"n{i}"].get()) self.settings.save() self.highlight_pattern_button(current_tab_widget, pattern_name) @@ -957,15 +1017,20 @@ class App: if not hasattr(current_tab_widget, "colors_in_palette"): return # Tab not fully initialized yet - # Update settings for the current tab in the self.settings object - self.settings["lights"][selected_server]["settings"]["colors"] = current_tab_widget.colors_in_palette - self.settings["lights"][selected_server]["settings"]["brightness"] = current_tab_widget.widgets["brightness_slider"].get() - # Convert slider position to actual delay using logarithmic scale + # Update settings for the current tab - save pattern-specific settings + current_pattern = self.settings["lights"][selected_server]["settings"].get("pattern", "on") slider_value = current_tab_widget.widgets["delay_slider"].get() delay = slider_to_delay(slider_value) - self.settings["lights"][selected_server]["settings"]["delay"] = delay - for i in range(1, 7): - self.settings["lights"][selected_server]["settings"][f"n{i}"] = int(current_tab_widget.widgets[f"n{i}"].get()) + n_params = {f"n{i}": int(current_tab_widget.widgets[f"n{i}_var"].get()) for i in range(1, 5)} + + # Save pattern-specific settings + self.save_pattern_settings(selected_server, current_pattern, + colors=current_tab_widget.colors_in_palette, + delay=delay, + n_params=n_params) + + # Brightness is global + self.settings["lights"][selected_server]["settings"]["brightness"] = current_tab_widget.widgets["brightness_slider"].get() # The pattern is updated in send_pattern already, but ensure consistency # For simplicity, we assume send_pattern is the primary way to change pattern.