Archiv des Autors: Stephan

Über Stephan

Als Kind der 90er Jahre wuchs ich in einer Übergangs-Ära von analog zu digital auf. Vorteil: man kennt analog und stieg direkt in's Digitale ein. Auch heute begeistert mich die digitale Welt noch - wenn nicht sogar mehr denn je. Heutzutage sind die kleinen Nullen und Einsen gar nicht mehr wegzudenken ...

Shelly Firmware Update per Skript

Shelly bringt in regelmäßigen Abständen neue Updates für die Module heraus. Über die Smartphone App lassen sich die Updates natürlich auf alle Shelly aufspielen – jedoch innerhalb von ioBroker gibt es nur die Möglichkeit, das Update via der Shelly Instanz zu installieren. Hier behelfen wir uns eines Skriptes, welches manuell den Update-Prozess auf den Shelly auslösen kann. Es wird auch nur das Update ausgelöst, wenn tatsächlich eines zur Verfügung steht.

Skript

Nun erstellen wir in ioBroker ein neues Skript im common Bereich. Es bekommt den Namen “Shelly-Firmware”.

/*
 * @copyright 2020 Stephan Kreyenborg <stephan@kreyenborg.koeln>
 *
 * @author 2020 Stephan Kreyenborg <stephan@kreyenborg.koeln>
 *
 * Dieses Skript dient zur freien Verwendung in ioBroker zur Verbrauchserfassung der Shelly Geräte.
 * Jegliche Verantwortung liegt beim Benutzer. Das Skript wurde unter Berücksichtigung der bestmöglichen Nutzung
 * und Performance entwickelt.
 * Der Entwickler versichert, das keine böswilligen Systemeingriffe im originalen Skript vorhanden sind.
 *
 * Sollte das Skript wider Erwarten nicht korrekt funktionieren, so hast Du jederzeit die Möglichkeit, Dich auf
 * https://www.kreyenborg.koeln
 * für Unterstützung zu melden. Jedes Skript besitzt seine eigene Kommentarseite, auf der,
 * nach zeitlicher Möglichkeit des Autors, Hilfe angeboten wird. Ein Anrecht hierauf besteht nicht!
 * 
 * Ansprüche gegenüber Dritten bestehen nicht. 
 * 
 * Skript Name:		Shelly-Firmware
 * Skript Version:	1.0
 * Erstell-Datum:	07. Juni 2021
 * 
 */

// Datenpunkt der Shelly (Standard: shelly.0)
var shelly_dp = "shelly.0";

// Datenpunkte der Shelly (!!! Bitte nicht ändern !!!)
var shellyDps = $('state[id=' + shelly_dp + '.*.firmware]');

let anzahl_shelly = 0;
let anzahl_shelly_update = 0;

// Starte Reboot
shellyDps.each(function (id, i) {
    if (getState(id).val == true) {
        setState(id + "update", true);
        anzahl_shelly_update++;
    }
    anzahl_shelly++;
});
stopScript("");
log("Shelly-Firmware: Der Update-Befehl wurde an " + anzahl_shelly_update + " von " + anzahl_shelly + " Shelly gesendet!");

Shelly Reboot per Skript

Normalerweise sollten die Shelly eigentlich wie gut geschmiertes Getriebe durchlaufen. Manchmal kann aber auch ein Reboot helfen, einen Shelly wieder zur besseren Performance zu verhelfen. Aber, jeden Shelly einzeln zu rebooten, ist ab einer bestimmten Anzahl lästig, oder? Somit stelle ich Dir heute ein Skript vor, welches dies übernimmt.

Skript

Nun erstellen wir in ioBroker ein neues Skript im common Bereich. Es bekommt den Namen “Shelly-Reboot”.

/*
 * @copyright 2020 Stephan Kreyenborg <stephan@kreyenborg.koeln>
 *
 * @author 2020 Stephan Kreyenborg <stephan@kreyenborg.koeln>
 *
 * Dieses Skript dient zur freien Verwendung in ioBroker zur Verbrauchserfassung der Shelly Geräte.
 * Jegliche Verantwortung liegt beim Benutzer. Das Skript wurde unter Berücksichtigung der bestmöglichen Nutzung
 * und Performance entwickelt.
 * Der Entwickler versichert, das keine böswilligen Systemeingriffe im originalen Skript vorhanden sind.
 *
 * Sollte das Skript wider Erwarten nicht korrekt funktionieren, so hast Du jederzeit die Möglichkeit, Dich auf
 * https://www.kreyenborg.koeln
 * für Unterstützung zu melden. Jedes Skript besitzt seine eigene Kommentarseite, auf der,
 * nach zeitlicher Möglichkeit des Autors, Hilfe angeboten wird. Ein Anrecht hierauf besteht nicht!
 * 
 * Ansprüche gegenüber Dritten bestehen nicht. 
 * 
 * Skript Name:		Shelly-Reboot
 * Skript Version:	1.0
 * Erstell-Datum:	07. Juni 2021
 * 
 */

// Datenpunkt der Shelly (Standard: shelly.0)
var shelly_dp = "shelly.0";

// Datenpunkte der Shelly (!!! Bitte nicht ändern !!!)
const shellyDps = $('state[id=' + shelly_dp + '.*.reboot]');

let anzahl_shelly = 0;

// Starte Reboot
shellyDps.each(function (id, i) {
    setState(id, true);
    anzahl_shelly++;
});
stopScript("");
log("Shelly-Reboot: Der Reboot-Befehl wurde an " + anzahl_shelly + " Shelly gesendet!");

Nach dem Aufruf werden alle gefundenen Shelly neugestartet und das Skript selbst beendet sich wieder.

Batterie Status der ZigBee Geräte abfragen und per WhatsApp zuschicken lassen

Dieses Skript dient der Abfrage des Batteriestandes Deiner ZigBee Geräte. Es läuft in Standardeinstellung nachts um 1 Uhr und stellt alle wesentlichen Informationen wie Gerätename, Raum und Batteriestand (in Volt und Prozent) bereit und schickt sie Dir per WhatsApp zu.

Vorbereitung

Damit dieses Skript zuverlässig funktioniert, benötigst Du mein Skript für den WhatsAppBot. Nebenbei erwähnt kannst Du das Skript auch verwenden, um die Mitteilungen per E-Mail zu versenden. Dazu sind ein paar Anpassungen notwendig.

Skript

Zuerst erstellst du ein neues Javascript im Bereich common.

/*
 * @copyright 2021 Stephan Kreyenborg <stephan@kreyenborg.koeln>
 *
 * @author 2021 Stephan Kreyenborg <stephan@kreyenborg.koeln>
 *
 * Dieses Skript dient zur freien Verwendung in ioBroker zur Verbrauchserfassung der Shelly Geräte.
 * Jegliche Verantwortung liegt beim Benutzer. Das Skript wurde unter Berücksichtigung der bestmöglichen Nutzung
 * und Performance entwickelt.
 * Der Entwickler versichert, das keine böswilligen Systemeingriffe im originalen Skript vorhanden sind.
 *
 * Sollte das Skript wider Erwarten nicht korrekt funktionieren, so hast Du jederzeit die Möglichkeit, Dich auf
 * https://www.kreyenborg.koeln
 * für Unterstützung zu melden. Jedes Skript besitzt seine eigene Kommentarseite, auf der,
 * nach zeitlicher Möglichkeit des Autors, Hilfe angeboten wird. Ein Anrecht hierauf besteht nicht!
 * 
 * Ansprüche gegenüber Dritten bestehen nicht. 
 * 
 * Skript Name:		ZigBee Batterie Status
 * Skript Version:	1.1
 * Erstell-Datum:	25. April 2021
 * 
 */

// Datenpunkt der ZigBee Geräte (Standard: zigbee.0)
var zigbee_dp = "zigbee.0";

// Datenpunkte der ZigBee (!!! Bitte nicht ändern !!!)
const zigbeeDpsBattery = $('state[id=' + zigbee_dp + '.*.battery]');

