first commit
This commit is contained in:
135
res/static/index.css
Normal file
135
res/static/index.css
Normal file
@@ -0,0 +1,135 @@
|
||||
* { box-sizing: border-box; }
|
||||
html, input { font-family: Arial, Helvetica, sans-serif; }
|
||||
html, body { margin: 0; padding: 0;}
|
||||
:root {
|
||||
--color-theme: rgb(3.921%, 39.21%, 68.23%);
|
||||
--min-size: calc(min(calc(100vw / 0.6), 100vh) / 3);
|
||||
}
|
||||
|
||||
body {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.widget {
|
||||
border: solid 1px var(--color-theme);
|
||||
border-radius: 2px;
|
||||
margin: 16px;
|
||||
}
|
||||
|
||||
.widget > header {
|
||||
background-color: var(--color-theme);
|
||||
color: white;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.widget > main {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@media screen and (min-width: 800px) {
|
||||
body {
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
#left-widget-container {
|
||||
width: 400px;
|
||||
}
|
||||
#right-widget-container {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
#login-form .widget {
|
||||
margin: 16px auto;
|
||||
width: 400px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 799.99px) {
|
||||
.widget {
|
||||
margin: 16px auto;
|
||||
min-width: 300px;
|
||||
max-width: 400px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
.raw-value {
|
||||
font-family: monospace;
|
||||
font-weight: bold;
|
||||
font-size: 1.3em;
|
||||
color: var(--color-theme);
|
||||
}
|
||||
|
||||
|
||||
|
||||
.big-value {
|
||||
text-align: right;
|
||||
position: relative;
|
||||
}
|
||||
.big-value .name {
|
||||
position: absolute;
|
||||
top: 0.2em; left: 0;
|
||||
}
|
||||
.big-value .int-value {
|
||||
font-size: 3.5em;
|
||||
font-family: monospace;
|
||||
font-weight: bold;
|
||||
color: var(--color-theme);
|
||||
}
|
||||
.big-value .dec-value {
|
||||
font-size: 1.6em;
|
||||
font-family: monospace;
|
||||
font-weight: bold;
|
||||
color: var(--color-theme);
|
||||
}
|
||||
.big-value .unit {
|
||||
position: absolute;
|
||||
top: 0.2em; right: 0;
|
||||
}
|
||||
|
||||
.big-value .meta {
|
||||
position: absolute;
|
||||
bottom: 1em; left: 0;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
.live-data-index:not(.index-current) {
|
||||
opacity: 33%;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
form input[type="text"],
|
||||
form input[type="password"],
|
||||
form input[type="email"],
|
||||
form input[type="number"],
|
||||
form input[type="date"],
|
||||
form input[type="time"],
|
||||
form textarea,
|
||||
form select {
|
||||
display: block;
|
||||
margin: 5px auto;
|
||||
border: solid 1px var(--color-theme);
|
||||
border-radius: 2px;
|
||||
padding: 5px;
|
||||
font-size: 1.1em;
|
||||
width: 100%;
|
||||
}
|
||||
form input[type="submit"] {
|
||||
display: block;
|
||||
margin: 5px auto;
|
||||
border: solid 1px var(--color-theme);
|
||||
border-radius: 2px;
|
||||
padding: 5px;
|
||||
font-size: 1.1em;
|
||||
width: 100%;
|
||||
}
|
157
res/static/index.html
Normal file
157
res/static/index.html
Normal file
@@ -0,0 +1,157 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr" dir="ltr">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Domotique</title>
|
||||
<link rel="stylesheet" type="text/css" href="index.css" media="screen"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
</head>
|
||||
<body>
|
||||
<div id="logged-in-content" style="display: none;">
|
||||
<div id="left-widget-container">
|
||||
<div class="widget">
|
||||
<header>Puissances</header>
|
||||
<main>
|
||||
<div class="big-value">
|
||||
<span class="name">Courante</span>
|
||||
<span class="int-value" id="live-data-papp"></span><span style="visibility: hidden;">.<span class="dec-value"> </span></span>
|
||||
<span class="unit">VA</span>
|
||||
</div>
|
||||
<div class="big-value">
|
||||
<span class="name">Coupure</span>
|
||||
<span class="int-value" id="live-data-pcoup"></span><span style="visibility: hidden;">.<span class="dec-value"> </span></span>
|
||||
<span class="unit">VA</span>
|
||||
</div>
|
||||
<div class="big-value">
|
||||
<span class="name">Référence (souscrite)</span>
|
||||
<span class="int-value" id="live-data-pref"></span><span style="visibility: hidden;">.<span class="dec-value"> </span></span>
|
||||
<span class="unit">VA</span>
|
||||
</div>
|
||||
<div class="big-value">
|
||||
<span class="name">Max aujourd’hui</span>
|
||||
<span class="meta" id="live-data-pj-time">--:--</span>
|
||||
<span class="int-value" id="live-data-pj"></span><span style="visibility: hidden;">.<span class="dec-value"> </span></span>
|
||||
<span class="unit">VA</span>
|
||||
</div>
|
||||
<div class="big-value">
|
||||
<span class="name">Max hier</span>
|
||||
<span class="meta" id="live-data-pj-1-time">--:--</span>
|
||||
<span class="int-value" id="live-data-pj-1"></span><span style="visibility: hidden;">.<span class="dec-value"> </span></span>
|
||||
<span class="unit">VA</span>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
<div class="widget">
|
||||
<header>Voltage</header>
|
||||
<main>
|
||||
<div class="big-value">
|
||||
<span class="name">U<sub>eff</sub></span>
|
||||
<span class="int-value" id="live-data-urms"></span><span style="visibility: hidden;">.<span class="dec-value"> </span></span>
|
||||
<span class="unit">V</span>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
<div class="widget">
|
||||
<header>Indexes cumulés</header>
|
||||
<main id="live-data-indexes">
|
||||
<div id="live-data-indexes-template" class="big-value live-data-index" style="display: none;">
|
||||
<span class="name"></span>
|
||||
<span class="int-value"></span>.<span class="dec-value"> </span>
|
||||
<span class="unit">kWh</span>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
<div class="widget">
|
||||
<header>Indexes jour</header>
|
||||
<main id="live-data-indexes-day">
|
||||
<div id="live-data-indexes-day-template" class="big-value" style="display: none;">
|
||||
<span class="name"></span>
|
||||
<span class="meta"></span>
|
||||
<span class="int-value"></span>.<span class="dec-value"></span>
|
||||
<span class="unit">kWh</span>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
<div class="widget">
|
||||
<header>Couts depuis minuit</header>
|
||||
<main id="live-data-price-day">
|
||||
<div id="live-data-price-day-template" class="big-value" style="display: none;">
|
||||
<span class="name"></span>
|
||||
<span class="meta"><span class="live-data-price-per-kwh"></span> € / kWh</span>
|
||||
<span class="int-value"></span>.<span class="dec-value"></span>
|
||||
<span class="unit">€</span>
|
||||
</div>
|
||||
<div class="big-value">
|
||||
<span class="name">Total</span>
|
||||
<span class="int-value" id="live-data-price-total-int"></span>.<span class="dec-value" id="live-data-price-total-dec"></span>
|
||||
<span class="unit">€</span>
|
||||
</div>
|
||||
<div class="big-value">
|
||||
<span class="name">Abonnement</span>
|
||||
<span class="meta"><span id="live-data-price-sub-per-month"></span> € / mois</span>
|
||||
<span class="int-value" id="live-data-price-sub-int"></span>.<span class="dec-value" id="live-data-price-sub-dec"></span>
|
||||
<span class="unit">€</span>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
<div class="widget">
|
||||
<header>Infos compteur</header>
|
||||
<main>
|
||||
Num série : <span class="raw-value" id="live-data-serial"></span><br>
|
||||
PRM/<span title="Référence Point De Livraison">PDL</span> : <span class="raw-value" id="live-data-prm"></span><br>
|
||||
Tarif : <span class="raw-value" id="live-data-OPTARIF"></span><br>
|
||||
<span class="raw-value" id="live-data-status"></span>
|
||||
</main>
|
||||
</div>
|
||||
<div class="widget">
|
||||
<header>Air</header>
|
||||
<main id="live-data-sensors">
|
||||
<div id="live-data-sensors-template" class="big-value" style="display: none;">
|
||||
<span class="name"></span>
|
||||
<span class="meta"><span class="live-data-sensor-hum"></span> % Hum.</span>
|
||||
<span class="int-value"></span>.<span class="dec-value"></span>
|
||||
<span class="unit">°C</span>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
<div class="widget">
|
||||
<header>Connexion</header>
|
||||
<main>
|
||||
<form method="get" action="/_logout" target="login_frame">
|
||||
<input type="submit" value="Se déconnecter"/>
|
||||
</form>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div id="right-widget-container">
|
||||
<div class="widget">
|
||||
<header>Widget 1</header>
|
||||
<main>Du contenu</main>
|
||||
</div>
|
||||
<div class="widget">
|
||||
<header>Widget 2</header>
|
||||
<main>Encore<br>du<br>contenu</main>
|
||||
</div>
|
||||
<div class="widget">
|
||||
<header>Widget 3</header>
|
||||
<main>Encore<br>du contenu</main>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
<div id="login-form" style="display: none;">
|
||||
<div class="widget">
|
||||
<header>Connexion</header>
|
||||
<main>
|
||||
<form method="post" action="/_login" onsubmit="setHTML('live-login-status', 'Connexion...');" target="login_frame">
|
||||
<input type="text" name="username" placeholder="Pseudo"/>
|
||||
<input type="password" name="password" placeholder="Mot de passe"/>
|
||||
<input type="submit" value="Connexion"/>
|
||||
<span class="raw-value" id="live-login-status"></span>
|
||||
</form>
|
||||
<iframe id="login_frame" name="login_frame" style="display: none;"></iframe>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
<script src="index.js"></script>
|
233
res/static/index.js
Normal file
233
res/static/index.js
Normal file
@@ -0,0 +1,233 @@
|
||||
|
||||
let subscriptionMonth = 10.60;
|
||||
let indexPrices = [0.1423, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
|
||||
|
||||
|
||||
let currentData = {};
|
||||
|
||||
function setHTML(id, html) {
|
||||
document.getElementById(id).innerHTML = html;
|
||||
}
|
||||
|
||||
function leftPadStr(value, pad, count) {
|
||||
value = value + '';
|
||||
while (value.length < count) {
|
||||
value = pad + value;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function onLiveDataReceived(data) {
|
||||
if (data.serialNumber != currentData.serialNumber) {
|
||||
setHTML("live-data-serial", data.serialNumber);
|
||||
}
|
||||
if (data.prm != currentData.prm) {
|
||||
setHTML("live-data-prm", data.prm);
|
||||
}
|
||||
if (data.subscribedOption != currentData.subscribedOption) {
|
||||
setHTML("live-data-OPTARIF", data.subscribedOption);
|
||||
}
|
||||
if (data.refPower != currentData.refPower) {
|
||||
setHTML("live-data-pref", data.refPower);
|
||||
}
|
||||
if (data.cutPower != currentData.cutPower) {
|
||||
setHTML("live-data-pcoup", data.cutPower);
|
||||
}
|
||||
if (data.maxPowerToday != currentData.maxPowerToday) {
|
||||
setHTML("live-data-pj", data.maxPowerToday);
|
||||
var d = new Date(data.maxPowerTimeToday);
|
||||
setHTML("live-data-pj-time", d.toLocaleTimeString());
|
||||
}
|
||||
if (data.maxPowerYesterday != currentData.maxPowerYesterday) {
|
||||
setHTML("live-data-pj-1", data.maxPowerYesterday);
|
||||
var d = new Date(data.maxPowerTimeYesterday);
|
||||
setHTML("live-data-pj-1-time", d.toLocaleTimeString());
|
||||
}
|
||||
if (data.appPower != currentData.appPower) {
|
||||
setHTML("live-data-papp", data.appPower);
|
||||
}
|
||||
if (data.rmsVoltage != currentData.rmsVoltage) {
|
||||
setHTML("live-data-urms", data.rmsVoltage);
|
||||
}
|
||||
|
||||
if (data.indexes != currentData.indexes) {
|
||||
var parent = document.getElementById('live-data-indexes');
|
||||
var template = document.getElementById('live-data-indexes-template');
|
||||
for (var i = 0; i < data.indexes.length; i++) {
|
||||
var value = data.indexes[i];
|
||||
var name = data.indexNames[i] || ('Index ' + (i + 1));
|
||||
if (value <= 0)
|
||||
continue;
|
||||
var el = document.getElementById('live-data-index-' + i);
|
||||
if (el == null) {
|
||||
el = template.cloneNode(true);
|
||||
el.id = 'live-data-index-' + i;
|
||||
el.style.display = "";
|
||||
if (data.currIndex == i)
|
||||
el.classList.add('index-current');
|
||||
el = parent.appendChild(el);
|
||||
var nameEl = el.querySelector('.name');
|
||||
nameEl.innerHTML = name;
|
||||
}
|
||||
var kWhEl = el.querySelector('.int-value');
|
||||
var whEl = el.querySelector('.dec-value');
|
||||
kWhEl.innerHTML = Math.floor(value / 1000);
|
||||
whEl.innerHTML = leftPadStr(value % 1000, '0', 3);
|
||||
}
|
||||
}
|
||||
|
||||
if (data.currIndex != currentData.currIndex) {
|
||||
var oldEl = document.getElementById('live-data-index-' + currentData.currIndex);
|
||||
if (oldEl != null) {
|
||||
oldEl.classList.remove('index-current');
|
||||
}
|
||||
var newEl = document.getElementById('live-data-index-' + data.currIndex);
|
||||
if (newEl != null) {
|
||||
newEl.classList.add('index-current');
|
||||
}
|
||||
}
|
||||
|
||||
var dayPriceSum = 0.0;
|
||||
|
||||
if (data.indexesMidnight != currentData.indexesMidnight) {
|
||||
var parentIndex = document.getElementById('live-data-indexes-day');
|
||||
var templateIndex = document.getElementById('live-data-indexes-day-template');
|
||||
var parentPrice = document.getElementById('live-data-price-day');
|
||||
var templatePrice = document.getElementById('live-data-price-day-template');
|
||||
for (var i = 0; i < data.indexesMidnight.length; i++) {
|
||||
var value = data.indexes[i] - data.indexesMidnight[i];
|
||||
var name = data.indexNames[i] || ('Index ' + (i + 1));
|
||||
if (value <= 0)
|
||||
continue;
|
||||
var el = document.getElementById('live-data-index-day-' + i);
|
||||
if (el == null) {
|
||||
el = templateIndex.cloneNode(true);
|
||||
el.id = 'live-data-index-day-' + i;
|
||||
el.style.display = "";
|
||||
el = parentIndex.appendChild(el);
|
||||
var nameEl = el.querySelector('.name');
|
||||
nameEl.innerHTML = name;
|
||||
}
|
||||
var kWhEl = el.querySelector('.int-value');
|
||||
var whEl = el.querySelector('.dec-value');
|
||||
kWhEl.innerHTML = Math.floor(value / 1000);
|
||||
whEl.innerHTML = leftPadStr(value % 1000, '0', 3);
|
||||
|
||||
el = document.getElementById('live-data-price-day-' + i);
|
||||
if (el == null) {
|
||||
el = templatePrice.cloneNode(true);
|
||||
el.id = 'live-data-price-day-' + i;
|
||||
el.style.display = "";
|
||||
el = parentPrice.appendChild(el);
|
||||
var nameEl = el.querySelector('.name');
|
||||
nameEl.innerHTML = name;
|
||||
}
|
||||
var price = value * (indexPrices[i] / 1000);
|
||||
dayPriceSum += price;
|
||||
var euroEl = el.querySelector('.int-value');
|
||||
var centsEl = el.querySelector('.dec-value');
|
||||
var perkWh = el.querySelector('.live-data-price-per-kwh');
|
||||
euroEl.innerHTML = Math.floor(price);
|
||||
centsEl.innerHTML = leftPadStr(Math.floor(price * 10000) % 10000, '0', 4);
|
||||
perkWh.innerHTML = indexPrices[i];
|
||||
}
|
||||
}
|
||||
|
||||
var dayPrice = subscriptionMonth / data.nbDayThisMonth;
|
||||
var currDayPrice = dayPrice * ((data.date - data.getDayStartTime) / 86400000);
|
||||
dayPriceSum += currDayPrice;
|
||||
setHTML('live-data-price-sub-int', Math.floor(currDayPrice));
|
||||
setHTML('live-data-price-sub-dec', leftPadStr(Math.floor(currDayPrice * 10000) % 10000, '0', 4));
|
||||
|
||||
setHTML('live-data-price-total-int', Math.floor(dayPriceSum));
|
||||
setHTML('live-data-price-total-dec', leftPadStr(Math.floor(dayPriceSum * 10000) % 10000, '0', 4));
|
||||
|
||||
setHTML('live-data-price-sub-per-month', subscriptionMonth);
|
||||
|
||||
|
||||
|
||||
if (data.sensorsData != currentData.sensorsData) {
|
||||
var parent = document.getElementById('live-data-sensors');
|
||||
var template = document.getElementById('live-data-sensors-template');
|
||||
for (var name in data.sensorsData) {
|
||||
var temp = data.sensorsData[name].temp;
|
||||
var hum = data.sensorsData[name].hum;
|
||||
var el = document.getElementById('live-data-sensors-' + name);
|
||||
if (el == null) {
|
||||
el = template.cloneNode(true);
|
||||
el.id = 'live-data-sensors-' + name;
|
||||
el.style.display = "";
|
||||
el = parent.appendChild(el);
|
||||
var nameEl = el.querySelector('.name');
|
||||
nameEl.innerHTML = name;
|
||||
}
|
||||
var intEl = el.querySelector('.int-value');
|
||||
var decEl = el.querySelector('.dec-value');
|
||||
var humEl = el.querySelector('.live-data-sensor-hum');
|
||||
intEl.innerHTML = Math.floor(temp);
|
||||
decEl.innerHTML = leftPadStr(Math.floor(temp * 10) % 10, '0', 1);
|
||||
humEl.innerHTML = Math.floor(hum);
|
||||
}
|
||||
}
|
||||
|
||||
currentData = data;
|
||||
}
|
||||
|
||||
function loginSuccess() {
|
||||
document.getElementById("logged-in-content").style.display = "";
|
||||
document.getElementById("login-form").style.display = "none";
|
||||
setHTML("live-login-status", "");
|
||||
}
|
||||
|
||||
function loginFail(message) {
|
||||
document.getElementById("logged-in-content").style.display = "none";
|
||||
document.getElementById("login-form").style.display = "";
|
||||
setHTML("live-login-status", message);
|
||||
}
|
||||
|
||||
function updateLiveData(afterLogin = false) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.onload = function() {
|
||||
if (xhr.readyState == 4) {
|
||||
if (xhr.status == 403) {
|
||||
loginFail(afterLogin ? "Mauvais identifiants" : "");
|
||||
return;
|
||||
}
|
||||
loginSuccess();
|
||||
if (xhr.status == 200) {
|
||||
setHTML("live-data-status", "");
|
||||
var jsonObj = JSON.parse(xhr.responseText);
|
||||
onLiveDataReceived(jsonObj.data);
|
||||
var delay = jsonObj.avgUpdateInterval - (jsonObj.now - jsonObj.lastUpdate) + 100;
|
||||
if (delay < 200) // dont spam if data source is too late than usual
|
||||
delay = 200;
|
||||
setTimeout(function() { updateLiveData(false); }, delay);
|
||||
}
|
||||
else {
|
||||
setHTML("live-data-status", "Erreur de connexion (backend offline)");
|
||||
setTimeout(function() { updateLiveData(false); }, 5000);
|
||||
}
|
||||
}
|
||||
}
|
||||
xhr.ontimeout = function() {
|
||||
setHTML("live-data-status", "Erreur de connexion (timeout)");
|
||||
setTimeout(function() { updateLiveData(false); }, 5000);
|
||||
}
|
||||
xhr.onerror = function() {
|
||||
setHTML("live-data-status", "Erreur de connexion");
|
||||
setTimeout(function() { updateLiveData(false); }, 5000);
|
||||
}
|
||||
xhr.timeout = 5000;
|
||||
xhr.open("GET", "/rest/currentData", true);
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
|
||||
|
||||
document.getElementById("login_frame").onload = function() {
|
||||
updateLiveData(true);
|
||||
}
|
||||
|
||||
|
||||
updateLiveData(false);
|
Reference in New Issue
Block a user