Device list and selection integrated into configuration interface

This commit is contained in:
2026-02-01 14:37:06 +01:00
parent 3729fcb0f1
commit 59cbc64b08
5 changed files with 165 additions and 24 deletions

View File

@@ -54,6 +54,20 @@
"description": "A password for configuration modifications is required.",
"category": "System"
},
{
"name": "instDesc1",
"label": "Description 1",
"type": "string",
"description": "NMEA2000 installation description 1",
"category": "System"
},
{
"name": "instDesc2",
"label": "Description 2",
"type": "string",
"description": "NMEA2000 installation description 2",
"category": "System"
},
{
"name": "apEnable",
"label": "Enable AP",
@@ -120,8 +134,8 @@
"type": "number",
"default": 64,
"min": 0,
"max": 255,
"description": "The brightness of the destination leds (0..255).",
"max": 4095,
"description": "The brightness of the destination leds (0..4095).",
"category": "Hardware"
},
{
@@ -130,8 +144,8 @@
"type": "number",
"default": 64,
"min": 0,
"max": 255,
"description": "The brightness of the rgb status led (0..255).",
"max": 4095,
"description": "The brightness of the rgb status led (0..4095).",
"category": "Hardware"
},
{
@@ -281,8 +295,8 @@
"name": "n2kDestA",
"label": "Destination A",
"type": "dynlist",
"source": "devicelist",
"interval": "5000",
"source": "dyndevlist",
"interval": "30000",
"description": "Destination device A",
"category": "NMEA2000"
},
@@ -290,8 +304,8 @@
"name": "n2kDestB",
"label": "Destination B",
"type": "dynlist",
"source": "devicelist",
"interval": "5000",
"source": "dyndevlist",
"interval": "30000",
"description": "Destination device B",
"category": "NMEA2000"
},
@@ -299,8 +313,8 @@
"name": "n2kDestC",
"label": "Destination C",
"type": "dynlist",
"source": "devicelist",
"interval": "5000",
"source": "dyndevlist",
"interval": "30000",
"description": "Destination device C",
"category": "NMEA2000"
},
@@ -312,7 +326,7 @@
"check": "checkMinMax",
"min": 1,
"max": 300,
"description": "interval seconds to send environment data [1..300]",
"description": "interval in seconds to send environment data [1..300]",
"category": "NMEA2000"
}
]

View File

@@ -104,11 +104,11 @@ if (!window.isSecureContext) {
</div>
</div>
<div class="deviceForm tabPage hidden" id="devicePage">
<div class="tabPage hidden" id="devicePage">
<div class="buttons">
<button id="reloadDevices">Reload</button>
</div>
<div class="deviceFormRows">
<div class="deviceListRows" id="deviceList">
</div>
</div>

View File

@@ -10,6 +10,7 @@
let buttonHandlers = {};
let checkers = {};
let userFormatters = {};
let dynLists = {}; // dynamic selection lists
let apiPrefix = "";
function addEl(type, clazz, parent, text) {
let el = document.createElement(type);
@@ -131,6 +132,12 @@
resetForm();
}
})
// check if any dynamic list needs update
for (let l in dynLists) {
if (loadDynList(l)) {
updateDynLists(l, configDefinitions)
}
}
}
function resetForm(ev) {
getJson(apiPrefix + "/api/config")
@@ -614,6 +621,12 @@
})
return el;
}
if (configItem.type === 'dynlist') {
el = addEl('select', clazz, frame);
if (configItem.readOnly) el.setAttribute('disabled', true);
el.setAttribute('name', configItem.name);
return el;
}
if (configItem.type === 'filter') {
return createFilterInput(configItem, frame, clazz);
}
@@ -834,6 +847,50 @@
return rt;
}
function loadDynList(apiname) {
// returns true if list was loaded
// TODO
// - implement generic all/none or static entries from config.json
// - check if current list is updated, so later dom refresh is not needed
let now = (new Date()).getTime();
if (now - dynLists[apiname].timestamp < dynLists[apiname].interval) {
// console.log("loading dynamic list: update interval not reached")
return false;
}
// console.log("loading dynamic list: " + apiname)
getJson("api/"+apiname)
.then(function(list) {
dynLists[apiname].timestamp = (new Date()).getTime();
dynLists[apiname].updated = true
dynLists[apiname].data = []
dynLists[apiname].data.push({"l": "all devices (broadcast)", "v": "all"})
dynLists[apiname].data.push({"l": "disabled (none)", "v": "none"})
for (let i in list) {
// let l = list[i].model + " - " + list[i].manufname + " (" + list[i].manufcode + ")"
// dynLists[apiname].data.push({"l": l, "v": list[i].name})
dynLists[apiname].data.push({"l": list[i].l, "v": list[i].v})
}
})
.catch(function (err) {
alert("unable to load dynamic list: " + err)
return false
})
return true
}
function updateDynLists(listname, defs) {
for (i in defs) {
if ((defs[i].type === "dynlist") && (defs[i].source === listname)) {
let el = document.querySelector("[name='" + defs[i].name + "']");
// TODO append here static list from configdefinition
// if defs[i] has key "list" then prepend it
// let options =
// options += dynLists[listname].data
updateSelectList(el, dynLists[listname].data, true)
}
}
}
function createConfigDefinitions(parent, capabilities, defs) {
let categories = {};
let frame = parent.querySelector('.configFormRows');
@@ -843,6 +900,22 @@
let currentCategoryPopulated = true;
defs.forEach(function (item) {
if (!item.type || item.category === undefined) return;
// look for dynamic lists
if (item.type === 'dynlist') {
if (item.source in dynLists) {
// check if interval is smaller as already defined
if (item.interval < dynLists[item.source].interval) {
dynLists[item.source].interval = item.interval;
}
} else {
// define new dynamic list
dynLists[item.source] = {
interval: item.interval,
updated: true
};
loadDynList(item.source);
}
}
let catEntry;
if (categories[item.category] === undefined) {
catEntry = {
@@ -974,10 +1047,32 @@
.catch(function (err) { alert("unable to load config: " + err) })
}
function loadDeviceList() {
getJson("api/devices")
getJson("api/devicelist")
.then(function(devices) {
let deviceForm = document.getElementById('devicePage');
console.log("Device form called");
let deviceList = document.getElementById('deviceList');
let row;
let el;
let even = true;
let n = 0
deviceList.replaceChildren();
for (let d in devices) {
row = addEl('div', even ? 'row even' : 'row', deviceList);
el = addEl('span', 'label', row);
el.innerHTML = devices[d].name;
el = addEl('span', 'value', row);
el.innerHTML = devices[d].model;
el.innerHTML += " - " + devices[d].manufname;
el.innerHTML += " (" + devices[d].manufcode+ "): "
el.innerHTML += devices[d].source
even = ! even;
n += 1;
}
addEl('hr', '', deviceList);
row = addEl('div', even ? 'row even' : 'row', deviceList);
el = addEl('span', 'label', row);
el.innerHTML = "total devices";
el = addEl('span', 'value', row);
el.innerHTML = n;
})
.catch(function (err) { alert("unable to load devicelist: " + err) })
}
@@ -1097,7 +1192,7 @@
});
}
buttonHandlers.reloadDevices=function() {
console.log("Button reload devices");
loadDeviceList()
}
function handleTab(el) {
let activeName = el.getAttribute('data-page');
@@ -1664,6 +1759,7 @@
})
})
})
loadDeviceList();
});
}());