Skip to content

Commit 5ff4124

Browse files
authored
Merge pull request #10 from VisualBoy/neoflux-preset-system
NeoFlux Preset Management and Event Automation
2 parents 472aefe + 24803cb commit 5ff4124

4 files changed

Lines changed: 196 additions & 4 deletions

File tree

octoprint_livegcodecontrol/__init__.py

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import threading
88
import time
99
import math
10+
import flask
1011

1112
class LedWorker(threading.Thread):
1213
def __init__(self, printer, logger):
@@ -100,7 +101,8 @@ def stop(self):
100101
class LiveGCodeControlPlugin(octoprint.plugin.SettingsPlugin,
101102
octoprint.plugin.AssetPlugin,
102103
octoprint.plugin.TemplatePlugin,
103-
octoprint.plugin.SimpleApiPlugin):
104+
octoprint.plugin.SimpleApiPlugin,
105+
octoprint.plugin.EventHandlerPlugin):
104106

105107
def __init__(self):
106108
# Initialize the logger
@@ -117,21 +119,59 @@ def on_after_startup(self):
117119

118120
##~~ SimpleApiPlugin mixin
119121

122+
def on_api_get(self, request):
123+
return flask.jsonify(presets=self._settings.get(["neoflux_presets"]))
124+
120125
def on_api_command(self, command, data):
121126
if command == "update_led_config":
122127
if self.led_worker:
123128
self.led_worker.update_config(data.get('payload', {}))
124129

130+
elif command == "save_preset":
131+
name = data.get("name")
132+
config = data.get("config")
133+
if name and config:
134+
presets = self._settings.get(["neoflux_presets"])
135+
presets[name] = config
136+
self._settings.set(["neoflux_presets"], presets)
137+
self._settings.save()
138+
self._logger.info(f"Saved preset: {name}")
139+
140+
elif command == "delete_preset":
141+
name = data.get("name")
142+
if name:
143+
presets = self._settings.get(["neoflux_presets"])
144+
if name in presets:
145+
del presets[name]
146+
self._settings.set(["neoflux_presets"], presets)
147+
self._settings.save()
148+
self._logger.info(f"Deleted preset: {name}")
149+
125150
def get_api_commands(self):
126151
return dict(
127-
update_led_config=["payload"]
152+
update_led_config=["payload"],
153+
save_preset=["name", "config"],
154+
delete_preset=["name"]
128155
)
129156

157+
def on_event(self, event, payload):
158+
events_mapping = self._settings.get(["neoflux_events"])
159+
if event in events_mapping:
160+
preset_name = events_mapping[event]
161+
presets = self._settings.get(["neoflux_presets"])
162+
if preset_name in presets:
163+
config = presets[preset_name]
164+
if self.led_worker:
165+
self._logger.info(f"Applying preset '{preset_name}' for event '{event}'")
166+
self.led_worker.update_config(config)
167+
130168
##~~ SettingsPlugin mixin
131169

132170
def get_settings_defaults(self):
133171
return dict(
134-
rules=[] # Default empty list for rules
172+
rules=[], # Default empty list for rules
173+
neoflux_presets={},
174+
neoflux_events={}
135175
)
136176

137177
def on_settings_initialized(self):

octoprint_livegcodecontrol/static/js/livegcodecontrol.js

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,87 @@ $(function() {
2323
self.neofluxMode = ko.observable("spatial_wave");
2424
self.neofluxSpeed = ko.observable(150);
2525

26+
self.neofluxPresets = ko.observableArray([]);
27+
self.selectedPreset = ko.observable();
28+
29+
self.neofluxEvents = ko.observableArray([
30+
"Startup", "PrintStarted", "PrintDone", "PrintFailed", "PrintPaused", "PrintResumed"
31+
]);
32+
self.neofluxEventMappings = ko.observableArray([]); // {event: "PrintStarted", preset: ko.observable("CyberPunk")}
33+
34+
self.refreshPresets = function() {
35+
OctoPrint.simpleApiGet("livegcodecontrol")
36+
.done(function(response) {
37+
var presets = [];
38+
if (response.presets) {
39+
for (var name in response.presets) {
40+
presets.push({name: name, config: response.presets[name]});
41+
}
42+
}
43+
self.neofluxPresets(presets);
44+
});
45+
};
46+
47+
self.selectedPreset.subscribe(function(newPresetName) {
48+
if (newPresetName) {
49+
var preset = ko.utils.arrayFirst(self.neofluxPresets(), function(item) {
50+
return item.name === newPresetName;
51+
});
52+
if (preset) {
53+
self.neofluxMode(preset.config.mode);
54+
self.neofluxSpeed(preset.config.speed);
55+
if (self.neofluxController && preset.config.colors) {
56+
self.neofluxController.colors = preset.config.colors; // Update colors
57+
}
58+
// Update preview immediately
59+
if (self.neofluxController) {
60+
self.neofluxController.updateConfig({
61+
mode: self.neofluxMode(),
62+
speed: parseInt(self.neofluxSpeed())
63+
});
64+
}
65+
}
66+
}
67+
});
68+
69+
self.savePreset = function() {
70+
var name = prompt("Enter preset name:");
71+
if (name) {
72+
if (self.neofluxController) {
73+
// Ensure local controller state is up to date before grabbing config
74+
self.neofluxController.updateConfig({
75+
mode: self.neofluxMode(),
76+
speed: parseInt(self.neofluxSpeed())
77+
});
78+
}
79+
80+
var config = self.neofluxController ? self.neofluxController.getConfigPayload() : {
81+
mode: self.neofluxMode(),
82+
speed: parseInt(self.neofluxSpeed()),
83+
colors: ["#FF0000", "#0000FF"] // Default fallback
84+
};
85+
86+
OctoPrint.simpleApiCommand("livegcodecontrol", "save_preset", {
87+
name: name,
88+
config: config
89+
}).done(function() {
90+
self.refreshPresets();
91+
});
92+
}
93+
};
94+
95+
self.deletePreset = function() {
96+
var name = self.selectedPreset();
97+
if (name && confirm("Are you sure you want to delete preset '" + name + "'?")) {
98+
OctoPrint.simpleApiCommand("livegcodecontrol", "delete_preset", {
99+
name: name
100+
}).done(function() {
101+
self.refreshPresets();
102+
self.selectedPreset(undefined);
103+
});
104+
}
105+
};
106+
26107
self.applyNeoFluxConfig = function() {
27108
if (!self.neofluxController) return;
28109

@@ -46,6 +127,21 @@ $(function() {
46127
console.error("Failed to update NEOFLUX config:", response);
47128
});
48129
};
130+
131+
self.testEventMapping = function(mapping) {
132+
var presetName = mapping.preset();
133+
if (presetName) {
134+
var preset = ko.utils.arrayFirst(self.neofluxPresets(), function(item) {
135+
return item.name === presetName;
136+
});
137+
if (preset) {
138+
// Just apply it live
139+
OctoPrint.simpleApiCommand("livegcodecontrol", "update_led_config", {
140+
payload: preset.config
141+
});
142+
}
143+
}
144+
};
49145
// ------------------------------
50146

51147
// --- Helper function to create a new rule object ---
@@ -135,12 +231,26 @@ $(function() {
135231
});
136232
self.rules(mappedRules);
137233
}
234+
235+
// NeoFlux Events
236+
self.refreshPresets();
237+
var savedEvents = self.settingsViewModel.settings.plugins.livegcodecontrol.neoflux_events();
238+
var mappings = [];
239+
ko.utils.arrayForEach(self.neofluxEvents(), function(evt) {
240+
var preset = savedEvents ? savedEvents[evt] : undefined;
241+
mappings.push({
242+
event: evt,
243+
preset: ko.observable(preset)
244+
});
245+
});
246+
self.neofluxEventMappings(mappings);
138247
};
139248

