Automatische Bewässerung

Nachdem ich nun die Bodenfeuchtigkeit meiner Topfpflanzen messen kann, möchte ich natürlich auch die Bewässerung automatisieren.

Ein wichtige Vorüberlegung dazu ist, dass dieses Projekt potentiell für eine Überschwemmung sorgen kann. Dem möchte ich mit verschiedenen Sicherheitsvorkehrungen entgegenwirken:

  • Nicht zu oft gießen.
    Egal, was die Feuchtigkeitssensoren sagen, die Pflanzen sollen nicht häufiger als alle zwei Tage gegossen werden. Diesen Mechanismus werde ich im Skript in ioBroker realisieren, welches das Signal zum Gießen der Pflanzen erzeugt.
  • Die Dauer der Pumpenansteuerung limitieren.
    Wenn der ioBroker das Signal zum Gießen einer Pflanze gibt, soll die Pumpe auf jeden Fall wieder ausgeschaltet werden, auch wenn der ioBroker zwischendurch abstürzen sollte oder das WLAN ausfällt. D.h. auf dem steuernden ESP8266 wird eine Logik implementiert, die nach einer vorgegebenen Zeit die Pumpe wieder abschaltet.
  • Die Logik muss so konzipiert sein, dass die Pumpen nicht anspringen wenn der ESP8266 startet. D.h. ESPHome wird so eingestellt, dass die entsprechenden Ausgänge beim Starten auf Low gezogen werden. Solange die GPIOs als Eingang konfiguriert sind darf die Pumpe nicht laufen.
  • Die Durchflussmenge sollte reduziert werden können, damit die Töpfe nicht überlaufen. Es darf also nicht schneller Wasser in den Topf gepumpt werden als in der Erde versickern kann.

Ursprünglich wollte ich zur Ansteuerung der Pumpen ein Relais-Modul verwenden. Die Durchflussmenge hätte ich in diesem Fall über ein Ventil reguliert. Das Relais-Modul habe ich bereits besorgt und auch ein Ventil habe ich bereits konstruiert und gedruckt.
Den Plan habe ich dann jedoch über den Haufen geworfen und die Ansteuerung der Pumpen über einen Transistor realisiert. Dadurch ist die Durchflussmenge über PWM-Steuerung der Pumpen möglich, außerdem ist die Schaltung dadurch günstiger und hat einen geringeren Stromverbrauch. Da die Stromversorgung bei mir nicht über Batterien sondern über Netzteil erfolgen soll, ist der Verbrauch zwar nicht so entscheidend und die ca. 50 mA zusätzlich für das Relais während der Aktivierung der Pumpen wären auch gut zu verkraften, aber nett ist das natürlich trotzdem.

Als Transistor habe ich einen S8050 gewählt, da dieser mit 500mA Collector-Strom (IC – angegeben bei Weitron) auf jeden Fall genug Leistung hat um die Motoren anzutreiben. Vermutlich hätte auch ein schwächerer Transistor genügt, da die Motoren mit max. 200mA bei 5V angegeben sind. Da Motoren jedoch eine induktive Last sind und damit der Einschaltstrom etwas höher ausfällt, habe ich lieber zu einem etwas zu hoch dimensionierten Bauteil gegriffen.
Für den Transistor ist eine Base-Emitter Saturation Voltage (VBE) von 1,2V angegeben. Damit ergibt sich, bei einer Spannung von 3,3V und einer maximalen Belastung von 12mA am GPIO des ESP8266 ein Basis-Vorwiderstand von mindestens (3,3V-1,2V)/0,012A=175 Ohm. Hier habe ich einen Widerstand von 220 Ohm (5%) gewählt, um den Ausgang nicht zu hoch zu belasten. Der Basis-Strom wird damit bei 9,1 – 11 mA liegen (gerechnet für VBE von 1,0 – 1,2V (1,0V angegeben bei UTC für IC von 10mA) und einem tatsächlichen Widerstandswert von 209 – 231 Ohm). Das reicht in jedem Fall um den Transistor bei einem Collector-Strom von max. 200mA voll durchzuschalten (niedrigster Verstärkungsfaktor hFE ist angegeben bei Weitron mit 85).
Zusätzlich wird noch eine Freilaufdiode zum Schutz des Transistors benötigt.

Schaltplan für die Ansteuerung von zwei Pumpen (M1 und M2)

