374 lines
9.4 KiB
HTML
374 lines
9.4 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="de">
|
|
<head>
|
|
<title id="title"></title>
|
|
<link rel="icon" type="image/svg" href="icon.svg">
|
|
<style>
|
|
body {
|
|
font-family: sans-serif;
|
|
font-size: 4.5vw;
|
|
margin: 0;
|
|
}
|
|
|
|
* {
|
|
font-size: inherit;
|
|
}
|
|
|
|
.relayBox {
|
|
margin: 0.25em;
|
|
}
|
|
|
|
.relay {
|
|
padding: 0.25em;
|
|
background-color: lightgray;
|
|
}
|
|
|
|
.relay div {
|
|
padding: 0.25em;
|
|
}
|
|
|
|
input, select {
|
|
all: unset;
|
|
width: 100%;
|
|
}
|
|
|
|
input::-webkit-outer-spin-button,
|
|
input::-webkit-inner-spin-button {
|
|
-webkit-appearance: none;
|
|
margin: 0;
|
|
}
|
|
|
|
input[type=number] {
|
|
-moz-appearance: textfield;
|
|
text-align: right;
|
|
}
|
|
|
|
.flex {
|
|
display: flex;
|
|
}
|
|
|
|
.name {
|
|
flex: 1;
|
|
padding: 0;
|
|
}
|
|
|
|
.state {
|
|
}
|
|
|
|
.topic {
|
|
flex: 1;
|
|
padding: 0;
|
|
}
|
|
|
|
.stateOn {
|
|
background-color: #7aca7a;
|
|
}
|
|
|
|
.stateOff {
|
|
background-color: #ae4d4d;
|
|
}
|
|
|
|
.switchOn {
|
|
border-radius: 0.5em;
|
|
filter: brightness(50%);
|
|
background-color: palegreen;
|
|
}
|
|
|
|
.switchOn_Active {
|
|
z-index: 9999;
|
|
filter: brightness(120%);
|
|
box-shadow: 0 0 5px #006400, 0 0 10px #228B22, 0 0 20px #32CD32;
|
|
}
|
|
|
|
.switchOff {
|
|
border-radius: 0.5em;
|
|
filter: brightness(50%);
|
|
background-color: indianred;
|
|
}
|
|
|
|
.switchOff_Active {
|
|
z-index: 9999;
|
|
filter: brightness(120%);
|
|
box-shadow: 0 0 5px #8B0000, 0 0 10px #B22222, 0 0 20px #FF4500;
|
|
}
|
|
|
|
.switchCycle {
|
|
border-radius: 0.5em;
|
|
filter: brightness(50%);
|
|
background-color: lightskyblue;
|
|
}
|
|
|
|
.switchCycle_Active {
|
|
z-index: 9999;
|
|
filter: brightness(120%);
|
|
box-shadow: 0 0 5px #00008B, 0 0 10px #1E90FF, 0 0 20px #00BFFF;
|
|
}
|
|
|
|
.config {
|
|
flex: 1;
|
|
}
|
|
|
|
.switch {
|
|
text-align: center;
|
|
flex: 1;
|
|
}
|
|
|
|
.initial {
|
|
text-align: right;
|
|
}
|
|
|
|
.adminHidden {
|
|
display: none;
|
|
}
|
|
|
|
@media (min-width: 1000px) {
|
|
body {
|
|
font-size: 16px;
|
|
}
|
|
|
|
.relayBox {
|
|
width: 400px;
|
|
}
|
|
}
|
|
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<div id="relayList"></div>
|
|
|
|
<button id="admin" onclick="toggleAdmin()">Admin</button>
|
|
|
|
<script>
|
|
let admin = false;
|
|
|
|
function toggleAdmin() {
|
|
admin = !admin;
|
|
update();
|
|
}
|
|
|
|
const title = document.getElementById("title");
|
|
const relayList = document.getElementById("relayList");
|
|
|
|
function getUrl(path) {
|
|
return `http://10.42.0.204/${path}`;
|
|
}
|
|
|
|
function set(key, index, value) {
|
|
request(`${key}${index}=${encodeURIComponent(value)}`);
|
|
}
|
|
|
|
let timeout;
|
|
|
|
function updateValue(tag, clazz, innerTag, value) {
|
|
const input = tag.getElementsByClassName(clazz)[0].getElementsByTagName(innerTag)[0];
|
|
if (document.activeElement !== input) {
|
|
input.value = value;
|
|
}
|
|
}
|
|
|
|
function updateState(relayTag, state) {
|
|
if (state) {
|
|
relayTag.classList.add("stateOn");
|
|
relayTag.classList.remove("stateOff");
|
|
} else {
|
|
relayTag.classList.add("stateOff");
|
|
relayTag.classList.remove("stateOn");
|
|
}
|
|
}
|
|
|
|
const SECOND = 1000;
|
|
const MINUTE = (60 * SECOND);
|
|
const HOUR = (60 * MINUTE);
|
|
const DAY = (24 * HOUR);
|
|
|
|
function countdownString(relayData, millis) {
|
|
const rest = Math.ceil((millis - relayData.stateAgeMillis - (Date.now() - dataAge)) / SECOND) * SECOND;
|
|
const cycle = relayData.onCount !== 0 && relayData.onMillis > 0 && relayData.offMillis > 0;
|
|
if (millis <= 0 || (!cycle && !relayData.state)) {
|
|
return "";
|
|
}
|
|
|
|
const days = rest / DAY;
|
|
const hours = rest / HOUR;
|
|
const minutes = rest / MINUTE;
|
|
const seconds = rest / SECOND;
|
|
if (days >= 1) {
|
|
return Math.floor(days) + "d " + Math.floor(hours % 24) + "h " + Math.ceil(minutes % 60) + "m " + Math.ceil(seconds % 60) + "s";
|
|
}
|
|
if (hours >= 1) {
|
|
return Math.floor(hours) + "h " + Math.floor(minutes % 60) + "m " + Math.ceil(seconds % 60) + "s";
|
|
}
|
|
if (minutes >= 1) {
|
|
return Math.floor(minutes) + "m " + Math.ceil(seconds % 60) + "s";
|
|
}
|
|
if (seconds >= 1) {
|
|
return Math.ceil(rest / SECOND) + "s";
|
|
}
|
|
return "Warte...";
|
|
}
|
|
|
|
function updateCountdown(relayTag, relayData) {
|
|
const tag = relayTag.getElementsByClassName("countdown")[0];
|
|
if (relayData.state) {
|
|
tag.innerText = countdownString(relayData, relayData.onMillis);
|
|
} else {
|
|
tag.innerText = countdownString(relayData, relayData.offMillis);
|
|
}
|
|
}
|
|
|
|
let data;
|
|
|
|
let dataAge;
|
|
|
|
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) {
|
|
data = JSON.parse(r.response);
|
|
dataAge = Date.now();
|
|
title.innerText = data.hostname;
|
|
for (let index = 0; index < data.relays.length; index++) {
|
|
const relayData = data.relays[index];
|
|
const relayTag = document.getElementById("relay" + index) || create(index);
|
|
updateValue(relayTag, "name", "input", relayData.name);
|
|
updateValue(relayTag, "topic", "input", relayData.topic);
|
|
updateState(relayTag, relayData.state);
|
|
updateCountdown(relayTag, relayData);
|
|
updateValue(relayTag, "onMillis", "input", relayData.onMillis);
|
|
updateValue(relayTag, "offMillis", "input", relayData.offMillis);
|
|
updateValue(relayTag, "initial", "select", relayData.initial);
|
|
|
|
const cycle = relayData.onCount !== 0 && relayData.onMillis > 0 && relayData.offMillis > 0;
|
|
|
|
const switchOn = relayTag.getElementsByClassName("switchOn")[0];
|
|
if (!cycle && relayData.state) {
|
|
switchOn.classList.add("switchOn_Active");
|
|
} else {
|
|
switchOn.classList.remove("switchOn_Active");
|
|
}
|
|
|
|
const switchOff = relayTag.getElementsByClassName("switchOff")[0];
|
|
if (!cycle && !relayData.state) {
|
|
switchOff.classList.add("switchOff_Active");
|
|
} else {
|
|
switchOff.classList.remove("switchOff_Active");
|
|
}
|
|
|
|
const switchCycle = relayTag.getElementsByClassName("switchCycle")[0];
|
|
if (cycle) {
|
|
switchCycle.classList.add("switchCycle_Active");
|
|
} else {
|
|
switchCycle.classList.remove("switchCycle_Active");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
r.send();
|
|
}
|
|
|
|
function newDiv(parent, name) {
|
|
const div = document.createElement("div")
|
|
div.className = name;
|
|
parent.append(div);
|
|
return div;
|
|
}
|
|
|
|
function newInput(relayIndex, parent, clazz, name, type) {
|
|
const div = newDiv(parent, clazz);
|
|
|
|
const input = document.createElement("input")
|
|
input.type = type;
|
|
input.onchange = () => set(name, relayIndex, input.value);
|
|
div.append(input);
|
|
|
|
return input;
|
|
}
|
|
|
|
function newButton(relayIndex, parent, clazz, key, value, text) {
|
|
const div = newDiv(parent, clazz);
|
|
|
|
const button = document.createElement("div")
|
|
button.innerText = text;
|
|
button.onclick = () => set(key, relayIndex, value);
|
|
div.append(button);
|
|
|
|
return button;
|
|
}
|
|
|
|
function newSelect(relayIndex, parent, clazz, name, options) {
|
|
const div = newDiv(parent, clazz);
|
|
|
|
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 relayBox = document.createElement("div");
|
|
relayBox.className = "relayBox";
|
|
relayList.append(relayBox);
|
|
|
|
const relay = document.createElement("div");
|
|
relay.id = "relay" + relayIndex;
|
|
relay.className = "relay";
|
|
relayBox.append(relay);
|
|
|
|
const header = newDiv(relay, "flex");
|
|
newInput(relayIndex, header, "name", "name", "text");
|
|
newDiv(header, "countdown");
|
|
|
|
const topicDiv = newDiv(relay, "flex admin adminHidden");
|
|
newInput(relayIndex, topicDiv, "topic", "topic", "text");
|
|
|
|
const config = newDiv(relay, "flex admin adminHidden");
|
|
newInput(relayIndex, config, "config onMillis", "onMillis", "number");
|
|
newInput(relayIndex, config, "config offMillis", "offMillis", "number");
|
|
newSelect(relayIndex, config, "config initial", "initial", [["OFF", "Init: Aus"], ["ON", "Init: Ein"], ["CYCLE", "Init: Zyklus"]]);
|
|
|
|
const switches = newDiv(relay, "flex");
|
|
newButton(relayIndex, switches, "switch switchOn", "state", "true", "Ein");
|
|
newButton(relayIndex, switches, "switch switchOff", "state", "false", "Aus");
|
|
newButton(relayIndex, switches, "switch switchCycle", "onCount", -1, "Zyklus");
|
|
return relay;
|
|
}
|
|
|
|
request();
|
|
|
|
function update() {
|
|
if (!data) {
|
|
return;
|
|
}
|
|
for (let index = 0; index < data.relays.length; index++) {
|
|
const relayData = data.relays[index];
|
|
const relayTag = document.getElementById("relay" + index) || create(index);
|
|
updateCountdown(relayTag, relayData);
|
|
for (let element of document.getElementsByClassName("admin")) {
|
|
if (admin) {
|
|
element.classList.remove("adminHidden");
|
|
} else {
|
|
element.classList.add("adminHidden");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
setInterval(() => update(), 500);
|
|
</script>
|
|
</body>
|
|
</html> |