140249
self.onSettingsShown = function() {
141250
// Could refresh data from server if necessary, but usually onBeforeBinding is enough for settings
142251
// Ensure editing state is clear when settings are reshown
143252
self.cancelEdit();
253+
self.refreshPresets(); // Ensure presets are up to date in settings
144254
};
145255

146256
self.onSettingsHidden = function() {
@@ -160,6 +270,15 @@ $(function() {
160270
};
161271
});
162272
self.settingsViewModel.settings.plugins.livegcodecontrol.rules(rulesToSave);
273+
274+
// Save NeoFlux Events
275+
var eventsToSave = {};
276+
ko.utils.arrayForEach(self.neofluxEventMappings(), function(mapping) {
277+
if (mapping.preset()) {
278+
eventsToSave[mapping.event] = mapping.preset();
279+
}
280+
});
281+
self.settingsViewModel.settings.plugins.livegcodecontrol.neoflux_events(eventsToSave);
163282
};
164283
}
165284

octoprint_livegcodecontrol/templates/livegcodecontrol_settings.jinja2

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,4 +66,28 @@
6666
</tr>
6767
</tbody>
6868
</table>
69+
70+
<hr>
71+
72+
<h3>NeoFlux Automation</h3>
73+
<table class="table table-striped table-bordered" id="neoflux_events_table">
74+
<thead>
75+
<tr>
76+
<th>Event</th>
77+
<th>Preset</th>
78+
<th style="width: 10%;">Actions</th>
79+
</tr>
80+
</thead>
81+
<tbody data-bind="foreach: neofluxEventMappings">
82+
<tr>
83+
<td data-bind="text: event"></td>
84+
<td>
85+
<select data-bind="options: $parent.neofluxPresets, optionsText: 'name', optionsValue: 'name', value: preset, optionsCaption: 'Select a preset...'"></select>
86+
</td>
87+
<td>
88+
<button class="btn btn-mini" data-bind="click: $parent.testEventMapping.bind($parent)">Test</button>
89+
</td>
90+
</tr>
91+
</tbody>
92+
</table>
6993
</div>

octoprint_livegcodecontrol/templates/livegcodecontrol_tab.jinja2

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@
77
</div>
88
<div class="neoflux-controls">
99
<h3>Configuration</h3>
10+
<div class="neoflux-input-group">
11+
<label>Preset</label>
12+
<select id="neoflux-preset" class="neoflux-input" data-bind="options: neofluxPresets, optionsText: 'name', optionsValue: 'name', value: selectedPreset, optionsCaption: 'Select a preset...'">
13+
</select>
14+
</div>
1015
<div class="neoflux-input-group">
1116
<label>Mode</label>
1217
<select id="neoflux-mode" class="neoflux-input" data-bind="value: neofluxMode">
@@ -19,7 +24,11 @@
1924
<input type="number" class="neoflux-input" data-bind="value: neofluxSpeed">
2025
</div>
2126
<!-- More controls can be added here -->
22-
<button class="neoflux-btn" data-bind="click: applyNeoFluxConfig">Apply Configuration</button>
27+
<div class="neoflux-button-group">
28+
<button class="neoflux-btn" data-bind="click: applyNeoFluxConfig">Activate Live</button>
29+
<button class="neoflux-btn secondary" data-bind="click: savePreset">Save as Preset</button>
30+
<button class="neoflux-btn btn-danger" data-bind="click: deletePreset, visible: selectedPreset" style="margin-left: 5px;">Delete</button>
31+
</div>
2332
</div>
2433
</div>
2534
</div>

0 commit comments

Comments
 (0)