function zigbeeBatterieStatus() {
    // Anzahl gefundene ZigBee Geräte
    var gefundene_geraete = new Array();

    // Namen der ZigBee Geräte
    var geraete_namen = new Array();

    // Batteriestand der ZigBee Geräte
    var batterie_stand = new Array();

    // Batteriestand in Volt der ZigBee Geräte
    var batterie_volt = new Array();

    // Räume der ZigBee Geräte - falls angegeben
    var raeume = new Array();

    // Batterie Status des ZigBee Gerät
    var status = new Array();
    log("ZigBee Batterie Status: Batterie Status wird abgefragt!");
    zigbeeDpsBattery.each(function (id) {
        let tmp_id = id.split(".");
        var tmp_id_g = tmp_id[0] + "." + tmp_id[1] + "." + tmp_id[2];
        gefundene_geraete.push(tmp_id_g);
        // Speichere Namen ab
        var name = getObject(tmp_id_g).common.name;
        if (name != "") {
            geraete_namen.push(name);
        } else {
            geraete_namen.push("");
        }

        // Hole Batteriestand
        var tmp_batterie_stand = getState(tmp_id_g + ".battery").val;
        if (tmp_batterie_stand != null) {
            batterie_stand.push(tmp_batterie_stand + "%");

            // Status der Batterie
            if (tmp_batterie_stand > 75) {
                status.push("voll");
            }
            if (tmp_batterie_stand <= 50) {
                status.push("");
            }
            if (tmp_batterie_stand <= 25) {
                status.push("kritisch");
            }
            if (tmp_batterie_stand < 25) {
                status.push("fast leer");
            }
        } else {
            batterie_stand.push("-%");
            status.push("keine Information");
        }

        // Hole Batterie %
        var tmp_batterie_volt = getState(tmp_id_g + ".voltage").val;
        if (tmp_batterie_volt != null) {
            batterie_volt.push(tmp_batterie_volt + "V");
        } else {
            batterie_volt.push("-V");
        }

        // Hole Raum
        var tmp_raum = getObject(tmp_id_g, 'rooms').enumNames[0];
        if (tmp_raum != undefined) {
            if (tmp_raum.de != undefined) {
                raeume.push(tmp_raum.de);
            } else {
                raeume.push(tmp_raum);
            }
        } else {
            raeume.push("kein Raum");
        }
    });
    // Generiere String
    var text = '*ZigBee Batteriestatus von ' + gefundene_geraete.length;
    if (gefundene_geraete.length > 1) {
        text += ' Geräten*<br><br>';
    } else {
        text += ' Gerät*<br><br>';
    }
    for (let i = 0; i < gefundene_geraete.length; i++) {
        text += "*" + geraete_namen[i] + ":* " + status[i] + "<br>Raum: " + raeume[i] + " | Batterie: " + batterie_stand[i] + " | Volt: " + batterie_volt[i] + "<br><br>";
    }

    log("ZigBee Batterie Status: Batterie Status abgefragt!");
    // Sende Nachricht
    WhatsAppBot(0, text);
}
zigbeeBatterieStatus();

// Nachts um 1 Uhr den Batteriestand abfragen
schedule('0 1 * * *', zigbeeBatterieStatus);

Skript anpassen

In Zeile 117 musst du den Namen des WhatsAppBot Skriptes anpassen, wenn dieses von Dir anders benannt wurde, als in der Anleitung angegeben. Außerdem wird der jeweilige Empfänger benötigt – aber dies ist ja schon in der Anleitung zum WhatsAppBot erwähnt 😉

Solltest Du das Skript verwenden wollen, um die Nachrichten per E-Mail zu erhalten, so musst Du in Zeile 117 das “WhatsAppBot” gegen “sendTo” austauschen und ggf. den Text etwas anpassen, da dieser für die Verwendung in WhatsApp formatiert ist (* für Fettschrift).

Status Mitteilung per WhatsApp empfangen

Die Statusmitteilung erscheint wie folgt auf Deinem Mobiltelefon.

ZigBee Batterie Status in WhatsApp

Sonoff/Tasmota Verbrauchs-Historie für Tag, Woche, Monat, Jahr

Dieses Skript ist eine Abwandlung des Skriptes, welches bereits für die Shelly Adapter zur Verfügung steht. Hier werden die Tasmota und Sonoff Geräte unterstützt.

Übersicht der Funktionen

Folgende Verbrauchswerte stehen in der Historie für jeden der Sonoff Geräte zur Verfügung:

FunktionBeschreibung
Verbrauch heuteabgefragter Verbrauch heute
Verbrauch gesternabgefragter Verbrauch gestern
Verbrauch diese Wocheabgefragter Verbrauch für die gesamte Woche
Verbrauch letzte Wocheabgefragter Verbrauch für die gesamte letzte Woche
Verbrauch dieser Monatabgefragter Verbrauch für den gesamten Monat
Verbrauch letzter Monatabgefragter Verbrauch für den gesamten letzten Monat
Verbrauch dieses Jahrabgefragter Verbrauch für das gesamte Jahr
Verbrauch letztes Jahrabgefragter Verbrauch für das gesamte letzte Jahr
Verbrauch Gesamtabgefragter Verbrauch gesamt
NameZugewiesener Name des Sonoff zum einfachen Wiederfinden
Übersicht über die einzelnen Datenpunkte

Screenshot der verfügbaren Variablen

Screenshot der verfügbaren Variablen
Screenshot der verfügbaren Variablen

Installation des Sonoff Adapter

Installation des Sonoff Adapter

Nach kurzer Zeit stehen die Datenpunkte aller gefundenen Sonoff Geräte unter sonoff.0 zur Verfügung.

Skript

/*
 * @copyright 2020 Stephan Kreyenborg <stephan@kreyenborg.koeln>
 *
 * @author 2020 Stephan Kreyenborg <stephan@kreyenborg.koeln>
 *
 * Dieses Skript dient zur freien Verwendung in ioBroker zur Verbrauchserfassung der Tasmota Geräte.
 * Jegliche Verantwortung liegt beim Benutzer. Das Skript wurde unter Berücksichtigung der bestmöglichen Nutzung
 * und Performance entwickelt.
 * Der Entwickler versichert, das keine böswilligen Systemeingriffe im originalen Skript vorhanden sind.
 *
 * Sollte das Skript wider Erwarten nicht korrekt funktionieren, so hast Du jederzeit die Möglichkeit, Dich auf
 * https://www.kreyenborg.koeln
 * für Unterstützung zu melden. Jedes Skript besitzt seine eigene Kommentarseite, auf der,
 * nach zeitlicher Möglichkeit des Autors, Hilfe angeboten wird. Ein Anrecht hierauf besteht nicht!
 * 
 * Ansprüche gegenüber Dritten bestehen nicht. 
 * 
 * Skript Name:		Tasmota-Verbrauch
 * Skript Version:	1.1
 * Erstell-Datum:	19. März 2021
 * 
 */

// Datenpunkte neu erstellen
var ueberschreiben = false;

// Hauptdatenpunkt unterhalb javascript
var datenpunkt = "TasmotaVerbrauch.";

// Verbrauchs Objekte der einzelnen Tasmota
const objekt = ["gesamt", "dieses_jahr", "letztes_jahr", "letzter_monat", "dieser_monat", "letzte_woche",
    "diese_woche", "gestern", "heute", "alter_wert", "aktueller_wert"];

// Beschreibung der Objekte
const beschreibung = ["Gesamter Vebrauch des Tasmota", "Verbrauch aktuelles Jahr", "Verbrauch letztes Jahr",
    "Verbrauch letzten Monat", "Verbrauch aktueller Monat", "Verbrauch letzte Woche", "Verbrauch diese Woche",
    "Verbrauch gestern", "Verbrauch heute", "Messwert alt", "Messwert neu"];

// Datenpunkt der Tasmota Geräte (Standard: sonoff.0)
var tasmota_dp = "sonoff.0";

// Datenpunkte der Tasmota (!!! Bitte nicht ändern !!!)
const tasmotaDps = $('state[id=' + tasmota_dp + '.*.ENERGY_Total]');

// Datenpunkte der Tasmota Namen (!!! Bitte nicht ändern !!!)
const tasmotaDpsName = $('state[id=' + tasmota_dp + '.*.DeviceName]');

// Tasmota Verbrauch aktualisieren - nachts um 00:00 Uhr
function tasmota_vebrauch_tag() {
    // Nochmals das Tagesupdate durchlaufen, damit die restlichen Werte gelesen werden
    tasmota_verbrauch_update();

    // Datumsvariable
    var heute = new Date();

    // Heute zu Gestern verschieben. Täglich um 00:00 Uhr
    verschiebe_verbrauch_objekt("heute", "gestern");
    log ("Tasmota Verbrauch: Werte für gestern und heute aktualisiert!");

    // aktuelle Woche in letzte Woche verschieben. Am Montag um 00:00 Uhr
    if (heute.getDay() === 1) {
        verschiebe_verbrauch_objekt("diese_woche", "letzte_woche");
        log ("Tasmota Verbrauch: Werte für diese und letzte Woche aktualisiert!");
    }

    // aktueller Monat in letzten Monat verschieben. Am 1. des Monats um 00:00 Uhr
    if (heute.getDate() === 1) {
        verschiebe_verbrauch_objekt("dieser_monat", "letzter_monat");
        log ("Tasmota Verbrauch: Werte für diesen und letzten Monat aktualisiert!");
    }

    // aktuelles Jahr in letztes Jahr verschieben. Am 1. des Monats am 1. Monat um 00:00 Uhr
    if (heute.getDate() === 1 && heute.getMonth() === 0) {
        verschiebe_verbrauch_objekt("dieses_jahr", "letztes_jahr");
        log ("Tasmota Verbrauch: Werte für dieses und letztes Jahr aktualisiert!");
    }
}

