linking helper started

This commit is contained in:
Vitor de Miranda Henrique
2021-10-19 23:17:51 -05:00
parent d299a28d6e
commit c57cd04a24
9 changed files with 523 additions and 66 deletions

View File

@@ -1,47 +1,49 @@
# coding=utf-8
from __future__ import absolute_import
import octoprint.plugin
import octoprint.util
import json
from abc import ABC
from enum import Enum
from uuid import UUID, uuid4
import json
from flask import jsonify, request, make_response, Response
import flask
import octoprint.plugin
import octoprint.util
from flask import Response, jsonify, make_response, request, abort
# class OutputType(Enum):
# ACTION_BASED_INPUT = 0
# STATE_BASED_INPUT = 1
class OutputType(Enum):
ACTION_BASED_INPUT = 0
STATE_BASED_INPUT = 1
class OutputPeripheral(ABC):
def __init__(self, id: UUID ,name:str, type:OutputType) -> None:
super().__init__()
# class OutputPeripheral(ABC):
# def __init__(self, id: UUID, name: str, type: OutputType) -> None:
# super().__init__()
def run_action(self):
"""
Output peripheral can be action based when it's:
- GCODE command
- Shell script
# def run_action(self):
# """
# Output peripheral can be action based when it's:
# - GCODE command
# - Shell script
Returns:
Bool: Success
"""
pass
# Returns:
# Bool: Success
# """
# pass
def set_state(self, state):
"""
Output peripheral can be state based when it's:
- GPIO: it can be set to on / off / toggle
- Led Strip: it can be set to a color in RGB
- Neopixel
- PWM
# def set_state(self, state):
# """
# Output peripheral can be state based when it's:
# - GPIO: it can be set to on / off / toggle
# - Led Strip: it can be set to a color in RGB
# - Neopixel
# - PWM
Returns:
Bool: Success
"""
pass
# Returns:
# Bool: Success
# """
# pass
class EnclosurePlugin(octoprint.plugin.StartupPlugin,
@@ -57,7 +59,7 @@ class EnclosurePlugin(octoprint.plugin.StartupPlugin,
self._ids = []
@octoprint.plugin.BlueprintPlugin.route("/available_id", methods=["GET"])
@octoprint.plugin.BlueprintPlugin.route("/uuid", methods=["GET"])
def get_available_id(self):
return Response(json.dumps(str(uuid4())), mimetype='application/json')
@@ -106,7 +108,6 @@ class EnclosurePlugin(octoprint.plugin.StartupPlugin,
if v == implementation:
return k
def register_plugin(self, implementation):
k = self._get_plugin_key(implementation)
@@ -145,8 +146,8 @@ def __plugin_load__():
global __plugin_helpers__
__plugin_helpers__ = dict(
# get_id = __plugin_implementation__.get_id,
output_peripheral_set_state = __plugin_implementation__.output_peripheral_set_state,
register_plugin = __plugin_implementation__.register_plugin,
register_output_peripheral = __plugin_implementation__.register_output_peripheral,
register_input_peripheral = __plugin_implementation__.register_input_peripheral,
output_peripheral_set_state= __plugin_implementation__.output_peripheral_set_state,
register_plugin= __plugin_implementation__.register_plugin,
register_output_peripheral= __plugin_implementation__.register_output_peripheral,
register_input_peripheral= __plugin_implementation__.register_input_peripheral,
)

View File

@@ -1,42 +1,236 @@
$(function () {
function EnclosureViewModel(parameters) {
var self = this;
self.settingsViewModel = parameters[0];
self.connectionViewModel = parameters[1];
self.printerStateViewModel = parameters[2];
self.enclosureOutputs = ko.observableArray();
self.enclosureInputs = ko.observableArray();
self.settings_unsaved = ko.observable(false);
self.onBeforeBinding = function () {
var cleanIFTTT = function (index_id) {
return {
index_id: index_id,
trigger_type: "io_trigger",
linked_input_id: "",
input_logic: "equals",
input_value: "",
action_type: "set_output_value",
linked_output: "",
output_set_value: ""
}
};
self.onSettingsBeforeSave = function () {
function generate_random_uuid() {
return ([1e2] + 1e2).replace(/[018]/g, c =>
(c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
);
}
function IFTTTEditorViewModel(parameters) {
var self = this;
self.isNew = ko.observable(false);
self.index_id = ko.observable();
self.trigger_type = ko.observable();
self.action_type = ko.observable();
self.linked_input_id = ko.observable();
self.linked_output = ko.observable();
self.output_set_value = ko.observable();
self.ifttt_logic = undefined;
self.enclosureOutputs = undefined;
self.enclosureInputs = undefined;
self.validInput = ko.pureComputed(function () {
return true;
});
self.linked_input = ko.pureComputed(function () {
if (self.trigger_type() == 'io_trigger') {
var _linked_input = ko.utils.arrayFilter(self.enclosureInputs(), function (item) {
return (self.linked_input_id() == item.index_id());
}).pop();
if(_linked_input){
return _linked_input
}
}
return undefined
});
// self.linked_input_type = ko.pureComputed(function () {
// if (self.linked_input()) {
// return self.linked_input.category;
// }
// return ko.observable("none");
// });
self.fromIFTTTData = function (data) {
self.isNew(data === undefined);
if (data === undefined) {
var arrRelaysLength = self.ifttt_logic().length;
var nextIndex = arrRelaysLength == 0 ? 1 : self.ifttt_logic()[arrRelaysLength - 1].index_id() + 1;
data = cleanIFTTT(nextIndex);
} else {
objIndex = self.ifttt_logic().findIndex((obj => obj.index_id == data.index_id));
data = ko.mapping.toJS(self.ifttt_logic()[objIndex]);
}
self.index_id(data.index_id);
self.trigger_type(data.trigger_type)
self.action_type(data.action_type)
self.linked_input_id(data.linked_input_id)
self.linked_output(data.linked_output)
self.output_set_value(data.output_set_value)
};
self.toIFTTTData = function (data) {
var output_data = {
index_id: self.index_id(),
trigger_type: self.trigger_type(),
action_type: self.action_type(),
linked_input_id: self.linked_input_id(),
linked_output: self.linked_output(),
output_set_value: self.output_set_value(),
}
return output_data;
};
// end of IFTTTEditorViewModel
};
self.onEventSettingsUpdated = function () {
function EnclosureViewModel(parameters) {
var self = this;
self.pluginName = "enclosure";
self.settingsViewModel = parameters[0];
self.connectionViewModel = parameters[1];
self.printerStateViewModel = parameters[2];
self.settings_unsaved = ko.observable(false);
self.ifttt_logic = ko.observableArray();
self.enclosureOutputs = ko.observableArray();
self.enclosureInputs = ko.observableArray();
self.settings_unsaved = ko.observable(false);
// self.settings_outputs_regular = ko.pureComputed(function () {
// return ko.utils.arrayFilter(self.settingsViewModel.settings.plugins.enclosure.rpi_outputs(), function (item) {
// return (item.output_type() === "regular_gpio" && !item.toggle_timer());
// });
// });
self.createIFTTTEditor = function (data) {
var inputEditor = new IFTTTEditorViewModel();
return inputEditor;
};
self.inputEditor = self.createIFTTTEditor();
self.inputEditor.ifttt_logic = self.ifttt_logic;
self.inputEditor.enclosureOutputs = self.enclosureOutputs;
self.inputEditor.enclosureInputs = self.enclosureInputs;
self.removeLogic = function (data) {
self.ifttt_logic.remove(data);
self.settings_unsaved(true);
}
self.buildPluginUrl = function (path) {
return window.PLUGIN_BASEURL + self.pluginName + path;
};
self.get_uuid = function () {
while (true) {
uuid_test = generate_random_uuid();
var used_id_output = ko.utils.arrayFilter(self.enclosureOutputs(), function (item) {
return (uuid_test == item.index_id());
}).pop();
var used_id_input = ko.utils.arrayFilter(self.enclosureInputs(), function (item) {
return (uuid_test == item.index_id());
}).pop();
if (!used_id_output && !used_id_input) {
return uuid_test
}
}
// response = $.ajax({
// type: "GET",
// url: self.buildPluginUrl("/uuid"),
// async: false
// }).responseText;
// return JSON.parse(response);
}
self.addLogic = function (callback) {
var isNew = self.inputEditor.isNew();
self.settings_unsaved(true);
var input = ko.mapping.fromJS(self.inputEditor.toIFTTTData());
if (isNew) {
self.ifttt_logic.push(input);
} else {
objIndex = self.ifttt_logic().findIndex((obj => obj.index_id() == input.index_id()));
var _old_input = self.ifttt_logic()[objIndex];
self.ifttt_logic.replace(_old_input, input);
}
if (callback !== undefined) {
callback();
}
};
self.confirmEditLogic = function () {
if (self.inputEditor.validInput()) {
var callback = function () {
$("#settings_ifttt_edit_dialog").modal("hide");
};
self.addLogic(callback);
}
};
self.showIFTTTDialog = function (data) {
self.inputEditor.fromIFTTTData(data);
var editDialog = $("#settings_ifttt_edit_dialog");
$('ul.nav-pills a[data-toggle="tab"]:first', editDialog).tab("show");
editDialog.modal({
minHeight: function () {
return Math.max($.fn.modal.defaults.maxHeight() - 80, 250);
}
}).css({
width: 'auto',
'margin-left': function () {
return -($(this).width());
}
});
};
self.onBeforeBinding = function () {
};
self.onSettingsBeforeSave = function () {};
self.onEventSettingsUpdated = function () {
};
};
self.print_data = function () {
};
};
OCTOPRINT_VIEWMODELS.push({
construct: EnclosureViewModel,
dependencies: ["settingsViewModel", "connectionViewModel", "printerStateViewModel"],
elements: ["#settings_plugin_enclosure"]
});
OCTOPRINT_VIEWMODELS.push({
construct: EnclosureViewModel,
dependencies: ["settingsViewModel", "connectionViewModel", "printerStateViewModel"],
elements: ["#settings_plugin_enclosure"]
});
})

View File

@@ -1,8 +1,30 @@
<form class="form-horizontal" onsubmit="return false;">
<h4>{{ _('Enclosure Plugin') }}</h4>
<h4>{{ _('Enclosure Plugin Logic') }}</h4>
{% include "ifttt_logic_table.jinja2" %}
<!-- <table class="table table-striped table-hover table-condensed table-hover" id="settings_enclosure_bla" style="">
<thead>
<tr>
<th class="">Id</th>
<th class="">Label</th>
</tr>
</thead>
<tbody data-bind="foreach: $root.enclosureOutputs">
<tr data-bind="attr: {title: name}">
<td class="" data-bind="text: index_id"></td>
<td class="" data-bind="text: label"></td>
</tr>
</tbody>
</table> -->
</form>
{% include "ifttt/main_screen.jinja2" %}

View File

@@ -0,0 +1,37 @@
<form class="form-horizontal">
<div class="control-group">
<label class="control-label">{{ _('Logic Id') }}</label>
<div class="controls">
<input type="text" data-bind="value: inputEditor.index_id" readonly>
<span class="help-inline">Auto generated ID for Input</span>
</div>
</div>
<div class="control-group">
<label class="control-label">{{ _('Trigger Type') }}</label>
<div class="controls">
<label class="radio">
<input type="radio" name="trigger_type_radio" value="printer_event"
data-bind="checked: inputEditor.trigger_type"> {{ _('Printer Event') }}
</label>
<label class="radio">
<input type="radio" name="trigger_type_radio" value="io_trigger" data-bind="checked: inputEditor.trigger_type">
{{ _('IO Trigger') }}
</label>
</div>
</div>
<div class="control-group">
<label class="control-label">{{ _('Action Type') }}</label>
<div class="controls">
<label class="radio">
<input type="radio" name="action_type_radio" value="set_output_value"
data-bind="checked: inputEditor.action_type"> {{ _('Set Output Value') }}
</label>
</div>
</div>
</form>

View File

@@ -0,0 +1,59 @@
<form class="form-horizontal">
<div class="control-group">
<label class="control-label">{{ _('Select IO') }}</label>
<div class="controls">
<label class="select">
<select data-bind="options: $root.enclosureInputs,
optionsText: 'label',
optionsValue: 'index_id',
value: $root.inputEditor.linked_input_id">
</select>
<span class="help-inline">When the linked IO trigger an event...</span>
</label>
</div>
</div>
<!-- ko if: (inputEditor.linked_input) -->
<div class="control-group">
<label class="control-label">{{ _('Logic') }}</label>
<div class="controls">
<label class="select">
<select data-bind="value: inputEditor.input_logic">
<!-- ko if: (inputEditor.linked_input().category() == 'sensor') -->
<option value="lower_than">Lower Than</option>
<option value="equals_to">Equals To</option>
<option value="higher_than">Higher Than</option>
<!-- /ko -->
<!-- ko if: (inputEditor.linked_input().category() == 'gpio') -->
<option value="equals_to">Equals To</option>
<!-- /ko -->
</select>
</label>
</div>
</div>
<div class="control-group">
<label class="control-label">{{ _('Value') }}</label>
<div class="controls">
<!-- ko if: (inputEditor.linked_input().category() == 'sensor') -->
<input type="text" class="input-block-level" data-bind="value: inputEditor.input_value">
<!-- /ko -->
<!-- ko if: (inputEditor.linked_input().category() == 'gpio') -->
<select data-bind="value: inputEditor.input_value">
<option value="rising">Rising</option>
<option value="falling">Falling</option>
</select>
<!-- /ko -->
</div>
</div>
<!-- /ko -->
</form>

View File

@@ -0,0 +1,62 @@
<div id="settings_ifttt_edit_dialog" class="modal hide fade">
<div class="modal-header">
<a href="#" class="close" data-dismiss="modal" aria-hidden="true">&times;</a>
<h3 class="modal-title">Add / Edit Logic</h3>
</div>
<div class="modal-body">
<div class="full-sized-box">
<ul class="nav nav-pills">
<li class="active">
<a href="#settings_ifttt_edit_dialog_general" data-toggle="tab">{{ _('General') }}</a>
</li>
<!-- ko if: (inputEditor.trigger_type() == "printer_event") -->
<li>
<a href="#settings_ifttt_edit_dialog_printer_event" data-toggle="tab">{{ _('Printer Trigger') }}</a>
</li>
<!-- /ko -->
<!-- ko if: (inputEditor.trigger_type() == "io_trigger") -->
<li>
<a href="#settings_ifttt_edit_dialog_io_trigger" data-toggle="tab">{{ _('IO Trigger') }}</a>
</li>
<!-- /ko -->
<!-- ko if: (inputEditor.action_type() == "set_output_value") -->
<li>
<a href="#settings_ifttt_edit_dialog_set_output_value" data-toggle="tab">{{ _('Set Output') }}</a>
</li>
<!-- /ko -->
</ul>
<div class="tab-content">
<div id="settings_ifttt_edit_dialog_general" class="tab-pane active">
{% include "ifttt/general_info.jinja2" %}
</div>
<div id="settings_ifttt_edit_dialog_printer_event" class="tab-pane">
{% include "ifttt/printer_event.jinja2" %}
</div>
<div id="settings_ifttt_edit_dialog_io_trigger" class="tab-pane">
{% include "ifttt/io_trigger.jinja2" %}
</div>
<div id="settings_ifttt_edit_dialog_set_output_value" class="tab-pane">
{% include "ifttt/set_output_value.jinja2" %}
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn" data-dismiss="modal" aria-hidden="true">{{ _('Abort') }}</button>
<button class="btn btn-primary btn-confirm"
data-bind="click: function() { $root.confirmEditLogic();}, css: {disabled: !inputEditor.validInput()}">{{ _('Save') }}</button>
</div>
</div>

View File

@@ -0,0 +1,21 @@
<form class="form-horizontal">
<div class="control-group">
<label class="control-label">{{ _('Printer Event') }}</label>
<div class="controls">
<label class="radio">
<input type="radio" name="action_type_radio" value="started_printing"
data-bind="checked: inputEditor.linked_input_id"> {{ _('Started Printing') }}
</label>
<label class="radio">
<input type="radio" name="action_type_radio" value="stopped_printing"
data-bind="checked: inputEditor.linked_input_id"> {{ _('Stopped Printing') }}
</label>
<label class="radio">
<input type="radio" name="action_type_radio" value="canceled_printing"
data-bind="checked: inputEditor.linked_input_id"> {{ _('Canceled Printing') }}
</label>
</div>
</div>
</form>

View File

@@ -0,0 +1,28 @@
<form class="form-horizontal">
<div class="control-group">
<label class="control-label">{{ _('Select IO') }}</label>
<div class="controls">
<label class="select">
<select data-bind="options: $root.enclosureOutputs,
optionsText: 'label',
optionsValue: 'index_id',
value: $root.inputEditor.linked_output">
</select>
<span class="help-inline">Linked output that will be set</span>
</label>
</div>
</div>
<div class="control-group">
<label class="control-label">{{ _('Value') }}</label>
<div class="controls">
<input type="text" class="input-block-level" data-bind="value: inputEditor.output_set_value">
<span class="help-inline">values should be "on", "off" or "toggle" or anything that the linked output supports</span>
</div>
</div>
</form>

View File

@@ -0,0 +1,33 @@
<table class="table table-striped table-hover table-condensed table-hover" id="settings_enclosure_ifttt_logic" style="">
<thead>
<tr>
<th class="">Id</th>
<th class="">Trigger</th>
<th class="">Input</th>
<th class="">Action</th>
<th class="">Output</th>
<th class="">Set Value</th>
<th class="">{{ _('Edit') }}</th>
</tr>
</thead>
<tbody data-bind="foreach: $root.ifttt_logic">
<tr data-bind="attr: {title: name}">
<td class="" data-bind="text: index_id"></td>
<td class="" data-bind="text: trigger_type"></td>
<td class="" data-bind="text: linked_input_id"></td>
<td class="" data-bind="text: action_type"></td>
<td class="" data-bind="text: linked_output"></td>
<td class="" data-bind="text: output_set_value"></td>
<td class="">
<a href="#" class="fa fa-pencil" title="{{ _('Edit Output')|edq }}" data-bind="click: function() { $root.showIFTTTDialog($data); }"></a>&nbsp;|&nbsp;
<a href="#" class="fa fa-trash-o" title="{{ _('Delete Output')|edq }}" data-bind="click: function() { $root.removeLogic($data); }"></a>
</td>
</tr>
</tbody>
</table>
<div class="control-group">
<button class="btn pull-right" data-bind="click: function() { $root.showIFTTTDialog(); }">Add
Inputs...</button>
</div>