Überarbeitung
Damit nicht unnötige Posts hier erscheinen, habe ich dieses überarbeitet.
Der Shelly stellt per Skript eine Webseite bereit, die in verschiedenen Modi zyklisch refresht werden kann - oder auch ohne Refresh.
Zusätzlich ist sie interaktiv. Der Anwender kann die Zieltemperatur um jeweils 0.5°C erhöhen oder absenken.
Vorteile
- Man kann mit dem Shelly auch über dessen Access Point Adresse 192.168.33.1 kommunizieren, wenn man bspw. das Smartphone mit dem Shelly AP gekoppelt hat.
- Es sind keine zusätzlichen Kommunikationspartner wie ein übergeordnetes System oder ein MQTT Broker erforderlich.
Screenshots der Webseite
Die folgenden Screenshots zeigen die Darstellungen in einem HTML 5 fähigen Web Browser. Sie erscheinen sowohl auf einem PC als auch auf einem Smartphone. Sogar ein relativ einfaches Smartphone aus dem Jahr 2013 kann mit installierten Firefox diese Seite darstellen und automatisch refreshen. Einzig die kleinen Links links und rechts der Zieltemperatur sind mit dem alten Smartphone nicht so leicht zu aktivieren. Mit meinem aktuellen Smartphone geht das leicht.
Jede Abbildung zeigt einen von vier konfigurierten Refresh Modi. Die aktivierbaren Links sind in gelb dargestellt.
Es sind vier Zeitangaben (Datum und Uhrzeit) enthalten, die alle lokal auf dem Shelly vorliegen sollten. Daran kann der Anwender erkennen, wie aktuell verschiedene Dinge sind. Derzeit habe ich den Fall "Shelly ist nach reboot offline" noch nicht getestet. Hierfür sind sicher Codeänderungen erforderlich - ein Punkt auf der todo Liste.
Die Zeitangabe oben, unterhalb des Gerätenamens gibt an, wann die Webseite vom Shelly geliefert wurde.
Unter "Zieltemperatur" liegt der Zeitpunkt der letzten Änderung.
Unter "Temperatur" und "Luftfeuchtigkeit" stehen die Zeitpunkte der letzten Wertregistrierung, also wann die Firmware des Shelly diese Werte per Event zur Verfügung stellte.
Der Inhalt kann nicht angezeigt werden, da Sie keine Berechtigung haben, diesen Inhalt zu sehen.
Der Inhalt kann nicht angezeigt werden, da Sie keine Berechtigung haben, diesen Inhalt zu sehen.
Der Inhalt kann nicht angezeigt werden, da Sie keine Berechtigung haben, diesen Inhalt zu sehen.
Der Inhalt kann nicht angezeigt werden, da Sie keine Berechtigung haben, diesen Inhalt zu sehen.
Code und HTTP Requests
Statt dieser Refresh-Zyklus-Werte können selbstverständlich auch andere genutzt werden. Sowohl die Namen als auch die Werte liegen in folgendem Datenfeld.
Mode = [ // e.g. four refresh modes, you may try one mode ;-P
{name:"ohne Ladezyklen", value:0}, // without refreshing
{name:"alle 60s", value:60}, // regular mode
{name:"per Taster einstellen", value:1}, // adjust mode
{name:"alle 10s", value:10} // one of possible multiple alternatives
]
Der Modus "per Taster einstellen" bezieht sich auf zwei Taster zum schrittweisen erhöhen bzw. absenken der Zieltemperatur. Diese Taster sind am AddOn angeschlossen, Tastendrücke werden per Skript verarbeitet. Da hierbei kein Display vorliegt, kann bspw. ein Smartphone mit der Shelly Skript-Website genutzt werden. Damit die Anzeige zügig reagiert, wird hier in 1s Abständen refresht.
Diese Struktur kann selbstverständlich editierbar im KVS abgelegt sein und von dort bei Skriptstart eingelesen werden.
Der URL hat die folgende Struktur.
http://<IP-Adresse des Shelly>/script/<Skript-Id>/data?m=<Modus Index>
bzw. ohne Wahl des Modus und Verwendung des default Index 0
http://<IP-Adresse des Shelly>/script/<Skript-Id>/data
Zum verändern der Zieltemperatur gibt es zwei Alternativen.
1. http://<IP-Adresse des Shelly>/script/<Skript-Id>/data?s=<neuer Wert>
2. http://<IP-Adresse des Shelly>/script/<Skript-Id>/data?d=<Schrittwert>
Der Request-Wert hinter data? kann alternativ drei verschiedene Parameter enthalten.
- m=<Refresh Modus als Index zum Datenfeld Mode (s.o.)>
m=1 lässt die Webseite alle 60s refreshen - s=<neuer Wert für die Zieltemperatur>
s=12 setzt die Zieltemperatur auf 12°C - d=<Änderung relativ zur vorliegenden Zieltemperatur, auch Vorzeichen behaftet>
d=-0.5 senkt die Zieltemperatur um 0.5°C
Der Codeabschnitt zu diesen Webseiten.
Nochmals vielen Dank für die gute Grundlage, die
@De kat zur Verfügung stellte.
// von eiche auf Anregung von De kat
// Shelly liefert Webseite mit Daten und Refreshing/Reloading an Hand einer
// erweiterbaren Liste (Datenfeld) mit verschiedenen Modi.
let Device = "Heizung Wintergarten",
Mode = [ // e.g. four refresh modes, you may try one mode ;-P
{name:"ohne Ladezyklen", value:0}, // without refreshing
{name:"alle 60s", value:60}, // regular mode
{name:"per Taster einstellen", value:1}, // adjust mode
{name:"alle 10s", value:10} // one of possible multiple alternatives
],
tTup = ' ∆ ', tTdown = ' ∇ ',
dataURL = '';
function d2(d) {
d = JSON.stringify(d);
return d.length<2 ? '0' + d : d;
}
function getTime(t) {
if(t===null) return;
let d;
if(t===undefined) d = new Date();
else d = new Date(t);
return d.getFullYear()+'-'+d2(JSON.parse(d.getMonth()) + 1)+'-'+d2(d.getDate())
+' '+d2(d.getHours())+':'+d2(d.getMinutes())+':'+d2(d.getSeconds());
}
let dataEP = HTTPServer.registerEndpoint,
localIP = Shelly.getComponentStatus('wifi').sta_ip,
scriptId = Shelly.getCurrentScriptId(),
htmlFirst = '<!DOCTYPE html>'
+'<html>'
+'<head>'
+'<meta name="viewport" content="width=device-width, initial-scale=1.0">',
htmlSecond = '<title>'+Device+'</title>'
+'</head>'
+'<style>'
+'body,table {background-color:#404040;color:white;font-family:sans-serif;text-align:center;font-size:0.9em}'
+'a {color:yellow;text-decoration:none;font-weight:bold}'
+'table {table-layout:auto;width:100%}'
+'table,th,td {border:1px solid white;border-collapse:collapse;padding:2px}'
+'td,th {text-align:center}'
+'.dev,.tt,.tmp,.hum {font-size:1.2em;font-weight:bold}'
+'.tt {color:white}'
+'.tmp {color:#ffd000}'
+'.hum {color:#00d0ff}'
+'.sm {font-size:0.6em}'
+'</style>'
+'<body>',
htmlLast = '</body></html>';
function data(request,response,url) {
//print('Endpoint Triggered!');
//print(url);
let q = request.query;
let mode = 0, par = [], tTchanged = false;
if(q.length>0) par = q.split('=');
if(par.length==2) {
if(!isNaN(par[1])) {
let val = JSON.parse(par[1]);
if(par[0]==='m') mode = Math.abs(val) % Mode.length;
else if(par[0]==='s' || par[0]==='d') {
u_set_tT(par[0]==='s' ? val : Status.tT+val);
tTchanged = true;
}
}
}
let next = (mode+1) % Mode.length;
let r = Mode[mode].value;
response.code = 200;
response.headers = [["Content-Type", "text/html"]];
response.body = htmlFirst
+(r>0 || tTchanged ? ('<meta http-equiv="refresh" content="'+(tTchanged ? '1; url='+url : r.toString())+'">') : '')
+htmlSecond
+'<p>Modus: "'+Mode[mode].name+'"</p>'
+'<p><a href="'+url+'?m='+next+'">Zum Modus "'+Mode[next].name+'"</a></p>'
+'<table>'
+'<tr><td class="dev" colspan="3">'+Device+'</td></tr>'
+'<tr><td colspan="3">' + getTime() + '</td></tr>'
+'<tr><td>Zieltemperatur</td><td>Temperatur</td><td>Luftfeuchtigkeit</td></tr>'
+'<tr><td class="sm">'+getTime(Status.tstT)+'</td>'
+'<td class="sm">'+getTime(Status.tsT)+'</td>'
+'<td class="sm">'+getTime(Status.tsH)+'</td></tr>'
+'<tr><td class="tt"><a href="'+url+'?d=-0.5">'+tTdown+'</a> '+Status.tT+' °C <a href="'+url+'?d=0.5">'+tTup+'</a></td>'
+'<td class="tmp">'+Status.tC+' °C</td>'
+'<td class="hum">'+Status.rh+' %</td></tr>'
+'</table>'
// +'<p><a href="'+url+'?d=0.5">'+tTup+'</a> <a href="'+url+'?d=-0.5">'+tTdown+'</a></p>'
+htmlLast;
response.send();
}
dataURL = 'http://'+localIP+dataEP('data',data);
dataEP('data',data,dataURL);
print(dataURL,'? m=mode | s=target | d=delta');
Alles anzeigen
Die Variable Status mit deren Komponenten tT, tC und rh liegen im gesamten Skript an anderer Stelle, dort, wo nicht ganz regelmäßig die gemessenen Werte abgelegt und in regelmäßigen Abständen (Schedule Job) von dort nehmend geliefert werden. Diese werden zusammen mit einem Zeitstempel übertragen und landen in einer Zeitreihendatenbank - aber das ist ein anderes Thema.
Die CSS-Angaben sind mir nicht sehr vertraut. Ich weiß halt, dass es so etwas gibt und die Struktur kenne ich auch, aber die genauen Bezeichnungen muss ich oft recherchieren und testen.
So, nun sollte das Projekt vorläufig komplett sein.