Script OpenWeather - Beliebige Wetterdaten in Systemvariablen schreiben

HMIP lokale Installation

Moderator: Co-Administratoren

Antworten
Benutzeravatar
Henke
Beiträge: 1151
Registriert: 27.06.2022, 20:51
System: CCU
Hat sich bedankt: 123 Mal
Danksagung erhalten: 214 Mal

Script OpenWeather - Beliebige Wetterdaten in Systemvariablen schreiben

Beitrag von Henke » 20.11.2023, 23:35

Ein weiteres recyceltes JavaScript aus meiner NodeRed Sammlung umgewandelt in ein CCU-Script.

Vorraussetzungen damit das Script läuft: nix
Dieses Script blockiert keine Rega Aufrufe und ist von der Konfiguration her möglichst simpel aufgebaut.
Systemvariablen werden z.B. automatisch angelegt.
Als Zeitraum zum Aufrufen verwende ich 20 Minuten.

Die Daten von OpenWeather werden aufbereitet, so das die Windgeschwindigkeiten in km/h erfolgen und eine Durchschnittsberechnung/Min/Max der nächsten 24 und 48 Stunden erfolgt. Diese Werte brauche ich für die Heizungssteuerung.

Nachdem der API-Key (Link: https://openweathermap.org/price - FREE "Get API Key") eingetragen ist, sollte es mit den mMn interessanten Tageswerten auch schon laufen.

Code: Alles auswählen

! HM_OpenWeather - Wetterdaten
! Copyleft Michael Henke
! Version: 0.8
!
! https://openweathermap.org/price - FREE
string appid = "Hier den API Key eintragen!"; 
! Werte eintragen, falls nicht vorhanden
var lat = system.Latitude(); 
var lon = system.Longitude();
! DP anpassen, 

string para = '{
    "DP": [
        // [ Datenpunkt, Einheit, Name der Systemvariable/topic, Typ/Beschreibung ]
        // Um Datenpunkte zu aktivieren die // entfernen

        // Erweiterte Datenpunkte
        ["current.description", "", "Kurztext", "Info"],
        // ["Temp24h.schnitt", "°C", "Next24h.Schnitt", "Info"],
        // ["Temp24h.max", "°C", "Next24h.Max", "Info"],
        // ["Temp24h.min", "°C", "Next24h.Min", "Info"],
        // ["Temp48h.schnitt", "°C", "Next48h.Schnitt", "Info"],
        // ["Temp48h.max", "°C", "Next48h.Max", "Info"],
        // ["Temp48h.min", "°C", "Next48h.Min", "Info"],

        ["current.ACTUAL_TEMPERATURE", "°C", "ACTUAL_TEMPERATURE", "DP"],
        ["current.HUMIDITY", "%", "HUMIDITY", "DP"],
        ["current.wind_cardinal", "", "Windrichtung", "Info"],

        // DP siehe https://openweathermap.org/api/one-call-3
        // Beispiel Zugriff auf tägliche Werte
        // ["daily.0.moon_phase", "", "MondPhase", "Info"],

        ["current.wind_speed", "km/h", "wind_speed", "DP"],
        ["current.wind_gust", "km/h", "wind_gust", "DP"],
        ["current.wind_deg", "°", "wind_deg", "DP"],
        ["current.pressure", "hPa", "pressure", "DP"],
        ["current.rain", "mm", "rain", "DP"],
        // ["current.feels_like", "°C", "feels_like", "Info"],
        // ["current.dew_point", "°C", "Taupunkt", "Info"],
        // ["current.uvi", "", "UV_index_öäü", "Info"],
        // ["current.clouds", "%", "Wolken", "Info"],
        // ["current.visibility", "m", "Sicht", "Info"],

		// NICHT ändern, muss als letzter bleiben
        ["","","",""]
    ],
    "CCU": { "hostname": "localhost", "auth": "" },
    "SysVar": { "Out": true, "Name": "OpenWeather" },
    "pt": {
        "hour12": false
    },
    // Werte für den Api Aufruf
    "api": {
        "lat": "${lat}",
        "lon": "${lon}",
        "appid": "${appid}",

        "units": "metric",
        "lang": "de",
        "exclude": "minutely"
    }
}';

! Ab hier, Finger weg :-)
para = para.Replace( "${lat}", lat).Replace( "${lon}", lon).Replace( "${appid}", appid);
!WriteLine ( para );

string js = '
const ScriptName = "HM_OpenWeather";
// HM_OpenWeather - Wetterdaten
// Copyleft Michael Henke
// Version: 0.8

var para = {};

