diff --git a/web/config.json b/web/config.json index 588c62f..f772808 100644 --- a/web/config.json +++ b/web/config.json @@ -5,7 +5,8 @@ "type": "string", "default": "ESP32NMEA2K", "check": "checkSystemName", - "description": "system name, used for the access point and for services" + "description": "system name, used for the access point and for services", + "category":"system" }, { "name":"talkerId", @@ -13,7 +14,16 @@ "type":"list", "default":"GP", "list":["AB","AD","AG","AP","AI","AN","AR","AS","AT","AX","BI","BN","CA","CD","CR","CS","CT","CV","CX","DF","DU","DP","EC","EI","EP","ER","FD","FE","FR","FS","GA","GB","GI","GL","GN","GP","GQ","HC","HE","HF","HN","HD","HS","II","IN","JA","JB","JC","JD","JE","JF","JG","JH","LC","NL","NV","RA","RB","RC","RI","SA","SC","SD","SG","SN","SS","TC","TI","UP","VD","VM","VW","VA","VS","VT","VR","WD","WI","WL","YX","ZA","ZC","ZQ","ZV"], - "description":"the talkerId used in generated NMEA0183 records" + "description":"the talkerId used in generated NMEA0183 records", + "category":"system" + }, + { + "name": "stopApTime", + "type": "number", + "default": "0", + "check": "checkStopApTime", + "description": "stop the access point after that many minutes if not used", + "category":"system" }, { "name": "usbBaud", @@ -21,42 +31,48 @@ "type": "list", "default": "115200", "description": "baud rate for the USB port", - "list": [1200,2400,4800,9600,14400,19200,28800,38400,57600,115200,230400,460800] + "list": [1200,2400,4800,9600,14400,19200,28800,38400,57600,115200,230400,460800], + "category":"usb port" }, { "name": "sendUsb", "label": "NMEA to USB", "type": "boolean", "default": "true", - "description": "send out NMEA data on the USB port" + "description": "send out NMEA data on the USB port", + "category":"usb port" }, { "name": "receiveUsb", "label": "NMEA from USB", "type": "boolean", "default": "true", - "description": "receive NMEA data on the USB port" + "description": "receive NMEA data on the USB port", + "category":"usb port" }, { "name": "usbToN2k", "label": "USB to NMEA2000", "type": "boolean", "default": "true", - "description": "convert NMEA0183 from the USB port to NMEA2000" + "description": "convert NMEA0183 from the USB port to NMEA2000", + "category":"usb port" }, { "name": "usbReadFilter", "label": "USB read Filter", "type": "filter", "default": "", - "description": "filter for NMEA0183 data when reading from USB\nselect aison|aisoff, set a whitelist or a blacklist with NMEA sentences like RMC,RMB" + "description": "filter for NMEA0183 data when reading from USB\nselect aison|aisoff, set a whitelist or a blacklist with NMEA sentences like RMC,RMB", + "category":"usb port" }, { "name": "usbWriteFilter", "label": "USB write Filter", "type": "filter", "default": "", - "description": "filter for NMEA0183 data when writing to USB\nselect aison|aisoff, set a whitelist or a blacklist with NMEA sentences like RMC,RMB" + "description": "filter for NMEA0183 data when writing to USB\nselect aison|aisoff, set a whitelist or a blacklist with NMEA sentences like RMC,RMB", + "category":"usb port" }, { "name": "serialDirection", @@ -65,7 +81,8 @@ "default": "receive", "list": ["send","receive","off"], "description": "use the serial port to send or receive data", - "capabilities":{"serialmode":["UNI"]} + "capabilities":{"serialmode":["UNI"]}, + "category":"serial port" }, { "name": "serialBaud", @@ -74,7 +91,8 @@ "default": "115200", "description": "baud rate for the serial port", "list": [1200,2400,4800,9600,14400,19200,28800,38400,57600,115200,230400,460800], - "capabilities":{"serialmode":["RX","TX","UNI","BI"]} + "capabilities":{"serialmode":["RX","TX","UNI","BI"]}, + "category":"serial port" }, { "name": "sendSerial", @@ -82,7 +100,8 @@ "type": "boolean", "default": "true", "description": "send out NMEA data on the serial port", - "capabilities":{"serialmode":["TX","BI"]} + "capabilities":{"serialmode":["TX","BI"]}, + "category":"serial port" }, { "name": "receiveSerial", @@ -90,7 +109,8 @@ "type": "boolean", "default": "true", "description": "receive NMEA data on the serial port", - "capabilities":{"serialmode":["RX","BI"]} + "capabilities":{"serialmode":["RX","BI"]}, + "category":"serial port" }, { "name": "serialToN2k", @@ -98,7 +118,8 @@ "type": "boolean", "default": "true", "description": "convert NMEA0183 from the serial port to NMEA2000", - "capabilities":{"serialmode":["RX","BI","UNI"]} + "capabilities":{"serialmode":["RX","BI","UNI"]}, + "category":"serial port" }, { "name": "serialReadFilter", @@ -106,7 +127,8 @@ "type": "filter", "default": "", "description": "filter for NMEA0183 data when reading from serial\nselect aison|aisoff, set a whitelist or a blacklist with NMEA sentences like RMC,RMB", - "capabilities":{"serialmode":["RX","BI","UNI"]} + "capabilities":{"serialmode":["RX","BI","UNI"]}, + "category":"serial port" }, { "name": "serialWriteFilter", @@ -114,14 +136,16 @@ "type": "filter", "default": "", "description": "filter for NMEA0183 data when writing to serial\nselect aison|aisoff, set a whitelist or a blacklist with NMEA sentences like RMC,RMB", - "capabilities":{"serialmode":["TX","BI","UNI"]} + "capabilities":{"serialmode":["TX","BI","UNI"]}, + "category":"serial port" }, { "name": "serverPort", "label": "TCP port", "type": "number", "default": "2222", - "description": "the TCP port we listen on" + "description": "the TCP port we listen on", + "category":"TCP port" }, { "name": "maxClients", @@ -129,63 +153,72 @@ "type": "number", "default": "10", "check":"checkMaxClients", - "description": "the number of clients we allow to connect to us" + "description": "the number of clients we allow to connect to us", + "category":"TCP port" }, { "name": "sendTCP", "label": "NMEA to TCP", "type": "boolean", "default": "true", - "description": "send out NMEA data to connected TCP clients" + "description": "send out NMEA data to connected TCP clients", + "category":"TCP port" }, { "name": "readTCP", "label": "NMEA from TCP", "type": "boolean", "default": "true", - "description": "receive NMEA data from connected TCP clients" + "description": "receive NMEA data from connected TCP clients", + "category":"TCP port" }, { "name": "tcpToN2k", "label": "TCP to NMEA2000", "type": "boolean", "default": "true", - "description": "convert NMEA0183 from TCP clients to NMEA2000" + "description": "convert NMEA0183 from TCP clients to NMEA2000", + "category":"TCP port" }, { "name": "tcpReadFilter", "label": "TCP read Filter", "type": "filter", "default": "", - "description": "filter for NMEA0183 data when reading from TCP\nselect aison|aisoff, set a whitelist or a blacklist with NMEA sentences like RMC,RMB" + "description": "filter for NMEA0183 data when reading from TCP\nselect aison|aisoff, set a whitelist or a blacklist with NMEA sentences like RMC,RMB", + "category":"TCP port" }, { "name": "tcpWriteFilter", "label": "TCP write Filter", "type": "filter", "default": "", - "description": "filter for NMEA0183 data when writing to TCP\nselect aison|aisoff, set a whitelist or a blacklist with NMEA sentences like RMC,RMB" + "description": "filter for NMEA0183 data when writing to TCP\nselect aison|aisoff, set a whitelist or a blacklist with NMEA sentences like RMC,RMB", + "category":"TCP port" }, { "name": "sendSeasmart", "label": "Seasmart to TCP", "type": "boolean", "default": "false", - "description": "send NMEA2000 as seasmart to connected TCP clients" + "description": "send NMEA2000 as seasmart to connected TCP clients", + "category":"TCP port" }, { "name": "wifiClient", "label": "wifi client", "type": "boolean", "default": "false", - "description": "connect to an external WIFI network" + "description": "connect to an external WIFI network", + "category":"wifi client" }, { "name": "wifiPass", "label": "wifi client password", "type": "password", "default": "", - "description": "the password for an external WIFI network" + "description": "the password for an external WIFI network", + "category":"wifi client" }, { "name": "wifiSSID", @@ -193,14 +226,8 @@ "type": "string", "default": "", "check": "checkSSID", - "description": "the SSID for an external WIFI network" - }, - { - "name": "stopApTime", - "type": "number", - "default": "0", - "check": "checkStopApTime", - "description": "stop the access point after that many minutes if not used" + "description": "the SSID for an external WIFI network", + "category":"wifi client" } diff --git a/web/index.html b/web/index.html index 4ab47bd..8a7915f 100644 --- a/web/index.html +++ b/web/index.html @@ -8,6 +8,13 @@ let self=this; let lastUpdate=(new Date()).getTime(); let reloadConfig=false; + function addEl(type,clazz,parent,text){ + let el=document.createElement(type); + el.classList.add(clazz); + if (text) el.textContent=text; + if (parent)parent.appendChild(el); + return el; + } function alertRestart(){ reloadConfig=true; alert("Board reset triggered, reconnect WLAN if necessary"); @@ -265,12 +272,29 @@ .then(function (capabilities) { getJson("config.json") .then(function (defs) { + let category; + let categoryEl; let frame = document.querySelector('.configFormRows'); if (!frame) throw Error("no config form"); frame.innerHTML = ''; configDefinitions = defs; defs.forEach(function (item) { if (!item.type) return; + if (item.category != category || ! categoryEl){ + let categoryFrame=addEl('div','category',frame); + let categoryTitle=addEl('div','title',categoryFrame); + let categoryButton=addEl('button','categoryButton',categoryTitle,'v'); + addEl('span','label',categoryTitle,item.category); + categoryEl=addEl('div','content',categoryFrame); + categoryEl.classList.add('hidden'); + let currentEl=categoryEl; + categoryTitle.addEventListener('click',function(ev){ + let rs=currentEl.classList.toggle('hidden'); + if (rs) categoryButton.textContent="v"; + else categoryButton.textContent="^"; + }) + category=item.category; + } if (item.capabilities !== undefined){ for (let capability in item.capabilities){ let values=item.capabilities[capability]; @@ -282,16 +306,10 @@ if (! found) return; } } - let row = document.createElement('div'); - row.classList.add('row'); + let row = addEl('div','row',categoryEl); let label = item.label || item.name; - let labelEl = document.createElement('span'); - labelEl.classList.add('label'); - labelEl.textContent = label; - row.appendChild(labelEl); - let valueFrame=document.createElement("div"); - valueFrame.classList.add("value"); - row.appendChild(valueFrame); + addEl('span','label',row,label); + let valueFrame=addEl('div','value',row); let valueEl = createInput(item,valueFrame); if (!valueEl) return; valueEl.setAttribute('data-default', item.default); @@ -300,27 +318,18 @@ checkChange(el,row); }) if (item.check) valueEl.setAttribute('data-check', item.check); - let btContainer=document.createElement("div"); - btContainer.classList.add("buttonContainer"); - row.appendChild(btContainer); - let bt = document.createElement('button'); - bt.classList.add('defaultButton'); + let btContainer=addEl('div','buttonContainer',row); + let bt = addEl('button','defaultButton',btContainer,'X'); bt.setAttribute('data-default', item.default); bt.addEventListener('click', function (ev) { valueEl.value = valueEl.getAttribute('data-default'); let changeEvent=new Event('change'); valueEl.dispatchEvent(changeEvent); }) - bt.textContent = "X"; - btContainer.appendChild(bt); - bt = document.createElement('button'); - bt.classList.add('infoButton'); + bt = addEl('button','infoButton',btContainer,'?'); bt.addEventListener('click', function (ev) { showOverlay(item.description); }); - bt.textContent = "?"; - btContainer.appendChild(bt); - frame.appendChild(row); }) resetForm(); }) @@ -338,6 +347,22 @@ text+="</p>"; showOverlay(text,true); }); + } + function handleTab(el){ + let activeName=el.getAttribute('data-page'); + if (! activeName) return; + let activeTab=document.getElementById(activeName); + if (!activeTab) return; + let all=document.querySelectorAll('.tabPage'); + for (let i=0;i<all.length;i++){ + all[i].classList.add('hidden'); + } + let tabs=document.querySelectorAll('.tab'); + for (let i=0;i<all.length;i++){ + tabs[i].classList.remove('active'); + } + el.classList.add('active'); + activeTab.classList.remove('hidden'); } window.setInterval(update,1000); window.addEventListener('load',function(){ @@ -350,17 +375,31 @@ cd.addEventListener('change',function(ev){ showCanDetails(ev.target.checked); }); + let tabs=document.querySelectorAll('.tab'); + for (let i=0;i<tabs.length;i++){ + tabs[i].addEventListener('click',function(ev){ + handleTab(ev.target); + }); + } loadConfigDefinitions(); }); </script> <style type="text/css"> .configForm { - margin-top: 1em; - margin-bottom: 1em; - padding-top: 0.5em; padding-bottom: 0.5em; - border: 1px solid grey; - max-width: 40em; +} +.configForm .buttons { + margin-top: 0.5em; +} +.configForm .content>div:nth-child(even) { + background-color: rgb(211 211 211 / 43%); +} +#statusPage .even { + background-color: rgb(211 211 211 / 43%); +} +.category .title .label { + opacity: 1; + margin-left: 1em; } .changed input{ color: green @@ -373,11 +412,13 @@ span.label { display: inline-block; opacity: 0.6; } -.value { - width: 19em; +.configForm .value { + width: 21em; display: flex; flex-direction: row; + margin-bottom: 0.2em; } + span#connected { display: inline-block; background-color: red; @@ -389,11 +430,16 @@ span#connected.ok{ background-color: #13ac13; } .row { - margin: 0.5em; + padding: 0.5em; display: flex; flex-direction: row; flex-wrap: wrap; } +input,select { + border: 1px solid #808080a1; + font-size: 0.9em; + padding: 0.2em; +} .filter { display: inline-block; } @@ -404,6 +450,16 @@ button.infoButton { margin-left: 1em; vertical-align: bottom; } +.category .title { + padding-left: 0.5em; + background-color: lightgray; + padding-top: 0.3em; + padding-bottom: 0.5em; + border-bottom: 1px solid darkgray; +} +.hidden{ + display: none !important; +} #canDetails{ display:none; } @@ -420,9 +476,7 @@ button.infoButton { background-color: #80808070; display: flex; } -.overlayContainer.hidden{ - display: none; -} + div#overlay { margin: auto; background-color: white; @@ -443,21 +497,56 @@ div#overlayContent.text{ flex-direction: row; justify-content: end; } +#tabs { + display: flex; + border-bottom: 1px solid grey; + margin-bottom: 0.5em; +} +#tabs .tab { + background-color: lightgray; + padding: 0.5em; + border-left: 1px; + border-right: 1px; + border-top: 1px; + border-bottom: 1px; + border-color: grey; + border-style: solid; + opacity: 0.6; +} +#tabs .tab.active{ + opacity: 1; +} +.buttons button{ + padding: 0.5em; +} +button#reset{ + padding: 0.5em; +} +h1{ + margin-bottom: 0; +} + </style> </head> <body> <div class="main"> <h1>NMEA 2000 Gateway </h1> +<div class="row"> + <span class="label">connected</span> + <span class="value" id="connected"></span> +</div> +<div id="tabs"> + <div class="tab active" data-page="statusPage">Status</div> + <div class="tab" data-page="configPage">Config</div> +</div> +<div id="statusPage" class="tabPage"> <div class="row"> <span class="label">VERSION</span> <span class="value" id="version">---</span> <button class="infoButton" id="converterInfo">?</button> </div> -<div class="row"> - <span class="label">connected</span> - <span class="value" id="connected"></span> -</div> -<div class="row"> + +<div class="row even"> <span class="label">Access Point IP</span> <span class="value" id="apIp">---</span> </div> @@ -465,19 +554,19 @@ div#overlayContent.text{ <span class="label"># NMEA2000 messages</span> <span class="value" id="numcan">---</span> </div> -<div class="row"> +<div class="row even"> <span class="label">NMEA2000 details</span> <input type="checkbox" id="showCanDetails"></span> </div> -<div class="row" id="canDetails"> +<div class="row even" id="canDetails"> </div> <div class="row"> <span class="label"># TCP clients</span> <span class="value" id="numClients">---</span> </div> -<div class="row"> +<div class="row even"> <span class="label">wifi client connected</span> <span class="value" id="wifiConnected">---</span> </div> @@ -486,7 +575,8 @@ div#overlayContent.text{ <span class="value" id="clientIP">---</span> </div> <button id="reset" >Reset</button> -<div class="configForm"> +</div> +<div class="configForm tabPage hidden" id="configPage" > <div class="configFormRows"> </div>