From 7b8cae34c4621a59e9671d86b6777aed400e4e35 Mon Sep 17 00:00:00 2001 From: Marcus Schref Date: Tue, 23 Aug 2022 11:07:33 +0200 Subject: [PATCH] luci-app-lorawan-basicstation: initial check-in LuCi Support for LoRaWAN basicstation Signed-off-by: Marcus Schref --- .../luci-app-lorawan-basicstation/Makefile | 19 ++ .../view/lorawan-basicstation/advanced.js | 104 ++++++++++ .../view/lorawan-basicstation/general.js | 180 ++++++++++++++++++ .../view/lorawan-basicstation/log.js | 22 +++ .../menu.d/luci-app-lorawan-basicstation.json | 39 ++++ .../acl.d/luci-app-lorawan-basicstation.json | 14 ++ 6 files changed, 378 insertions(+) create mode 100644 applications/luci-app-lorawan-basicstation/Makefile create mode 100644 applications/luci-app-lorawan-basicstation/htdocs/luci-static/resources/view/lorawan-basicstation/advanced.js create mode 100644 applications/luci-app-lorawan-basicstation/htdocs/luci-static/resources/view/lorawan-basicstation/general.js create mode 100644 applications/luci-app-lorawan-basicstation/htdocs/luci-static/resources/view/lorawan-basicstation/log.js create mode 100644 applications/luci-app-lorawan-basicstation/root/usr/share/luci/menu.d/luci-app-lorawan-basicstation.json create mode 100644 applications/luci-app-lorawan-basicstation/root/usr/share/rpcd/acl.d/luci-app-lorawan-basicstation.json diff --git a/applications/luci-app-lorawan-basicstation/Makefile b/applications/luci-app-lorawan-basicstation/Makefile new file mode 100644 index 0000000000..392587767b --- /dev/null +++ b/applications/luci-app-lorawan-basicstation/Makefile @@ -0,0 +1,19 @@ +# +# Copyright (C) 2022 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 LoRaWAN basicstation +LUCI_DEPENDS:=+basicstation +LUCI_PKGARCH:=all + +PKG_MAINTAINER:=Marcus Schref +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 index 0000000000..2f80e24c5c --- /dev/null +++ b/applications/luci-app-lorawan-basicstation/htdocs/luci-static/resources/view/lorawan-basicstation/advanced.js @@ -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 index 0000000000..67cb187769 --- /dev/null +++ b/applications/luci-app-lorawan-basicstation/htdocs/luci-static/resources/view/lorawan-basicstation/general.js @@ -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 index 0000000000..625333f67a --- /dev/null +++ b/applications/luci-app-lorawan-basicstation/htdocs/luci-static/resources/view/lorawan-basicstation/log.js @@ -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 index 0000000000..a488b6c916 --- /dev/null +++ b/applications/luci-app-lorawan-basicstation/root/usr/share/luci/menu.d/luci-app-lorawan-basicstation.json @@ -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 index 0000000000..fe2ae1d17d --- /dev/null +++ b/applications/luci-app-lorawan-basicstation/root/usr/share/rpcd/acl.d/luci-app-lorawan-basicstation.json @@ -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" ] + } + } +} -- 2.30.2