// Tagesverbrauch alle 15 Min von der Original Variable des Tasmota in eigene Variable kopieren
function tasmota_verbrauch_update() {
    var anzahl_updates = 0;
    var anzahl_reboots = 0;
    var anzahl_gleich = 0;
    tasmotaDps.each(function (id, i) {
        var tasmota_verbrauch = getState(id).val;
        // Einige Tasmota haben keine Verbrauchswerte (noch nicht)
        if (tasmota_verbrauch != null) {
            // Hole aktuellen Wert, um zu kontrollieren, ob ein Reboot stattgefunden hat
            var aktueller_wert = getState(tasmota_DP(id) + "aktueller_wert").val;
            var alter_wert = 0;
            // Prüfe alten und neuen Wert
            if (tasmota_verbrauch > aktueller_wert) {
                // Verbrauchswert ist größer als alter Wert -> es wurde kein Reboot durchgeführt
                setState(tasmota_DP(id) + "alter_wert", aktueller_wert,true);
                alter_wert = aktueller_wert;
                anzahl_updates++;
            }
            if (aktueller_wert > tasmota_verbrauch) {
                // Verbrauchswert ist kleiner als alter Wert -> es wurde ein Reboot durchgeführt
                setState(tasmota_DP(id) + "alter_wert", 0,true);
                alter_wert = tasmota_verbrauch;
                anzahl_reboots++;
            }
            if (tasmota_verbrauch == aktueller_wert) {
                // Verbrauchswert ist gleich wie alter Wert -> kein Update notwendig
                alter_wert = aktueller_wert;
                anzahl_gleich++;
            }

            setState(tasmota_DP(id) + "aktueller_wert", tasmota_verbrauch);
            // Alter und neuer Wert -> aktuelle Differenz

            var verbrauch = parseFloat(tasmota_verbrauch) - alter_wert;
            // Tagesverbrauch aktualisieren
            aktualisiere_vebrauch_objekt(id, "heute", verbrauch);

            // Wochenverbrauch aktualisieren
            aktualisiere_vebrauch_objekt(id, "diese_woche", verbrauch);

            // Monatsverbrauch aktualisieren
            aktualisiere_vebrauch_objekt(id, "dieser_monat", verbrauch);

            // Jahresverbrauch aktualisieren
            aktualisiere_vebrauch_objekt(id, "dieses_jahr", verbrauch);

            // Gesamten Vebrauch aktualisieren
            aktualisiere_vebrauch_objekt(id, "gesamt", verbrauch);
        }
    });
    aktualisiere_namen();
    log("Tasmota Verbrauch: Verbrauchswerte aktualisiert: " + anzahl_updates + " | Reboots korrigiert: " + anzahl_reboots + " | Unveränderte Werte: " + anzahl_gleich);
}

// aktualisiert das jeweilige Verbrauchs-Objekt und addiert den Verbrauch dazu
function aktualisiere_vebrauch_objekt(dp, objekt, wert) {
    var verbrauch = parseFloat(getState(tasmota_DP(dp) + objekt).val) + parseFloat(wert);
    verbrauch = parseFloat(verbrauch.toFixed(3));
    setState(tasmota_DP(dp) + objekt, verbrauch,true);
}

// Verschiebt das jeweilige Verbrauchs-Objekt und nullt den Ursprung (Tag, Woche, Monat, Jahr)
function verschiebe_verbrauch_objekt(von, nach) {
    $('state[id=*.' + datenpunkt + '*.' + von + ']').each(function (id, i) {
        // Temporärer Gruppen-Datenpunkt
        var tmp_dp = id.slice(0, -(von.length));
        var verbrauch = getState(id).val;
        if (verbrauch != null) {
            setState(tmp_dp + nach, verbrauch,true);
        }
        // Setze heute zurück
        setState(id, 0,true);
    });
}

// Funktion um die aktuellen Namen der Tasmota abzuholen
function aktualisiere_namen() {
    tasmotaDpsName.each(function (id, i) {
        setState(tasmota_DP_Name(id),String(getState(id).val),true);
    });
}

// Erstelle die benötigten Datenpunkte
function datenpunkte_erstellen() {
    // Anzahl der gefundenen Tasmota
    var anzahl = tasmotaDps.length;

    tasmotaDps.each(function (id, j) {
        var initial_wert = 0;
        for (var i = 0; i < objekt.length; i++) {
            // Startwerte werden nur bei alter_wert und aktueller_wert eingetragen
            if (i > 8) {
                initial_wert = getState(id).val;
            }
            createState(tasmota_DP(id) + objekt[i], initial_wert, ueberschreiben, {
                name: beschreibung[i],
                desc: beschreibung[i],
                type: "number",
                role: "value.power",
                unit: "kWh"
            });
        }
    });
    
    // Alle Datenpunkte erstellt. Frage ersten Verbrauch ab!
    log("Tasmota Verbrauch: Datenpunkte erstellt! Erster Verbrauch steht nach 1 Minute zur Verfügung! Anzahl gefundener Tasmota Datenpunkte: " + anzahl);
    setTimeout(tasmota_verbrauch_update, 20000);
    
    // Datenpunkte für die Namen der Tasmota erstellen
    tasmotaDpsName.each(function(id, j) {
        createState(tasmota_DP_Name(id), "", ueberschreiben, {
            name: "Name des Tasmota",
            desc: "Name des Tasmota",
            type: "string",
            role: "value",
            unit: ""
        });
    });
}

function tasmota_DP(dp) {
    dp = dp.split(".");
    dp = datenpunkt + dp[2] + ".";
    return dp;
}

function tasmota_DP_Name(dp) {
    dp = dp.split(".");
    dp = datenpunkt + dp[2] + "." + dp[3];
    return dp;
}

function tasmota_verbrauch_erster_start() {
    log("Tasmota Verbrauch: Erster Start des Skriptes! Datenpunkte werden erstellt!");
    // Datenpunkte werden erstellt
    datenpunkte_erstellen();
}

// Erster Start und Initialisierung
tasmota_verbrauch_erster_start();

// Alle 15 Minuten das Skript für den Tagesverbrauch ausführen
schedule('*/15 * * * *', tasmota_verbrauch_update);

// Nachts um 24 Uhr werden die Werte in andere Variablen gespeichert, um den Verlauf zu erstellen
schedule('0 0 * * *', tasmota_vebrauch_tag);

Speedtest für ioBroker

Für ioBroker gibt es aktuell 2 Speedtest Adapter, die bei vielen funktionieren – jedoch bei vielen eben auch nicht. Das ist besonders der Fall, wenn man einen Vodafone Cable Max 1000 (ehemals Unitymedia) Tarif hat. Ich habe noch nicht herausgefunden, wieso diese Speedtests nicht richtig laufen – jedoch habe ich mir selbst Abhilfe geschaffen, um hier auch selbst vernünftige Werte zu erhalten.

Vorbereitung

Damit der Speedtest richtig funktioniert, muss das Paket von Ookla (Speedtest.net) installiert werden. Dazu loggen wir uns auf dem Pi ein und führen folgende Befehle aus. Solltest Du bereits einen Speedtest installiert haben, der bspw. mit “speedtest-cli” aufgerufen wird, so ist dieser vorher zu entfernen.

Speedtest-CLI entfernen:

sudo apt-get remove speedtest-cli

Ookla Speedtest installieren

sudo apt-get install gnupg1 apt-transport-https dirmngr
export INSTALL_KEY=379CE192D401AB61
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys $INSTALL_KEY
echo "deb https://ookla.bintray.com/debian generic main" | sudo tee  /etc/apt/sources.list.d/speedtest.list
sudo apt-get update
sudo apt-get install speedtest

Datenpunkte

Das Skript füllt die eigenen Datenpunkte mit allen verfügbaren Rückgabewerten, die dieser Test zur Verfügung stellt. Des Weiteren sind die Datenpunkte strukturiert aufgebaut. Das heißt, der Datenpunkt Ergebnisse enthält alle Daten eines Test, wohingegen der Datenpunkt Test (mit Unterpunkten: Server und Daten) Informationen zu dem eigentlichen Test enthält. Auch gibt es einen Datenpunkt Server, welcher die Informationen zur Gegenstelle vorhält.

DatenpunktBeschreibung
JSON_OutputAusgabe der Konsole
PingPing in ms
JitterJitter in ms
Download_MBitDownload Geschwindigkeit in MBit/s
Upload_MBitUpload Geschwindigkeit in MBit/s
Download_MBDownload Geschwindigkeit in MB/s
Upload_MBUpload Geschwindigkeit in MB/s
OriginalDownloadDownload Geschwindigkeit in Byte/s
OriginalUploadUpload Geschwindigkeit in Byte/s
ISPInternet Service Provider
IPexterne IP
URLAdresse der Ergebnisse
IDID der Ergebnisse
ServerIDID des getesteten Servers
ServerIPIP des getesteten Servers
NameAnbieter des getesteten Servers
StadtStadt des getesteten Servers
LandLand des getesteten Servers
AdresseURL des getesteten Servers
DownloadDownload Daten in MB
UploadUpload Daten in MB
OriginalDownloadDownload Daten in Byte
OriginalUploadUpload Daten in Byte
DauerDownloadDauer des Download Test in Sekunden
DauerUploadDauer des Upload Test in Sekunden
Verfügbare Datenpunkte des Skripts

Skript

Nun erstellen wir in ioBroker ein neues Skript im common Bereich. Es bekommt den Namen “Speedtest”.

Der Aufruf erfolgt alle 30 Minuten und kann in der letzten Zeile angepasst werden. Auch lässt sich zur besseren Diagnose der eigenen Leitung ein fester Server in Zeile 32 eintragen. Eine Liste mit Servern in Deiner Umgebung gibt es hier:
https://www.speedtest.net/speedtest-servers.php

Soll das Skript immer den besten Server nehmen und automatisch entscheiden, so lasse diesen bei 0.

