luci-mod-system: add led plugin infrastructure
This commit creates the possibility that not only kernel-led-triggers can
be selected but also application-led-triggers from user space.
This is done via a plugin mechanism. The application-led-triggers are scripts
that set kernel-led-triggers on system events or services. Until now this
has not been possible. The package rssileds is a kind of
application-led-trigger.
The following new packages are added:
* luci-app-ledtrig-rssi (application-led-trigger)
* luci-app-ledtrig-switch (kernel-led-trigger) not needed on every most devices
* luci-app-ledtrig-usport (kernel-led-trigger) optional trigger
Since we have now a plugin mechanism I have added the following triggers
as a dependency. So this triggers are now installed per default on LuCI
installation.
* kmod-ledtrig-default-on
* kmod-ledtrig-heartbeat
* kmod-ledtrig-netdev
* kmod-ledtrig-timer
The kernel trigger kmod-ledtrig-usbdev was removed with the commit
d0b50c2770
So I have not ported the relevant code anymore.
Signed-off-by: Florian Eckert <fe@dev.tdt.de>
This commit is contained in:
parent
effb720741
commit
638f5ce071
14 changed files with 306 additions and 125 deletions
16
applications/luci-app-ledtrig-rssi/Makefile
Normal file
16
applications/luci-app-ledtrig-rssi/Makefile
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
#
|
||||||
|
# Copyright (C) 2020 TDT AG <development@tdt.de>
|
||||||
|
#
|
||||||
|
# This is free software, licensed under the Apache License Version 2.0.
|
||||||
|
# See https://www.apache.org/licenses/LICENSE-2.0 for more information.
|
||||||
|
#
|
||||||
|
|
||||||
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
|
LUCI_TITLE:= LuCI Support for ledtrigger rssi
|
||||||
|
LUCI_DEPENDS:=+rssileds
|
||||||
|
LUCI_PKGARCH:=all
|
||||||
|
|
||||||
|
include ../../luci.mk
|
||||||
|
|
||||||
|
# call BuildPackage - OpenWrt buildroot signature
|
|
@ -0,0 +1,38 @@
|
||||||
|
'use strict';
|
||||||
|
'require form';
|
||||||
|
'require tools.widgets as widgets';
|
||||||
|
|
||||||
|
return L.Class.extend({
|
||||||
|
trigger: _('rssi (service)'),
|
||||||
|
kernel: false,
|
||||||
|
addFormOptions(s){
|
||||||
|
var o;
|
||||||
|
|
||||||
|
o = s.option(widgets.DeviceSelect, '_rssi_iface', _('Device'));
|
||||||
|
o.rmempty = true;
|
||||||
|
o.ucioption = 'iface';
|
||||||
|
o.modalonly = true;
|
||||||
|
o.noaliases = true;
|
||||||
|
o.depends('trigger', 'rssi');
|
||||||
|
|
||||||
|
o = s.option(form.Value, 'minq', _('Minimal quality'));
|
||||||
|
o.rmempty = true;
|
||||||
|
o.modalonly = true;
|
||||||
|
o.depends('trigger', 'rssi');
|
||||||
|
|
||||||
|
o = s.option(form.Value, 'maxq', _('Maximal quality'));
|
||||||
|
o.rmempty = true;
|
||||||
|
o.modalonly = true;
|
||||||
|
o.depends('trigger', 'rssi');
|
||||||
|
|
||||||
|
o = s.option(form.Value, 'offset', _('Value offset'));
|
||||||
|
o.rmempty = true;
|
||||||
|
o.modalonly = true;
|
||||||
|
o.depends('trigger', 'rssi');
|
||||||
|
|
||||||
|
o = s.option(form.Value, 'factor', _('Multiplication factor'));
|
||||||
|
o.rmempty = true;
|
||||||
|
o.modalonly = true;
|
||||||
|
o.depends('trigger', 'rssi');
|
||||||
|
}
|
||||||
|
});
|
15
applications/luci-app-ledtrig-switch/Makefile
Normal file
15
applications/luci-app-ledtrig-switch/Makefile
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
#
|
||||||
|
# Copyright (C) 2020 TDT AG <development@tdt.de>
|
||||||
|
#
|
||||||
|
# This is free software, licensed under the Apache License Version 2.0.
|
||||||
|
# See https://www.apache.org/licenses/LICENSE-2.0 for more information.
|
||||||
|
#
|
||||||
|
|
||||||
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
|
LUCI_TITLE:= LuCI Support for ledtrigger switch
|
||||||
|
LUCI_PKGARCH:=all
|
||||||
|
|
||||||
|
include ../../luci.mk
|
||||||
|
|
||||||
|
# call BuildPackage - OpenWrt buildroot signature
|
|
@ -0,0 +1,18 @@
|
||||||
|
'use strict';
|
||||||
|
'require form';
|
||||||
|
|
||||||
|
return L.Class.extend({
|
||||||
|
trigger: _('switch0 (kernel)'),
|
||||||
|
kernel: true,
|
||||||
|
addFormOptions(s){
|
||||||
|
var o;
|
||||||
|
|
||||||
|
o = s.option(form.Value, 'port_mask', _('Switch Port Mask'));
|
||||||
|
o.modalonly = true;
|
||||||
|
o.depends('trigger', 'switch0');
|
||||||
|
|
||||||
|
o = s.option(form.Value, 'speed_mask', _('Switch Speed Mask'));
|
||||||
|
o.modalonly = true;
|
||||||
|
o.depends('trigger', 'switch0');
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,18 @@
|
||||||
|
'use strict';
|
||||||
|
'require form';
|
||||||
|
|
||||||
|
return L.Class.extend({
|
||||||
|
trigger: _('switch1 (kernel)'),
|
||||||
|
kernel: true,
|
||||||
|
addFormOptions(s){
|
||||||
|
var o;
|
||||||
|
|
||||||
|
o = s.option(form.Value, 'port_mask', _('Switch Port Mask'));
|
||||||
|
o.modalonly = true;
|
||||||
|
o.depends('trigger', 'switch1');
|
||||||
|
|
||||||
|
o = s.option(form.Value, 'speed_mask', _('Switch Speed Mask'));
|
||||||
|
o.modalonly = true;
|
||||||
|
o.depends('trigger', 'switch1');
|
||||||
|
}
|
||||||
|
});
|
16
applications/luci-app-ledtrig-usbport/Makefile
Normal file
16
applications/luci-app-ledtrig-usbport/Makefile
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
#
|
||||||
|
# Copyright (C) 2020 TDT AG <development@tdt.de>
|
||||||
|
#
|
||||||
|
# This is free software, licensed under the Apache License Version 2.0.
|
||||||
|
# See https://www.apache.org/licenses/LICENSE-2.0 for more information.
|
||||||
|
#
|
||||||
|
|
||||||
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
|
LUCI_TITLE:= LuCI Support for ledtrigger usbport
|
||||||
|
LUCI_DEPENDS:=+kmod-usb-ledtrig-usbport
|
||||||
|
LUCI_PKGARCH:=all
|
||||||
|
|
||||||
|
include ../../luci.mk
|
||||||
|
|
||||||
|
# call BuildPackage - OpenWrt buildroot signature
|
|
@ -0,0 +1,46 @@
|
||||||
|
'use strict';
|
||||||
|
'require rpc';
|
||||||
|
'require uci';
|
||||||
|
'require form';
|
||||||
|
|
||||||
|
var callUSB = rpc.declare({
|
||||||
|
object: 'luci',
|
||||||
|
method: 'getUSBDevices',
|
||||||
|
expect: { 'ports': [] }
|
||||||
|
});
|
||||||
|
|
||||||
|
return L.Class.extend({
|
||||||
|
trigger: _('usbport (kernel)'),
|
||||||
|
kernel: true,
|
||||||
|
addFormOptions(s){
|
||||||
|
var o;
|
||||||
|
|
||||||
|
o = s.option(form.Value, 'port', _('USB Ports'));
|
||||||
|
o.depends('trigger', 'usbport');
|
||||||
|
o.rmempty = true;
|
||||||
|
o.modalonly = true;
|
||||||
|
o.load = function(s) {
|
||||||
|
return Promise.all([
|
||||||
|
callUSB()
|
||||||
|
]).then(L.bind(function(usbport){
|
||||||
|
for (var i = 0; i < usbport[0].length; i++)
|
||||||
|
o.value(usbport[0][i].port, _('Port %s').format(usbport[0][i].port));
|
||||||
|
},this));
|
||||||
|
};
|
||||||
|
o.cfgvalue = function(section_id) {
|
||||||
|
var ports = [],
|
||||||
|
value = uci.get('system', section_id, 'port');
|
||||||
|
|
||||||
|
if (!Array.isArray(value))
|
||||||
|
value = String(value || '').split(/\s+/);
|
||||||
|
|
||||||
|
for (var i = 0; i < value.length; i++)
|
||||||
|
if (value[i].match(/^(\d+)-(\d+)$/))
|
||||||
|
ports.push('usb%d-port%d'.format(Regexp.$1, Regexp.$2));
|
||||||
|
else
|
||||||
|
ports.push(value[i]);
|
||||||
|
|
||||||
|
return ports;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
|
@ -7,7 +7,11 @@
|
||||||
include $(TOPDIR)/rules.mk
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
LUCI_TITLE:=LuCI Administration - Global System Settings
|
LUCI_TITLE:=LuCI Administration - Global System Settings
|
||||||
LUCI_DEPENDS:=+luci-base
|
LUCI_DEPENDS:=+luci-base \
|
||||||
|
+kmod-ledtrig-default-on \
|
||||||
|
+kmod-ledtrig-heartbeat \
|
||||||
|
+kmod-ledtrig-netdev \
|
||||||
|
+kmod-ledtrig-timer
|
||||||
|
|
||||||
PKG_LICENSE:=Apache-2.0
|
PKG_LICENSE:=Apache-2.0
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
'use strict';
|
||||||
|
'require form';
|
||||||
|
|
||||||
|
return L.Class.extend({
|
||||||
|
trigger: _('default-on (kernel)'),
|
||||||
|
kernel: true,
|
||||||
|
addFormOptions(s){
|
||||||
|
var o;
|
||||||
|
|
||||||
|
o = s.option(form.Flag, 'default', _('Default state'));
|
||||||
|
o.rmempty = false;
|
||||||
|
o.depends('trigger', 'default-on');
|
||||||
|
o.textvalue = function(section_id) {
|
||||||
|
var cval = this.cfgvalue(section_id);
|
||||||
|
if (cval == null)
|
||||||
|
cval = this.default;
|
||||||
|
return (cval == this.enabled) ? _('On') : _('Off');
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,9 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
return L.Class.extend({
|
||||||
|
trigger: _('heartbeat (kernel)'),
|
||||||
|
kernel: true,
|
||||||
|
addFormOptions(s){
|
||||||
|
var o;
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,26 @@
|
||||||
|
'use strict';
|
||||||
|
'require form';
|
||||||
|
'require tools.widgets as widgets';
|
||||||
|
|
||||||
|
return L.Class.extend({
|
||||||
|
trigger: _("netdev (kernel)"),
|
||||||
|
kernel: true,
|
||||||
|
addFormOptions(s){
|
||||||
|
var o;
|
||||||
|
|
||||||
|
o = s.option(widgets.DeviceSelect, '_net_dev', _('Device'));
|
||||||
|
o.rmempty = true;
|
||||||
|
o.ucioption = 'dev';
|
||||||
|
o.modalonly = true;
|
||||||
|
o.noaliases = true;
|
||||||
|
o.depends('trigger', 'netdev');
|
||||||
|
|
||||||
|
o = s.option(form.MultiValue, 'mode', _('Trigger Mode'));
|
||||||
|
o.rmempty = true;
|
||||||
|
o.modalonly = true;
|
||||||
|
o.depends('trigger', 'netdev');
|
||||||
|
o.value('link', _('Link On'));
|
||||||
|
o.value('tx', _('Transmit'));
|
||||||
|
o.value('rx', _('Receive'));
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,9 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
return L.Class.extend({
|
||||||
|
trigger: _('none (kernel)'),
|
||||||
|
kernel: true,
|
||||||
|
addFormOptions(s){
|
||||||
|
var o;
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,18 @@
|
||||||
|
'use strict';
|
||||||
|
'require form';
|
||||||
|
|
||||||
|
return L.Class.extend({
|
||||||
|
trigger: _('timer (kernel)'),
|
||||||
|
kernel: true,
|
||||||
|
addFormOptions(s){
|
||||||
|
var o;
|
||||||
|
|
||||||
|
o = s.option(form.Value, 'delayon', _('On-State Delay'));
|
||||||
|
o.modalonly = true;
|
||||||
|
o.depends('trigger', 'timer');
|
||||||
|
|
||||||
|
o = s.option(form.Value, 'delayoff', _('Off-State Delay'));
|
||||||
|
o.modalonly = true;
|
||||||
|
o.depends('trigger', 'timer');
|
||||||
|
}
|
||||||
|
});
|
|
@ -2,39 +2,56 @@
|
||||||
'require uci';
|
'require uci';
|
||||||
'require rpc';
|
'require rpc';
|
||||||
'require form';
|
'require form';
|
||||||
'require tools.widgets as widgets';
|
'require fs';
|
||||||
|
|
||||||
var callLeds, callUSB;
|
var callLeds = rpc.declare({
|
||||||
|
|
||||||
callLeds = rpc.declare({
|
|
||||||
object: 'luci',
|
object: 'luci',
|
||||||
method: 'getLEDs',
|
method: 'getLEDs',
|
||||||
expect: { '': {} }
|
expect: { '': {} }
|
||||||
});
|
});
|
||||||
|
|
||||||
callUSB = rpc.declare({
|
|
||||||
object: 'luci',
|
|
||||||
method: 'getUSBDevices',
|
|
||||||
expect: { '': {} }
|
|
||||||
});
|
|
||||||
|
|
||||||
return L.view.extend({
|
return L.view.extend({
|
||||||
load: function() {
|
load: function() {
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
callLeds(),
|
callLeds(),
|
||||||
callUSB()
|
L.resolveDefault(fs.list('/www' + L.resource('view/system/led-trigger')), [])
|
||||||
]);
|
]).then(function(data) {
|
||||||
|
var plugins = data[1];
|
||||||
|
var tasks = [];
|
||||||
|
|
||||||
|
for (var i = 0; i < plugins.length; i++) {
|
||||||
|
var m = plugins[i].name.match(/^(.+)\.js$/);
|
||||||
|
|
||||||
|
if (plugins[i].type != 'file' || m == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
tasks.push(L.require('view.system.led-trigger.' + m[1]).then(L.bind(function(name){
|
||||||
|
return L.resolveDefault(L.require('view.system.led-trigger.' + name)).then(function(form) {
|
||||||
|
return {
|
||||||
|
name: name,
|
||||||
|
form: form,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}, this, m[1])));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.all(tasks).then(function(plugins) {
|
||||||
|
var value = {};
|
||||||
|
value[0] = data[0];
|
||||||
|
value[1] = plugins;
|
||||||
|
return value;
|
||||||
|
});
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function(results) {
|
render: function(data) {
|
||||||
var leds = results[0],
|
var m, s, o, triggers = [];
|
||||||
usb = results[1],
|
var leds = data[0];
|
||||||
triggers = {},
|
var plugins = data[1];
|
||||||
m, s, o;
|
|
||||||
|
|
||||||
for (var k in leds)
|
for (var k in leds)
|
||||||
for (var i = 0; i < leds[k].triggers.length; i++)
|
for (var i = 0; i < leds[k].triggers.length; i++)
|
||||||
triggers[leds[k].triggers[i]] = true;
|
triggers[i] = leds[k].triggers[i];
|
||||||
|
|
||||||
m = new form.Map('system',
|
m = new form.Map('system',
|
||||||
_('<abbr title="Light Emitting Diode">LED</abbr> Configuration'),
|
_('<abbr title="Light Emitting Diode">LED</abbr> Configuration'),
|
||||||
|
@ -49,117 +66,28 @@ return L.view.extend({
|
||||||
s.option(form.Value, 'name', _('Name'));
|
s.option(form.Value, 'name', _('Name'));
|
||||||
|
|
||||||
o = s.option(form.ListValue, 'sysfs', _('<abbr title="Light Emitting Diode">LED</abbr> Name'));
|
o = s.option(form.ListValue, 'sysfs', _('<abbr title="Light Emitting Diode">LED</abbr> Name'));
|
||||||
Object.keys(leds).sort().forEach(function(name) { o.value(name) });
|
Object.keys(leds).sort().forEach(function(name) {
|
||||||
|
o.value(name)
|
||||||
o = s.option(form.Flag, 'default', _('Default state'));
|
});
|
||||||
o.rmempty = false;
|
|
||||||
o.textvalue = function(section_id) {
|
|
||||||
var cval = this.cfgvalue(section_id);
|
|
||||||
|
|
||||||
if (cval == null)
|
|
||||||
cval = this.default;
|
|
||||||
|
|
||||||
return (cval == this.enabled) ? _('On') : _('Off');
|
|
||||||
};
|
|
||||||
|
|
||||||
o = s.option(form.ListValue, 'trigger', _('Trigger'));
|
o = s.option(form.ListValue, 'trigger', _('Trigger'));
|
||||||
if (usb.devices && usb.devices.length)
|
for (var i = 0; i < plugins.length; i++) {
|
||||||
triggers['usbdev'] = true;
|
var plugin = plugins[i];
|
||||||
if (usb.ports && usb.ports.length)
|
|
||||||
triggers['usbport'] = true;
|
|
||||||
Object.keys(triggers).sort().forEach(function(t) { o.value(t, t.replace(/-/g, '')) });
|
|
||||||
|
|
||||||
o = s.option(form.Value, 'delayon', _('On-State Delay'));
|
if ( plugin.form.kernel == false )
|
||||||
o.modalonly = true;
|
o.value(plugin.name, plugin.form.trigger);
|
||||||
o.depends('trigger', 'timer');
|
else
|
||||||
|
for (var k = 0; k < triggers.length; k++)
|
||||||
|
if ( plugin.name == triggers[k] )
|
||||||
|
o.value(plugin.name, plugin.form.trigger);
|
||||||
|
}
|
||||||
|
|
||||||
o = s.option(form.Value, 'delayoff', _('Off-State Delay'));
|
s.addModalOptions = function(s) {
|
||||||
o.modalonly = true;
|
for (var i = 0; i < plugins.length; i++) {
|
||||||
o.depends('trigger', 'timer');
|
var plugin = plugins[i];
|
||||||
|
plugin.form.addFormOptions(s);
|
||||||
o = s.option(widgets.DeviceSelect, '_net_dev', _('Device'));
|
|
||||||
o.rmempty = true;
|
|
||||||
o.ucioption = 'dev';
|
|
||||||
o.modalonly = true;
|
|
||||||
o.noaliases = true;
|
|
||||||
o.depends('trigger', 'netdev');
|
|
||||||
o.remove = function(section_id) {
|
|
||||||
var topt = this.map.lookupOption('trigger', section_id),
|
|
||||||
tval = topt ? topt[0].formvalue(section_id) : null;
|
|
||||||
|
|
||||||
if (tval != 'netdev' && tval != 'usbdev')
|
|
||||||
uci.unset('system', section_id, 'dev');
|
|
||||||
};
|
|
||||||
|
|
||||||
o = s.option(form.MultiValue, 'mode', _('Trigger Mode'));
|
|
||||||
o.rmempty = true;
|
|
||||||
o.modalonly = true;
|
|
||||||
o.depends('trigger', 'netdev');
|
|
||||||
o.value('link', _('Link On'));
|
|
||||||
o.value('tx', _('Transmit'));
|
|
||||||
o.value('rx', _('Receive'));
|
|
||||||
|
|
||||||
if (usb.devices && usb.devices.length) {
|
|
||||||
o = s.option(form.ListValue, '_usb_dev', _('USB Device'));
|
|
||||||
o.depends('trigger', 'usbdev');
|
|
||||||
o.rmempty = true;
|
|
||||||
o.ucioption = 'dev';
|
|
||||||
o.modalonly = true;
|
|
||||||
o.remove = function(section_id) {
|
|
||||||
var topt = this.map.lookupOption('trigger', section_id),
|
|
||||||
tval = topt ? topt[0].formvalue(section_id) : null;
|
|
||||||
|
|
||||||
if (tval != 'netdev' && tval != 'usbdev')
|
|
||||||
uci.unset('system', section_id, 'dev');
|
|
||||||
}
|
}
|
||||||
o.value('');
|
};
|
||||||
usb.devices.forEach(function(usbdev) {
|
|
||||||
o.value(usbdev.id, '%s (%s - %s)'.format(usbdev.id, usbdev.vendor || '?', usbdev.product || '?'));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (usb.ports && usb.ports.length) {
|
|
||||||
o = s.option(form.MultiValue, 'port', _('USB Ports'));
|
|
||||||
o.depends('trigger', 'usbport');
|
|
||||||
o.rmempty = true;
|
|
||||||
o.modalonly = true;
|
|
||||||
o.cfgvalue = function(section_id) {
|
|
||||||
var ports = [],
|
|
||||||
value = uci.get('system', section_id, 'port');
|
|
||||||
|
|
||||||
if (!Array.isArray(value))
|
|
||||||
value = String(value || '').split(/\s+/);
|
|
||||||
|
|
||||||
for (var i = 0; i < value.length; i++)
|
|
||||||
if (value[i].match(/^(\d+)-(\d+)$/))
|
|
||||||
ports.push('usb%d-port%d'.format(Regexp.$1, Regexp.$2));
|
|
||||||
else
|
|
||||||
ports.push(value[i]);
|
|
||||||
|
|
||||||
return ports;
|
|
||||||
};
|
|
||||||
usb.ports.forEach(function(usbport) {
|
|
||||||
var dev = (usbport.device && Array.isArray(usb.devices))
|
|
||||||
? usb.devices.filter(function(d) { return d.id == usbport.device })[0] : null;
|
|
||||||
|
|
||||||
var label = _('Port %s').format(usbport.port);
|
|
||||||
|
|
||||||
if (dev)
|
|
||||||
label += ' (%s - %s)'.format(dev.vendor || '?', dev.product || '?');
|
|
||||||
|
|
||||||
o.value(usbport.port, label);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
o = s.option(form.Value, 'port_mask', _('Switch Port Mask'));
|
|
||||||
o.modalonly = true;
|
|
||||||
o.depends('trigger', 'switch0');
|
|
||||||
o.depends('trigger', 'switch1');
|
|
||||||
|
|
||||||
o = s.option(form.Value, 'speed_mask', _('Switch Speed Mask'));
|
|
||||||
o.modalonly = true;
|
|
||||||
o.depends('trigger', 'switch0');
|
|
||||||
o.depends('trigger', 'switch1');
|
|
||||||
|
|
||||||
return m.render();
|
return m.render();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue