working UI including upload
This commit is contained in:
parent
65507f96a7
commit
f54f387b10
1
.gitignore
vendored
1
.gitignore
vendored
@ -1 +1,2 @@
|
||||
.pio
|
||||
/index.html.min
|
||||
5
icon.svg
Normal file
5
icon.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M17 12a1 1 0 0 1 1 1v9h-2v-8H8v8H6v-9a1 1 0 0 1 1-1h10zm-5 4v2h-2v-2h2zm0-10a6 6 0 0 1 5.368 3.316l-1.79.895a4 4 0 0 0-7.157 0l-1.789-.895A6 6 0 0 1 12 6zm0-4a10 10 0 0 1 8.946 5.527l-1.789.895A8 8 0 0 0 12 4a8 8 0 0 0-7.157 4.422l-1.79-.895A10 10 0 0 1 12 2z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 420 B |
167
index.html
Normal file
167
index.html
Normal file
@ -0,0 +1,167 @@
|
||||
<html lang="de">
|
||||
<head>
|
||||
<title id="title"></title>
|
||||
<link rel="icon" type="image/svg" href="icon.svg">
|
||||
<style>
|
||||
.relay {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.relay > * {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.name {
|
||||
flex: 3;
|
||||
}
|
||||
|
||||
.header {
|
||||
font-weight: bold;;
|
||||
}
|
||||
|
||||
input, select, button {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
input[type=number], select {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
#info {
|
||||
display: none;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="relayList">
|
||||
<div class="relay">
|
||||
<div class="header name">Name</div>
|
||||
<div class="header state">Status</div>
|
||||
<div class="header initial">Initial</div>
|
||||
<div class="header onMillis">Auto Aus</div>
|
||||
<div class="header offMillis">Auto An</div>
|
||||
<div class="header switchOn">Ein</div>
|
||||
<div class="header switchOff">Aus</div>
|
||||
<div class="header switchCycle">Zyklus</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<pre id="info"></pre>
|
||||
|
||||
<script>
|
||||
const title = document.getElementById("title");
|
||||
const info = document.getElementById("info");
|
||||
const relayList = document.getElementById("relayList");
|
||||
|
||||
function getUrl(path) {
|
||||
return `http://10.42.0.204/${path}`;
|
||||
}
|
||||
|
||||
function setState(index, value) {
|
||||
set("state", index, value ? 'true' : 'false');
|
||||
}
|
||||
|
||||
function set(key, index, value) {
|
||||
request(`${key}${index}=${encodeURIComponent(value)}`);
|
||||
}
|
||||
|
||||
let timeout;
|
||||
|
||||
function request(query = "") {
|
||||
if (timeout) {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
timeout = setTimeout(() => request(), 2000);
|
||||
|
||||
const r = new XMLHttpRequest();
|
||||
r.open("GET", getUrl(`set?${query}`));
|
||||
r.onreadystatechange = () => {
|
||||
if (r.readyState === 4 && r.status === 200) {
|
||||
const data = JSON.parse(r.response);
|
||||
title.innerText = data.hostname;
|
||||
for (let index = 0; index < data.relays.length; index++) {
|
||||
const relayData = data.relays[index];
|
||||
const tag = document.getElementById("relay" + index) || create(index);
|
||||
tag.getElementsByClassName("name")[0].getElementsByTagName("input")[0].value = relayData.name;
|
||||
tag.getElementsByClassName("state")[0].innerText = relayData.state ? "Ein" : "Aus";
|
||||
tag.getElementsByClassName("onMillis")[0].getElementsByTagName("input")[0].value = relayData.onMillis;
|
||||
tag.getElementsByClassName("offMillis")[0].getElementsByTagName("input")[0].value = relayData.offMillis;
|
||||
tag.getElementsByClassName("initial")[0].getElementsByTagName("select")[0].value = relayData.initial;
|
||||
}
|
||||
info.innerText = JSON.stringify(data, null, 2);
|
||||
}
|
||||
}
|
||||
r.send();
|
||||
}
|
||||
|
||||
function newDiv(relayIndex, relayTag, name) {
|
||||
const div = document.createElement("div")
|
||||
div.className = name;
|
||||
relayTag.append(div);
|
||||
return div;
|
||||
}
|
||||
|
||||
function newInput(relayIndex, relayTag, name, type) {
|
||||
const div = newDiv(relayIndex, relayTag, name);
|
||||
|
||||
const input = document.createElement("input")
|
||||
input.type = type;
|
||||
input.onchange = () => set(name, relayIndex, input.value);
|
||||
div.append(input);
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
function newButton(relayIndex, relayTag, clazz, key, value, text) {
|
||||
const div = newDiv(relayIndex, relayTag, clazz);
|
||||
|
||||
const button = document.createElement("button")
|
||||
button.innerText = text;
|
||||
button.onclick = () => set(key, relayIndex, value);
|
||||
div.append(button);
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
function newSelect(relayIndex, relayTag, name, options) {
|
||||
const div = newDiv(relayIndex, relayTag, name);
|
||||
|
||||
const select = document.createElement("select")
|
||||
select.onchange = () => set(name, relayIndex, select.value);
|
||||
div.append(select);
|
||||
|
||||
for (const [value, text] of options) {
|
||||
const option = document.createElement("option");
|
||||
option.value = value;
|
||||
option.innerText = text;
|
||||
select.appendChild(option);
|
||||
}
|
||||
|
||||
return select;
|
||||
}
|
||||
|
||||
function create(relayIndex) {
|
||||
const relayTag = document.createElement("div");
|
||||
relayTag.id = "relay" + relayIndex;
|
||||
relayTag.className = "relay";
|
||||
relayList.append(relayTag);
|
||||
|
||||
newInput(relayIndex, relayTag, "name", "text");
|
||||
newDiv(relayIndex, relayTag, "state");
|
||||
newSelect(relayIndex, relayTag, "initial", [["OFF", "Aus"], ["ON", "Ein"], ["CYCLE", "Zyklus"]]);
|
||||
newInput(relayIndex, relayTag, "onMillis", "number");
|
||||
newInput(relayIndex, relayTag, "offMillis", "number");
|
||||
newButton(relayIndex, relayTag, "switchOn", "state", "true", "Ein");
|
||||
newButton(relayIndex, relayTag, "switchOff", "state", "false", "Aus");
|
||||
newButton(relayIndex, relayTag, "switchCycle", "onCount", -1, "Zyklus");
|
||||
return relayTag;
|
||||
}
|
||||
|
||||
request();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@ -4,15 +4,15 @@
|
||||
enum Initial {
|
||||
INITIAL_OFF,
|
||||
INITIAL_ON,
|
||||
INITIAL_BLINK,
|
||||
INITIAL_CYCLE,
|
||||
};
|
||||
|
||||
inline Initial stringToInitial(const String &str) {
|
||||
if (str == "ON") {
|
||||
return INITIAL_ON;
|
||||
}
|
||||
if (str == "BLINK") {
|
||||
return INITIAL_BLINK;
|
||||
if (str == "CYCLE") {
|
||||
return INITIAL_CYCLE;
|
||||
}
|
||||
return INITIAL_OFF;
|
||||
}
|
||||
@ -21,8 +21,8 @@ inline const char *initialToString(const Initial value) {
|
||||
switch (value) {
|
||||
case INITIAL_ON:
|
||||
return "ON";
|
||||
case INITIAL_BLINK:
|
||||
return "BLINK";
|
||||
case INITIAL_CYCLE:
|
||||
return "CYCLE";
|
||||
default:
|
||||
return "OFF";
|
||||
}
|
||||
|
||||
@ -39,7 +39,7 @@ protected:
|
||||
|
||||
void _applyInitial() {
|
||||
_write(initial != INITIAL_OFF);
|
||||
if (initial != INITIAL_BLINK) {
|
||||
if (initial != INITIAL_CYCLE) {
|
||||
onCount = 0;
|
||||
} else {
|
||||
onCount = -1;
|
||||
@ -72,7 +72,7 @@ public:
|
||||
set(!get());
|
||||
}
|
||||
|
||||
void blink(const unsigned long onMillis_, const unsigned long offMillis_, const unsigned long onCount_ = -1) {
|
||||
void cycle(const unsigned long onMillis_, const unsigned long offMillis_, const unsigned long onCount_ = -1) {
|
||||
this->onMillis = onMillis_;
|
||||
this->offMillis = offMillis_;
|
||||
this->onCount = onCount_;
|
||||
|
||||
@ -26,7 +26,7 @@ bool configWrite(const String &name, const long fallback, const long value) {
|
||||
if (configRead(name, fallback) == value) {
|
||||
return false;
|
||||
}
|
||||
Serial.printf("\"%s\" = \"%ld\"", name.c_str(), value);
|
||||
Serial.printf("[CONFIG] \"%s\" = \"%ld\"\n", name.c_str(), value);
|
||||
if (auto file = configOpen(name, "w")) {
|
||||
const auto content = String(value);
|
||||
file.write(reinterpret_cast<const uint8_t *>(content.c_str()), content.length());
|
||||
@ -65,7 +65,7 @@ bool configWrite(const String &name, const String &fallback, const String &value
|
||||
if (configRead(name, fallback) == value) {
|
||||
return false;
|
||||
}
|
||||
Serial.printf("\"%s\" = \"%s\"", name.c_str(), value.c_str());
|
||||
Serial.printf("[CONFIG] \"%s\" = \"%s\"\n", name.c_str(), value.c_str());
|
||||
if (auto file = configOpen(name, "w")) {
|
||||
file.write(reinterpret_cast<const uint8_t *>(value.c_str()), value.length());
|
||||
file.close();
|
||||
|
||||
77
src/http.cpp
77
src/http.cpp
@ -2,6 +2,7 @@
|
||||
#include "io.h"
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <LittleFS.h>
|
||||
|
||||
#ifdef ESP8266
|
||||
#include <ESP8266WebServer.h>
|
||||
@ -18,6 +19,12 @@ WebServer server(80);
|
||||
bool httpRunning = false;
|
||||
|
||||
void httpRelay(const int index, Output &relay) {
|
||||
const auto nameKey = String("name") + index;
|
||||
if (server.hasArg(nameKey)) {
|
||||
const auto name = server.arg(nameKey);
|
||||
relay.setName(name);
|
||||
}
|
||||
|
||||
const auto stateKey = String("state") + index;
|
||||
if (server.hasArg(stateKey)) {
|
||||
const auto state = server.arg(stateKey);
|
||||
@ -39,12 +46,19 @@ void httpRelay(const int index, Output &relay) {
|
||||
} else if (initial == "ON") {
|
||||
Serial.printf("[HTTP] %s = %s\n", initialKey.c_str(), initial.c_str());
|
||||
relay.setInitial(INITIAL_ON);
|
||||
} else if (initial == "BLINK") {
|
||||
} else if (initial == "CYCLE") {
|
||||
Serial.printf("[HTTP] %s = %s\n", initialKey.c_str(), initial.c_str());
|
||||
relay.setInitial(INITIAL_BLINK);
|
||||
relay.setInitial(INITIAL_CYCLE);
|
||||
}
|
||||
}
|
||||
|
||||
const auto onCountKey = String("onCount") + index;
|
||||
if (server.hasArg(onCountKey)) {
|
||||
const auto value = server.arg(onCountKey).toInt();
|
||||
Serial.printf("[HTTP] %s = %ld\n", onCountKey.c_str(), value);
|
||||
relay.setOnCount(value);
|
||||
}
|
||||
|
||||
const auto onMillisKey = String("onMillis") + index;
|
||||
if (server.hasArg(onMillisKey)) {
|
||||
const auto value = server.arg(onMillisKey).toInt();
|
||||
@ -58,13 +72,6 @@ void httpRelay(const int index, Output &relay) {
|
||||
Serial.printf("[HTTP] %s = %ld\n", offMillisKey.c_str(), value);
|
||||
relay.setOffMillis(value);
|
||||
}
|
||||
|
||||
const auto onCountKey = String("onCount") + index;
|
||||
if (server.hasArg(onCountKey)) {
|
||||
const auto value = server.arg(onCountKey).toInt();
|
||||
Serial.printf("[HTTP] %s = %ld\n", onCountKey.c_str(), value);
|
||||
relay.setOnCount(value);
|
||||
}
|
||||
}
|
||||
|
||||
void httpRelayJson(const Output &relay, const JsonObject json) {
|
||||
@ -79,12 +86,13 @@ void httpRelayJson(const Output &relay, const JsonObject json) {
|
||||
|
||||
void httpStatus() {
|
||||
JsonDocument json;
|
||||
json.to<JsonObject>();
|
||||
json["hostname"] = WiFi.getHostname();
|
||||
|
||||
httpRelayJson(relay1, json["relay1"].to<JsonObject>());
|
||||
httpRelayJson(relay2, json["relay2"].to<JsonObject>());
|
||||
httpRelayJson(relay3, json["relay3"].to<JsonObject>());
|
||||
httpRelayJson(relay4, json["relay4"].to<JsonObject>());
|
||||
const auto relays = json["relays"].to<JsonArray>();
|
||||
httpRelayJson(relay0, relays.add<JsonObject>());
|
||||
httpRelayJson(relay1, relays.add<JsonObject>());
|
||||
httpRelayJson(relay2, relays.add<JsonObject>());
|
||||
httpRelayJson(relay3, relays.add<JsonObject>());
|
||||
|
||||
String response;
|
||||
serializeJson(json, response);
|
||||
@ -92,27 +100,60 @@ void httpStatus() {
|
||||
}
|
||||
|
||||
void httpSet() {
|
||||
httpRelay(0, relay0);
|
||||
httpRelay(1, relay1);
|
||||
httpRelay(2, relay2);
|
||||
httpRelay(3, relay3);
|
||||
httpRelay(4, relay4);
|
||||
httpStatus();
|
||||
}
|
||||
|
||||
void httpOff() {
|
||||
relay0.set(false);
|
||||
relay1.set(false);
|
||||
relay2.set(false);
|
||||
relay3.set(false);
|
||||
relay4.set(false);
|
||||
httpStatus();
|
||||
}
|
||||
|
||||
File httpUploadFile;
|
||||
|
||||
void httpUpload(const char *name) {
|
||||
const auto upload = server.upload();
|
||||
if (upload.status == UPLOAD_FILE_START) {
|
||||
char path[64];
|
||||
snprintf(path, sizeof(path), "/%s", name);
|
||||
httpUploadFile = LittleFS.open(path, "w");
|
||||
Serial.printf("[HTTP] Uploading: %s\n", name);
|
||||
} else if (upload.status == UPLOAD_FILE_WRITE) {
|
||||
if (httpUploadFile) {
|
||||
httpUploadFile.write(upload.buf, upload.currentSize);
|
||||
}
|
||||
} else if (upload.status == UPLOAD_FILE_END) {
|
||||
if (httpUploadFile) {
|
||||
httpUploadFile.close();
|
||||
Serial.printf("[HTTP] Upload complete: %s (%d bytes)\n", name, upload.currentSize);
|
||||
server.send(200);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void httpSetup() {
|
||||
server.on("", httpSet);
|
||||
server.on("/", httpSet);
|
||||
server.enableCORS(true);
|
||||
|
||||
server.serveStatic("", LittleFS, "/index.html");
|
||||
server.serveStatic("/", LittleFS, "/index.html");
|
||||
server.serveStatic("/icon.svg", LittleFS, "/icon.svg");
|
||||
|
||||
server.on("/set", httpSet);
|
||||
server.on("/set/", httpSet);
|
||||
server.on("/off", httpOff);
|
||||
server.on("/off/", httpOff);
|
||||
|
||||
server.on("/upload/index", HTTP_POST, [] { server.send(200); }, [] { httpUpload("index.html"); });
|
||||
server.on("/upload/icon", HTTP_POST, [] { server.send(200); }, [] { httpUpload("icon.svg"); });
|
||||
|
||||
server.begin();
|
||||
|
||||
Serial.println("HTTP server started");
|
||||
httpRunning = true;
|
||||
}
|
||||
|
||||
16
src/io.cpp
16
src/io.cpp
@ -8,23 +8,23 @@
|
||||
#define STATUS_INVERT true
|
||||
#endif
|
||||
|
||||
Relay relay1(1, "RELAY #1", 12, false, true);
|
||||
Relay relay0(1, "RELAY #0", 12, false, true);
|
||||
|
||||
Relay relay2(2, "RELAY #2", 5, false, true);
|
||||
Relay relay1(2, "RELAY #1", 5, false, true);
|
||||
|
||||
Relay relay3(3, "RELAY #3", 4, false, true);
|
||||
Relay relay2(3, "RELAY #2", 4, false, true);
|
||||
|
||||
Relay relay4(4, "RELAY #4", 15, false, true);
|
||||
Relay relay3(4, "RELAY #3", 15, false, true);
|
||||
|
||||
Output status("Status", STATUS_PIN, STATUS_INVERT, false);
|
||||
|
||||
Button button1(0, true, true, [](const ButtonEvent event) { buttonCallback(relay1, event); });
|
||||
Button button0(0, true, true, [](const ButtonEvent event) { buttonCallback(relay0, event); });
|
||||
|
||||
Button button2(9, true, true, [](const ButtonEvent event) { buttonCallback(relay2, event); });
|
||||
Button button1(9, true, true, [](const ButtonEvent event) { buttonCallback(relay1, event); });
|
||||
|
||||
Button button3(10, true, true, [](const ButtonEvent event) { buttonCallback(relay3, event); });
|
||||
Button button2(10, true, true, [](const ButtonEvent event) { buttonCallback(relay2, event); });
|
||||
|
||||
Button button4(14, true, true, [](const ButtonEvent event) { buttonCallback(relay4, event); });
|
||||
Button button3(14, true, true, [](const ButtonEvent event) { buttonCallback(relay3, event); });
|
||||
|
||||
void buttonCallback(Output &output, const ButtonEvent event) {
|
||||
if (event == BUTTON_PRESSED) {
|
||||
|
||||
18
src/io.h
18
src/io.h
@ -6,50 +6,50 @@
|
||||
|
||||
void buttonCallback(Output &output, ButtonEvent event);
|
||||
|
||||
extern Relay relay0;
|
||||
|
||||
extern Relay relay1;
|
||||
|
||||
extern Relay relay2;
|
||||
|
||||
extern Relay relay3;
|
||||
|
||||
extern Relay relay4;
|
||||
|
||||
extern Output status;
|
||||
|
||||
extern Button button0;
|
||||
|
||||
extern Button button1;
|
||||
|
||||
extern Button button2;
|
||||
|
||||
extern Button button3;
|
||||
|
||||
extern Button button4;
|
||||
|
||||
inline void ioSetup() {
|
||||
button0.setup();
|
||||
button1.setup();
|
||||
button2.setup();
|
||||
button3.setup();
|
||||
button4.setup();
|
||||
|
||||
status.setup();
|
||||
|
||||
relay0.setup();
|
||||
relay1.setup();
|
||||
relay2.setup();
|
||||
relay3.setup();
|
||||
relay4.setup();
|
||||
}
|
||||
|
||||
inline void ioLoop() {
|
||||
button0.loop();
|
||||
button1.loop();
|
||||
button2.loop();
|
||||
button3.loop();
|
||||
button4.loop();
|
||||
|
||||
status.loop();
|
||||
|
||||
relay1.loop();
|
||||
relay0.loop();
|
||||
relay0.loop();
|
||||
relay2.loop();
|
||||
relay3.loop();
|
||||
relay4.loop();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@ -24,7 +24,7 @@ void wifiConnect() {
|
||||
WiFi.setAutoReconnect(false);
|
||||
yield();
|
||||
|
||||
status.blink(500, 500);
|
||||
status.cycle(500, 500);
|
||||
|
||||
const auto hostname = configRead(CONFIG_HOSTNAME, DEFAULT_HOSTNAME);
|
||||
const auto wifiSSID = configRead(CONFIG_WIFI_SSID, DEFAULT_WIFI_SSID);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user