/*
 * @copyright 2020 Stephan Kreyenborg <stephan@kreyenborg.koeln>
 *
 * @author 2020 Stephan Kreyenborg <stephan@kreyenborg.koeln>
 *
 * Dieses Skript dient zur freien Verwendung in ioBroker zur Verbrauchserfassung der Shelly Geräte.
 * Jegliche Verantwortung liegt beim Benutzer. Das Skript wurde unter Berücksichtigung der bestmöglichen Nutzung
 * und Performance entwickelt.
 * Der Entwickler versichert, das keine böswilligen Systemeingriffe im originalen Skript vorhanden sind.
 *
 * Sollte das Skript wider Erwarten nicht korrekt funktionieren, so hast Du jederzeit die Möglichkeit, Dich auf
 * https://www.kreyenborg.koeln
 * für Unterstützung zu melden. Jedes Skript besitzt seine eigene Kommentarseite, auf der,
 * nach zeitlicher Möglichkeit des Autors, Hilfe angeboten wird. Ein Anrecht hierauf besteht nicht!
 * 
 * Ansprüche gegenüber Dritten bestehen nicht. 
 * 
 * Skript Name:		Speedtest
 * Skript Version:	1.1
 * Erstell-Datum:	11. März 2021
 * 
 */

// Datenpunkte neu erstellen
var ueberschreiben = false;

// Hauptdatenpunkt unterhalb javascript
var datenpunkt = "Speedtest.";

// Favorisierter Server
// Liste: https://www.speedtest.net/speedtest-servers-static.php
var fav_server = 0;

// Speedtest Objekte
var objekt = ["JSON_Output",
    "Ergebnisse.Ping",
    "Ergebnisse.Jitter",
    "Ergebnisse.Download_MBit",
    "Ergebnisse.Upload_MBit",
    "Ergebnisse.Download_MB",
    "Ergebnisse.Upload_MB",
    "Ergebnisse.OriginalDownload",
    "Ergebnisse.OriginalUpload",
    "ISP",
    "IP",
    "Ergebnisse.URL",
    "Ergebnisse.ID",
    "Test.Server.ServerID",
    "Test.Server.ServerIP",
    "Test.Server.Name",
    "Test.Server.Stadt",
    "Test.Server.Land",
    "Test.Server.Adresse",
    "Test.Daten.Download",
    "Test.Daten.Upload",
    "Test.Daten.OriginalDownload",
    "Test.Daten.OriginalUpload",
    "Test.Daten.DauerDownload",
    "Test.Daten.DauerUpload"
];

// Beschreibung der Objekte
var beschreibung = ["JSON Ausgabe der Konsole",
    "Ping in ms",
    "Jitter in ms",
    "Download Geschwindigkeit in MBit/s",
    "Upload Geschwindigkeit in MBit/s",
    "Download Geschwindigkeit in MB/s",
    "Upload Geschwindigkeit in MB/s",
    "Download Geschwindigkeit in Byte/s",
    "Upload Geschwindigkeit in Byte/s",
    "Internet Service Provider",
    "externe IP",
    "Adresse der Ergebnisse",
    "ID der Ergebnisse",
    "ID des getesteten Servers",
    "IP des getesteten Servers",
    "Anbieter des getesteten Servers",
    "Stadt des getesteten Servers",
    "Land des getesteten Servers",
    "URL des getesteten Servers",
    "Download Daten in MB",
    "Upload Daten in MB",
    "Download Daten in Byte",
    "Upload Daten in Byte",
    "Dauer des Download Test",
    "Dauer des Upload Test"
];

// Einheiten der Objekte
var einheiten = ["",
    "ms",
    "ms",
    "MBit/s",
    "MBit/s",
    "MB/s",
    "MB/s",
    "Byte/s",
    "Byte/s",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "MB",
    "MB",
    "Byte",
    "Byte",
    "s",
    "s"
];

// Typen der Objekte
var typen = ["string",
    "number",
    "number",
    "number",
    "number",
    "number",
    "number",
    "number",
    "number",
    "string",
    "string",
    "string",
    "string",
    "number",
    "string",
    "string",
    "string",
    "string",
    "string",
    "number",
    "number",
    "number",
    "number",
    "number",
    "number"
];

// Rollen der Objekte
var rolle = ["json",
    "value",
    "value",
    "value",
    "value",
    "value",
    "value",
    "value",
    "value",
    "text",
    "text",
    "text",
    "text",
    "value",
    "text",
    "text",
    "text",
    "text",
    "text",
    "value",
    "value",
    "value",
    "value",
    "value",
    "value"
];

// Schreibe Werte des JSON String in ein Array
function generiere_array(json_array) {
    var j = JSON.parse(json_array);
    var array_werte = [json_array,
        j.ping.latency,
        j.ping.jitter,
        parseFloat((j.download.bandwidth / 125000).toFixed(2)),
        parseFloat((j.upload.bandwidth / 125000).toFixed(2)),
        parseFloat((j.download.bandwidth / (1024 * 1024)).toFixed(2)),
        parseFloat((j.upload.bandwidth / (1024 * 1024)).toFixed(2)),
        j.download.bandwidth,
        j.upload.bandwidth,
        j.isp,
        j.interface.externalIp,
        j.result.url,
        j.result.id,
        j.server.id,
        j.server.ip,
        j.server.name,
        j.server.location,
        j.server.country,
        j.server.host,
        parseFloat((j.download.bytes / (1024 * 1024)).toFixed(2)),
        parseFloat((j.upload.bytes / (1024 * 1024)).toFixed(2)),
        j.download.bytes,
        j.upload.bytes,
        parseFloat((j.download.elapsed / 1000).toFixed(2)),
        parseFloat((j.upload.elapsed / 1000).toFixed(2)),
    ];
    return array_werte;
}

function speedtest() {
    // temporäre Variable für das Array
    var tmp_json;
    // Kommando für den Speedtest
    var kommando = "speedtest -f json --accept-license --accept-gdpr";
    if (fav_server > 0) {
        kommando = kommando + " -s " + fav_server;
        log("Speedtest mit Server " + fav_server + " gestartet! Der Test dauert zwischen 10 - 20 Sekunden!");
    } else {
        log("Speedtest gestartet! Der Test dauert zwischen 10 - 20 Sekunden!");
    }
    exec(kommando,
        function (error, stdout) {
            if (error) {
                log('Speedtest konnte nicht ausgeführt werden! ' + error, 'error');
                return;
            } else {
                tmp_json = generiere_array(stdout);
                aktualisiere_datenpunkt(tmp_json);
                log('Speedtest durchgeführt. Ergebisse: Download: ' + tmp_json[5] + ' MB/s | Upload: ' + tmp_json[6] + ' MB/s | Ping: ' + tmp_json[1] + 'ms');
            }
        });
}

function aktualisiere_datenpunkt(werte) {
    for (let i = 0; i < objekt.length; i++) {
        setState(datenpunkt + objekt[i], werte[i], true);
    }
}

// Erstelle die benötigten Datenpunkte
function datenpunkte_erstellen() {
    for (var i = 0; i < objekt.length; i++) {
        createState(datenpunkt + objekt[i], "", ueberschreiben, {
            name: beschreibung[i],
            desc: beschreibung[i],
            type: typen[i],
            role: rolle[i],
            unit: einheiten[i]
        });
    }

    // Alle Datenpunkte erstellt. Führe ersten Speedtest aus!
    log('Speedtest: Datenpunkte erstellt! Erster Speedtest wird in 30 Sekunden ausgeführt!');
    setTimeout(speedtest, 30000);
}

function speedtest_erster_start() {
    log("Speedtest: Erster Start des Skriptes! Datenpunkte werden erstellt!");
    // Datenpunkte werden erstellt
    datenpunkte_erstellen();
}

// Erster Start und Initialisierung
speedtest_erster_start();

// Alle 30 Minuten einen Speedtest ausführen
schedule('*/30 * * * *', speedtest);

Shelly Schalter mit Xiaomi Aqara Bewegungsmelder in ioBroker schalten

Wer einen Xiaomi Aqara sein Eigen nennt, kann in Verbindung mit dem ioBroker auch andere Geräte schalten. Ich habe bei mir so eine Schaltung mit einem Aqara und einem Shelly gekoppelt. Wie das geht, zeige ich Dir in diesem Beitrag.

Sobald der Aqara Sensor über den ZigBee Adapter eingebunden ist, erscheint er unterhalb des Datenpunkt zigbee.0 . Wie dies funktioniert, habe ich in diesem Beitrag beschrieben.

Damit Du nun Deinen Shelly per Bewegungsmelder schalten kannst und dieser automatisch ausschaltet, musst Du in deinem Shelly Datenpunkt den AutoTimerOff auf die entsprechenden Sekunden einstellen.

Shelly Timer

Ich habe bei mir 300 Sekunden – also 5 Minuten – gewählt. Diese Einstellung kannst natürlich an deine Bedürfnisse anpassen.

Im Weiteren Schritt erstellst Du dir nun ein Skript im common Bereich.

on({ id: "zigbee.0.00158d000659f68c.occupancy", change: "any" }, function (obj) {
    if (getState("zigbee.0.00158d0006XXXXXX.illuminance").val < 35) {
        // Helligkeit bei unter 35 Lux, Licht einschalten
        setState("shelly.0.SHSW-PM#XXXXXX#1.Relay0.Switch", true);
    }
    // Wenn Lampe an, dann Lampe erneut an, dadurch Timer restart
    if (getState("shelly.0.SHSW-PM#XXXXXX#1.Relay0.Switch").val == true) {
        setState("shelly.0.SHSW-PM#XXXXXX#1.Relay0.Switch", true);
    }
});

