599 lines
19 KiB
HTML
599 lines
19 KiB
HTML
|
<!DOCTYPE html>
|
||
|
<html>
|
||
|
|
||
|
<head>
|
||
|
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
||
|
<title>Smart Switch</title>
|
||
|
<meta name="viewport" content="width=device-width">
|
||
|
<link rel="apple-touch-icon" href="/favicon.ico" type="image/x-icon" />
|
||
|
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />
|
||
|
<link rel="icon" href="/favicon.ico" type="image/x-icon" />
|
||
|
<link rel="stylesheet" href="app.css" type="text/css" />
|
||
|
<style>
|
||
|
body {
|
||
|
font-family: arial;
|
||
|
color: #999
|
||
|
}
|
||
|
|
||
|
table {
|
||
|
margin: auto;
|
||
|
max-width: 600px;
|
||
|
}
|
||
|
|
||
|
td {
|
||
|
padding: 1%;
|
||
|
padding-inline: 2%
|
||
|
}
|
||
|
|
||
|
input[type=text] {
|
||
|
width: 70%;
|
||
|
text-align: center;
|
||
|
padding: 0px;
|
||
|
box-sizing: border-box;
|
||
|
border: none;
|
||
|
border-bottom: 1px solid #2196f3;
|
||
|
font-size: 22px;
|
||
|
color: #2196f3;
|
||
|
font-family: arial;
|
||
|
}
|
||
|
|
||
|
input[type=button] {
|
||
|
background-color: #2196f3;
|
||
|
border: none;
|
||
|
color: #fff;
|
||
|
padding: 10px 10px;
|
||
|
width: 62px;
|
||
|
text-decoration: none;
|
||
|
margin: 4px 2px;
|
||
|
cursor: pointer;
|
||
|
border-radius: 34px;
|
||
|
font-family: arial;
|
||
|
font-weight: 700;
|
||
|
-webkit-appearance: none;
|
||
|
-moz-appearance: none;
|
||
|
appearance: none;
|
||
|
}
|
||
|
|
||
|
.switch {
|
||
|
position: relative;
|
||
|
display: inline-block;
|
||
|
width: 60px;
|
||
|
height: 34px
|
||
|
}
|
||
|
|
||
|
.switch input {
|
||
|
opacity: 0;
|
||
|
width: 0;
|
||
|
height: 0
|
||
|
}
|
||
|
|
||
|
.slider {
|
||
|
position: absolute;
|
||
|
cursor: pointer;
|
||
|
top: 0;
|
||
|
left: 0;
|
||
|
right: 0;
|
||
|
bottom: 0;
|
||
|
background-color: #ccc;
|
||
|
-webkit-transition: .4s;
|
||
|
transition: .4s
|
||
|
}
|
||
|
|
||
|
.slider:before {
|
||
|
position: absolute;
|
||
|
content: "";
|
||
|
height: 26px;
|
||
|
width: 26px;
|
||
|
left: 4px;
|
||
|
bottom: 4px;
|
||
|
background-color: #fff;
|
||
|
-webkit-transition: .4s;
|
||
|
transition: .4s
|
||
|
}
|
||
|
|
||
|
input:checked+.slider {
|
||
|
background-color: #2196f3
|
||
|
}
|
||
|
|
||
|
input:focus+.slider {
|
||
|
box-shadow: 0 0 1px #2196f3
|
||
|
}
|
||
|
|
||
|
input:checked+.slider:before {
|
||
|
-webkit-transform: translateX(26px);
|
||
|
-ms-transform: translateX(26px);
|
||
|
transform: translateX(26px)
|
||
|
}
|
||
|
|
||
|
.slider.round {
|
||
|
border-radius: 34px
|
||
|
}
|
||
|
|
||
|
.slider.round:before {
|
||
|
border-radius: 50%
|
||
|
}
|
||
|
|
||
|
.clk {
|
||
|
font-size: 54px;
|
||
|
color: #444;
|
||
|
}
|
||
|
|
||
|
.clear:after,
|
||
|
.clear:before {
|
||
|
content: "";
|
||
|
display: table
|
||
|
}
|
||
|
|
||
|
.clear:after {
|
||
|
clear: both
|
||
|
}
|
||
|
|
||
|
.wrapper {
|
||
|
position: relative;
|
||
|
top: 0px;
|
||
|
right: 0;
|
||
|
bottom: 0px;
|
||
|
left: 0;
|
||
|
margin: auto;
|
||
|
max-width: 500px;
|
||
|
border: 0px solid #ccc
|
||
|
}
|
||
|
|
||
|
.gauge {
|
||
|
display: block;
|
||
|
float: left
|
||
|
}
|
||
|
|
||
|
#g1 {
|
||
|
width: 50%
|
||
|
}
|
||
|
|
||
|
#g2 {
|
||
|
width: 50%
|
||
|
}
|
||
|
|
||
|
.son {
|
||
|
color: green;
|
||
|
font-weight: bold
|
||
|
}
|
||
|
|
||
|
.soff {
|
||
|
color: red;
|
||
|
font-weight: bold
|
||
|
}
|
||
|
|
||
|
.blinking {
|
||
|
animation: blinkingText 1.2s infinite;
|
||
|
-webkit-animation: blinkingText 1.2s infinite;
|
||
|
}
|
||
|
|
||
|
@keyframes blinkingText {
|
||
|
0% {
|
||
|
color: #ec0b0b;
|
||
|
}
|
||
|
49% {
|
||
|
color: #ea7272;
|
||
|
}
|
||
|
60% {
|
||
|
color: transparent;
|
||
|
}
|
||
|
99% {
|
||
|
color: transparent;
|
||
|
}
|
||
|
100% {
|
||
|
color: #ff0404;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
.container {
|
||
|
display: inline-block;
|
||
|
position: relative;
|
||
|
padding-left: 28px;
|
||
|
padding-top: 4px;
|
||
|
margin-top: 8px;
|
||
|
margin-bottom: 8px;
|
||
|
cursor: pointer;
|
||
|
font-size: 14px;
|
||
|
-webkit-user-select: none;
|
||
|
-moz-user-select: none;
|
||
|
-ms-user-select: none;
|
||
|
user-select: none
|
||
|
}
|
||
|
|
||
|
.container input {
|
||
|
position: absolute;
|
||
|
opacity: 0;
|
||
|
cursor: pointer
|
||
|
}
|
||
|
|
||
|
.checkmark {
|
||
|
position: absolute;
|
||
|
top: 0;
|
||
|
left: 0;
|
||
|
height: 25px;
|
||
|
width: 25px;
|
||
|
background-color: #eee;
|
||
|
border-radius: 50%
|
||
|
}
|
||
|
|
||
|
.container:hover input ~ .checkmark {
|
||
|
background-color: #ccc
|
||
|
}
|
||
|
|
||
|
.container input:checked ~ .checkmark {
|
||
|
background-color: #2196F3
|
||
|
}
|
||
|
|
||
|
.checkmark:after {
|
||
|
content: "";
|
||
|
position: absolute;
|
||
|
display: none
|
||
|
}
|
||
|
|
||
|
.container input:checked ~ .checkmark:after {
|
||
|
display: block
|
||
|
}
|
||
|
|
||
|
.container .checkmark:after {
|
||
|
top: 6px;
|
||
|
left: 6px;
|
||
|
width: 13px;
|
||
|
height: 13px;
|
||
|
border-radius: 50%;
|
||
|
background: white
|
||
|
}
|
||
|
|
||
|
</style>
|
||
|
</head>
|
||
|
|
||
|
<body>
|
||
|
|
||
|
<table align="center">
|
||
|
<tr align="center">
|
||
|
<td colspan=3>
|
||
|
<form name="sched">
|
||
|
<label class="container" id="LZ0">Auto
|
||
|
<input type="radio" name="radio" onclick="handleClick(this);" value="Z0"><span class="checkmark"></span></label>
|
||
|
<label class="container" id="LZ1">M-F
|
||
|
<input type="radio" name="radio" onclick="handleClick(this);" value="Z1"><span class="checkmark"></span></label>
|
||
|
<label class="container" id="LZ2">Sat
|
||
|
<input type="radio" name="radio" onclick="handleClick(this);" value="Z2"><span class="checkmark"></span></label>
|
||
|
<label class="container" id="LZ3">Sun
|
||
|
<input type="radio" name="radio" onclick="handleClick(this);" value="Z3"><span class="checkmark"></span></label>
|
||
|
</form>
|
||
|
</td>
|
||
|
</tr>
|
||
|
<tr align="center">
|
||
|
<td onclick="ent1Click();">
|
||
|
<label for="input-temperature">Temp °C</label>
|
||
|
<input type="text" readonly="readonly" id="input-temperature" />
|
||
|
</td>
|
||
|
<td onclick="ent2Click();">
|
||
|
<label for="input-popup-start">Time On</label>
|
||
|
<input type="text" readonly="readonly" id="input-popup-start" />
|
||
|
</td>
|
||
|
<td onclick="ent2Click();">
|
||
|
<label for="input-popup-stop">Time Off</label>
|
||
|
<input type="text" readonly="readonly" id="input-popup-stop" />
|
||
|
</td>
|
||
|
</tr>
|
||
|
<tr align="center">
|
||
|
<td>
|
||
|
<input type="button" id="W" value="Temp" onclick="button2Click(this);" />
|
||
|
</td>
|
||
|
<td>
|
||
|
<label class="switch">
|
||
|
<input id="cbStyle" type="checkbox" onclick="checkboxClick(this);"><span class="slider round"></span></label>
|
||
|
</td>
|
||
|
<td>
|
||
|
<input type="button" id="T" value="Timer" onclick="buttonClick(this);" />
|
||
|
</td>
|
||
|
</tr>
|
||
|
<tr align="center">
|
||
|
<td>
|
||
|
<div id="reconnect">
|
||
|
<input type="button" id="N" value="WSoff" onclick="statusWs();" />
|
||
|
</div>
|
||
|
</td>
|
||
|
<td><span id="sid"></span></td>
|
||
|
<td>
|
||
|
<div id="hw-reset">
|
||
|
<input type="button" id="R" value="HWrst" onclick="loadDoc('hw-reset', 1);" />
|
||
|
</div>
|
||
|
</td>
|
||
|
</tr>
|
||
|
<tr align="center">
|
||
|
<td>
|
||
|
<div id="hbut">
|
||
|
<input type="button" id="H" value="Heap" onclick="loadDoc('free-ram', 0);" />
|
||
|
</div>
|
||
|
</td>
|
||
|
<td>
|
||
|
<div id="free-ram"></div>
|
||
|
</td>
|
||
|
<td>
|
||
|
<div id="ebut">
|
||
|
<input type="button" id="E" value="WEdit" onclick="buttonEClick();" />
|
||
|
</div>
|
||
|
</td>
|
||
|
</tr>
|
||
|
</table>
|
||
|
|
||
|
<div class="wrapper clear">
|
||
|
<div id="g1" class="gauge"></div>
|
||
|
<div id="g2" class="gauge"></div>
|
||
|
<center>
|
||
|
<label id="C" class="clk"></label>
|
||
|
</center>
|
||
|
</div>
|
||
|
|
||
|
<script src="app.min.js"></script>
|
||
|
|
||
|
<script type="text/javascript">
|
||
|
const MYCORS = '192.168.0.12';
|
||
|
var g1, g2;
|
||
|
var Analog0 = new Array();
|
||
|
var auto = true;
|
||
|
|
||
|
const successNotification = window.createNotification({
|
||
|
positionClass: 'nfc-bottom-right',
|
||
|
theme: 'info',
|
||
|
showDuration: 3000
|
||
|
});
|
||
|
|
||
|
const warningNotification = window.createNotification({
|
||
|
positionClass: 'nfc-bottom-right',
|
||
|
theme: 'warning',
|
||
|
showDuration: 6000
|
||
|
});
|
||
|
|
||
|
document.addEventListener("DOMContentLoaded", function(event) {
|
||
|
console.log("DOM fully loaded and parsed");
|
||
|
g1 = new JustGage({
|
||
|
id: "g1",
|
||
|
value: -5.5,
|
||
|
min: -40,
|
||
|
max: 50,
|
||
|
decimals: 1,
|
||
|
title: "Temperature",
|
||
|
titlePosition: "below",
|
||
|
label: "°C",
|
||
|
relativeGaugeSize: true,
|
||
|
pointer: true,
|
||
|
customSectors: [{
|
||
|
color: "#328da8",
|
||
|
lo: -40,
|
||
|
hi: 0
|
||
|
}, {
|
||
|
color: "#32a852",
|
||
|
lo: 0,
|
||
|
hi: 25
|
||
|
}, {
|
||
|
color: "#ff4d4d",
|
||
|
lo: 25,
|
||
|
hi: 50
|
||
|
}],
|
||
|
formatNumber: true
|
||
|
});
|
||
|
|
||
|
g2 = new JustGage({
|
||
|
id: "g2",
|
||
|
value: 40.8,
|
||
|
min: 0,
|
||
|
max: 100,
|
||
|
decimals: 1,
|
||
|
title: "Humidity",
|
||
|
titlePosition: "below",
|
||
|
label: "%",
|
||
|
relativeGaugeSize: true,
|
||
|
pointer: true,
|
||
|
customSectors: [{
|
||
|
color: "#ffc926",
|
||
|
lo: 0,
|
||
|
hi: 45
|
||
|
}, {
|
||
|
color: "#32a852",
|
||
|
lo: 45,
|
||
|
hi: 55
|
||
|
}, {
|
||
|
color: "#328da8",
|
||
|
lo: 55,
|
||
|
hi: 100
|
||
|
}],
|
||
|
formatNumber: true
|
||
|
});
|
||
|
|
||
|
});
|
||
|
|
||
|
var servurl = document.location.host;
|
||
|
if (servurl.length < 5) servurl = MYCORS;
|
||
|
var connection = new WebSocket('ws://' + servurl + '/ws', ['arduino']);
|
||
|
|
||
|
connection.onopen = function() {
|
||
|
//connection.send('get_something');
|
||
|
document.getElementById("sid").className = "son";
|
||
|
document.getElementById('sid').innerHTML = "Smart Switch";
|
||
|
console.log("connection opened");
|
||
|
};
|
||
|
|
||
|
connection.onclose = function() {
|
||
|
document.getElementById("sid").className = "blinking";
|
||
|
document.getElementById('sid').innerHTML = "Detached";
|
||
|
document.getElementById("N").value = "WSon";
|
||
|
console.log("connection closed");
|
||
|
};
|
||
|
|
||
|
connection.onerror = function(error) {
|
||
|
document.getElementById("sid").className = "soff";
|
||
|
document.getElementById('sid').innerHTML = "Detached";
|
||
|
document.getElementById("N").value = "WSon";
|
||
|
console.log('WebSocket Error ', error);
|
||
|
};
|
||
|
|
||
|
connection.onmessage = function(evt) {
|
||
|
// handle websocket message. update attributes or values of elements that match the name on incoming message
|
||
|
console.log("msg rec", evt.data);
|
||
|
var msgArray = evt.data.split(","); // split message by delimiter into a string array
|
||
|
console.log("msgArray", msgArray[0]);
|
||
|
console.log("msgArray", msgArray[1]);
|
||
|
console.log("msgArray", msgArray[2]);
|
||
|
console.log("msgArray", msgArray[3]);
|
||
|
console.log("msgArray", msgArray[4]);
|
||
|
var indicator = msgArray[1]; // the first element in the message array is the ID of the object to update
|
||
|
console.log("indiactor", indicator);
|
||
|
var a = document.getElementById('cbStyle');
|
||
|
if (indicator) // if an object by the name of the message exists, update its value or its attributes
|
||
|
{
|
||
|
switch (msgArray[1]) {
|
||
|
case "Arduino":
|
||
|
console.log("Got Temp / Humidity");
|
||
|
g1.refresh(msgArray[2], null);
|
||
|
g2.refresh(msgArray[3], null);
|
||
|
if (msgArray[4] == "1") document.getElementById("T").style.backgroundColor = "#32a852";
|
||
|
else document.getElementById("T").style.backgroundColor = "#2196f3";
|
||
|
var delta = (parseFloat(document.getElementById('input-temperature').value).toFixed(1) - parseFloat(msgArray[2]).toFixed(1));
|
||
|
if (delta > 0.5) document.getElementById("W").style.backgroundColor = "red";
|
||
|
else if (delta < -0.5) document.getElementById("W").style.backgroundColor = "#2196f3";
|
||
|
break;
|
||
|
case "Clock":
|
||
|
var dn = parseInt(msgArray[3]);
|
||
|
var days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
|
||
|
var dayName = days[dn] || '*'; // 0...6
|
||
|
var shedtype = 0;
|
||
|
if (dn == 0) shedtype = 3;
|
||
|
else if (dn == 6) shedtype = 2;
|
||
|
else if ((dn > 0) && (dn < 6)) shedtype = 1;
|
||
|
document.getElementById('C').innerHTML = msgArray[2] + ' ' + dayName;
|
||
|
if (auto) document.getElementById('LZ' + shedtype).classList.add('son');
|
||
|
else document.getElementById('LZ' + shedtype).classList.remove('son');
|
||
|
break;
|
||
|
case "Setting":
|
||
|
document.getElementById('input-popup-start').value = msgArray[2];
|
||
|
document.getElementById('input-popup-stop').value = msgArray[3];
|
||
|
document.getElementById('input-temperature').value = parseFloat(msgArray[4]).toFixed(1);
|
||
|
|
||
|
document.getElementById('input-popup-start').className = "";
|
||
|
document.getElementById('input-popup-stop').className = "";
|
||
|
document.getElementById('input-temperature').className = "";
|
||
|
|
||
|
tpick.attach("input-popup-start");
|
||
|
tpick.attach("input-popup-stop");
|
||
|
fpick.attach("input-temperature");
|
||
|
warningNotification({
|
||
|
message: 'Client UPD'
|
||
|
});
|
||
|
break;
|
||
|
case "ledon":
|
||
|
a.checked = true;
|
||
|
break;
|
||
|
case "ledoff":
|
||
|
a.checked = false;
|
||
|
break;
|
||
|
case "remoff":
|
||
|
successNotification({
|
||
|
message: 'Client quit'
|
||
|
});
|
||
|
break;
|
||
|
case "OTA":
|
||
|
warningNotification({
|
||
|
message: 'OTA begin'
|
||
|
});
|
||
|
statusWs();
|
||
|
break;
|
||
|
case "sched":
|
||
|
document.sched.radio[msgArray[2]].checked = true;
|
||
|
break;
|
||
|
default:
|
||
|
// unrecognized message type. do nothing
|
||
|
break;
|
||
|
} // switch
|
||
|
} // if (indicator)
|
||
|
}; // connection.onmessage
|
||
|
|
||
|
function buttonClick(e) {
|
||
|
if (connection.readyState === WebSocket.OPEN) {
|
||
|
connection.send(e.id + document.getElementById("input-popup-start").value + "|" + document.getElementById("input-popup-stop").value + "|");
|
||
|
successNotification({
|
||
|
message: 'Timer REQ'
|
||
|
});
|
||
|
}
|
||
|
};
|
||
|
|
||
|
function button2Click(e) {
|
||
|
if (connection.readyState === WebSocket.OPEN) {
|
||
|
connection.send(e.id + parseFloat(document.getElementById("input-temperature").value).toFixed(1) + "|");
|
||
|
successNotification({
|
||
|
message: 'Temp. REQ'
|
||
|
});
|
||
|
}
|
||
|
};
|
||
|
|
||
|
function buttonEClick() {
|
||
|
var murl = '/edit';
|
||
|
if (document.location.host.length < 5) murl = 'http://' + MYCORS + '/edit'; //CORS
|
||
|
window.open(murl, '_blank')
|
||
|
warningNotification({
|
||
|
message: 'Editor'
|
||
|
});
|
||
|
};
|
||
|
|
||
|
function checkboxClick(e) {
|
||
|
if (connection.readyState === WebSocket.OPEN) {
|
||
|
if (e.checked) connection.send('L1');
|
||
|
else connection.send('L0');
|
||
|
}
|
||
|
};
|
||
|
|
||
|
function ent1Click() {
|
||
|
document.getElementById("input-temperature").className = "blinking";
|
||
|
};
|
||
|
|
||
|
function ent2Click() {
|
||
|
document.getElementById("input-popup-stop").className = "blinking";
|
||
|
document.getElementById("input-popup-start").className = "blinking";
|
||
|
};
|
||
|
|
||
|
function handleClick(e) {
|
||
|
if (e.value == 'Z0' ) auto = true;
|
||
|
else auto = false;
|
||
|
document.getElementById('L' + e.value).classList.remove('son');
|
||
|
if (connection.readyState === WebSocket.OPEN) connection.send(e.value + "|");
|
||
|
}
|
||
|
|
||
|
// XMLHttpRequest /non WebSocket/ command. same as command' div' id to get response to
|
||
|
function loadDoc(cmd, r) {
|
||
|
var murl = '/' + cmd;
|
||
|
if (document.location.host.length < 5) murl = 'http://' + MYCORS + '/' + cmd; //CORS
|
||
|
var xhttp = new XMLHttpRequest();
|
||
|
xhttp.onreadystatechange = function() {
|
||
|
if (this.readyState == 4 && this.status == 200) {
|
||
|
document.getElementById(cmd).innerHTML = this.responseText;
|
||
|
}
|
||
|
};
|
||
|
xhttp.open("GET", murl, true);
|
||
|
xhttp.send();
|
||
|
warningNotification({
|
||
|
message: 'Cmd: ' + cmd
|
||
|
});
|
||
|
if (r) { //restart?
|
||
|
connection.close();
|
||
|
document.getElementById("N").value = "WSon";
|
||
|
}
|
||
|
};
|
||
|
|
||
|
function statusWs() {
|
||
|
if (connection.readyState === WebSocket.OPEN) {
|
||
|
connection.close();
|
||
|
document.getElementById("N").value = "WSon";
|
||
|
warningNotification({
|
||
|
message: 'WS Closed'
|
||
|
});
|
||
|
} else {
|
||
|
window.location.reload();
|
||
|
}
|
||
|
};
|
||
|
</script>
|
||
|
|
||
|
</body>
|
||
|
|
||
|
</html>
|