try {
    if (node) { }
    if (msg.payload)
        para = msg.payload;
}
catch (e) {
    var node = null;
    var msg = {};
    ErrOut("script");
}

if (!node) {
    if (process.argv[2]) {
    let zeilen = process.argv[2].split ("\\n");
    let jp = "";
            zeilen.forEach(function (element) {
let z =             element.trim();
if ( z.substring ( 0, 2 ) != "//" )
jp += z;
            });

        para = JSON.parse(jp);
         console.log(para);
    }
}

let url = "https://api.openweathermap.org/data/2.5/onecall";
{
    let addUrl = "";
    for (const key of Object.keys(para.api)) {
        if (addUrl)
            addUrl += "&";
        addUrl += key + "=" + para.api[key];
    }
    if (addUrl)
        url += "?" + addUrl;
}

httpGet(url, "OW")
    .then((res) => {
        let json = JSON.parse(res.data);
        {
            let akt = json.current;

            if (typeof akt.temp != "number")
                return;

            if (akt.wind_deg == 360)
                akt.wind_deg = 0;

            {
                let rain = 0;
                if (akt.rain && akt.rain["1h"])
                    rain = akt.rain["1h"];
                akt.rain = rain;
            }
            if (!akt.wind_gust) {
                akt.wind_gust = 0; // msg.payload.hourly[0].wind_gust;
            }
            else {
                // node.warn("Böen: " + pay.wind_gust);
            }

            function schnitt(hourly, anz) {
                if (anz < hourly.lenght)
                    return;

                let sch = {
                    max: Number.NEGATIVE_INFINITY,
                    min: Number.POSITIVE_INFINITY
                };

                // node.send({ payload: [] });
                let sum = 0;

                for (let index = 0; index < anz; index++) {
                    const ele = hourly[index];
                    if (ele.temp < sch.min)
                        sch.min = ele.temp;
                    if (ele.temp > sch.max)
                        sch.max = ele.temp;
                    sum += ele.temp;

                }
                sch.schnitt = Math.round(sum / anz * 10) / 10;
                return sch;
            }
            json["Temp24h"] = schnitt(json.hourly, 24);
            json["Temp48h"] = schnitt(json.hourly, 48);

            akt.description = akt.weather[0].description;
            akt.ACTUAL_TEMPERATURE = Math.round(akt.temp * 10) / 10;
            akt.HUMIDITY = Math.round(akt.humidity * 100) / 100;
            akt.wind_speed_mph = akt.wind_speed;
            akt.wind_speed = Math.round(akt.wind_speed * 3.6 * 100) / 100;
            akt.wind_gust_mph = akt.wind_gust;
            akt.wind_gust = Math.round(akt.wind_gust * 3.6 * 100) / 100;
            akt.wind_cardinal = degreesToCardinal(json.current.wind_deg);

            para.DP.forEach(function (element) {
            if ( !element[0] )
            return;
                let subAr = element[0].split(".");
                let pos = json;
                subAr.forEach(function (ss) {
                    pos = pos[ss];
                });
                sendDp(element, pos);
            });
        }
        if (node)
      {
        dashboard(json);
            node.status({ fill: "green", shape: "dot", text: new Date().toLocaleString("de") });
      }
    })
    .catch((error) => {
        ErrOut(error);
    });

/**
* @param {any[]} ele
* @param {any} pay
*/
function sendDp(ele, pay) {
    if (node)
        node.send([null, null, { ts: new Date().getTime(), datapoint: ele[2], payload: pay, unit: ele[1], para: ele[3] }]);
    // else
    {
        setSysvar(para.SysVar.Name + "." + ele[2], pay, ele[1], ele[3]);
        // console.log("datapoint: " + ele[2] + "Wert: " + pay + " " + ele[1] + " - " + ele[3]);
    }
}

/**
* @param {number} deg
*/
function degreesToCardinal(deg) {
    if (deg > 11.25 && deg <= 33.75) { return "NNE"; }
    else if (deg > 33.75 && deg < 56.25) { return "NE"; }
    else if (deg > 56.25 && deg < 78.75) { return "ENE"; }
    else if (deg > 78.75 && deg < 101.25) { return "E"; }
    else if (deg > 101.25 && deg < 123.75) { return "ESE"; }
    else if (deg > 123.75 && deg < 146.25) { return "SE"; }
    else if (deg > 146.25 && deg < 168.75) { return "SSE"; }
    else if (deg > 168.75 && deg < 191.25) { return "S"; }
    else if (deg > 191.25 && deg < 213.75) { return "SSW"; }
    else if (deg > 213.75 && deg < 236.25) { return "SW"; }
    else if (deg > 236.25 && deg < 258.75) { return "WSW"; }
    else if (deg > 258.75 && deg < 281.25) { return "W"; }
    else if (deg > 281.25 && deg < 303.75) { return "WNW"; }
    else if (deg > 303.75 && deg < 326.25) { return "NW"; }
    else if (deg > 326.25 && deg < 348.75) { return "NNW"; }
    else { return "N"; }
}