Hier ist es notwendig, deine Datenpunkte in den markierten Zeilen entsprechend anzupassen. Ich habe die Schaltung so eingestellt, das der Sensor unterhalb von 35 Lux den Shelly einschaltet. Sollte während dieser Zeit erneut eine Bewegung erkannt werden, so wird der Shelly erneut mit einem Schaltbefehl versorgt, sodass der integrierte AutTimerOff erneut gestartet wird.

System Updates für den Raspberry PI über VIS installieren

Normalerweise nutzt man die VIS Oberfläche um den Status von Geräten oder Aktoren darzustellen oder mit ihnen zu interagieren.
Warum aber sollte man sie nicht auch zur Interaktion von Befehlen nutzen?
Ich stelle Dir hier die Möglichkeit vor, die System Updates, welche für den Raspberry Pi vorliegen, mit einem Klick zu installieren und dies auch in einem Statusfenster zu verfolgen.

Skript

Zuerst erstellst Du ein neues Javascript im Bereich common.
Dieses Skript bitte nicht starten.

/*
 * @copyright 2020 Stephan Kreyenborg <stephan@kreyenborg.koeln>
 *
 * @author 2020 Stephan Kreyenborg <stephan@kreyenborg.koeln>
 *
 * Dieses Skript dient zur freien Verwendung in ioBroker zur Verbrauchserfassung der Shelly Geräte.
 * Jegliche Verantwortung liegt beim Benutzer. Das Skript wurde unter Berücksichtigung der bestmöglichen Nutzung
 * und Performance entwickelt.
 * Der Entwickler versichert, das keine böswilligen Systemeingriffe im originalen Skript vorhanden sind.
 *
 * Sollte das Skript wider Erwarten nicht korrekt funktionieren, so hast Du jederzeit die Möglichkeit, Dich auf
 * https://www.kreyenborg.koeln
 * für Unterstützung zu melden. Jedes Skript besitzt seine eigene Kommentarseite, auf der,
 * nach zeitlicher Möglichkeit des Autors, Hilfe angeboten wird. Ein Anrecht hierauf besteht nicht!
 * 
 * Ansprüche gegenüber Dritten bestehen nicht. 
 * 
 * Skript Name:		APT Update installieren
 * Skript Version:	1.2
 * Erstell-Datum:	02. April 2021
 * 
 */

// Kommandos
var command_update = "sudo apt update";
var command_anzahl_updates = "apt list --upgradable 2>/dev/null | wc -l | awk '{print $S1-1}'";
var command_upgrade = "sudo apt-get -y upgrade";
var anzahl_updates = 0;

// Datenpunkt anlegen
var DataPoint = 'Systeminfos.ioBroker.raspi_updates_ausgabe';
createState(DataPoint, "", true, {
    name: 'Raspi Updates Ausgabe',
    desc: 'Ausgabe Systemupdates für den Raspi',
    type: 'string',
    unit: '',
    role: 'value'
},
    function () {
        installiere_updates();
    }
);

function installiere_updates() {
    // Paket-Update
    exec(command_update);
    update_datenpunkt("Prüfe auf neue Updates ...");

    // Anzahl neuer Updates
    exec(command_anzahl_updates,
        function (error, stdout, stderr) {
            if (stdout == '0') {
                anzahl_updates = 0;
            } else {
                anzahl_updates = parseInt(stdout);
            }
            if (anzahl_updates > 0) {
                log("Neue Updates: " + anzahl_updates + "<br>Neue Updates werden installiert. Bitte warten ...");
                update_datenpunkt("Neue Updates: " + anzahl_updates + "<br>Neue Updates werden installiert. Bitte warten ...");
                exec(command_upgrade, function (error, stdout, stderr) {
                    if (stdout.includes("autoremove")) {
                        update_datenpunkt("Alle Updates wurden installiert!<br>Es wurden nicht benötigte Pakete gefunden!<br><br>Fenster kann geschlossen werden!");
                    } else {
                        update_datenpunkt("Alle Updates wurden installiert!<br><br>Fenster kann geschlossen werden!");
                    }

                    // Beende Skript
                    setState("javascript.0.Systeminfos.ioBroker.raspi_updates", "0");
                    setState("javascript.0.scriptEnabled.common.APT_Update_installieren", false);
                });
            } else {
                update_datenpunkt("Keine neuen Updates zum Installieren!<br><br>Fenster kann geschlossen werden!");
                // Beende Skript
                setState("javascript.0.Systeminfos.ioBroker.raspi_updates", "0");
                setState("javascript.0.scriptEnabled.common.APT_Update_installieren", false);
            }
        });
}

function update_datenpunkt(text) {
    var tmp_text = getState(DataPoint).val;
    var neuer_text = tmp_text + "<br>" + text;
    setState(DataPoint, neuer_text, true);
}

Nachdem Du das Skript erstellt hast, musst Du die Zeile 68 im Skript auf den Namen anpassen, da dies für die Beendigung notwendig ist. Schaue dazu bitte in den Datenpunkt:

javascript.0.scriptEnabled.common

und kopiere Dir den Namen des Datenpunkt in die Zwischenablage.

Skript Datenpunkt
Skript Datenpunkt

Diesen Datenpunkt fügst Du nun in Zeile 68 ein. Standardmäßig ist dieser auf den Namen de Skripts gesetzt.

setState("javascript.0.scriptEnabled.common.APT_Update_installieren", false);

Dieses Skript dient im weiteren Verlauf zur Darstellung in VIS und beinhaltet das Prüfen auf Updates und das Installieren von Updates.

Integration in VIS

Vorbereitung

Zuerst erstellst Du eine eigene View mit dem Namen “APT_Update”. In diese View ziehst Du ein HTML Widget. Dieses HTML Widget bekommt als Text den Datenpunkt aus Zeile 34. Solltest Du diesen Datenpunkt angepasst haben, so musst Du ihn in VIS auch entsprechend anpassen. Die geschweiften Klammern vorne und hinten müssen zwingend angegeben werden, da sonst nur der Text und nicht der Datenpunkt ausgegeben wird.

{javascript.0.Systeminfos.ioBroker.raspi_updates_ausgabe}
Neue View APT_Update mit HTML Widget
Neue View APT_Update mit HTML Widget

Eigentlicher Aufruf

Damit Du nun diese View aufrufen kannst, benötigst Du in deiner “Haupt”-View ein “jqui – container – Button – view” Widget. Dieses ziehst Du an die Position, von wo aus Du das Update gerne starten wollen würdest.

Folgende Werte habe ich in meinem Widget gesetzt:

Widget Einstellungen

Als Objekt ID gibst Du den Datenpunkt aus Zeile 68 des Skriptes an und als Viewname verwendest Du den Namen der eigentlichen View. In meinem Beispielt ist dies “APT_Update”. Der Knopftext ist frei wählbar und kann heissen, wie Du gerne möchtest 😉

Wenn Du nun deine View aufrufst, kannst mit einem Klick auf den Button den Update Prozess in der VIS Oberfläche auslösen und in der Statusausgabe des Widget verfolgen, was passiert.

APT Update Ausgabe
APT Update Ausgabe

WhatsApp Nachrichten mit ioBroker versenden

In ioBroker gibt es so gut wie für jede Funktion einen eigenen Adapter. Diese verbrauchen jedoch Arbeitsspeicher, der auf einem Raspberry Pi leider nicht erweitert werden kann. Viele Dinge lassen sich mit Hilfe eines Skriptes ähnlich oder genauso gut implementieren, wie es der Adapter selbst kann. Wer also schon verschiedene Skripte laufen hat, der benötigt nicht unbedingt den WhatsApp Adapter, sondern kann sich mit meinem Skript genauso gut helfen – wenn nicht sogar noch besser, da mein Skript die Versendung an viele Empfänger bietet 😉

Update: Es werden nun auch Zeilenumbrüche “<br> unterstützt!

Hinweis

Aktuell ist die Versendung auf 25 Nachrichten pro 4 Stunden begrenzt!

Vorbereitung

Zuerst musst Du den WhatsApp Service für deine eigene Telefonnummer freischalten. Dazu nimmst Du den sogenannten WhatsApp Bot in dein Telefonbuch auf.

Nummer: +34 644 59 71 67

Nachdem Du dies erledigt hast, schickst du via WhatsApp folgenden Text an den neuen Kontakt:

I allow callmebot to send me messages

Da sich der Service aktuell noch in einer Art Testphase befindet, kommen die Nachrichten leicht verzögert (max. 1 Minute) an. Nach etwa dieser Zeit bekommst Du eine Bestätigung, das Deine Nummer für den Service aktiviert wurde. Hierin enthalten ist der sogenannte API-Key. Dieser ist zwingend für die Versendung der Nachrichten notwendig.

Skript

Zuerst erstellst Du ein neues Javascript im Bereich global.

