From: Florian Eckert Date: Thu, 20 Feb 2020 11:04:12 +0000 (+0100) Subject: luci-mod-system: add led plugin infrastructure X-Git-Url: http://git.openwrt.org/?a=commitdiff_plain;h=638f5ce071f229fb0c11456a0b59d425179ab288;p=project%2Fluci.git 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 https://github.com/openwrt/openwrt/commit/d0b50c2770a0e2d54b37153f2801e2e7dc865fa6 So I have not ported the relevant code anymore. Signed-off-by: Florian Eckert --- diff --git a/applications/luci-app-ledtrig-rssi/Makefile b/applications/luci-app-ledtrig-rssi/Makefile new file mode 100644 index 0000000000..b3be2065c8 --- /dev/null +++ b/applications/luci-app-ledtrig-rssi/Makefile @@ -0,0 +1,16 @@ +# +# Copyright (C) 2020 TDT AG +# +# 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 diff --git a/applications/luci-app-ledtrig-rssi/htdocs/luci-static/resources/view/system/led-trigger/rssi.js b/applications/luci-app-ledtrig-rssi/htdocs/luci-static/resources/view/system/led-trigger/rssi.js new file mode 100644 index 0000000000..611adc8b1b --- /dev/null +++ b/applications/luci-app-ledtrig-rssi/htdocs/luci-static/resources/view/system/led-trigger/rssi.js @@ -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'); + } +}); diff --git a/applications/luci-app-ledtrig-switch/Makefile b/applications/luci-app-ledtrig-switch/Makefile new file mode 100644 index 0000000000..7cefc50294 --- /dev/null +++ b/applications/luci-app-ledtrig-switch/Makefile @@ -0,0 +1,15 @@ +# +# Copyright (C) 2020 TDT AG +# +# 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 diff --git a/applications/luci-app-ledtrig-switch/htdocs/luci-static/resources/view/system/led-trigger/switch0.js b/applications/luci-app-ledtrig-switch/htdocs/luci-static/resources/view/system/led-trigger/switch0.js new file mode 100644 index 0000000000..2f59866a6c --- /dev/null +++ b/applications/luci-app-ledtrig-switch/htdocs/luci-static/resources/view/system/led-trigger/switch0.js @@ -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'); + } +}); diff --git a/applications/luci-app-ledtrig-switch/htdocs/luci-static/resources/view/system/led-trigger/switch1.js b/applications/luci-app-ledtrig-switch/htdocs/luci-static/resources/view/system/led-trigger/switch1.js new file mode 100644 index 0000000000..7800cba227 --- /dev/null +++ b/applications/luci-app-ledtrig-switch/htdocs/luci-static/resources/view/system/led-trigger/switch1.js @@ -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'); + } +}); diff --git a/applications/luci-app-ledtrig-usbport/Makefile b/applications/luci-app-ledtrig-usbport/Makefile new file mode 100644 index 0000000000..1a176676fa --- /dev/null +++ b/applications/luci-app-ledtrig-usbport/Makefile @@ -0,0 +1,16 @@ +# +# Copyright (C) 2020 TDT AG +# +# 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 diff --git a/applications/luci-app-ledtrig-usbport/htdocs/luci-static/resources/view/system/led-trigger/usbport.js b/applications/luci-app-ledtrig-usbport/htdocs/luci-static/resources/view/system/led-trigger/usbport.js new file mode 100644 index 0000000000..970016c42b --- /dev/null +++ b/applications/luci-app-ledtrig-usbport/htdocs/luci-static/resources/view/system/led-trigger/usbport.js @@ -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; + }; + } +}); diff --git a/modules/luci-mod-system/Makefile b/modules/luci-mod-system/Makefile index a6d5a7a456..f0ca7987ed 100644 --- a/modules/luci-mod-system/Makefile +++ b/modules/luci-mod-system/Makefile @@ -7,7 +7,11 @@ include $(TOPDIR)/rules.mk 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 diff --git a/modules/luci-mod-system/htdocs/luci-static/resources/view/system/led-trigger/default-on.js b/modules/luci-mod-system/htdocs/luci-static/resources/view/system/led-trigger/default-on.js new file mode 100644 index 0000000000..6ccc70ac27 --- /dev/null +++ b/modules/luci-mod-system/htdocs/luci-static/resources/view/system/led-trigger/default-on.js @@ -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'); + }; + } +}); diff --git a/modules/luci-mod-system/htdocs/luci-static/resources/view/system/led-trigger/heartbeat.js b/modules/luci-mod-system/htdocs/luci-static/resources/view/system/led-trigger/heartbeat.js new file mode 100644 index 0000000000..7495843baa --- /dev/null +++ b/modules/luci-mod-system/htdocs/luci-static/resources/view/system/led-trigger/heartbeat.js @@ -0,0 +1,9 @@ +'use strict'; + +return L.Class.extend({ + trigger: _('heartbeat (kernel)'), + kernel: true, + addFormOptions(s){ + var o; + } +}); diff --git a/modules/luci-mod-system/htdocs/luci-static/resources/view/system/led-trigger/netdev.js b/modules/luci-mod-system/htdocs/luci-static/resources/view/system/led-trigger/netdev.js new file mode 100644 index 0000000000..dad631b533 --- /dev/null +++ b/modules/luci-mod-system/htdocs/luci-static/resources/view/system/led-trigger/netdev.js @@ -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')); + } +}); diff --git a/modules/luci-mod-system/htdocs/luci-static/resources/view/system/led-trigger/none.js b/modules/luci-mod-system/htdocs/luci-static/resources/view/system/led-trigger/none.js new file mode 100644 index 0000000000..31fed55458 --- /dev/null +++ b/modules/luci-mod-system/htdocs/luci-static/resources/view/system/led-trigger/none.js @@ -0,0 +1,9 @@ +'use strict'; + +return L.Class.extend({ + trigger: _('none (kernel)'), + kernel: true, + addFormOptions(s){ + var o; + } +}); diff --git a/modules/luci-mod-system/htdocs/luci-static/resources/view/system/led-trigger/timer.js b/modules/luci-mod-system/htdocs/luci-static/resources/view/system/led-trigger/timer.js new file mode 100644 index 0000000000..23ddd6d906 --- /dev/null +++ b/modules/luci-mod-system/htdocs/luci-static/resources/view/system/led-trigger/timer.js @@ -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'); + } +}); diff --git a/modules/luci-mod-system/htdocs/luci-static/resources/view/system/leds.js b/modules/luci-mod-system/htdocs/luci-static/resources/view/system/leds.js index edd4a24e06..849ead9528 100644 --- a/modules/luci-mod-system/htdocs/luci-static/resources/view/system/leds.js +++ b/modules/luci-mod-system/htdocs/luci-static/resources/view/system/leds.js @@ -2,39 +2,56 @@ 'require uci'; 'require rpc'; 'require form'; -'require tools.widgets as widgets'; +'require fs'; -var callLeds, callUSB; - -callLeds = rpc.declare({ +var callLeds = rpc.declare({ object: 'luci', method: 'getLEDs', expect: { '': {} } }); -callUSB = rpc.declare({ - object: 'luci', - method: 'getUSBDevices', - expect: { '': {} } -}); - return L.view.extend({ load: function() { return Promise.all([ 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) { - var leds = results[0], - usb = results[1], - triggers = {}, - m, s, o; + render: function(data) { + var m, s, o, triggers = []; + var leds = data[0]; + var plugins = data[1]; for (var k in leds) 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', _('LED Configuration'), @@ -49,117 +66,28 @@ return L.view.extend({ s.option(form.Value, 'name', _('Name')); o = s.option(form.ListValue, 'sysfs', _('LED 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'); - }; + Object.keys(leds).sort().forEach(function(name) { + o.value(name) + }); o = s.option(form.ListValue, 'trigger', _('Trigger')); - if (usb.devices && usb.devices.length) - triggers['usbdev'] = true; - 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')); - o.modalonly = true; - o.depends('trigger', 'timer'); - - o = s.option(form.Value, 'delayoff', _('Off-State Delay')); - o.modalonly = true; - o.depends('trigger', 'timer'); - - 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); - }); + for (var i = 0; i < plugins.length; i++) { + var plugin = plugins[i]; + + if ( plugin.form.kernel == false ) + o.value(plugin.name, plugin.form.trigger); + 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, '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'); + s.addModalOptions = function(s) { + for (var i = 0; i < plugins.length; i++) { + var plugin = plugins[i]; + plugin.form.addFormOptions(s); + } + }; return m.render(); }