function dashboard(json) {
    let mg = msg;
    mg.payload = json;

    let pt_hour12 = para.pt.hour12;
    let units = para.api.units;
    if (units === undefined) { units = "imperial"; }
    let pt_lang = para.api.lang;

    function timeConvert(UNIX_timestamp) {
        let dateObject = new Date(UNIX_timestamp * 1000);
        if (pt_hour12) {  // 12 hour time format
            return dateObject.toLocaleString("en", { timeZone: json.timezone, hour12: true, hour: "numeric", minute: "2-digit" }).toLowerCase();
        } else {  // 24 hour time format
            return dateObject.toLocaleString("en", { timeZone: json.timezone, hour12: false, hour: "numeric", minute: "2-digit" });
        }
    }

    if (units == "imperial") {
        mg.payload.temp = json.current.temp.toFixed() + " °F";
        mg.payload.wind_speed_Txt = json.current.wind_speed_mph.toFixed() + " mph";
    }
    else  // metric units
    {
        mg.payload.temp = json.current.temp.toFixed(1) + " °C";
        mg.payload.wind_speed_Txt = (json.current.wind_speed_mph * 3.6).toFixed();
        if (json.current.wind_gust)
            mg.payload.wind_speed_Txt += "/" + (json.current.wind_gust_mph * 3.6).toFixed();
        mg.payload.wind_speed_Txt += " km/h";
    }
    mg.payload.wind_speed_Txt += " " + json.current.wind_cardinal;

    mg.payload.sunrise = timeConvert(json.current.sunrise);
    mg.payload.sunset = timeConvert(json.current.sunset);

    function formatTemp(high, low) {
        let temp = "";
        if (units == "imperial") {
            if (low) {
                temp = parseFloat(high).toFixed() + "/" + parseFloat(low).toFixed()
            }
            else {
                temp = parseFloat(high).toFixed() + "°F"
            }
        }
        else { // metric
            if (low) {
                temp = parseFloat(high).toFixed() + "/" + parseFloat(low).toFixed()
            }
            else {
                temp = parseFloat(high).toFixed() + "°C"
            }
        }
        return temp;
    }

    function dayName(unixTime) {
        let dateObject = new Date(unixTime * 1000);
        return dateObject.toLocaleString(pt_lang, { timeZone: json.timezone, weekday: "short" });
    }

    function timeConvertIcons(UNIX_timestamp) {
        let dateObject = new Date(UNIX_timestamp * 1000);
        if (pt_hour12) {  // 12 hour time format
            return dateObject.toLocaleString("en", { timeZone: json.timezone, hour12: true, hour: "numeric" }).toLowerCase();
        } else {  // 24 hour time format
            return dateObject.toLocaleString("en", { timeZone: json.timezone, hour12: false, hour: "numeric" }) /*+ ":00"*/;
        }
    }

    // prepare forecast data for CSS based ui widget
    mg.hourly = { rowtext: { data01: {}, data02: {} }, rowicons: { data01: {} }, rowInfoIcon: { data01: {} } };
    let count = 1;
    json.hourly.forEach(function (element) {
        mg.hourly.rowicons.data01["cell" + count] = element.weather[0].icon;
        mg.hourly.rowtext.data01["cell" + count] = timeConvertIcons(element.dt);
        mg.hourly.rowtext.data02["cell" + count] = formatTemp(element.temp);
        mg.hourly.rowInfoIcon.data01["cell" + count] = info2(element);
        if (count > 10)
            return;
        count++;
    });

    mg.daily = { rowtext: { data01: {}, data02: {} }, rowicons: { data01: {} }, rowInfoIcon: { data01: {} } };
    count = 1;
    json.daily.forEach(function (element) {
        mg.daily.rowicons.data01["cell" + count] = element.weather[0].icon;
        mg.daily.rowtext.data01["cell" + count] = dayName(element.dt);
        mg.daily.rowtext.data02["cell" + count] = formatTemp(element.temp.max, element.temp.min);
        mg.daily.rowInfoIcon.data01["cell" + count] = info2(element);
        count++;
    });

    let iconString = "wi-owm-" + json.current.weather[0].icon + " wi-4x";
    let icon = {
        ui_control: {
            icon: iconString
        }
    };
    if (node)
        node.send([mg, icon]);

    function info2(element) {
        let rain = 0;
        if (element.rain) {
            if (element.rain["1h"])
                rain = element.rain["1h"];
            else
                rain = element.rain;
        }

        let txt = "";
        let icon = "";
        if (rain >= 1) {
            txt = Math.round(rain);
            icon = "fa fa-tint";
        }

        if (element.wind_gust && (element.wind_gust >= 60 / 3.6)) {
            txt = Math.round(element.wind_gust * 3.6);
            icon = "wi wi-darksky-wind";
        }
        return [icon, txt];
    }
}