/*
 * @copyright 2021 Stephan Kreyenborg <stephan@kreyenborg.koeln>
 *
 * @author 2021 Stephan Kreyenborg <stephan@kreyenborg.koeln>
 *
 * Dieses Skript dient zur freien Verwendung in ioBroker zur Verbrauchserfassung der Shelly Geräte.
 * Jegliche Verantwortung liegt beim Benutzer. Das Skript wurde unter Berücksichtigung der bestmöglichen Nutzung
 * und Performance entwickelt.
 * Der Entwickler versichert, das keine böswilligen Systemeingriffe im originalen Skript vorhanden sind.
 *
 * Sollte das Skript wider Erwarten nicht korrekt funktionieren, so hast Du jederzeit die Möglichkeit, Dich auf
 * https://www.kreyenborg.koeln
 * für Unterstützung zu melden. Jedes Skript besitzt seine eigene Kommentarseite, auf der,
 * nach zeitlicher Möglichkeit des Autors, Hilfe angeboten wird. Ein Anrecht hierauf besteht nicht!
 * 
 * Ansprüche gegenüber Dritten bestehen nicht. 
 * 
 * Skript Name:		WhatsAppBot
 * Skript Version:	1.1
 * Erstell-Datum:	19. April 2021
 * 
 */
function WhatsAppBot(empfaenger, text) {
    // Hier kommen Deine API Keys rein
    var api_keys = ["283786", "xxxxxx"];

    // Hier kommen die zughörigen Telefonnummern rein
    var telephone_nummer = ["+491729887930", "+4915254733234"];

    // Textnachricht bereinigen
    var nachricht = text.replace(/\s/g, "+");
    nachricht = nachricht.replace(/<br>/g,"%0A");

    // An alle oder nur an einen bestimmten Empfänger
    if (Array.isArray(empfaenger)) {
        for (let i = 0; i < empfaenger.length; i++) {
            sendeWhatsApp(telephone_nummer[empfaenger[i]], api_keys[empfaenger[i]], nachricht);
        }
    }
    if (empfaenger == "alle") {
        for (let i = 0; i < telephone_nummer.length; i++) {
            sendeWhatsApp(telephone_nummer[i], api_keys[i], nachricht);
        }
    }

    if (Number.isInteger(empfaenger)) {
        sendeWhatsApp(telephone_nummer[empfaenger], api_keys[empfaenger], nachricht);
    }
}

function sendeWhatsApp(empfaenger, api_key, nachricht) {
    if (empfaenger != "" && api_key != "" && empfaenger != undefined && api_key != undefined) {
        exec("curl -X GET 'https://api.callmebot.com/whatsapp.php?phone=" + empfaenger + "&text=" + nachricht + "&apikey=" + api_key + "'",
            function (error, stdout, stderr) {
                if (stdout.includes("Message queued") || stdout.includes("invalid") || stdout.includes("currently a limit")) {
                    if (stdout.includes("Message queued")) {
                        log("WhatsAppBot: Erfolg: Nachricht an " + empfaenger + " verschickt!");
                    }
                    if (stdout.includes("invalid")) {
                        log("WhatsAppBot: Fehler: API Key für " + empfaenger + " ist ungültig!");
                    }
                    if (stdout.includes("currently a limit")) {
                        log("WhatsAppBot: Fehler: 25 Nachrichten pro 4 Stunden Limit erreicht für " + empfaenger + "!");
                    }
                } else {
                    log("WhatsAppBot: Fehler: Nachricht an " + empfaenger + " konnte nicht verschickt werden! Error: " + error);
                }
            });
    } else {
        log("WhatsAppBot: Fehler: Empfänger oder API Key für diese Position ungültig!");
    }
}

Skript anpassen

Sobald du das Skript angelegt hast, musst Du es noch etwas anpassen.

Hierzu benötigst Du den empfangenen API Key und deine Telefonnummer.

Verwendung mit einer Telefonnummer und einem API Key

Wenn Du nur eine Telefonnummer in dem Skript verwenden möchtest, brauchst Du nur die “x”-Zeichen in den Zeilen 25 und 28 mit deinen Daten ersetzen. Denke bitte an das internationale Format der Telefonnummer.

// Hier kommen Deine API Keys rein
var api_keys = ["xxxxxx"];

// Hier kommen die zughörigen Telefonnummern rein
var telephone_nummer = ["+49171xxxxxxx"];

Verwendung mit mehreren Telefonnummern und mehreren API Key’s

Solltest Du mehrere Telefonnummern ablegen wollen, so geht dies natürlich auch. Hier bitte darauf achten, das die Position der API Keys und Telefonnummern an den gleichen Positionen im Array stehen.
Position 1: Telefonnummer 1, API Key 1
Position 2: Telefonnummer 2, API Key 2

Die Nachrichten können dann an alle oder einzelne Telefonnummern geschickt werden. Dazu kommen wir im nächsten Abschnitt.

// Hier kommen Deine API Keys rein
var api_keys = ["xxxxxx","xxxxxx","xxxxxx"];

// Hier kommen die zughörigen Telefonnummern rein
var telephone_nummer = ["+49171xxxxxxx","+49151xxxxxxx","+49150xxxxxxx"];

Skript verwenden

Sobald Du das Skript angepasst und abgespeichert hast, steht die Funktion “WhatsAppBot” in allen Skripten im “common” Bereich zur Verfügung.

Mit einer Telefonnummer

Möchtest Du nun eine Nachricht an eine Telefonnummer versenden und hast auch nur eine Nummer hinterlegt, so ist dies mit folgendem Code möglich:

WhatsAppBot(0, "Dies ist eine Testnachricht!");

Mit mehreren Telefonnummern

Verwendest Du das Skript mit mehreren Telefonnummern, so kannst Du beim Aufruf entscheiden, ob Du die Nachricht nun an alle Nummern oder nur bestimmte Nummern versenden möchtest.

An alle Nummern
WhatsAppBot("alle", "Dies ist eine Testnachricht!");
An bestimmte Nummern

Um eine Nachricht an mehrere, bestimmte hinterlegte Nummern zu versenden, musst Du den Aufruf ein klein wenig abändern und auf die Zählweise achten. In der Programmierung startet das 1. Element immer bei 0 – somit hat die erste Nummer, die gespeichert ist, die Nummer 0.
Im folgenden Beispiel wird es deutlicher.

Nehmen wir an, Du hast 4 Nummern gespeichert, es sollen aber nur die erste und letzte Nummer diese Nachricht erhalten. In dem Falle wäre der Aufruf wie folgt:

WhatsAppBot([0,3], "Dies ist eine Testnachricht!");

Natürlich lassen sich auch alle 4 Nummern eintragen. Entweder mit “alle” oder mit dem Wert [0,1,2,3].

Nachrichten formatieren

In WhatsApp gibt es auch die Möglichkeit, Nachrichten zu formatieren.

Kursiv

Um deine Nachricht kursiv zu formatieren, schreibe einen Unterstrich auf beide Seiten des Textes:

_Text_

Fett

Um deine Nachricht fett zu formatieren, schreibe ein Sternchen auf beide Seiten des Textes:

*Text*

Durchgestrichen

Um deine Nachricht durchgestrichen zu formatieren, schreibe ein Tilde-Zeichen auf beide Seiten des Textes:

~Text~

Monospace

Um deine Nachricht in Monospace zu formatieren, schreibe drei Hochkomma auf beide Seiten des Textes:

“`Text“`

Zeilenumbruch

Ein Zeilenumbruch ist mit dem Kürzel <br> möglich.

So ergibt: Hallo Du,<br>wie geht es Dir?
Hallo Du,
wie geht es Dir?

Die Formartierungen lassen sich natürlich auch kombinieren. So ergibt:

*_Hallo_* -> Hallo (fett und kursiv)

Viel Spaß mit dem Skript!

ZigBee Stick in ioBroker einbinden

Da ioBroker nun seit längerer Zeit den ZigBee Stick unterstützt, wollte ich Euch hier einmal zeigen, wie einfach dieser Stick inzwischen zu installieren ist.

Ich persönlich verwende den Stick von Sascha Sturn. Dieser ist bei eBay erhältlich.
CC2531  ZigBee USB-Sick zigbee2mqtt ioBroker FHEM openHAB SMA Antenne + Firmware

Zigbee Adapter installieren

Installation des ZigBee Adapter
Installation des ZigBee Adapter

Nach kurzer Zeit ist der Adapter installiert und kann verwendet werden. Hierzu stecken wir den ZigBee USB Stick in einen freien Port im Raspberry und rufen die Einstellungen des Adapters auf. Instanzen -> ZigBee Adapter -> Einstellungen.

ZigBee Adapter Einstellungen aufrufen
ZigBee Adapter Einstellungen aufrufen

ZigBee Adapter einrichten

In den Einstellungen des Adapters wählen wir nun “Einstellungen” aus.

ZigBee Dapter Einstellungen
ZigBee Dapter Einstellungen

COM – Anschluss

Hier ist es für die Kommunikation zwischen USB Stick und ioBroker wichtig, den richtigen COM-Anschluss auszuwählen. “ttyACM0” wird hier vom System schon richtig erkannt.

ZigBee Adapter Einstellungen - COM Anschluss
ZigBee Adapter Einstellungen – COM Anschluss

Damit sich Dein ZigBee Stick nicht mit dem Deines Nachbarn stört, empfehle ich, die Network Kennung zu verändern. Hier kannst Du 16 zufällige Zeichen von a-f und 0-9 eingeben.

Network ID

ZigBee Adapter Einstellungen - Network
ZigBee Adapter Einstellungen – Network

Kanal