Bei ESPHome konnte ich dieses Mal leider nicht die API verwenden um die entsprechenden Elemente direkt im ESPHome-Adapter zur Verfügung zu haben, da der Adapter zur Zeit number noch nicht akzeptiert. Die Kommunikation muss deshalb über MQTT laufen. Dabei ist es ein wenig lästig, dass lesen und setzen eines Wertes über zwei unterschiedliche Topics laufen muss. Im ioBroker habe ich mir damit beholfen, zu den zwei Topics einen Alias zu definieren und dabei unterschiedliche Datenpunkte für Lesen und Schreiben zu definieren.

esphome:
  name: pflanzenwasser

esp8266:
  board: d1_mini

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: !secret ap_ssid
    password: !secret ap_password

ota:
  password: !secret ota_password

# Enable logging
logger:

# Enable Home Assistant API
#api:
#  password: !secret api_password
#  encryption:
#    key: !secret api_key

mqtt:
  broker: 192.168.1.2
  username: username
  topic_prefix: pflanzenwasser

captive_portal:

web_server:
  port: 80

sensor:
  - platform: wifi_signal
    name: "Wifi Power"
    update_interval: 10s
    state_topic: pflanzenwasser/wifi_power

number:
  - platform: template
    name: "Dauer1"
    id: dauer1
    unit_of_measurement: ms
    optimistic: true
    min_value: 0
    max_value: 30000
    step: 1
    restore_value: true
    initial_value: 3500
    state_topic: pflanzenwasser/dauer1
    command_topic: pflanzenwasser/dauer1-set

  - platform: template
    name: "Dauer2"
    id: dauer2
    unit_of_measurement: ms
    optimistic: true
    min_value: 0
    max_value: 30000
    step: 1
    restore_value: true
    initial_value: 3500
    state_topic: pflanzenwasser/dauer2
    command_topic: pflanzenwasser/dauer2-set

output:
  - platform: esp8266_pwm
    pin: D1
    frequency: 200 Hz
    id: wasser1_pwm
    min_power: 0.3
    max_power: 1.0
    zero_means_zero: true

  - platform: esp8266_pwm
    pin: D2
    frequency: 200 Hz
    id: wasser2_pwm
    min_power: 0.3
    max_power: 1.0
    zero_means_zero: true

fan:
  - platform: speed
    id: wasser1
    output: wasser1_pwm
    name: "Wasser1"
    on_turn_on:
      - delay: !lambda |-
          return id(dauer1).state;
      - fan.turn_off: wasser1

  - platform: speed
    id: wasser2
    output: wasser2_pwm
    name: "Wasser2"
    on_turn_on:
      - delay: !lambda |-
          return id(dauer2).state;
      - fan.turn_off: wasser2

Jetzt fehlt nur noch das Script im ioBroker, mit dem dann die Pumpen aktiviert werden. Hier mit einem schönen Gemisch aus Deutsch und Englisch:

var cacheSelectorBodenfeuchte
   = $('state[id=*](functions="Bodenfeuchte")');
let warning_timer = null;
const warning_timeout = 240; // Minuten
const watering = true;

cacheSelectorBodenfeuchte.on (checkAllFeuchtigkeit);

checkAllFeuchtigkeit (null);

//cacheSelectorBodenfeuchte.each (function(id, i) {
//    console.log ("id: " + JSON.stringify (id));
//    console.log ("i: " + JSON.stringify (i));
//});

function holePflanzen () {
    const obj
      = getObject("enum.functions.Bodenfeuchte");
    
    if (obj === null)
        return [];

    let pflanzen = [];

    for (let i in obj.common.members) {
        const c
          = getObject (obj.common.members[i]).common;
        let warn_low = 40;
        let warn_high = 50;

        if ("warning_range" in c) {
            //console.log
            //  ("Warning range: "
            //   + JSON.stringify (c.warning_range));
            warn_low = c.warning_range[0];
            warn_high = c.warning_range[1];
        }

        pflanzen.push
          ({ id: obj.common.members[i],
             warn_low: warn_low,
             warn_high: warn_high });
    }
    
    return pflanzen;
}

