luci-app-lorawan-basicstation: initial check-in 5938/head
authorMarcus Schref <mschref@web.de>
Tue, 23 Aug 2022 09:07:33 +0000 (11:07 +0200)
committerMarcus Schref <mschref@web.de>
Tue, 23 Aug 2022 09:07:40 +0000 (11:07 +0200)
LuCi Support for LoRaWAN basicstation

Signed-off-by: Marcus Schref <mschref@web.de>
applications/luci-app-lorawan-basicstation/Makefile [new file with mode: 0644]
applications/luci-app-lorawan-basicstation/htdocs/luci-static/resources/view/lorawan-basicstation/advanced.js [new file with mode: 0644]
applications/luci-app-lorawan-basicstation/htdocs/luci-static/resources/view/lorawan-basicstation/general.js [new file with mode: 0644]
applications/luci-app-lorawan-basicstation/htdocs/luci-static/resources/view/lorawan-basicstation/log.js [new file with mode: 0644]
applications/luci-app-lorawan-basicstation/root/usr/share/luci/menu.d/luci-app-lorawan-basicstation.json [new file with mode: 0644]
applications/luci-app-lorawan-basicstation/root/usr/share/rpcd/acl.d/luci-app-lorawan-basicstation.json [new file with mode: 0644]

diff --git a/applications/luci-app-lorawan-basicstation/Makefile b/applications/luci-app-lorawan-basicstation/Makefile
new file mode 100644 (file)
index 0000000..3925877
--- /dev/null
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2022 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 LoRaWAN basicstation
+LUCI_DEPENDS:=+basicstation
+LUCI_PKGARCH:=all
+
+PKG_MAINTAINER:=Marcus Schref <mschref@tdt.de>
+PKG_LICENSE:=APACHE-2.0
+
+include ../../luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
diff --git a/applications/luci-app-lorawan-basicstation/htdocs/luci-static/resources/view/lorawan-basicstation/advanced.js b/applications/luci-app-lorawan-basicstation/htdocs/luci-static/resources/view/lorawan-basicstation/advanced.js
new file mode 100644 (file)
index 0000000..2f80e24
--- /dev/null
@@ -0,0 +1,104 @@
+'use strict';
+'require form';
+'require view';
+'require uci';
+
+return view.extend({
+       load: function() {
+               return Promise.all([
+                       uci.load('basicstation')
+               ]);
+       },
+
+       render: function(data) {
+               var m, s, o, options;
+               
+               /* Advanced Settings */
+               m = new form.Map('basicstation', _('Advanced Settings'));
+
+               /* RF Configuration */
+               s = m.section(form.GridSection, 'rfconf', _('RF Configuration'));
+               s.addremove = true;
+               s.anonymous = false;
+               s.nodescriptions = true;
+
+               o = s.option(form.ListValue, 'type', _('Type'), 
+                       _('RF front end type'));
+               o.value('SX1250');
+               o.default = 'SX1250';
+
+               o = s.option(form.Flag, 'txEnable', _('Tx enable'),
+                       _('Enable transmission capabilities'));
+               o.default = 'false';
+
+               o = s.option(form.Value, 'freq', _('Frequency'),
+                       _('Frequency in Hz'));
+               o.datatype = 'uinteger';
+
+               o = s.option(form.Value, 'antennaGain', _('Antenna Gain'),
+                       _('Antenna gain in dBi'));
+               o.datatype = 'uinteger';
+               
+               o = s.option(form.Value, 'rssiOffset', _('RSSI Offset'),
+                       _('RSSI offset in dBm'));
+               o.datatype = 'float';
+               
+               o = s.option(form.ListValue, 'useRssiTcomp', _('RSSI Tcomp'),
+                       _('RSSI Tcomp object to be used for this RF configuration'));
+               options = uci.sections('basicstation', 'rssitcomp')
+               for (var i = 0; i < options.length; i++) {
+                       var value = options[i]['.name'];
+                       o.value(value);
+               }
+               o.default = 'std';
+
+               /* RSSI Tcomp */
+               s = m.section(form.GridSection, 'rssitcomp', _('RSSI Tcomp'));
+               s.addremove = true;
+               s.anonymous = false;
+               s.nodescripitons = true;
+
+               o = s.option(form.Value, 'coeff_a', _('Coeff A'));
+               o.datatype = 'float';
+               
+               o = s.option(form.Value, 'coeff_b', _('Coeff B'));
+               o.datatype = 'float';
+               
+               o = s.option(form.Value, 'coeff_c', _('Coeff C'));
+               o.datatype = 'float';
+               
+               o = s.option(form.Value, 'coeff_d', _('Coeff D'));
+               o.datatype = 'float';
+               
+               o = s.option(form.Value, 'coeff_e', _('Coeff E'));
+               o.datatype = 'float';
+
+               /* TX Gain Lookup Table */
+               s = m.section(form.GridSection, 'txlut', _('TX Gain Lookup Table'));
+               s.addremove = true;
+               s.anonymous = true;
+               s.nodescriptions = true;
+
+               o = s.option(form.Value, 'rfPower', _('RF Power'),
+                       _('RF output power target in dBm'));
+               o.datatype = 'uinteger';
+               
+               o = s.option(form.Flag, 'paGain', _('PA Enable'),
+                       _('Power amplifier enabled'));
+               o.default = false;
+               
+               o = s.option(form.Value, 'pwrIdx', _('Power Index'),
+                       _('Possible gain settings from 0 (min. gain) to 22 (max. gain)'));
+               o.datatype = 'range(0,22)';
+
+               o = s.option(form.DynamicList, 'usedBy', _('Used By'),
+                       _('RF configurations that use this tx gain object'));
+               options = uci.sections('basicstation', 'rfconf');
+               for (var i = 0; i < options.length; i++) {
+                       var value = options[i]['.name'];
+                       o.value(value);
+               }
+
+               return m.render();
+       },
+});
diff --git a/applications/luci-app-lorawan-basicstation/htdocs/luci-static/resources/view/lorawan-basicstation/general.js b/applications/luci-app-lorawan-basicstation/htdocs/luci-static/resources/view/lorawan-basicstation/general.js
new file mode 100644 (file)
index 0000000..67cb187
--- /dev/null
@@ -0,0 +1,180 @@
+'use strict';
+'require form';
+'require view';
+'require uci';
+'require fs';
+'require network';
+'require tools.widgets as widgets'; 
+
+return view.extend({
+       load: function() {
+               return Promise.all([
+                       uci.load('basicstation')
+               ]);
+       },
+
+       render: function(data) {
+               var m, s, o;
+
+               /* General Settings */
+               m = new form.Map('basicstation', _('General Settings'));
+               
+               /* Station Identity */
+               s = m.section(form.NamedSection, 'station', 'station',
+                       _('Station Identity'));
+
+               o = s.option(widgets.DeviceSelect, 'idGenIf',
+                       _('Interface for station ID generation'),
+                       _('Station ID is derived from the MAC address of the choosen interface'));
+               o.filter = function(section_id, value) {
+                       var dev = this.devices.filter(function(dev) { return dev.getName() == value })[0];
+                       return (dev && dev.getMAC() != null && dev.getMAC() != '00:00:00:00:00:00');
+               }
+               o.nobridges = true;
+               o.novirtual = true;
+               o.noaliases = true;
+               o.default = 'eth0';
+
+               o.write = function(sid, value) {
+                       var path = "/sys/class/net/" + value + "/address";
+                       uci.set('basicstation', sid, 'idGenIf', value);
+                       uci.set('basicstation', sid, 'routerid', path);
+               }
+
+               o = s.option(form.Value, 'stationid', _('Station ID'),
+                       _('Click save and apply to generate station ID'));
+               o.readonly = true;
+
+               /* Authentication */
+               s = m.section(form.NamedSection, 'auth', 'auth', 
+                       _('Authentication'));
+
+               o = s.option(form.ListValue, 'cred', _('Credentials'), 
+                       _('Credentials for LNS (TC) or CUPS (CUPS)'));
+               o.value('tc', _('TC'));
+               o.value('cups', _('CUPS'));
+               o.default = 'tc';
+
+               o = s.option(form.ListValue, 'mode', _('Authentication mode'), 
+                       _('Authentication mode for server connection'));
+               o.value('no', _('No Authentication'));
+               o.value('server', _('TLS Server Authentication'));
+               o.value('serverAndClient', _('TLS Server and Client Authentication'));
+               o.value('serverAndClientToken', _('TLS Server Authentication and Client Token'));
+               o.default = 'no';
+
+               o = s.option(form.Value, 'addr', _('Server address'));
+               o.optional = false;
+               o.rmempty = false;
+               o.placeholder = 'eu1.cloud.thethings.network';
+
+               o = s.option(form.Value, 'port', _('Port'));
+               o.optional = false;
+               o.rmempty = false;
+               o.datatype = 'uinteger';
+               o.placeholder = '8887';
+
+               o = s.option(form.Value, 'token', _('Authorization token'));
+               o.optional = false;
+               o.rmempty = false;
+               o.depends({ mode: 'serverAndClientToken' });
+
+               o = s.option(form.Value, 'key', _('Private station key'));
+               o.optional = false;
+               o.rmempty = false;
+               o.depends({ mode: 'serverAndClient' });
+
+               o = s.option(form.FileUpload, 'crt', _('Private station certificate'));
+               o.optional = false;
+               o.rmempty = false;
+               o.depends({ mode: "serverAndClient" });
+
+               o = s.option(form.FileUpload, 'trust', _('CA certificate'));
+               o.optional = false;
+               o.rmempty = false;
+               o.depends({ mode: "no", "!reverse": true });
+               
+               /* Radio Configuration */
+               s = m.section(form.NamedSection, 'sx130x', 'sx130x',
+                       _('Radio Configuration'));
+
+               o = s.option(form.ListValue, 'comif', _('Communication interface'), 
+                       _('Currently only USB devices are supported'));
+               o.value('usb', 'USB');
+               o.default = 'usb';
+
+               o = s.option(form.Value, 'devpath', _('Device path'),
+                       _('Device path of the LoRaWAN concentrator card'));
+               o.optional = false;
+               o.rmempty = false;
+               o.placeholder = '/dev/ttyACM0';
+
+               o = s.option(form.Flag, 'pps', _('PPS'),
+                       _('PPS (pulse per second) provided by GPS device or other source'));
+               o.default = false
+
+               o = s.option(form.Flag, 'public', _('Public network'),
+                       _('Public or private LoRaWAN network'));
+               o.default = true;
+
+               o = s.option(form.ListValue, 'clksrc', _('Clock source'),
+                       _('Radio to provide clock to Basicstation'));
+               o.value('0', 'Radio 0');
+               o.value('1', 'Radio 1');
+               o.default = '0';
+
+               var options = uci.sections('basicstation', 'rfconf');
+
+               o = s.option(form.ListValue, 'radio0', _('Radio 0'),
+                       _('RF configuration for Radio 0'));
+               for (var i = 0; i < options.length; i++) {
+                       var value = options[i]['.name'];
+                       o.value(value);
+               }
+               o.default = 'rfconf0';
+
+               o = s.option(form.ListValue, 'radio1', _('Radio 1'),
+                       _('RF configuration for Radio 1'));
+               for (var i = 0; i < options.length; i++) {
+                       var value = options[i]['.name'];
+                       o.value(value);
+               }
+               o.default = 'rfconf1';
+               
+               /* Logging */
+               s = m.section(form.NamedSection, 'station','station', 
+                       _('Logging'));
+
+               o = s.option(form.ListValue, 'logLevel', _('Level'), 
+                       _('Level to which messages are to be logged'));
+               o.value('XDEBUG', 'xdebug');
+               o.value('DEBUG', 'debug');
+               o.value('VERBOSE', 'verbose');
+               o.value('INFO', 'info');
+               o.value('NOTICE', 'notice');
+               o.value('WARNING', 'warning');
+               o.value('ERROR', 'error');
+               o.value('CRITICAL', 'critical');
+               o.default = 'DEBUG';
+
+               o = s.option(form.Value, 'logSize', _('Size'), 
+                       _('Maximum size of log file in MB'));
+               o.value('1');
+               o.value('2');
+               o.value('3');
+               o.value('4');
+               o.default = '1';
+               o.datatype = 'range(1,10)';
+
+               o = s.option(form.Value, 'logRotate', _('Rotate'), 
+                       _('Number of old log files to be kept'));
+               o.value('1');
+               o.value('2');
+               o.value('3');
+               o.value('4');
+               o.default = '1';
+               o.datatype = 'range(1, 10)';
+               
+               return m.render();
+       },
+});
diff --git a/applications/luci-app-lorawan-basicstation/htdocs/luci-static/resources/view/lorawan-basicstation/log.js b/applications/luci-app-lorawan-basicstation/htdocs/luci-static/resources/view/lorawan-basicstation/log.js
new file mode 100644 (file)
index 0000000..625333f
--- /dev/null
@@ -0,0 +1,22 @@
+'use strict';
+'require fs';
+'require view';
+
+return view.extend({
+    load: function() {
+        return L.resolveDefault(fs.read_direct('/tmp/basicstation/log'), '');
+    },
+
+    render: function(log) {
+        return E('div', { 'class': 'cbi-map', 'id': 'map' }, [
+            E('h2', _('Log Messages')),
+            E('div', { 'class': 'cbi-section' }, [
+                E('pre', [ log ])
+            ]),
+        ])
+    },
+
+    handleSaveApply: null,
+    handleSave: null,
+    handleReset: null
+})
diff --git a/applications/luci-app-lorawan-basicstation/root/usr/share/luci/menu.d/luci-app-lorawan-basicstation.json b/applications/luci-app-lorawan-basicstation/root/usr/share/luci/menu.d/luci-app-lorawan-basicstation.json
new file mode 100644 (file)
index 0000000..a488b6c
--- /dev/null
@@ -0,0 +1,39 @@
+{
+       "admin/network/lorawan-basicstation": {
+               "title": "LoRaWAN Basicstation",
+               "order": "600",
+               "action": {
+                       "type": "firstchild"
+               },
+               "depends": {
+                       "acl": [ "luci-app-lorawan-basicstation" ]
+               }
+       },
+
+       "admin/network/lorawan-basicstation/general": {
+               "title": "General Settings",
+               "order": 10,
+               "action": {
+                       "type": "view",
+                       "path": "lorawan-basicstation/general"
+               }
+       },
+
+       "admin/network/lorawan-basicstation/advanced": {
+               "title": "Advanced Settings",
+               "order": 20,
+               "action": {
+                       "type": "view",
+                       "path": "lorawan-basicstation/advanced"
+               }
+       },
+
+       "admin/network/lorawan-basicstation/log": {
+               "title": "Log Messages",
+               "order": 30,
+               "action": {
+                       "type": "view",
+                       "path": "lorawan-basicstation/log"
+               }
+       }
+}
diff --git a/applications/luci-app-lorawan-basicstation/root/usr/share/rpcd/acl.d/luci-app-lorawan-basicstation.json b/applications/luci-app-lorawan-basicstation/root/usr/share/rpcd/acl.d/luci-app-lorawan-basicstation.json
new file mode 100644 (file)
index 0000000..fe2ae1d
--- /dev/null
@@ -0,0 +1,14 @@
+{
+       "luci-app-lorawan-basicstation": {
+               "description": "Grant UCI access for luci-app-lorawan-basicstation",
+               "read": {
+                       "file": {
+                               "/tmp/basicstation/log": [ "read" ]
+                       },
+                       "uci": [ "basicstation" ]
+               },
+               "write": {
+                       "uci": [ "basicstation" ]
+               }
+       }
+}