Zusätzlich macht es Sinn, einmal einen Kanal-Scan auszuführen. Hier wählst Du dann den Kanal mit der geringsten Prozentangabe als deinen Kanal aus.

ZigBee Adapter Einstellungen - Kanaltabelle
ZigBee Adapter Einstellungen – Kanaltabelle
ZigBee Adapter Einstellungen - Kanal
ZigBee Adapter Einstellungen – Kanal

Zusätzlich kannst Du die LED, die auf dem USB Stick verbaut ist, bei Bedarf ausschalten.

ZigBee Adapter Einstellungen - LED
ZigBee Adapter Einstellungen – LED

Abschluss

Mit einem Klick auf “Speichern und Schließen” ist die Einrichtung des Adapters abgeschlossen. Der Adapter startet dann neu. Nachdem dies geschehen ist, kannst Du erneut in die Einstellungen des Adapters gehen und über die doppelte Funkwelle den Stick in den Kopplungs-Modus bringen.

ZigBee Adapter - Kopplung
ZigBee Adapter – Kopplung

Nun hast du 60 Sekunden Zeit, Dein ZigBee Gerät anzumelden. Wie dies im Einzelnen funktioniert, berichte ich in einem späteren Beitrag!

Shelly Verbrauchs-Historie für Tag, Woche, Monat, Jahr

Hinweis: für dieses Skript steht ein Update zur Verfügung!
Es wird nun auch der Shelly 3EM unterstützt.

In der Shelly Cloud App für iOS oder Android sieht man natürlich immer auf einen Blick, wie der Verbrauch der einzelnen oder aller Shelly ist.
Was ist aber, wenn man diese Daten auch gerne Offline zur weiteren Verarbeitung speichern möchte?
Hier stößt man leider schnell auf ein Problem, das der Verbrauch des Shelly zwar abgelesen werden kann – jedoch nur der Verbrauch seit dem letzten Neustart.

Somit würde sich der Verbrauch immer verändern, wenn der Shelly aufgrund von Stromausfall oder Software-Update neugestartet wurde.

Hierzu habe ich mir lange Gedanken gemacht und ein Skript entwickelt, welches die Werte aus den einzelnen Shelly in einer einstellbaren Zeit abfragt und in eigene Datenpunkte speichert. Auch ist eine Korrektur von Reboot Werten vorhanden. Somit werden die Zähler der temporären Berechnung genullt, sobald festgestellt wurde, das der Shelly neu gestartet wurde. Dies hat keinen Einfluss auf die gesamte Berechnung des Verbrauchs – im Gegenteil: Die Berechnung des Verbrauchs wird genauer!

Übersicht der Funktionen

Folgende Verbrauchswerte stehen in der Historie für jeden der Shelly zur Verfügung:

FunktionBeschreibung
Verbrauch heuteabgefragter Verbrauch heute
Verbrauch gesternabgefragter Verbrauch gestern
Verbrauch diese Wocheabgefragter Verbrauch für die gesamte Woche
Verbrauch letzte Wocheabgefragter Verbrauch für die gesamte letzte Woche
Verbrauch dieser Monatabgefragter Verbrauch für den gesamten Monat
Verbrauch letzter Monatabgefragter Verbrauch für den gesamten letzten Monat
Verbrauch dieses Jahrabgefragter Verbrauch für das gesamte Jahr
Verbrauch letztes Jahrabgefragter Verbrauch für das gesamte letzte Jahr
Verbrauch Gesamtabgefragter Verbrauch gesamt
NameZugewiesener Name des Shelly zum einfachen Wiederfinden
Übersicht über die einzelnen Datenpunkte

Screenshot der verfügbaren Variablen

Screenshot der verfügbaren Variablen
Screenshot der verfügbaren Variablen

Installation des Shelly Adapter

Installation des Shelly Adapter
Installation des Shelly Adapter

Nach kurzer Zeit stehen die Datenpunkte aller gefundenen Shellys unter shelly.0 zur Verfügung.

Skript

/*
 * @copyright 2020 Stephan Kreyenborg <stephan@kreyenborg.koeln>
 *
 * @author 2020 Stephan Kreyenborg <stephan@kreyenborg.koeln>
 *
 * Dieses Skript dient zur freien Verwendung in ioBroker zur Verbrauchserfassung der Shelly Geräte.
 * Jegliche Verantwortung liegt beim Benutzer. Das Skript wurde unter Berücksichtigung der bestmöglichen Nutzung
 * und Performance entwickelt.
 * Der Entwickler versichert, das keine böswilligen Systemeingriffe im originalen Skript vorhanden sind.
 *
 * Sollte das Skript wider Erwarten nicht korrekt funktionieren, so hast Du jederzeit die Möglichkeit, Dich auf
 * https://www.kreyenborg.koeln
 * für Unterstützung zu melden. Jedes Skript besitzt seine eigene Kommentarseite, auf der,
 * nach zeitlicher Möglichkeit des Autors, Hilfe angeboten wird. Ein Anrecht hierauf besteht nicht!
 * 
 * Ansprüche gegenüber Dritten bestehen nicht. 
 * 
 * Skript Name:		Shelly-Verbrauch
 * Skript Version:	1.40
 * Erstell-Datum:	05. Mai 2021
 * 
 */

// Datenpunkte neu erstellen
var ueberschreiben = false;

// Hauptdatenpunkt unterhalb javascript
var datenpunkt = "ShellyVerbrauch.";

// Verbrauchs Objekte der einzelnen Shelly
const objekt = ["gesamt", "dieses_jahr", "letztes_jahr", "letzter_monat", "dieser_monat", "letzte_woche",
    "diese_woche", "gestern", "heute", "alter_wert", "aktueller_wert"];

// Beschreibung der Objekte
const beschreibung = ["Gesamter Vebrauch des Shelly", "Verbrauch aktuelles Jahr", "Verbrauch letztes Jahr",
    "Verbrauch letzten Monat", "Verbrauch aktueller Monat", "Verbrauch letzte Woche", "Verbrauch diese Woche",
    "Verbrauch gestern", "Verbrauch heute", "Messwert alt", "Messwert neu"];

// Datenpunkt der Shelly (Standard: shelly.0)
var shelly_dp = "shelly.0";

// Datenpunkte der Shelly (!!! Bitte nicht ändern !!!)
const shellyDps = $('state[id=' + shelly_dp + '.*.*.Energy]');

// Datenpunkte der Shelly 3EM DP
const shelly3EMDps = $('state[id=' + shelly_dp + '.*.*.Total]');

// Datenpunkte der Shelly Namen (!!! Bitte nicht ändern !!!)
const shellyDpsName = $('state[id=' + shelly_dp + '.*.name]');

// Shelly Verbrauch aktualisieren - nachts um 00:00 Uhr
function shelly_vebrauch_tag() {
    // Nochmals das Tagesupdate durchlaufen, damit die restlichen Werte gelesen werden
    shelly_verbrauch_update();

    // Datumsvariable
    var heute = new Date();

    // Heute zu Gestern verschieben. Täglich um 00:00 Uhr
    verschiebe_verbrauch_objekt("heute", "gestern");
    log("Shelly Verbrauch: Werte für gestern und heute aktualisiert!");

    // aktuelle Woche in letzte Woche verschieben. Am Montag um 00:00 Uhr
    if (heute.getDay() === 1) {
        verschiebe_verbrauch_objekt("diese_woche", "letzte_woche");
        log("Shelly Verbrauch: Werte für diese und letzte Woche aktualisiert!");
    }

    // aktueller Monat in letzten Monat verschieben. Am 1. des Monats um 00:00 Uhr
    if (heute.getDate() === 1) {
        verschiebe_verbrauch_objekt("dieser_monat", "letzter_monat");
        log("Shelly Verbrauch: Werte für diesen und letzten Monat aktualisiert!");
    }

    // aktuelles Jahr in letztes Jahr verschieben. Am 1. des Monats am 1. Monat um 00:00 Uhr
    if (heute.getDate() === 1 && heute.getMonth() === 0) {
        verschiebe_verbrauch_objekt("dieses_jahr", "letztes_jahr");
        log("Shelly Verbrauch: Werte für dieses und letztes Jahr aktualisiert!");
    }
}