function ErrOut(txt = "") {
    if (node) {
        node.error(txt);
        node.status({ fill: "red", shape: "dot", text: "Fehler: " + txt });
    }
    else {
        console.error(txt);
    }
}

function httpGet(url, typ = "") {
    if (!url)
        return null;

    return new Promise((resolve, reject) => {
        const httpUse = (url.indexOf("https://") === 0) ? require("https") : require("http");

        // debugger;

        httpUse.get(url, (res) => {
            //	console.log("statusCode:", res.statusCode);
            if (res.statusCode != 200) {
                reject("statusCode: " + res.statusCode);
                return;
            }
            // console.log("headers:", res.headers);
            let data = "";

            res.on("data", (chunk) => {
                // console.log(chunk);
                data += chunk;
            });
            res.on("end", () => {
                // console.log(data);
                resolve({ typ: typ, data: data });
            });

        }).on("error", (e) => {
            reject(e);
        });
    });
}

// Systemvariable CCU anlegen und Wert schreiben
function setSysvar(name, wert, unit = "", beschreibung = "") {
//    beschreibung += "öäs";
    if (!para.SysVar.Out)
        return;
    // debugger;
    let type;
    let addMinMax = false;
    switch (typeof wert) {
        case "number":
            type = { t: "ivtFloat", s: "istGeneric" };
            addMinMax = true;
            break;
        case "string":
        default:
            type = { t: "ivtString", s: "istChar8859" };
//            wert += "äää";
            break;
    }
    let script = `
var ww = "${wert}";
if ( ${type.t} == ivtFloat ) { ww = ww.ToFloat(); }
string sName = "${name}";
object svObj = dom.GetObject (ID_SYSTEM_VARIABLES).Get (sName);
if (!svObj) {
 svObj = dom.CreateObject(OT_VARDP,sName);
 svObj.DPInfo("${beschreibung}");
 svObj.ValueUnit("${unit}");
 dom.GetObject (ID_SYSTEM_VARIABLES).Add(svObj);
 svObj.ValueType(${type.t});
 svObj.ValueSubType(${type.s});
if ( ${addMinMax} )
{
 svObj.ValueMin(0);
 svObj.ValueMax(0);
}
 svObj.State (ww);
 dom.RTUpdate(0);
}
else
{ svObj.State(ww);}
`;
    runScript(script);
}

function runScript(script, callback = null) {
    const http = require("http");
    let postData = Buffer.from(script, "latin1");

    const options = {
        hostname: para.CCU.hostname,
        port: 8181,
        path: "/rega.exe",
        method: "POST",
        encoding: "iso-8859-1",
        // encoding: "utf-8",
        headers: {
            "Content-Type": "application/x-www-form-urlencoded",
            "Content-Length": postData.length,
        },
    };
    if (para.CCU.auth)
        options.auth = para.CCU.auth;

    const req = http.request(options, (res) => {
        // console.log(`STATUS: ${res.statusCode}`);
        // console.log(`HEADERS: ${JSON.stringify(res.headers)}`);
        // res.setEncoding("utf-8");
        res.setEncoding("binary");
        let data = "";

        res.on("data", (chunk) => {
            // debugger;
            // console.log(chunk);
            data += chunk;
        });
        res.on("end", () => {
            if (callback)
                callback(data);
        });
    });

    req.on("error", (e) => {
        ErrOut("CCU problem with request: " + e);
    });

    // Write data to request body
    req.write(postData);
    req.end();
}
';

string stdout; string stderr;
system.Exec( "node -e '" # js # "' ' ' '" # para.ToUTF8() # "' &",&stdout,&stderr);
js = "";
para = "";
if (stderr) { Write(stderr); quit; }
WriteLine( stdout);
RedMatic aktualisieren

dumm, dümmer, Horst
Die Größe der Signatur ist linear proportional zur Anzahl der tolerierten Beleidigungen.

Antworten

Zurück zu „HomeMatic IP mit CCU“