Hinzufügen HTML Seiten zur Konfiguration der Ports und Darstellung des jeweiligen Status
This commit is contained in:
parent
2348fc59c7
commit
1bf8c6545f
146
data/admin.html
Normal file
146
data/admin.html
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
|
||||
<!DOCTYPE HTML> <!-- For more information visit: https://fipsok.de -->
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<title>ESP8266 Admin</title>
|
||||
<script>
|
||||
addEventListener('load', () => {
|
||||
renew(), once();
|
||||
let output = document.querySelector('#note');
|
||||
let btn = document.querySelectorAll('button');
|
||||
let span = document.querySelectorAll('#right span');
|
||||
btn[0].addEventListener('click', () => {
|
||||
location = '/fs.html';
|
||||
});
|
||||
btn[1].addEventListener('click', () => {
|
||||
location = '/';
|
||||
});
|
||||
btn[2].addEventListener('click', () => {
|
||||
location = '/portconfig';
|
||||
});
|
||||
btn[3].addEventListener('click', check.bind(this, document.querySelector('input')));
|
||||
btn[4].addEventListener('click', re.bind(this, 'reconnect'));
|
||||
btn[5].addEventListener('click', () => {
|
||||
if (confirm('Bist du sicher!')) re('restart');
|
||||
});
|
||||
async function once(val = '',arg) {
|
||||
try {
|
||||
let resp = await fetch('/admin/once', { method: 'POST', body: val});
|
||||
let obj = await resp.json();
|
||||
output.innerHTML = '';
|
||||
output.classList.remove('note');
|
||||
document.querySelector('form').reset();
|
||||
if (val.length == 0) myIv = setInterval(renew, 1000);
|
||||
if (arg == 'reconnect') re(arg);
|
||||
document.getElementById('file').innerHTML = obj['File'];
|
||||
document.getElementById('build').innerHTML = obj['Build'];
|
||||
document.getElementById('size').innerHTML = obj['SketchSize'];
|
||||
document.getElementById('space').innerHTML = obj['SketchSpace'];
|
||||
document.getElementById('ip').innerHTML = obj['LocalIP'];
|
||||
document.getElementById('hostname').innerHTML = obj['Hostname'];
|
||||
document.getElementById('ssid').innerHTML = obj['SSID'];
|
||||
document.getElementById('gateway').innerHTML = obj['GatewayIP'];
|
||||
document.getElementById('channel').innerHTML = obj['Channel'];
|
||||
document.getElementById('mac').innerHTML = obj['MacAddress'];
|
||||
document.getElementById('subnet').innerHTML = obj['SubnetMask'];
|
||||
document.getElementById('bssid').innerHTML = obj['BSSID'];
|
||||
document.getElementById('clientip').innerHTML = obj['ClientIP'];
|
||||
document.getElementById('dnsip').innerHTML = obj['DnsIP'];
|
||||
document.getElementById('reset').innerHTML = obj['ResetReason'];
|
||||
document.getElementById('cpu').innerHTML = obj['CpuFreqMHz'] + " MHz";
|
||||
document.getElementById('heap').innerHTML = obj['FreeHeap'];
|
||||
document.getElementById('frag').innerHTML = obj['HeapFrag'] + "%";
|
||||
document.getElementById('flashsize').innerHTML = obj['ChipSize'];
|
||||
document.getElementById('flashspeed').innerHTML = obj['ChipSpeed'] + " MHz";
|
||||
document.getElementById('flashmode').innerHTML = obj['ChipMode'];
|
||||
document.getElementById('ide').innerHTML = obj['IdeVersion'].replace(/(\d)(\d)(\d)(\d)/,obj['IdeVersion'][3]!=0 ? '$1.$3.$4' : '$1.$3.');
|
||||
document.getElementById('core').innerHTML = obj['CoreVersion'].replace(/_/g,'.');
|
||||
document.getElementById('sdk').innerHTML = obj['SdkVersion'];
|
||||
} catch(err) {
|
||||
re();
|
||||
}
|
||||
}
|
||||
async function renew() {
|
||||
const resp = await fetch('admin/renew');
|
||||
const array = await resp.json();
|
||||
document.getElementById('runtime').innerHTML = array[0];
|
||||
document.getElementById('rssi').innerHTML = array[1];
|
||||
document.getElementById('adc').innerHTML = array[2];
|
||||
}
|
||||
function check(inObj) {
|
||||
!inObj.checkValidity() ? (output.innerHTML = inObj.validationMessage, output.classList.add('note')) : (once(inObj.value, 'reconnect'));
|
||||
}
|
||||
function re(arg = '') {
|
||||
clearInterval(myIv);
|
||||
fetch(arg);
|
||||
output.classList.add('note');
|
||||
if (arg == 'restart') {
|
||||
output.innerHTML = 'Der Server wird neu gestartet. Die Daten werden in 15 Sekunden neu geladen.';
|
||||
setTimeout(once, 15000);
|
||||
}
|
||||
else if (arg == 'reconnect'){
|
||||
output.innerHTML = 'Die WiFi Verbindung wird neu gestartet. Daten werden in 10 Sekunden neu geladen.';
|
||||
setTimeout(once, 10000);
|
||||
}
|
||||
else {
|
||||
output.innerHTML = 'Es ist ein Verbindungfehler aufgetreten. Es wird versucht neu zu verbinden.';
|
||||
setTimeout(once, 3000);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>ESP8266 Admin Page</h1>
|
||||
<main>
|
||||
<table>
|
||||
<tr><td>Runtime ESP:</td><td><span id="runtime">0</span></td></tr>
|
||||
<tr><td>WiFi RSSI:</td><td><div><span id="rssi"></span> dBm</div></td></tr>
|
||||
<tr><td>ADC/VCC:</td><td><span id="adc">0</span></td></tr>
|
||||
<tr><td>Sketch Name:</td><td><span id="file">?</span></td></tr>
|
||||
<tr><td>Sketch Build:</td><td><span id="build">0</span></td></tr>
|
||||
<tr><td>SketchSize:</td><td><span id="size">0</span></td></tr>
|
||||
<tr><td>FreeSketchSpace:</td><td><span id="space">0</span></td></tr>
|
||||
<tr><td>IPv4 Address:</td><td><span id="ip">0</span></td></tr>
|
||||
<tr><td>Hostname:</td><td><span id="hostname">?</span></td></tr>
|
||||
<tr><td>Connected to:</td><td><span id="ssid">?</span></td></tr>
|
||||
<tr><td>Gateway IP:</td><td><span id="gateway">0</span></td></tr>
|
||||
<tr><td>Channel:</td><td><span id="channel">0</span></td></tr>
|
||||
<tr><td>MacAddress:</td><td><span id="mac">0</span></td></tr>
|
||||
<tr><td>SubnetMask:</td><td><span id="subnet">0</span></td></tr>
|
||||
<tr><td>BSSID:</td><td><span id="bssid">0</span></td></tr>
|
||||
<tr><td>Client IP:</td><td><span id="clientip">0</span></td></tr>
|
||||
<tr><td>DnsIP:</td><td><span id="dnsip">0</span></td></tr>
|
||||
<tr><td>Reset Ground:</td><td><span id="reset">?</span></td></tr>
|
||||
<tr><td>CPU Freq:</td><td><span id="cpu">0</span> MHz</td></tr>
|
||||
<tr><td>FreeHeap:</td><td><span id="heap">0</span></td></tr>
|
||||
<tr><td>Heap Fragmentation:</td><td><span id="frag">0</span>%</td></tr>
|
||||
<tr><td>FlashSize:</td><td><span id="flashsize">0</span></td></tr>
|
||||
<tr><td>FlashSpeed:</td><td><span id="flashspeed">0</span> MHz</td></tr>
|
||||
<tr><td>FlashMode:</td><td><span id="flashmode">0</span></td></tr>
|
||||
<tr><td>Arduino IDE Version:</td><td><span id="ide">0</span></td></tr>
|
||||
<tr><td>Esp Core Version:</td><td><span id="core">0</span></td></tr>
|
||||
<tr><td>SDK Version:</td><td><span id="sdk">0</span></td></tr>
|
||||
</table>
|
||||
</main>
|
||||
<div>
|
||||
<button>Filesystem</button>
|
||||
<button>Startseite</button>
|
||||
<button>Port Konfiguration</button>
|
||||
</div>
|
||||
<div id="note"></div>
|
||||
<div>
|
||||
<form>
|
||||
<input placeholder="neuer Hostname" pattern="([A-Za-z0-9\-]{1,32})" title="Es dürfen nur Buchstaben (a-z, A-Z), Ziffern (0-9) und Bindestriche (-) enthalten sein. Maximal 32 Zeichen" required>
|
||||
<button type="button">Name Senden</button>
|
||||
</form>
|
||||
</div>
|
||||
<div>
|
||||
<button>WiFi Reconnect</button>
|
||||
<button>ESP Restart</button>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
77
data/fs.html
Normal file
77
data/fs.html
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
|
||||
<!DOCTYPE HTML> <!-- For more information visit: https://fipsok.de -->
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<title>Filesystem Manager</title>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
list(JSON.parse(localStorage.getItem('sortBy')));
|
||||
btn.addEventListener('click', () => {
|
||||
if (!confirm(`Alle Daten gehen verloren.\nDu musst anschließend fs.html wieder laden.`)) event.preventDefault();
|
||||
});
|
||||
});
|
||||
async function list(to){
|
||||
let resp = await fetch(`?sort=${to}`);
|
||||
let json = await resp.json();
|
||||
let myList = document.querySelector('main'), noted = '';
|
||||
myList.innerHTML = '<nav><input type="radio" id="/" name="group" checked="checked"><label for="/"> 📁</label><span id="cr">+📁</nav></span><span id="si"></span>';
|
||||
for (var i = 0; i < json.length - 1; i++) {
|
||||
let dir = '', f = json[i].folder, n = json[i].name;
|
||||
if (f != noted) {
|
||||
noted = f;
|
||||
dir = `<nav><input type="radio" id="${f}" name="group"><label for="${f}"></label> 📁 ${f} <a href="?delete=/${f}">🗑️</a></nav>`;
|
||||
}
|
||||
if (n != '') dir += `<li><a href="${f}/${n}">${n}</a><small> ${json[i].size}</small><a href="${f}/${n}"download="${n}"> Download</a> or<a href="?delete=${f}/${n}"> Delete</a>`;
|
||||
myList.insertAdjacentHTML('beforeend', dir);
|
||||
}
|
||||
myList.insertAdjacentHTML('beforeend', `<li><b id="so">${to ? '▼' : '▲'} LittleFS</b> belegt ${json[i].usedBytes.replace(".00", "")} von ${json[i].totalBytes.replace(".00", "")}`);
|
||||
var free = json[i].freeBytes;
|
||||
cr.addEventListener('click', () => {
|
||||
document.getElementById('no').classList.toggle('no');
|
||||
});
|
||||
so.addEventListener('click', () => {
|
||||
list(to=++to%2);
|
||||
localStorage.setItem('sortBy', JSON.stringify(to));
|
||||
});
|
||||
document.addEventListener('change', (e) => {
|
||||
if (e.target.id == 'fs') {
|
||||
for (var bytes = 0, i = 0; i < event.target.files.length; i++) bytes += event.target.files[i].size;
|
||||
for (var output = `${bytes} Byte`, i = 0, circa = bytes / 1024; circa > 1; circa /= 1024) output = circa.toFixed(2) + [' KB', ' MB', ' GB'][i++];
|
||||
if (bytes > free) {
|
||||
si.innerHTML = `<li><b> ${output}</b><strong> Ungenügend Speicher frei</strong></li>`;
|
||||
up.setAttribute('disabled', 'disabled');
|
||||
}
|
||||
else {
|
||||
si.innerHTML = `<li><b>Dateigröße:</b> ${output}</li>`;
|
||||
up.removeAttribute('disabled');
|
||||
}
|
||||
}
|
||||
document.querySelectorAll(`input[type=radio]`).forEach(el => { if (el.checked) document.querySelector('form').setAttribute('action', '/upload?f=' + el.id)});
|
||||
});
|
||||
document.querySelectorAll('[href^="?delete=/"]').forEach(node => {
|
||||
node.addEventListener('click', () => {
|
||||
if (!confirm('Sicher!')) event.preventDefault();
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h2>ESP8266 Filesystem Manager</h2>
|
||||
<form method="post" enctype="multipart/form-data" action="/upload?f=/">
|
||||
<input id="fs" type="file" name="up[]" multiple>
|
||||
<button id="up" disabled>Upload</button>
|
||||
</form>
|
||||
<form id="no" class="no" method="POST">
|
||||
<input name="new" placeholder="Ordner Name" pattern="[^\x22\/%&\\:;]{0,31}[^\x22\/%&\\:;\s]{1}" title="Zeichen “ % & / : ; \ sind nicht erlaubt." required="">
|
||||
<button>Create</button>
|
||||
</form>
|
||||
<main></main>
|
||||
<form action="/format" method="POST">
|
||||
<button id="btn">Format LittleFS</button>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
79
data/index.html
Normal file
79
data/index.html
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<title>Port Status</title>
|
||||
<style>
|
||||
.port {
|
||||
margin: 10px;
|
||||
padding: 10px;
|
||||
border: 1px solid #ccc;
|
||||
display: inline-block;
|
||||
width: 200px;
|
||||
}
|
||||
.disabled {
|
||||
background-color: lightgray;
|
||||
color: gray;
|
||||
}
|
||||
.ok {
|
||||
background-color: lightgreen;
|
||||
}
|
||||
.missing {
|
||||
background-color: red;
|
||||
animation: blink 1s infinite;
|
||||
}
|
||||
@keyframes blink {
|
||||
0%, 50% { background-color: red; }
|
||||
51%, 100% { background-color: white; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Port Status Übersicht</h1>
|
||||
<div id="portsContainer"></div>
|
||||
<br>
|
||||
<button onclick="location.href='/portconfig.html'">Zur Konfiguration</button>
|
||||
<button onclick="location.href='/admin.html'">Zur Admin Seite</button>
|
||||
|
||||
|
||||
<script>
|
||||
async function loadStatus() {
|
||||
try {
|
||||
const response = await fetch('/status/data');
|
||||
const data = await response.json();
|
||||
const container = document.getElementById('portsContainer');
|
||||
container.innerHTML = '';
|
||||
data.ports.forEach((port, index) => {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'port';
|
||||
let statusClass = '';
|
||||
let statusText = '';
|
||||
if (!port.enabled) {
|
||||
statusClass = 'disabled';
|
||||
statusText = 'disabled';
|
||||
} else if (port.state === 0) {
|
||||
statusClass = 'ok';
|
||||
statusText = 'OK';
|
||||
} else {
|
||||
statusClass = 'missing';
|
||||
statusText = 'Fehlt';
|
||||
}
|
||||
div.classList.add(statusClass);
|
||||
div.innerHTML = `
|
||||
<strong>Port ${index}: ${port.name}</strong><br>
|
||||
Status: ${statusText}
|
||||
`;
|
||||
container.appendChild(div);
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Laden des Status:', error);
|
||||
}
|
||||
}
|
||||
|
||||
loadStatus();
|
||||
setInterval(loadStatus, 1000); // Aktualisiere jede Sekunde
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
65
data/portconfig.html
Normal file
65
data/portconfig.html
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<title>Port Konfiguration</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Port Konfiguration</h1>
|
||||
<form id="portForm">
|
||||
<div id="portsContainer"></div>
|
||||
<input type="submit" value="Speichern">
|
||||
</form>
|
||||
<a href="/admin.html">Zurück</a>
|
||||
|
||||
<script>
|
||||
async function loadConfig() {
|
||||
try {
|
||||
const response = await fetch('/portconfig/data');
|
||||
const data = await response.json();
|
||||
const container = document.getElementById('portsContainer');
|
||||
container.innerHTML = '';
|
||||
data.ports.forEach((port, index) => {
|
||||
const div = document.createElement('div');
|
||||
div.innerHTML = `
|
||||
<label>
|
||||
Name: <input type="text" name="name${index}" value="${port.name}">
|
||||
Aktiviert: <input type="checkbox" name="enabled${index}" ${port.enabled ? 'checked' : ''}>
|
||||
</label>
|
||||
`;
|
||||
container.appendChild(div);
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Laden der Konfiguration:', error);
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById('portForm').addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
const formData = new FormData(e.target);
|
||||
const ports = [];
|
||||
for (let i = 0; i < 16; i++) {
|
||||
ports.push({
|
||||
name: formData.get(`name${i}`) || `Port ${i}`,
|
||||
enabled: formData.has(`enabled${i}`)
|
||||
});
|
||||
}
|
||||
try {
|
||||
await fetch('/portconfig', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ ports })
|
||||
});
|
||||
alert('Konfiguration gespeichert!');
|
||||
loadConfig(); // Reload
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Speichern:', error);
|
||||
}
|
||||
});
|
||||
|
||||
loadConfig();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -5,4 +5,8 @@ framework = arduino
|
|||
monitor_speed = 115200
|
||||
lib_deps =
|
||||
adafruit/Adafruit NeoPixel @ ^1.11.0
|
||||
https://github.com/RobTillaart/PCF8575
|
||||
https://github.com/RobTillaart/PCF8575
|
||||
bblanchon/ArduinoJson @ ^6.21.0
|
||||
|
||||
board_build.filesystem = littlefs
|
||||
board_upload.maximum_size = 1048576
|
||||
134
src/KeyPatch.ino
134
src/KeyPatch.ino
|
|
@ -6,6 +6,7 @@
|
|||
#include <LittleFS.h>
|
||||
#include "Config.ino"
|
||||
#include <DNSServer.h>
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
// Globale Deklarationen für alle Tabs
|
||||
ESP8266WebServer server(80);
|
||||
|
|
@ -21,13 +22,62 @@ String sketchName() { // Dateiname für den Admin Tab ab EspCoreVersio
|
|||
#define AMOUNTOFPORTS 16 // currently max 16 ports are supported
|
||||
#define EXPANDER1ADDRESS 0x21 // address of expander 1
|
||||
|
||||
struct PortConfig {
|
||||
String name;
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
PCF8575 expander1(EXPANDER1ADDRESS);
|
||||
uint8_t portStates[AMOUNTOFPORTS];
|
||||
uint8_t lastStates[AMOUNTOFPORTS] = {0};
|
||||
PortConfig portConfigs[AMOUNTOFPORTS];
|
||||
byte number=0;
|
||||
|
||||
extern Adafruit_NeoPixel* pixels;
|
||||
|
||||
void loadPortConfig() {
|
||||
File file = LittleFS.open("/portconfig.json", "r");
|
||||
if (file) {
|
||||
DynamicJsonDocument doc(1024);
|
||||
DeserializationError error = deserializeJson(doc, file);
|
||||
file.close();
|
||||
if (!error) {
|
||||
JsonArray ports = doc["ports"];
|
||||
for (size_t i = 0; i < AMOUNTOFPORTS && i < ports.size(); i++) {
|
||||
portConfigs[i].name = ports[i]["name"] | ("Port " + String(i));
|
||||
portConfigs[i].enabled = ports[i]["enabled"] | true;
|
||||
}
|
||||
} else {
|
||||
// Default
|
||||
for (int i = 0; i < AMOUNTOFPORTS; i++) {
|
||||
portConfigs[i].name = "Port " + String(i);
|
||||
portConfigs[i].enabled = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Default all enabled
|
||||
for (int i = 0; i < AMOUNTOFPORTS; i++) {
|
||||
portConfigs[i].name = "Port " + String(i);
|
||||
portConfigs[i].enabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void savePortConfig() {
|
||||
DynamicJsonDocument doc(1024);
|
||||
JsonArray ports = doc.createNestedArray("ports");
|
||||
for (int i = 0; i < AMOUNTOFPORTS; i++) {
|
||||
JsonObject port = ports.createNestedObject();
|
||||
port["name"] = portConfigs[i].name;
|
||||
port["enabled"] = portConfigs[i].enabled;
|
||||
}
|
||||
File file = LittleFS.open("/portconfig.json", "w");
|
||||
if (file) {
|
||||
serializeJson(doc, file);
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(SERIAL_SPEED);
|
||||
delay(100);
|
||||
|
|
@ -78,6 +128,7 @@ void setup() {
|
|||
|
||||
// SetUp WebServer
|
||||
setupFS(); // setupFS(); oder spiffs(); je nach Dateisystem
|
||||
loadPortConfig(); // Lade Port-Konfiguration
|
||||
connectWifi();
|
||||
admin();
|
||||
ArduinoOTA.onStart([]() {
|
||||
|
|
@ -86,7 +137,70 @@ void setup() {
|
|||
ArduinoOTA.begin();
|
||||
server.begin();
|
||||
|
||||
|
||||
// Handler für Port-Konfiguration
|
||||
server.on("/portconfig", HTTP_GET, []() {
|
||||
File file = LittleFS.open("/html/portconfig.html", "r");
|
||||
if (file) {
|
||||
server.streamFile(file, "text/html");
|
||||
file.close();
|
||||
} else {
|
||||
server.send(404, "text/plain", "File not found");
|
||||
}
|
||||
});
|
||||
|
||||
server.on("/portconfig/data", HTTP_GET, []() {
|
||||
DynamicJsonDocument doc(1024);
|
||||
JsonArray ports = doc.createNestedArray("ports");
|
||||
for (int i = 0; i < AMOUNTOFPORTS; i++) {
|
||||
JsonObject port = ports.createNestedObject();
|
||||
port["name"] = portConfigs[i].name;
|
||||
port["enabled"] = portConfigs[i].enabled;
|
||||
}
|
||||
String json;
|
||||
serializeJson(doc, json);
|
||||
server.send(200, "application/json", json);
|
||||
});
|
||||
|
||||
server.on("/portconfig", HTTP_POST, []() {
|
||||
DynamicJsonDocument doc(1024);
|
||||
DeserializationError error = deserializeJson(doc, server.arg("plain"));
|
||||
if (!error) {
|
||||
JsonArray ports = doc["ports"];
|
||||
for (size_t i = 0; i < AMOUNTOFPORTS && i < ports.size(); i++) {
|
||||
portConfigs[i].name = ports[i]["name"] | ("Port " + String(i));
|
||||
portConfigs[i].enabled = ports[i]["enabled"] | true;
|
||||
}
|
||||
savePortConfig();
|
||||
}
|
||||
server.sendHeader("Location", "/portconfig");
|
||||
server.send(303);
|
||||
});
|
||||
|
||||
// Handler für Startseite
|
||||
server.on("/", HTTP_GET, []() {
|
||||
File file = LittleFS.open("/index.html", "r");
|
||||
if (file) {
|
||||
server.streamFile(file, "text/html");
|
||||
file.close();
|
||||
} else {
|
||||
server.send(404, "text/plain", "File not found");
|
||||
}
|
||||
});
|
||||
|
||||
// Handler für Status-Daten
|
||||
server.on("/status/data", HTTP_GET, []() {
|
||||
DynamicJsonDocument doc(1024);
|
||||
JsonArray ports = doc.createNestedArray("ports");
|
||||
for (int i = 0; i < AMOUNTOFPORTS; i++) {
|
||||
JsonObject port = ports.createNestedObject();
|
||||
port["name"] = portConfigs[i].name;
|
||||
port["enabled"] = portConfigs[i].enabled;
|
||||
port["state"] = portStates[i];
|
||||
}
|
||||
String json;
|
||||
serializeJson(doc, json);
|
||||
server.send(200, "application/json", json);
|
||||
});
|
||||
|
||||
Serial.println("Setup End...");
|
||||
}
|
||||
|
|
@ -114,15 +228,19 @@ void loop() {
|
|||
lastStates[i] = portStates[i];
|
||||
}
|
||||
// LED aktualisieren
|
||||
if (portStates[i] == 0) {
|
||||
pixels->setPixelColor(i, pixels->Color(0, 150, 0)); // grün
|
||||
} else {
|
||||
bool globalBlinkState = (millis() / BLINK_INTERVAL) % 2;
|
||||
if (globalBlinkState) {
|
||||
pixels->setPixelColor(i, pixels->Color(150, 0, 0)); // rot
|
||||
if (portConfigs[i].enabled) {
|
||||
if (portStates[i] == 0) {
|
||||
pixels->setPixelColor(i, pixels->Color(0, 150, 0)); // grün
|
||||
} else {
|
||||
pixels->setPixelColor(i, pixels->Color(0, 0, 0)); // aus
|
||||
bool globalBlinkState = (millis() / BLINK_INTERVAL) % 2;
|
||||
if (globalBlinkState) {
|
||||
pixels->setPixelColor(i, pixels->Color(150, 0, 0)); // rot
|
||||
} else {
|
||||
pixels->setPixelColor(i, pixels->Color(0, 0, 0)); // aus
|
||||
}
|
||||
}
|
||||
} else {
|
||||
pixels->setPixelColor(i, pixels->Color(0, 0, 0)); // aus
|
||||
}
|
||||
}
|
||||
pixels->show();
|
||||
|
|
|
|||
|
|
@ -18,9 +18,12 @@
|
|||
btn[1].addEventListener('click', () => {
|
||||
location = '/';
|
||||
});
|
||||
btn[2].addEventListener('click', check.bind(this, document.querySelector('input')));
|
||||
btn[3].addEventListener('click', re.bind(this, 'reconnect'));
|
||||
btn[4].addEventListener('click', () => {
|
||||
btn[2].addEventListener('click', () => {
|
||||
location = '/portconfig.html';
|
||||
});
|
||||
btn[3].addEventListener('click', check.bind(this, document.querySelector('input')));
|
||||
btn[4].addEventListener('click', re.bind(this, 'reconnect'));
|
||||
btn[5].addEventListener('click', () => {
|
||||
if (confirm('Bist du sicher!')) re('restart');
|
||||
});
|
||||
async function once(val = '',arg) {
|
||||
|
|
@ -32,30 +35,30 @@
|
|||
document.querySelector('form').reset();
|
||||
if (val.length == 0) myIv = setInterval(renew, 1000);
|
||||
if (arg == 'reconnect') re(arg);
|
||||
span[3].innerHTML = obj['File'];
|
||||
span[4].innerHTML = obj['Build'];
|
||||
span[5].innerHTML = obj['SketchSize'];
|
||||
span[6].innerHTML = obj['SketchSpace'];
|
||||
span[7].innerHTML = obj['LocalIP'];
|
||||
span[8].innerHTML = obj['Hostname'];
|
||||
span[9].innerHTML = obj['SSID'];
|
||||
span[10].innerHTML = obj['GatewayIP'];
|
||||
span[11].innerHTML = obj['Channel'];
|
||||
span[12].innerHTML = obj['MacAddress'];
|
||||
span[13].innerHTML = obj['SubnetMask'];
|
||||
span[14].innerHTML = obj['BSSID'];
|
||||
span[15].innerHTML = obj['ClientIP'];
|
||||
span[16].innerHTML = obj['DnsIP'];
|
||||
span[17].innerHTML = obj['ResetReason'];
|
||||
span[18].innerHTML = obj['CpuFreqMHz'] + " MHz";
|
||||
span[19].innerHTML = obj['FreeHeap'];
|
||||
span[20].innerHTML = obj['HeapFrag'] + "%";
|
||||
span[21].innerHTML = obj['ChipSize'];
|
||||
span[22].innerHTML = obj['ChipSpeed'] + " MHz";
|
||||
span[23].innerHTML = obj['ChipMode'];
|
||||
span[24].innerHTML = obj['IdeVersion'].replace(/(\d)(\d)(\d)(\d)/,obj['IdeVersion'][3]!=0 ? '$1.$3.$4' : '$1.$3.');
|
||||
span[25].innerHTML = obj['CoreVersion'].replace(/_/g,'.');
|
||||
span[26].innerHTML = obj['SdkVersion'];
|
||||
document.getElementById('file').innerHTML = obj['File'];
|
||||
document.getElementById('build').innerHTML = obj['Build'];
|
||||
document.getElementById('size').innerHTML = obj['SketchSize'];
|
||||
document.getElementById('space').innerHTML = obj['SketchSpace'];
|
||||
document.getElementById('ip').innerHTML = obj['LocalIP'];
|
||||
document.getElementById('hostname').innerHTML = obj['Hostname'];
|
||||
document.getElementById('ssid').innerHTML = obj['SSID'];
|
||||
document.getElementById('gateway').innerHTML = obj['GatewayIP'];
|
||||
document.getElementById('channel').innerHTML = obj['Channel'];
|
||||
document.getElementById('mac').innerHTML = obj['MacAddress'];
|
||||
document.getElementById('subnet').innerHTML = obj['SubnetMask'];
|
||||
document.getElementById('bssid').innerHTML = obj['BSSID'];
|
||||
document.getElementById('clientip').innerHTML = obj['ClientIP'];
|
||||
document.getElementById('dnsip').innerHTML = obj['DnsIP'];
|
||||
document.getElementById('reset').innerHTML = obj['ResetReason'];
|
||||
document.getElementById('cpu').innerHTML = obj['CpuFreqMHz'] + " MHz";
|
||||
document.getElementById('heap').innerHTML = obj['FreeHeap'];
|
||||
document.getElementById('frag').innerHTML = obj['HeapFrag'] + "%";
|
||||
document.getElementById('flashsize').innerHTML = obj['ChipSize'];
|
||||
document.getElementById('flashspeed').innerHTML = obj['ChipSpeed'] + " MHz";
|
||||
document.getElementById('flashmode').innerHTML = obj['ChipMode'];
|
||||
document.getElementById('ide').innerHTML = obj['IdeVersion'].replace(/(\d)(\d)(\d)(\d)/,obj['IdeVersion'][3]!=0 ? '$1.$3.$4' : '$1.$3.');
|
||||
document.getElementById('core').innerHTML = obj['CoreVersion'].replace(/_/g,'.');
|
||||
document.getElementById('sdk').innerHTML = obj['SdkVersion'];
|
||||
} catch(err) {
|
||||
re();
|
||||
}
|
||||
|
|
@ -63,7 +66,9 @@
|
|||
async function renew() {
|
||||
const resp = await fetch('admin/renew');
|
||||
const array = await resp.json();
|
||||
array.forEach((v, i) => {span[i].innerHTML = v});
|
||||
document.getElementById('runtime').innerHTML = array[0];
|
||||
document.getElementById('rssi').innerHTML = array[1];
|
||||
document.getElementById('adc').innerHTML = array[2];
|
||||
}
|
||||
function check(inObj) {
|
||||
!inObj.checkValidity() ? (output.innerHTML = inObj.validationMessage, output.classList.add('note')) : (once(inObj.value, 'reconnect'));
|
||||
|
|
@ -91,71 +96,40 @@
|
|||
<body>
|
||||
<h1>ESP8266 Admin Page</h1>
|
||||
<main>
|
||||
<aside id="left">
|
||||
<span>Runtime ESP:</span>
|
||||
<span>WiFi RSSI:</span>
|
||||
<span>ADC/VCC:</span>
|
||||
<span>Sketch Name:</span>
|
||||
<span>Sketch Build:</span>
|
||||
<span>SketchSize:</span>
|
||||
<span>FreeSketchSpace:</span>
|
||||
<span>IPv4 Address:</span>
|
||||
<span>Hostname:</span>
|
||||
<span>Connected to:</span>
|
||||
<span>Gateway IP:</span>
|
||||
<span>Channel:</span>
|
||||
<span>MacAddress:</span>
|
||||
<span>SubnetMask:</span>
|
||||
<span>BSSID:</span>
|
||||
<span>Client IP:</span>
|
||||
<span>DnsIP:</span>
|
||||
<span>Reset Ground:</span>
|
||||
<span>CPU Freq:</span>
|
||||
<span>FreeHeap:</span>
|
||||
<span>Heap Fragmentation:</span>
|
||||
<span>FlashSize:</span>
|
||||
<span>FlashSpeed:</span>
|
||||
<span>FlashMode:</span>
|
||||
<span>Arduino IDE Version:</span>
|
||||
<span>Esp Core Version:</span>
|
||||
<span>SDK Version:</span>
|
||||
</aside>
|
||||
<aside id="right">
|
||||
<span>0</span>
|
||||
<div>
|
||||
<span></span>
|
||||
dBm
|
||||
</div>
|
||||
<span>0</span>
|
||||
<span>?</span>
|
||||
<span>0</span>
|
||||
<span>0</span>
|
||||
<span>0</span>
|
||||
<span>0</span>
|
||||
<span>?</span>
|
||||
<span>?</span>
|
||||
<span>0</span>
|
||||
<span>0</span>
|
||||
<span>0</span>
|
||||
<span>0</span>
|
||||
<span>0</span>
|
||||
<span>?</span>
|
||||
<span>0</span>
|
||||
<span>0</span>
|
||||
<span>0</span>
|
||||
<span>0</span>
|
||||
<span>0</span>
|
||||
<span>0</span>
|
||||
<span>0</span>
|
||||
<span>0</span>
|
||||
<span>0</span>
|
||||
<span>0</span>
|
||||
<span>0</span>
|
||||
</aside>
|
||||
<table>
|
||||
<tr><td>Runtime ESP:</td><td><span id="runtime">0</span></td></tr>
|
||||
<tr><td>WiFi RSSI:</td><td><div><span id="rssi"></span> dBm</div></td></tr>
|
||||
<tr><td>ADC/VCC:</td><td><span id="adc">0</span></td></tr>
|
||||
<tr><td>Sketch Name:</td><td><span id="file">?</span></td></tr>
|
||||
<tr><td>Sketch Build:</td><td><span id="build">0</span></td></tr>
|
||||
<tr><td>SketchSize:</td><td><span id="size">0</span></td></tr>
|
||||
<tr><td>FreeSketchSpace:</td><td><span id="space">0</span></td></tr>
|
||||
<tr><td>IPv4 Address:</td><td><span id="ip">0</span></td></tr>
|
||||
<tr><td>Hostname:</td><td><span id="hostname">?</span></td></tr>
|
||||
<tr><td>Connected to:</td><td><span id="ssid">?</span></td></tr>
|
||||
<tr><td>Gateway IP:</td><td><span id="gateway">0</span></td></tr>
|
||||
<tr><td>Channel:</td><td><span id="channel">0</span></td></tr>
|
||||
<tr><td>MacAddress:</td><td><span id="mac">0</span></td></tr>
|
||||
<tr><td>SubnetMask:</td><td><span id="subnet">0</span></td></tr>
|
||||
<tr><td>BSSID:</td><td><span id="bssid">0</span></td></tr>
|
||||
<tr><td>Client IP:</td><td><span id="clientip">0</span></td></tr>
|
||||
<tr><td>DnsIP:</td><td><span id="dnsip">0</span></td></tr>
|
||||
<tr><td>Reset Ground:</td><td><span id="reset">?</span></td></tr>
|
||||
<tr><td>CPU Freq:</td><td><span id="cpu">0</span> MHz</td></tr>
|
||||
<tr><td>FreeHeap:</td><td><span id="heap">0</span></td></tr>
|
||||
<tr><td>Heap Fragmentation:</td><td><span id="frag">0</span>%</td></tr>
|
||||
<tr><td>FlashSize:</td><td><span id="flashsize">0</span></td></tr>
|
||||
<tr><td>FlashSpeed:</td><td><span id="flashspeed">0</span> MHz</td></tr>
|
||||
<tr><td>FlashMode:</td><td><span id="flashmode">0</span></td></tr>
|
||||
<tr><td>Arduino IDE Version:</td><td><span id="ide">0</span></td></tr>
|
||||
<tr><td>Esp Core Version:</td><td><span id="core">0</span></td></tr>
|
||||
<tr><td>SDK Version:</td><td><span id="sdk">0</span></td></tr>
|
||||
</table>
|
||||
</main>
|
||||
<div>
|
||||
<button>Filesystem</button>
|
||||
<button>Startseite</button>
|
||||
<button>Port Konfiguration</button>
|
||||
</div>
|
||||
<div id="note"></div>
|
||||
<div>
|
||||
|
|
|
|||
79
src/html/index.html
Normal file
79
src/html/index.html
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<title>Port Status</title>
|
||||
<style>
|
||||
.port {
|
||||
margin: 10px;
|
||||
padding: 10px;
|
||||
border: 1px solid #ccc;
|
||||
display: inline-block;
|
||||
width: 200px;
|
||||
}
|
||||
.disabled {
|
||||
background-color: lightgray;
|
||||
color: gray;
|
||||
}
|
||||
.ok {
|
||||
background-color: lightgreen;
|
||||
}
|
||||
.missing {
|
||||
background-color: red;
|
||||
animation: blink 1s infinite;
|
||||
}
|
||||
@keyframes blink {
|
||||
0%, 50% { background-color: red; }
|
||||
51%, 100% { background-color: white; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Port Status Übersicht</h1>
|
||||
<div id="portsContainer"></div>
|
||||
<br>
|
||||
<button onclick="location.href='/portconfig.html'">Zur Konfiguration</button>
|
||||
<button onclick="location.href='/admin.html'">Zur Admin Seite</button>
|
||||
|
||||
|
||||
<script>
|
||||
async function loadStatus() {
|
||||
try {
|
||||
const response = await fetch('/status/data');
|
||||
const data = await response.json();
|
||||
const container = document.getElementById('portsContainer');
|
||||
container.innerHTML = '';
|
||||
data.ports.forEach((port, index) => {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'port';
|
||||
let statusClass = '';
|
||||
let statusText = '';
|
||||
if (!port.enabled) {
|
||||
statusClass = 'disabled';
|
||||
statusText = 'disabled';
|
||||
} else if (port.state === 0) {
|
||||
statusClass = 'ok';
|
||||
statusText = 'OK';
|
||||
} else {
|
||||
statusClass = 'missing';
|
||||
statusText = 'Fehlt';
|
||||
}
|
||||
div.classList.add(statusClass);
|
||||
div.innerHTML = `
|
||||
<strong>Port ${index}: ${port.name}</strong><br>
|
||||
Status: ${statusText}
|
||||
`;
|
||||
container.appendChild(div);
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Laden des Status:', error);
|
||||
}
|
||||
}
|
||||
|
||||
loadStatus();
|
||||
setInterval(loadStatus, 1000); // Aktualisiere jede Sekunde
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
66
src/html/portconfig.html
Normal file
66
src/html/portconfig.html
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<title>Port Konfiguration</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Port Konfiguration</h1>
|
||||
<form id="portForm">
|
||||
<div id="portsContainer"></div>
|
||||
<input type="submit" value="Speichern">
|
||||
</form>
|
||||
<button onclick="location.href='/'">Zurück</button>
|
||||
|
||||
|
||||
<script>
|
||||
async function loadConfig() {
|
||||
try {
|
||||
const response = await fetch('/portconfig/data');
|
||||
const data = await response.json();
|
||||
const container = document.getElementById('portsContainer');
|
||||
container.innerHTML = '';
|
||||
data.ports.forEach((port, index) => {
|
||||
const div = document.createElement('div');
|
||||
div.innerHTML = `
|
||||
<label>
|
||||
Name: <input type="text" name="name${index}" value="${port.name}">
|
||||
Aktiviert: <input type="checkbox" name="enabled${index}" ${port.enabled ? 'checked' : ''}>
|
||||
</label>
|
||||
`;
|
||||
container.appendChild(div);
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Laden der Konfiguration:', error);
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById('portForm').addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
const formData = new FormData(e.target);
|
||||
const ports = [];
|
||||
for (let i = 0; i < 16; i++) {
|
||||
ports.push({
|
||||
name: formData.get(`name${i}`) || `Port ${i}`,
|
||||
enabled: formData.has(`enabled${i}`)
|
||||
});
|
||||
}
|
||||
try {
|
||||
await fetch('/portconfig', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ ports })
|
||||
});
|
||||
alert('Konfiguration gespeichert!');
|
||||
loadConfig(); // Reload
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Speichern:', error);
|
||||
}
|
||||
});
|
||||
|
||||
loadConfig();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
120
src/html/style.css
Normal file
120
src/html/style.css
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
|
||||
/* For more information visit:https://fipsok.de */
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
background-color: #87cefa;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
align-items: center;
|
||||
}
|
||||
h1,h2 {
|
||||
color: #e1e1e1;
|
||||
text-shadow: 2px 2px 2px black;
|
||||
}
|
||||
li {
|
||||
background-color: #feb1e2;
|
||||
list-style-type: none;
|
||||
margin-bottom: 10px;
|
||||
padding: 2px 5px 1px 0;
|
||||
box-shadow: 5px 5px 5px rgba(0,0,0,0.7);
|
||||
}
|
||||
li a:first-child, li b {
|
||||
background-color: #8f05a5;
|
||||
font-weight: bold;
|
||||
color: white;
|
||||
text-decoration:none;
|
||||
padding: 2px 5px;
|
||||
text-shadow: 2px 2px 1px black;
|
||||
cursor:pointer;
|
||||
}
|
||||
li strong {
|
||||
color: red;
|
||||
}
|
||||
input {
|
||||
height:35px;
|
||||
font-size:14px;
|
||||
padding-left: .3em;
|
||||
}
|
||||
label + a {
|
||||
text-decoration: none;
|
||||
}
|
||||
h1 + main {
|
||||
display: flex;
|
||||
}
|
||||
aside {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0.2em;
|
||||
}
|
||||
button {
|
||||
height:40px;
|
||||
width:130px;
|
||||
font-size:16px;
|
||||
margin-top: 1em;
|
||||
box-shadow: 5px 5px 5px rgba(0,0,0,0.7);
|
||||
}
|
||||
div button {
|
||||
background-color: #7bff97;
|
||||
}
|
||||
nav {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: space-between;
|
||||
}
|
||||
#left {
|
||||
align-items:flex-end;
|
||||
text-shadow: 0.5px 0.5px 1px #757474;
|
||||
}
|
||||
#cr {
|
||||
font-weight: bold;
|
||||
cursor:pointer;
|
||||
font-size: 1.5em;
|
||||
}
|
||||
#up {
|
||||
width: auto;
|
||||
}
|
||||
.note {
|
||||
background-color: #fecdee;
|
||||
padding: 0.5em;
|
||||
margin-top: 1em;
|
||||
text-align: center;
|
||||
max-width: 320px;
|
||||
border-radius: 0.5em;
|
||||
}
|
||||
.no {
|
||||
display: none;
|
||||
}
|
||||
form [title] {
|
||||
background-color: skyblue;
|
||||
font-size: 1em;
|
||||
width: 120px;
|
||||
}
|
||||
form:nth-of-type(2) {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
[value*=Format] {
|
||||
margin-top: 1em;
|
||||
box-shadow: 5px 5px 5px rgba(0,0,0,0.7);
|
||||
}
|
||||
[name="group"] {
|
||||
display: none;
|
||||
}
|
||||
[name="group"] + label {
|
||||
font-size: 1.5em;
|
||||
margin-right: 5px;
|
||||
}
|
||||
[name="group"] + label::before {
|
||||
content: "\002610";
|
||||
}
|
||||
[name="group"]:checked + label::before {
|
||||
content: '\002611\0027A5';
|
||||
}
|
||||
@media only screen and (max-width: 500px) {
|
||||
.ip {
|
||||
right: 6em;
|
||||
position: relative;
|
||||
}
|
||||
aside {
|
||||
max-width: 50vw;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user