async function checkAllFeuchtigkeit (obj) {
    let r = {finish: false, enough_water: true };

    let pflanzen = holePflanzen ();

    let trockene_pflanzen = [];
    let trockene_pflanzen_ids = [];

    for (let i in pflanzen) {
        r = checkFeuchtigkeit
              (pflanzen[i], r.enough_water);

        if (r.finish) {
            trockene_pflanzen.push
              (pflanzen[i].id.replace (/^.*\.([^.]+)$/,
                                       "$1"));
            trockene_pflanzen_ids.push (pflanzen[i].id);
        }
    }

    if (trockene_pflanzen.length > 0) {
        if (watering)
            waterPlants (trockene_pflanzen_ids);

        if (warning_timer === null) {
            warning_timer
              = setTimeout (async function () {
                    warning_timer = null;
                  }, warning_timeout * 60 * 1000);
            
            let msg = 'Pflanzen benötigen Wasser: ';
            for (let i=0;
                 i < trockene_pflanzen.length; ++i) {
                if (i > 0)
                    msg += ', ';

                msg
                  += trockene_pflanzen[i] + ' ('
                     + String (getState(
                         trockene_pflanzen_ids[i]).val)
                     + '%)';
            }
            sendTo ("telegram", "send",
                    {text: msg,
                     disable_notification: true});
        }
        return;
    }
    
    if (r.enough_water)
        setState
          ("0_userdata.0.Warnings.Pflanze_ohne_Wasser",
           false, true);
}

function checkFeuchtigkeit (pflanze,
                            previous_enough_water) {
    const f = getState (pflanze.id).val;
    let finish = false;

    if (f < pflanze.warn_low) {
        setState
          ("0_userdata.0.Warnings.Pflanze_ohne_Wasser",
           true, true);
        finish = true;
    }

    return { finish: finish,
             enough_water: previous_enough_water
                           && f > pflanze.warn_high };
}

var warning_timer_not_watering = null;

function msToReadable (time) {
    if (time < 5)
        return String (time / 1000) + " s";
    
    let s = Math.round (time / 1000);

    if (s < 60)
        return String (s) + " s";

    let m = Math.floor (s / 60);
    s -= m * 60;

    if (m < 5)
        return String (m) + " min, "
               + String (s) + " s";

    if (m < 60)
        return String (m) + " min";

    let h = Math.floor (m / 60);
    m -= h * 60;

    if (h < 5)
        return String (h) + ":"
               + String (m) + " Stunden";

    if (h < 24)
        return String (h) + " Stunden";

    let d = Math.floor (h / 24);
    h -= d * 24;

    if (d < 5)
        return String (d) + " Tage, "
               + String (h) + " Stunden";

    if (d < 14)
        return String (d) + " Tage";

    let w = Math.floor (d / 7);
    d -= w * 7;

    if (w < 5)
        return String (w) + " Wochen, "
               + String (d) + " Tage";

    return String (w) + " Wochen";
}

function getWasserIdFromPflanzenId (pflanzen_id) {
  switch (trockene_pflanzen_ids[i]) {
    case
      "alias.0.Wohnzimmer.Bodenfeuchte_Bitterorange":
      return "alias.0.Wohnzimmer.Pflanzenbewässerung"
             + ".Bitterorange.state";
      break;
    case "alias.0.Wohnzimmer.Bodenfeuchte_Schefflera":
      return "alias.0.Wohnzimmer.Pflanzenbewässerung"
             + ".Schefflera.state";
      break;
  }
  return null;
}

function waterPlants (trockene_pflanzen_ids) {
  for (let i in trockene_pflanzen_ids) {
    let wasser_id
      = getWasserIdFromPflanzenId
          (trockene_pflanzen_ids[i]);

    if (wasser_id !== null) {
      let s = getState (wasser_id);
      let last = Date.now () - s.lc;

      let msg = null;
      if (last > 5 * 24 * 60 * 60 * 1000) {
        let szene = getState (SZENE_ID).val;
        if (getState
              ("0_userdata.0.Presence.Gast_anwesend")
              .val
            && (!compareTime ("10:00", "20:00",
                              "between")
                || szene === SZENE_NACHT
                || szene === SZENE_FILM)) {
          msg = "Wasser für " + wasser_id
                + " nicht aktiviert wegen Gast"
                + " anwesend, Feuchte: "
                + String (getState
                    (trockene_pflanzen_ids[i]).val)
                + "%";
        } else {
          msg = "Wasser für " + wasser_id
                + " aktiviert, Feuchte: "
                + String (getState
                    (trockene_pflanzen_ids[i]).val)
                + "%";
          setState (wasser_id, true);
        }
      } else {
        if (warning_timer_not_watering === null) {
          warning_timer_not_watering
          = setTimeout (async function () {
              warning_timer_not_watering = null;
             }, warning_timeout * 60 * 1000);
          msg = "Wasser für " + wasser_id
                + " nicht aktiviert, Feuchte: "
                + String (getState
                    (trockene_pflanzen_ids[i]).val)
                + "%, letzte Wässerung vor "
                + msToReadable (last);
        }
      }

      if (msg !== null) {
        console.log (msg);
        sendTo ("telegram", "send",
                {text: msg,
                 disable_notification: true});
      }
    }
  }
}

