From 24803cbde634d56b133ed3c677d41f2514223615 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sat, 29 Nov 2025 08:23:36 +0000 Subject: [PATCH] Implement Preset Management and Event Automation for NeoFlux - Added `neoflux_presets` and `neoflux_events` to settings. - Implemented API endpoints for saving, deleting, and fetching presets. - Added `EventHandlerPlugin` mixin to support `on_event` hook for automation. - Updated frontend UI with preset dropdown, save/delete buttons, and automation settings. - Refactored `LiveGCodeControlViewModel` to handle preset logic and event mappings. - Added `flask` dependency to `__init__.py`. --- octoprint_livegcodecontrol/__init__.py | 46 ++++++- .../static/js/livegcodecontrol.js | 119 ++++++++++++++++++ .../livegcodecontrol_settings.jinja2 | 24 ++++ .../templates/livegcodecontrol_tab.jinja2 | 11 +- 4 files changed, 196 insertions(+), 4 deletions(-) diff --git a/octoprint_livegcodecontrol/__init__.py b/octoprint_livegcodecontrol/__init__.py index 1324544..c9af908 100644 --- a/octoprint_livegcodecontrol/__init__.py +++ b/octoprint_livegcodecontrol/__init__.py @@ -7,6 +7,7 @@ import threading import time import math +import flask class LedWorker(threading.Thread): def __init__(self, printer, logger): @@ -100,7 +101,8 @@ def stop(self): class LiveGCodeControlPlugin(octoprint.plugin.SettingsPlugin, octoprint.plugin.AssetPlugin, octoprint.plugin.TemplatePlugin, - octoprint.plugin.SimpleApiPlugin): + octoprint.plugin.SimpleApiPlugin, + octoprint.plugin.EventHandlerPlugin): def __init__(self): # Initialize the logger @@ -117,21 +119,59 @@ def on_after_startup(self): ##~~ SimpleApiPlugin mixin + def on_api_get(self, request): + return flask.jsonify(presets=self._settings.get(["neoflux_presets"])) + def on_api_command(self, command, data): if command == "update_led_config": if self.led_worker: self.led_worker.update_config(data.get('payload', {})) + elif command == "save_preset": + name = data.get("name") + config = data.get("config") + if name and config: + presets = self._settings.get(["neoflux_presets"]) + presets[name] = config + self._settings.set(["neoflux_presets"], presets) + self._settings.save() + self._logger.info(f"Saved preset: {name}") + + elif command == "delete_preset": + name = data.get("name") + if name: + presets = self._settings.get(["neoflux_presets"]) + if name in presets: + del presets[name] + self._settings.set(["neoflux_presets"], presets) + self._settings.save() + self._logger.info(f"Deleted preset: {name}") + def get_api_commands(self): return dict( - update_led_config=["payload"] + update_led_config=["payload"], + save_preset=["name", "config"], + delete_preset=["name"] ) + def on_event(self, event, payload): + events_mapping = self._settings.get(["neoflux_events"]) + if event in events_mapping: + preset_name = events_mapping[event] + presets = self._settings.get(["neoflux_presets"]) + if preset_name in presets: + config = presets[preset_name] + if self.led_worker: + self._logger.info(f"Applying preset '{preset_name}' for event '{event}'") + self.led_worker.update_config(config) + ##~~ SettingsPlugin mixin def get_settings_defaults(self): return dict( - rules=[] # Default empty list for rules + rules=[], # Default empty list for rules + neoflux_presets={}, + neoflux_events={} ) def on_settings_initialized(self): diff --git a/octoprint_livegcodecontrol/static/js/livegcodecontrol.js b/octoprint_livegcodecontrol/static/js/livegcodecontrol.js index 5ab1673..3616e0f 100644 --- a/octoprint_livegcodecontrol/static/js/livegcodecontrol.js +++ b/octoprint_livegcodecontrol/static/js/livegcodecontrol.js @@ -23,6 +23,87 @@ $(function() { self.neofluxMode = ko.observable("spatial_wave"); self.neofluxSpeed = ko.observable(150); + self.neofluxPresets = ko.observableArray([]); + self.selectedPreset = ko.observable(); + + self.neofluxEvents = ko.observableArray([ + "Startup", "PrintStarted", "PrintDone", "PrintFailed", "PrintPaused", "PrintResumed" + ]); + self.neofluxEventMappings = ko.observableArray([]); // {event: "PrintStarted", preset: ko.observable("CyberPunk")} + + self.refreshPresets = function() { + OctoPrint.simpleApiGet("livegcodecontrol") + .done(function(response) { + var presets = []; + if (response.presets) { + for (var name in response.presets) { + presets.push({name: name, config: response.presets[name]}); + } + } + self.neofluxPresets(presets); + }); + }; + + self.selectedPreset.subscribe(function(newPresetName) { + if (newPresetName) { + var preset = ko.utils.arrayFirst(self.neofluxPresets(), function(item) { + return item.name === newPresetName; + }); + if (preset) { + self.neofluxMode(preset.config.mode); + self.neofluxSpeed(preset.config.speed); + if (self.neofluxController && preset.config.colors) { + self.neofluxController.colors = preset.config.colors; // Update colors + } + // Update preview immediately + if (self.neofluxController) { + self.neofluxController.updateConfig({ + mode: self.neofluxMode(), + speed: parseInt(self.neofluxSpeed()) + }); + } + } + } + }); + + self.savePreset = function() { + var name = prompt("Enter preset name:"); + if (name) { + if (self.neofluxController) { + // Ensure local controller state is up to date before grabbing config + self.neofluxController.updateConfig({ + mode: self.neofluxMode(), + speed: parseInt(self.neofluxSpeed()) + }); + } + + var config = self.neofluxController ? self.neofluxController.getConfigPayload() : { + mode: self.neofluxMode(), + speed: parseInt(self.neofluxSpeed()), + colors: ["#FF0000", "#0000FF"] // Default fallback + }; + + OctoPrint.simpleApiCommand("livegcodecontrol", "save_preset", { + name: name, + config: config + }).done(function() { + self.refreshPresets(); + }); + } + }; + + self.deletePreset = function() { + var name = self.selectedPreset(); + if (name && confirm("Are you sure you want to delete preset '" + name + "'?")) { + OctoPrint.simpleApiCommand("livegcodecontrol", "delete_preset", { + name: name + }).done(function() { + self.refreshPresets(); + self.selectedPreset(undefined); + }); + } + }; + self.applyNeoFluxConfig = function() { if (!self.neofluxController) return; @@ -46,6 +127,21 @@ $(function() { console.error("Failed to update NEOFLUX config:", response); }); }; + + self.testEventMapping = function(mapping) { + var presetName = mapping.preset(); + if (presetName) { + var preset = ko.utils.arrayFirst(self.neofluxPresets(), function(item) { + return item.name === presetName; + }); + if (preset) { + // Just apply it live + OctoPrint.simpleApiCommand("livegcodecontrol", "update_led_config", { + payload: preset.config + }); + } + } + }; // ------------------------------ // --- Helper function to create a new rule object --- @@ -135,12 +231,26 @@ $(function() { }); self.rules(mappedRules); } + + // NeoFlux Events + self.refreshPresets(); + var savedEvents = self.settingsViewModel.settings.plugins.livegcodecontrol.neoflux_events(); + var mappings = []; + ko.utils.arrayForEach(self.neofluxEvents(), function(evt) { + var preset = savedEvents ? savedEvents[evt] : undefined; + mappings.push({ + event: evt, + preset: ko.observable(preset) + }); + }); + self.neofluxEventMappings(mappings); }; self.onSettingsShown = function() { // Could refresh data from server if necessary, but usually onBeforeBinding is enough for settings // Ensure editing state is clear when settings are reshown self.cancelEdit(); + self.refreshPresets(); // Ensure presets are up to date in settings }; self.onSettingsHidden = function() { @@ -160,6 +270,15 @@ $(function() { }; }); self.settingsViewModel.settings.plugins.livegcodecontrol.rules(rulesToSave); + + // Save NeoFlux Events + var eventsToSave = {}; + ko.utils.arrayForEach(self.neofluxEventMappings(), function(mapping) { + if (mapping.preset()) { + eventsToSave[mapping.event] = mapping.preset(); + } + }); + self.settingsViewModel.settings.plugins.livegcodecontrol.neoflux_events(eventsToSave); }; } diff --git a/octoprint_livegcodecontrol/templates/livegcodecontrol_settings.jinja2 b/octoprint_livegcodecontrol/templates/livegcodecontrol_settings.jinja2 index b9ebdef..61b2421 100644 --- a/octoprint_livegcodecontrol/templates/livegcodecontrol_settings.jinja2 +++ b/octoprint_livegcodecontrol/templates/livegcodecontrol_settings.jinja2 @@ -66,4 +66,28 @@ + +
+ +

NeoFlux Automation

+ + + + + + + + + + + + + + + +
EventPresetActions
+ + + +
diff --git a/octoprint_livegcodecontrol/templates/livegcodecontrol_tab.jinja2 b/octoprint_livegcodecontrol/templates/livegcodecontrol_tab.jinja2 index 098d265..e713846 100644 --- a/octoprint_livegcodecontrol/templates/livegcodecontrol_tab.jinja2 +++ b/octoprint_livegcodecontrol/templates/livegcodecontrol_tab.jinja2 @@ -7,6 +7,11 @@

Configuration

+
+ + +
- +
+ + + +