Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 43 additions & 3 deletions octoprint_livegcodecontrol/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import threading
import time
import math
import flask

class LedWorker(threading.Thread):
def __init__(self, printer, logger):
Expand Down Expand Up @@ -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
Expand All @@ -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):
Expand Down
119 changes: 119 additions & 0 deletions octoprint_livegcodecontrol/static/js/livegcodecontrol.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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 ---
Expand Down Expand Up @@ -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() {
Expand All @@ -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);
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,28 @@
</tr>
</tbody>
</table>

<hr>

<h3>NeoFlux Automation</h3>
<table class="table table-striped table-bordered" id="neoflux_events_table">
<thead>
<tr>
<th>Event</th>
<th>Preset</th>
<th style="width: 10%;">Actions</th>
</tr>
</thead>
<tbody data-bind="foreach: neofluxEventMappings">
<tr>
<td data-bind="text: event"></td>
<td>
<select data-bind="options: $parent.neofluxPresets, optionsText: 'name', optionsValue: 'name', value: preset, optionsCaption: 'Select a preset...'"></select>
</td>
<td>
<button class="btn btn-mini" data-bind="click: $parent.testEventMapping.bind($parent)">Test</button>
</td>
</tr>
</tbody>
</table>
</div>
11 changes: 10 additions & 1 deletion octoprint_livegcodecontrol/templates/livegcodecontrol_tab.jinja2
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
</div>
<div class="neoflux-controls">
<h3>Configuration</h3>
<div class="neoflux-input-group">
<label>Preset</label>
<select id="neoflux-preset" class="neoflux-input" data-bind="options: neofluxPresets, optionsText: 'name', optionsValue: 'name', value: selectedPreset, optionsCaption: 'Select a preset...'">
</select>
</div>
<div class="neoflux-input-group">
<label>Mode</label>
<select id="neoflux-mode" class="neoflux-input" data-bind="value: neofluxMode">
Expand All @@ -19,7 +24,11 @@
<input type="number" class="neoflux-input" data-bind="value: neofluxSpeed">
</div>
<!-- More controls can be added here -->
<button class="neoflux-btn" data-bind="click: applyNeoFluxConfig">Apply Configuration</button>
<div class="neoflux-button-group">
<button class="neoflux-btn" data-bind="click: applyNeoFluxConfig">Activate Live</button>
<button class="neoflux-btn secondary" data-bind="click: savePreset">Save as Preset</button>
<button class="neoflux-btn btn-danger" data-bind="click: deletePreset, visible: selectedPreset" style="margin-left: 5px;">Delete</button>
</div>
</div>
</div>
</div>