Sensoren für Bodenfeuchtigkeit

Wie bereits im letzten Beitrag zum CO2-Sensor erwähnt, habe ich auch Sensoren für die Bodenfeuchtigkeit meiner Zimmerpflanzen angeschlossen.

Ich habe, wie auch beim CO2-Sensor versucht mich im Voraus zu erkundigen, welche Sensoren hier sinnvoll und empfehlenswert sind. Dabei war sehr schnell klar, es muss ein kapazitiver Feuchtigkeitssensor werden welche meist unter der Bezeichnung Capacitive Soil Moisture Sensor v1.2 angeboten werden. Bei Sensoren, welche die elektrische Leitfähigkeit des Bodens messen löst sich über die Zeit eine der Elektroden durch Elektrolyse auf. Dadurch hat nicht nur der Sensor eine beschränkte Haltbarkeit, das gelöste Metall wandert in die Erde und wird von der Pflanze aufgenommen. Außerdem hängt die Höhe des Messwerts in dem Fall nicht nur von der Wassermenge sondern auch maßgeblich vom Salzgehalt ab.

Leider habe ich auch hier trotzdem erst im Nachhinein erfahren, worauf man genau bei den angebotenen kapazitiven Feuchtigkeitssensoren achten muss und das v1.2 nicht genau eine Bauweise bezeichnet. Damit ihr darauf nicht reinfallt, hier ein Link zu einem Video, in dem die Unterschiede erklärt werden. Interessanterweise habe ich wohl die schlechteste Variante erhalten, glücklicherweise funktioniert sie trotzdem.

Im Internet findet man auch jede Menge Hinweise, dass man die Kanten der kapazitiven Feuchtigkeitssensoren noch mit einem Lack versiegeln soll um zu verhindern, dass dort Feuchtigkeit eindringt und auf Dauer doch den Sensor zerstört. Da ich dies zuerst mit Acryl-Lack versucht habe, habe ich eine nützliche kleine Entdeckung gemacht.

Zuerst: Nutzt Nagellack oder ähnliches für die Kanten, Acryl-Lack ist zwar wasserfest, aber nicht wasserdicht. Er saugt sich im Laufe der Zeit voll und würde dementsprechend nicht richtig abdichten.

Nützlich ist Acryl-Lack trotzdem: Trägt man eine dünne Schicht auf die Oberfläche des Sensors auf, so wird die Feuchtigkeitsmessung unabhängiger von der Bodenbeschaffenheit. Die gemessene Bodenfeuchte nähert sich damit den Werten eines Tensiometers an und kann eher als verfügbares Wasser für die Pflanzen interpretiert werden statt als reiner Wassergehalt. Als Nachteil erhält man eine deutlich langsamere Sprungantwort. Da man jedoch normalerweise eine sprunghafte Änderung der Wassermenge nur beim Wässern erhält, nicht jedoch beim Austrocknen des Bodens und mich natürlich der letztere Fall deutlich mehr interessiert, sehe ich das nicht als großen Nachteil an.

Bei den Sensoren habe ich die Buchse entfernt und ein Kabel direkt angelötet. Um die Elektronik zu schützen habe ich ein kleines Gehäuse entworfen, mit PLA gedruckt und mit einem Dichtkleber verschlossen.

Bodenfeuchtesensor mit gedrucktem Gehäuse

Die Spannung, welche die Sensoren liefern werden bei mir von einem ADS1115 ausgewertet und über I²C-Bus an einen WeMos D1 Mini (ESP8266) gesendet auf dem ESPHome läuft und die Daten per WLAN an meinen ioBroker sendet.

Die Sensoren sind an dem gleichen Controller wie der CO2-Sensor angeschlossen. Das YAML zur Ansteuerung mit ESPHome auf dem WeMos D1 Mini ist dafür um folgende Zeilen ergänzt:

