/***** Markise – Sonnen/Wetter-Logik (Shelly Plus 2PM, InfluxDB = wunderweather)
* Datum: 2025-10-06
*****/
/*** Ersatz für encodeURIComponent (da Shelly JS 1.71 das nicht kennt) ***/
function encodeURIComponent(str) {
let map = {' ':'%20','"':'%22','%':'%25','&':'%26','\'':'%27','(':'%28',')':'%29','+':'%2B',',':'%2C','/':'%2F',':':'%3A',';':'%3B','<':'%3C','=':'%3D','>':'%3E','?':'%3F','@':'%40','[':'%5B',']':'%5D'};
let out=""; for (let i=0;i<str.length;i++){let c=str[i]; out+=map[c]?map[c]:c;} return out;
}
/*** === Konfiguration === ***/
let CFG = {
IPCOVER_SELF: "192.168.178.131",
IPCOVER_BATH: "192.168.178.195",
INFLUX: {
url: "http://192.168.178.54:8086/query",
db: "wunderweather", // ✅ Korrigiert
meas: "weather",
fields: {
irradiance: "Sonnenstrahlung",
temperature: "Temperatur",
wind: "Windgeschwindigkeit",
humidity: "Luftfeuchtigkeit"
}
},
POLL_MS: 60000,
IRR_ON: 300,
IRR_OFF: 200,
T_MIN: 20.0,
W_MID: 8.0,
W_HIGH: 10.0,
HUMI_RAIN: 90.0,
STAB_SUN_SEC: 120,
STAB_TEMP_SEC: 120,
STAB_WMID_SEC: 120,
STAB_WHIGH_SEC: 120,
STAB_RAIN_SEC: 120,
STAB_NIGHT_SEC: 120,
POS_FULL_OUT: 100,
POS_HALF: 50,
POS_IN: 0,
CMD_COOLDOWN_MS: 30000
};
/*** === Helper === ***/
function nowMs() { return Date.now(); }
function log() { let s=""; for(let i=0;i<arguments.length;i++){s+=(i>0?" ":"")+arguments[i];} print("[Markise] "+s); }
/*** === Debouncer === ***/
function Debouncer(sec){this.stabMs=sec*1000;this.sinceTrue=null;this.update=function(v){if(v){if(this.sinceTrue===null)this.sinceTrue=nowMs();}else this.sinceTrue=null;};this.stableTrue=function(){return this.sinceTrue!==null&&(nowMs()-this.sinceTrue)>=this.stabMs;};}
let DB={sunOn:new Debouncer(CFG.STAB_SUN_SEC),sunOff:new Debouncer(CFG.STAB_SUN_SEC),tOk:new Debouncer(CFG.STAB_TEMP_SEC),wMid:new Debouncer(CFG.STAB_WMID_SEC),wHigh:new Debouncer(CFG.STAB_WHIGH_SEC),rain:new Debouncer(CFG.STAB_RAIN_SEC),night:new Debouncer(CFG.STAB_NIGHT_SEC)};
let state={lastCmdAt:0,lastTargetPos:null,coverPos:null,irr:null,temp:null,wind:null,humi:null,isDay:null};
/*** === Cover === ***/
function refreshSelfCover(){Shelly.call("Cover.GetStatus",{id:0},function(r,e,c){if(c===200&&r){state.coverPos=r.current_pos;}});}
function setCoverPos(target){let t=nowMs();if(t-state.lastCmdAt<CFG.CMD_COOLDOWN_MS)return;if(state.lastTargetPos!==null&&Math.abs(state.lastTargetPos-target)<2)return;state.lastCmdAt=t;state.lastTargetPos=target;log("→ Fahre Markise auf",target,"%");Shelly.call("Cover.GoToPosition",{id:0,pos:target});}
/*** === HTTP Wrapper === ***/
function httpGet(url,cb){Shelly.call("HTTP.GET",{url:url,timeout:8},function(r,e,m){if(e===0&&r&&r.code===200){cb(r);}else{log("❌ HTTP Fehler",e,m);cb(null);}});}
/*** === Bad-Rollo Tag/Nacht === ***/
function fetchBathDayNight(cb){let url="http://"+CFG.IPCOVER_BATH+"/rpc/Cover.GetStatus?id=0";log("HTTP →",url);httpGet(url,function(r){if(!r){log("❌ Keine Antwort vom Bad-Rollo");cb(false);return;}log("← Bad-Rollo:",r.body);try{let j=JSON.parse(r.body);let pos=j.current_pos;let isDay=(pos>=100);DB.night.update(!isDay);state.isDay=isDay;cb(true);}catch(e){log("❌ JSON Fehler Bad-Rollo",e);cb(false);}});}
/*** === Influx Abfrage === ***/
function buildInfluxQuery(){let f=CFG.INFLUX.fields,m=CFG.INFLUX.meas;return 'SELECT last("'+f.irradiance+'") AS irr, last("'+f.temperature+'") AS temp, last("'+f.wind+'") AS wind, last("'+f.humidity+'") AS humi FROM "'+m+'" WHERE time > now() - 10m';}
function fetchInflux(cb){let query=buildInfluxQuery();let url=CFG.INFLUX.url+"?db="+CFG.INFLUX.db+"&q="+encodeURIComponent(query);log("HTTP →",url);httpGet(url,function(r){if(!r){log("❌ Keine Antwort von Influx");cb(false);return;}log("← Influx:",r.body);try{let d=JSON.parse(r.body);let s=d.results[0].series[0];let cols=s.columns;let vals=s.values[0];let map={};for(let i=0;i<cols.length;i++)map[cols[i]]=vals[i];state.irr=map.irr;state.temp=map.temp;state.wind=map.wind;state.humi=map.humi;log("→ Werte:",state.irr,state.temp,state.wind,state.humi);cb(true);}catch(e){log("❌ JSON Fehler Influx",e);cb(false);}});}
/*** === Entscheidungslogik === ***/
function decideAndAct(){let sunOn=(state.irr!==null&&state.irr>=CFG.IRR_ON);let sunOff=(state.irr!==null&&state.irr<=CFG.IRR_OFF);DB.sunOn.update(sunOn);DB.sunOff.update(sunOff);DB.tOk.update(state.temp>=CFG.T_MIN);DB.wHigh.update(state.wind>=CFG.W_HIGH);DB.wMid.update(state.wind>=CFG.W_MID&&state.wind<CFG.W_HIGH);DB.rain.update(state.humi>=CFG.HUMI_RAIN);let night=DB.night.stableTrue();log("Check:",state.isDay,state.irr,state.temp,state.wind,state.humi);if(night){log("→ Nacht ⇒ 0%");setCoverPos(CFG.POS_IN);return;}if(DB.rain.stableTrue()){log("→ Regen ⇒ 0%");setCoverPos(CFG.POS_IN);return;}if(DB.wHigh.stableTrue()){log("→ Wind hoch ⇒ 0%");setCoverPos(CFG.POS_IN);return;}if(DB.wMid.stableTrue()){log("→ Wind mittel ⇒ 50%");setCoverPos(CFG.POS_HALF);return;}if(DB.sunOn.stableTrue()&&DB.tOk.stableTrue()&&state.isDay){log("→ Sonne & Temp OK ⇒ 100%");setCoverPos(CFG.POS_FULL_OUT);return;}if((DB.sunOff.stableTrue()||!DB.tOk.stableTrue())&&state.isDay){log("→ Sonne weg / kalt ⇒ 0%");setCoverPos(CFG.POS_IN);return;}log("→ Keine stabile Änderung – wartet…");}
/*** === Loop === ***/
function loop(){refreshSelfCover();fetchBathDayNight(function(){fetchInflux(function(){decideAndAct();});});}
log("Script gestartet – Poll =",CFG.POLL_MS,"ms");loop();Timer.set(CFG.POLL_MS,true,loop);
Alles anzeigen
Hier mal wieder was neues aus meiner Spielwiese ein markisen Skript für einen shelly 2pm gen 4 er fragt meine influx DB wetterstation ab und vergleicht die Werte ebenfalls holt er sich den Status einen Rollos für die Tag Nacht schaltung für das skript die Werte werden bei mir zuverlässig bgefragt ob das ganze funktioniert sehe ich in ein paar Tagen