// Tagesverbrauch alle 15 Min von der Original Variable des Shelly in eigene Variable kopieren
function shelly_verbrauch_update() {
    var anzahl_updates = 0;
    var anzahl_reboots = 0;
    var anzahl_gleich = 0;
    shellyDps.each(function (id, i) {
        var shelly_verbrauch = getState(id).val;
        // Einige Shelly haben keine Verbrauchswerte (noch nicht)
        if (shelly_verbrauch != null) {
            // Hole aktuellen Wert, um zu kontrollieren, ob ein Reboot stattgefunden hat
            var aktueller_wert = getState(shelly_DP(id) + "aktueller_wert").val;
            var alter_wert = 0;
            // Prüfe alten und neuen Wert
            if (shelly_verbrauch > aktueller_wert) {
                // Verbrauchswert ist größer als alter Wert -> es wurde kein Reboot durchgeführt
                setState(shelly_DP(id) + "alter_wert", aktueller_wert, true);
                alter_wert = aktueller_wert;
                anzahl_updates++;
            }
            if (aktueller_wert > shelly_verbrauch) {
                // Verbrauchswert ist kleiner als alter Wert -> es wurde ein Reboot durchgeführt
                setState(shelly_DP(id) + "alter_wert", 0, true);
                alter_wert = 0;
                anzahl_reboots++;
            }
            if (shelly_verbrauch == aktueller_wert) {
                // Verbrauchswert ist gleich wie alter Wert -> kein Update notwendig
                alter_wert = aktueller_wert;
                anzahl_gleich++;
            }

            setState(shelly_DP(id) + "aktueller_wert", shelly_verbrauch, true);
            // Alter und neuer Wert -> aktuelle Differenz

            var verbrauch = parseFloat(shelly_verbrauch) - alter_wert;
            // Tagesverbrauch aktualisieren
            aktualisiere_vebrauch_objekt(id, "heute", verbrauch);

            // Wochenverbrauch aktualisieren
            aktualisiere_vebrauch_objekt(id, "diese_woche", verbrauch);

            // Monatsverbrauch aktualisieren
            aktualisiere_vebrauch_objekt(id, "dieser_monat", verbrauch);

            // Jahresverbrauch aktualisieren
            aktualisiere_vebrauch_objekt(id, "dieses_jahr", verbrauch);

            // Gesamten Vebrauch aktualisieren
            aktualisiere_vebrauch_objekt(id, "gesamt", verbrauch);
        }
    });
    shelly3EMDps.each(function (id, i) {
        var shelly_verbrauch = getState(id).val;
        // Einige Shelly haben keine Verbrauchswerte (noch nicht)
        if (shelly_verbrauch != null) {
            // Hole aktuellen Wert, um zu kontrollieren, ob ein Reboot stattgefunden hat
            var aktueller_wert = getState(shelly_DP(id) + "aktueller_wert").val;
            var alter_wert = 0;
            // Prüfe alten und neuen Wert
            if (shelly_verbrauch > aktueller_wert) {
                // Verbrauchswert ist größer als alter Wert -> es wurde kein Reboot durchgeführt
                setState(shelly_DP(id) + "alter_wert", aktueller_wert, true);
                alter_wert = aktueller_wert;
                anzahl_updates++;
            }
            if (aktueller_wert > shelly_verbrauch) {
                // Verbrauchswert ist kleiner als alter Wert -> es wurde ein Reboot durchgeführt
                setState(shelly_DP(id) + "alter_wert", 0, true);
                alter_wert = 0;
                anzahl_reboots++;
            }
            if (shelly_verbrauch == aktueller_wert) {
                // Verbrauchswert ist gleich wie alter Wert -> kein Update notwendig
                alter_wert = aktueller_wert;
                anzahl_gleich++;
            }

            setState(shelly_DP(id) + "aktueller_wert", shelly_verbrauch, true);
            // Alter und neuer Wert -> aktuelle Differenz

            var verbrauch = parseFloat(shelly_verbrauch) - alter_wert;
            // Tagesverbrauch aktualisieren
            aktualisiere_vebrauch_objekt(id, "heute", verbrauch);

            // Wochenverbrauch aktualisieren
            aktualisiere_vebrauch_objekt(id, "diese_woche", verbrauch);

            // Monatsverbrauch aktualisieren
            aktualisiere_vebrauch_objekt(id, "dieser_monat", verbrauch);

            // Jahresverbrauch aktualisieren
            aktualisiere_vebrauch_objekt(id, "dieses_jahr", verbrauch);

            // Gesamten Vebrauch aktualisieren
            aktualisiere_vebrauch_objekt(id, "gesamt", verbrauch);
        }
    });
    aktualisiere_namen();
    log("Shelly Verbrauch: Verbrauchswerte aktualisiert: " + anzahl_updates + " | Reboots korrigiert: " + anzahl_reboots + " | Unveränderte Werte: " + anzahl_gleich);
}

// aktualisiert das jeweilige Verbrauchs-Objekt und addiert den Verbrauch dazu
function aktualisiere_vebrauch_objekt(dp, objekt, wert) {
    var verbrauch = parseFloat(getState(shelly_DP(dp) + objekt).val) + parseFloat(wert);
    verbrauch = parseFloat(verbrauch.toFixed(2));
    setState(shelly_DP(dp) + objekt, verbrauch, true);
}

// Verschiebt das jeweilige Verbrauchs-Objekt und nullt den Ursprung (Tag, Woche, Monat, Jahr)
function verschiebe_verbrauch_objekt(von, nach) {
    $('state[id=*.' + datenpunkt + '*.*.' + von + ']').each(function (id, i) {
        // Temporärer Gruppen-Datenpunkt
        var tmp_dp = id.slice(0, -(von.length));
        var verbrauch = getState(id).val;
        if (verbrauch != null) {
            setState(tmp_dp + nach, verbrauch, true);
        }
        // Setze heute zurück
        setState(id, 0, true);
    });
}

// Funktion um die aktuellen Namen des Shelly abzuholen
function aktualisiere_namen() {
    shellyDpsName.each(function (id, i) {
        setState(shelly_DP_Name(id), String(getState(id).val), true);
        extendObject("javascript.0." + shelly_DP_Name_Main(id), {
            common: {
                name: String(getState(id).val),
                desc: String(getState(id).val)
            }, type: "channel"
        });
    });
    shelly3EMDps.each(function (id, i) {
        setState(shelly_DP_Name(id), String(getState(id).val), true);
        extendObject("javascript.0." + shelly_DP_Name_Main(id), {
            common: {
                name: String(getState(id).val),
                desc: String(getState(id).val)
            }, type: "channel"
        });
    });
}

// Erstelle die benötigten Datenpunkte
function datenpunkte_erstellen() {
    // Anzahl der gefundenen Shelly
    var anzahl = shellyDps.length;

    shellyDps.each(function (id, j) {
        var initial_wert = 0;
        for (var i = 0; i < objekt.length; i++) {
            // Startwerte werden nur bei alter_wert und aktueller_wert eingetragen
            if (i > 8) {
                initial_wert = getState(id).val;
            }
            createState(shelly_DP(id) + objekt[i], initial_wert, ueberschreiben, {
                name: beschreibung[i],
                desc: beschreibung[i],
                type: "number",
                role: "value.power",
                unit: "Wh"
            });
        }
    });

    // Anzahl der gefundenen Shelly 3EM
    var anzahl_3em = shelly3EMDps.length;

    shelly3EMDps.each(function (id, j) {
        var initial_wert = 0;
        for (var i = 0; i < objekt.length; i++) {
            // Startwerte werden nur bei alter_wert und aktueller_wert eingetragen
            if (i > 8) {
                initial_wert = getState(id).val;
            }
            createState(shelly_DP(id) + objekt[i], initial_wert, ueberschreiben, {
                name: beschreibung[i],
                desc: beschreibung[i],
                type: "number",
                role: "value.power",
                unit: "Wh"
            });
        }
    });

    // Alle Datenpunkte erstellt. Frage ersten Verbrauch ab!
    log("Shelly Verbrauch: Datenpunkte erstellt! Erster Verbrauch steht nach 30 Sekunden zur Verfügung! Anzahl gefundener Shelly Datenpunkte: " + (anzahl_3em + anzahl));
    setTimeout(shelly_verbrauch_update, 30000);

    // Datenpunkte für die Namen der Shelly erstellen
    shellyDpsName.each(function (id, j) {
        createState(shelly_DP_Name(id), "", ueberschreiben, {
            name: "Name des Shelly",
            desc: "Name des Shelly",
            type: "string",
            role: "value",
            unit: ""
        });
    });
    // Datenpunkte für die Namen der Shelly 3EM erstellen
    shelly3EMDps.each(function (id, j) {
        createState(shelly_DP_Name(id), "", ueberschreiben, {
            name: "Name des Shelly",
            desc: "Name des Shelly",
            type: "string",
            role: "value",
            unit: ""
        });
    });
}

function shelly_DP(dp) {
    dp = dp.split(".");
    dp = datenpunkt + dp[2] + "." + dp[3] + ".";
    return dp;
}

function shelly_DP_Name(dp) {
    dp = dp.split(".");
    dp = datenpunkt + dp[2] + "." + dp[3];
    return dp;
}

function shelly_DP_Name_Main(dp) {
    dp = dp.split(".");
    dp = datenpunkt + dp[2];
    return dp;
}

function shelly_verbrauch_erster_start() {
    log("Shelly Verbrauch: Erster Start des Skriptes! Datenpunkte werden erstellt!");
    // Datenpunkte werden erstellt
    datenpunkte_erstellen();
}

// Erster Start und Initialisierung
shelly_verbrauch_erster_start();

// Alle 15 Minuten das Skript für den Tagesverbrauch ausführen
schedule('*/15 * * * *', shelly_verbrauch_update);

// Nachts um 24 Uhr werden die Werte in andere Variablen gespeichert, um den Verlauf zu erstellen
schedule('0 0 * * *', shelly_vebrauch_tag);

Datenpunkte

Nachdem das Skript angelaufen ist und regelmäßig die Verbräuche der Shelly abgefragt wurden, findest Du diese im Datenpunkt: javascript.0.ShellyVerbrauch

Neue Shelly eingebunden, das Skript liest die Daten nicht aus!

Solltest Du zur Laufzeit des Skripts neue Shelly eingebunden haben, so kannst du das Skript einmal stoppen und erneut starten. Dann werden auch die passenden Datenpunkte für die neuen Shelly gefunden und ausgelesen.