i2c:
  sda: GPIO14
  scl: GPIO12
  scan: true
  id: i2c_bus
  
ads1115:
  - address: 0x48

sensor:
  - platform: ads1115
    multiplexer: 'A0_GND'
    gain: 6.144
    name: "Soil1"
    unit_of_measurement: "%"
    filters:
      - calibrate_linear:
        - 3.83475 -> 0.0
        - 2.25 -> 100.0
      - median:
          window_size: 5
          send_every: 1
          send_first_at: 1

  - platform: ads1115
    multiplexer: 'A1_GND'
    gain: 6.144
    name: "Soil2"
    unit_of_measurement: "%"
    filters:
      - calibrate_linear:
        - 3.8325 -> 0.0
        - 2.2125 -> 100.0
      - median:
          window_size: 5
          send_every: 1
          send_first_at: 1

Über die Filter habe ich die gemessene Spannung in einen Prozentwert übersetzt. Wobei ich jeden Sensor separat kalibriert habe indem ich in einmal trocknen ließ und einmal in ein Wasserglas gestellt habe. Aufgrund der Acryllack-Schicht musste ich jeweils einige Zeit warten bis die Werte stabil waren. Die so gemessenen Spannungen können dann sehr einfach über calibrate_linear in Prozentwerte übersetzt werden.

Außerdem habe ich noch einen Median-Filter hinzugefügt, da die Werte bei mir leicht zappelten, was vermutlich an meinen eher schlechten Sensoren liegt.

Jetzt fehlen nur noch Pumpen, damit die Pflanzen auch automatisch bewässert werden können.

Nachtrag:

Inzwischen sind zwei der Sensoren bei mir fast ein halbes Jahr im Einsatz. Vor einigen Wochen hatte einer der Sensoren angefangen falsche Werte zu liefern. Trotz nasser Erde wurde eine Bodenfeuchte von 30% gemessen. Meine Annahme war, dass das Gehäuse nicht richtig dicht war und Feuchtigkeit an die Elektronik gekommen ist.
Also: Sensor rausholen und nachgucken.
Zu meiner Überraschung war die Elektronik durch das Gehäuse und den Dichtkleber hervorragend geschützt, aber anscheinend wurde die Lackschicht angekratzt als ich den Sensor in die Erde steckte. Im inneren Bereich der Sensorfläche wurde fast das gesamte Kupfer zersetzt. Bei dem nächsten Sensor werde ich also nicht nur die Kanten mit Nagellack behandeln sondern auch die gesamte Vorderseite, damit der Sensor besser geschützt ist.
Hier ein Bild des defekten Sensors. Ich habe die Oberfläche leicht abgeschmirgelt um sie von Erde und losen Lackstücken zu befreien. Man kann deutlich sehen, dass der Lack in großen Bereichen abgelöst war. An den Stellen, an denen der Lack gehalten hat, ist er auch noch über dem Kupfer. Die äußere Elektrode ist einfach nur blank, bei der inneren Elektrode hat es das Kupfer durch Elektrolyse zersetzt. Das ist jetzt leider in der Erde. Ich hoffe, die Pflanze wird es überleben.

CO2, ESP8266, ESPHome, ESP Easy

Es wird ja empfohlen regelmäßig zu lüften, doch wann ist es dafür mal wieder Zeit? Jetzt, zum Sommeranfang ist das ja kein Problem, wenn ich zu Hause bin ist das Fenster natürlich offen. Als es noch kälter war und auch jetzt, wenn es wärmer wird, hilft einem ein CO2-Sensor, rechtzeitig das Fenster zu öffnen um die verbrauchte Luft auszutauschen.

Da ich auch einen CO2-Sensor haben wollte und mir die fertigen Geräte entweder viel zu teuer waren oder etwas zu teuer aber mit zwangsweiser Cloud-Anbindung, habe ich mich umgesehen, wie man einen solchen Sensor selber realisieren kann. Nach kurzer Suche habe ich dann ein MH-Z19B und den ESP8266 in Form eines WeMos D1 Mini gefunden. Bestellt, zusammengebaut und als System für den ESP die Software ESP Easy gefunden. In ESP Easy war es sehr einfach den Sensor einzurichten und die Werte dann über MQTT an den ioBroker zu schicken. Ich musste für die Hardware nur den Sensor und den ESP über vier Kabel miteinander verbinden und in der Software die Pins für den Bus auswählen, den Sensor eintragen, MQTT aktivieren und den entsprechenden Rechner angeben, sowie ein paar Haken setzen und mich für ein Intervall entscheiden.

Der MH-Z19 hat die Möglichkeit, eine automatische Kalibrierung zu verwenden (ABC: automatic baseline calibration) oder explizit eine Kalibrierung zu triggern. Unter ESP Easy habe ich es nicht geschafft eine Kalibrierung zu triggern, ich konnte nur die automatische Kalibrierung ein- oder ausschalten. Die automatische Kalibrierung funktioniert leider nur sinnvoll, wenn man dafür sorgt, dass der Sensor jeden Tag mindestens einmal für einige Minuten (vielleicht eine viertel Stunde) an frischer Luft mit ca. 400 ppm CO2 steht. Kann man dies nicht einrichten, kann es passieren, dass der Sensor plötzlich deutlich zu niedrige Werte anzeigt.

Im ioBroker habe ich dann festgestellt, dass die Werte manchmal ganz schön rauschen, zu anderen Zeiten aber wieder völlig glatt aussehen. Auf Nachfrage im Forum, ob auch andere dieses Problem haben, bekam ich leider zuerst nur die etwas flapsige Antwort, dass ich ja „einen billigen chinesischen Nachbau“ gekauft hätte. Mein Resümee ist, dass dieser Sensor eigentlich ganz brauchbare Werte liefert, er allerdings keine Luftbewegung verträgt und deshalb in ein Gehäuse eingebaut werden sollte um ihn vor stärkerer Luftbewegung zu schützen. (Das habe ich bisher aber leider immer noch nicht geschafft).

Dafür habe ich im Laufe der Zeit ESPHome entdeckt. Am Anfang hatte mich die Seite etwas abgeschreckt, da ich nicht so recht wusste, wie ich das eigentlich bei mir installiert bekomme. ESPHome ist sehr eng mit Home Assistant verbunden. Da ich jedoch bisher ioBroker benutze und damit sehr zufrieden bin, wollte ich ein weiteres Smart-Home-System eigentlich nicht ausprobieren. Dank des ESPHome-Adapters für ioBroker (leider bisher nur in einer Beta-Version und mit einigen noch nicht unterstützten Funktionen) ist es aber auch mit ioBroker sehr leicht ESPHome zu verwenden. Wenn man das erste mal die Software überträgt, kann man das theoretisch wohl auch an einem USB-Port des Rechners, auf dem der Browser läuft falls man über HTTPS zugreift. Ich bin den Weg über den USB-Port des Raspberry PI gegangen, auf dem ioBroker und damit auch der ESPHome-Adapter läuft. Das funktionierte völlig problemlos. Im Anschluss kann man bei Änderungen einfach über das WLAN die Software aktualisieren (OTA: over the air).
Bei ESPHome hat man deutlich flexiblere Möglichkeiten, wie Sensoren ausgelesen werden sollen und wie evtl. die Werte gefiltert werden sollen. Bei Bedarf hat man sogar die Möglichkeit sehr einfach eigenen C++-Code einzubauen um das Verhalten exakt entsprechend der eigenen Bedürfnisse anzupassen, falls wider erwarten doch mal eine Funktionalität nicht bereits zur Verfügung steht.

Der ESPHome-Adapter unterstützt bisher nur fünf Sensortypen. D.h. für diese fünf Sensortypen ist auch die direkte Unterstützung der entsprechenden Elemente über die ESPHome-API im Adapter verfügbar und die Werte stehen direkt als Objekte des Adapters zur Verfügung. Benutzt man andere Typen, so kann es passieren, dass einfach nur die entsprechenden Werte nicht gelesen werden können, aber auch, dass der gesamte ESP über diese API nicht ansprechbar ist, also auch die Sensoren mit Typen, die eigentlich unterstützt werden.
Abhilfe schafft in dem Fall mal wieder MQTT. Es ist zwar nicht ganz so einfach das in ESPHome einzurichten (im Vergleich zu ESPEasy) aber letzten Endes ist auch das keine große Hürde.

Die einfache Art, wie man mit einem ESP verschiedenste Hardware ansprechen kann hat mit dazu motiviert mal wieder etwas mehr mit Elektronik zu basteln. Inzwischen haben meine Pflanzen Feuchtigkeitssensoren, es gibt einen weiteren Knopf auf meinem Nachtisch, der zur Steuerung der Lampen in meinem Schlafzimmer dient und, nachdem nun die Feuchtigkeit der Erde meiner Pflanzen automatisch erkannt wird, ist natürlich eine kleine Station mit Pumpen in Arbeit.

Hier die ESPHome-Konfiguration für den WeMos D1 Mini mit angeschlossenem MH-Z19B. Die automatische Kalibrierung habe ich deaktiviert und einen Switch definiert über den ich eine Kalibrierung des Sensors triggern kann.

esphome:
  name: co2-sniffer2

esp8266:
  board: d1_mini

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: !secret ap_ssid
    password: !secret ap_password

ota:
  password: !secret ota_password

# Enable logging
logger:

# Enable Home Assistant API
api:
  password: !secret api_password

switch:
  - platform: template
    name: "CO2-Calibration"
    turn_on_action:
      - logger.log: CO2-Calibration started2
      - mhz19.calibrate_zero: mhz19_id

web_server:
  port: 80

uart:
  rx_pin: GPIO4
  tx_pin: GPIO5
  baud_rate: 9600

sensor:
  - platform: mhz19
    id: mhz19_id
    co2:
      name: "CO2 Value"
    temperature:
      name: "Temperature"
    update_interval: 10s
    automatic_baseline_calibration: false

  - platform: wifi_signal
    name: "Wifi Power"
    update_interval: 10s

captive_portal:

Von OpenHAB zu ioBroker

Nachdem ich letztes Jahr mit OpenHAB mein Smart Home startete, bin ich nun zu ioBroker gewechselt und muss sagen, der Umstieg hat viele Vorteile gebracht.

Bei ioBroker werden die Zigbee Geräte durch einen eigenen Adapter unterstützt. Dadurch kann man neue Geräte relativ einfach durch Bedienung der Weboberfläche hinzufügen. Auch der Ärger durch parsen der JSON-Daten des MQTT entfällt.

Leider gibt es keinen Skript-Interpreter für Python Skripte im ioBroker. Nun gut, lerne ich halt JavaScript. Dafür ist die Unterstützung von Blockly im ioBroker deutlich besser. Es sind alle benötigten Funktionen als Elemente vorhanden. Da man sich für ein erstelltes Blockly auch den JavaScript Code anzeigen lassen kann, kann man sehr einfach herausfinden, welche Funktionen man in JavaScript benötigt. Das erleichtert das Lernen ungemein.

Nachteil an ioBroker: Mein Beamer wurde nicht unterstützt.

Am Anfang habe ich das darüber gelöst, dass ich den Beamer weiterhin über OpenHAB angesteuert habe. Im ioBroker gibt es einen OpenHAB-Adapter, über den man die entsprechenden Datenpunkte aus OpenHAB ansprechen kann. Da das am Ende der einzige Grund war, OpenHAB noch laufen zu lassen, habe ich mich entschlossen kurzerhand einen entsprechenden Adapter für ioBroker selber zu schreiben. Dies ist nicht sehr aufwendig und ging dementsprechend schnell (siehe hier).

OpenHAB 3 und Zigbee-Geräte

Da steht man im Ikea, möchte Rollos für seine Wohnung besorgen und sieht FYRTUR mit Motor – sehr nett. Zu Hause stellt man dann fest, dass die Rollos Zigbee sprechen und da man sowieso noch einen Raspberry PI rumstehen hat, besorgt man ihm kurzerhand einen Zigbee-USB-Adapter und schon wird die Lernkurve steil.

Ein paar Tipps dazu:

  • Ein Raspberry 3 Modell B (1 GB Hauptspeicher) ist mit OpenHAB 3 ganz schnell an den Grenzen des Hauptspeichers angekommen. OpenHAB ist in Java implementiert und geht alles andere als sparsam mit dem Hauptspeicher um
  • Beim Eintragen neuer Geräte in die Konfigurationsdatei von Zigbee2mqtt (configuration.yaml) muss man, wenn man einen Friendly-Name eingetragen hat zigbee2mqtt neu starten bevor man das nächste Gerät hinzufügt, ansonsten sind die eigenen Änderungen direkt wieder weg.
  • Das Hinzufügen eines Zigbee-Gerätes in OpenHAB ist mir am Anfang, als Neuling der Materie, sehr unklar gewesen und ich habe lange gebraucht, aufgrund eher schlechter Beschreibungen im Netz, herauszufinden, wie ich die Kanäle eines Zigbee-Gerätes konfigurieren muss um tatsächlich Daten empfangen und senden zu können:
    • Die Kommunikation läuft über MQTT, wie Zigbee2mqtt schon verät.
    • Man fügt in OpenHAB einen MQTT Broker hinzu
    • Nun kann man Generic MQTT Things erzeugen
    • Jedem Ding kann man nun die entsprechenden Kanäle hinzufügen für alle auslesbaren Werte und mögliche Steuerungen.
    • Das MQTT State Topic muss dabei auf zigbee2mqtt/<zigbeename> gesetzt werden (<zigbeename> ist dabei der friendly name, welchen man in der zigbee2mqtt-Konfiguration hinterlegt hat) – nur benötigt, wenn der entsprechende Kanal gelesen werden kann.
    • Das MQTT Command Topic muss auf zigbee2mqtt/<zigbeename>/set gesetzt werden – nur benötigt wenn auf dem entsprechenden Kanal geschrieben werden kann.
    • Damit man dann auch sinnvolle Werte erhält muss man Show advanced aktivieren und bei Incoming Value Transformations den Text JSONPATH:$.<kanalname> eintragen (nur wenn der Kanal gelesen werden kann)
      und bei Outgoing Value Format so etwas wie {„<kanalname>“:%s} eintragen (nur wenn auf dem Kanal geschrieben werden kann).
      Hat man hierbei einen Zahlenwert, so muss statt %s im Outgoing Value Format %d eingetragen werden.

Die entsprechenden Kanalnamen kann man für das entsprechende Gerät in der Liste der unterstützen Geräte nachlesen. Dort steht für jedes Gerät auch die Anleitung, wie das Gerät dem Netzwerk hinzugefügt werden kann.

Und falls mal ein Gerät in der Liste steht, jedoch bei Euch einfach nicht funktionieren möchte, könnte es sein, dass Ihr zigbee2mqtt aktualisieren müsst. Auf der Website ist der aktuelle Stand der unterstützen Geräte. In einer älteren Version sind womöglich weniger Geräte unterstützt und dementsprechend nicht verwendbar.

Falls das noch nicht klappen sollte, überprüft, dass das Add-on JSONPATH Transformation eingebunden ist unter Einstellungen – Add-ons – Transformations.

Nachtrag: Natürlich gibt es auch das TRÅDFRI Gateway von Ikea, welches auch als eigenes Gerät in OpenHAB unterstützt wird. Damit fällt das Eintragen der einzelnen Geräte über Generic MQTT Things weg, allerdings redet das Gateway (vermutlich) nur mit Ikea-Geräten. Da ich mir direkt auch Geräte anderer Hersteller besorgt habe (Fensterkontakte, Temperatur- und Luftfeuchtigkeits-Sensor), war das kein Weg, den ich gehen wollte.

Torg again

Das Rollenspiel Torg wurde letztes Jahr als Torg Eternity in Englisch neu aufgelegt. Jetzt soll es auch auf Deutsch bei Ulisses erscheinen. Das Crowdfunding dafür geht noch bis zum 15.8.2018, der Versand erfolgt vermutlich Anfang 2019.
Ich bin gespannt, das Original hab ich damals sehr gerne gespielt.

Windows und Focus follows Mouse

Der Standard unter Windows ist: Wenn Du mit einem Fenster arbeiten möchtest, klick es an. Es kommt dann in den Vordergrund und Du kannst damit arbeiten.
Möchtest Du in einem Fenster Eingaben machen, dabei aber ein anderes Fenster sehen, so ist die Windows-Lösung: Mache das Fenster in dem Du tippen möchtest so klein, dass Du das Fenster, welches Du noch sehen möchtest, daneben platzieren kannst. Im Zweifelsfall leiste Dir einen weiteren Bildschirm damit Du genug Platz dafür hast.

Es gibt ein anderes Konzept, welches sogar zum Teil von Windows auch unterstützt wird: Focus follows mouse.
Hierbei bekommt ein Fenster, über welches man den Mauszeiger bewegt, den Eingabefokus, ohne dass man das Fenster anklicken muss.
Wenn dabei das Fenster, welches nun den Fokus bekommt sich auch direkt nach oben drängelt, ist das Ganze echt lästig. Wenn man sich nur den Klick zur Aktivierung des Fensters sparen möchte sollte man an dieser Stelle eine kleine Verzögerung einstellen.
„Windows und Focus follows Mouse“ weiterlesen