Gaszähler anbinden

Posted on So 19 November 2023 in Computer & Electronics

Wir haben eine Gasheizung. Nun steht das Haus nicht in der Stadt, sondern in einem kleinen Dorf und so gab es keine Gasleitung. Stattdessen haben wir einen großen unterirdischen Propangastank im Vorgarten verbuddelt. Auch das funktioniert sehr gut, aber einen Nachteil hat das ganze: Es ist furchtbar lästig, immer wieder mal zum Tank zu müssen, um den aktuellen Füllstand abzulesen. Und so habe ich noch einen Gaszähler einbauen lassen. Und den möchte ich ganz gerne elektronisch auslesen und in HASSIO einbinden. Auf diese Weise merken wir nicht nur viel bequemer wenn das Gas zur Neige geht, sondern man kann auch ein bisschen den Gasverbrauch in Abhängigkeit von Parametern wie der Außentemperatur, oder der angestrebten Raumtemperatur analysieren.

Zähler auslesen

Die meisten Zähler haben bereits einen Mechanismus dafür. Typischerweise einen Magneten, der an einer der Zählerwalzen angebracht ist. Und wenn man da einen Reedkontakt oder Halleffektsensor anbringt, kann man die Umdrehungen zählen. Mein Zähler hat aber keinen Magneten, sondern so eine kleine Metallplatte auf einem Zeiger – der Hersteller nennt das Cyble Target:

Das Metallplättchen wird dann mit einem induktiven Näherungssensor registriert. Es gibt vom Hersteller einen fertigen Lesekopf. Alternativ kann man das sicher auch selber bauen – z.B. mit einem LJ12 A3-4-Z/BX. Aber ich hab grade nicht so irre viel Zeit und habe deshalb den Original-Lesekopf gekauft:

Das Loch in der Mitte ist für die Befestigungsschraube, der helle Knopf auf der Rückseite scheint den Lesekopf einzuschalten.

Der Sensor enthält eine (offiziell nicht wechselbare) Lithiumbatterie und wird über ein 2-adriges Kabel an ein geeignetes Lesegerät angeschlossen. Die beiden Drähte verhalten sich genauso wie ein Reed-Kontakt – also open/closed. Die Pulslänge beträgt laut Datenblatt 65ms. Bei meinem konkreten Gaszähler sollte jeder Puls einem Volumen von 0,01m³ = 10l entsprechen. Das entspricht einer 360 Grad Drehung des Cyble Targets. Ich wünschte die Auflösung wäre 1l, aber es ist wie es ist.

Ich hätte den Lesekopf nun eigentlich gerne geöffnet und mir das Innenleben genauer angeschaut, aber leider gibt es keine Schrauben und meine Versuche, die Gehäusehälften zum separieren zu bewegen scheiterten. Ich denke, da muss ich mit dem Dremel ran, wenn die Batterie eines Tages den Geist aufgibt.

So lang der Kopf nicht montiert, bzw. der Knopf auf der Rückseite nicht gedrückt ist, zeigt das Multimeter "overload" wenn man den Widerstand zwischen den beiden Adern misst. Ist er aber gedrückt messe ich ca. 50MΩ. Als nächstes montieren wir den Lesekopf und versuchen das Signal aufzufangen.

Dazu brauchen wir eine Spannungsquelle, einen Widerstand und das Oszilloskop. Laut Datenblatt verträgt der Lesekopf max 30V und bis zu 100mA. Zum Testen nehmen wir mal eine 9V Batterie und einen 4.7kΩ Widerstand. Mit dem Oszilloskop greifen wir dann die Spannung über dem Widerstand ab:

Schließt der Lesekopf den Kontakt fällt dann der Löwenanteil der Batteriespannung an unserem Widerstand ab. Und das wollen wir jetzt messen. Und weil ich keine Lust habe, mein "richtiges" Oszilloskop zum Gaszähler zu schleifen, nehmen wir heute mal mein altes Hantek Scopemeter (DSO1062BV), das ist handlicher. Alles anschließen, Trigger auf Single stellen und abwarten. Irgendwann ist die Heizung angesprungen und fing an Gas zu ziehen und wir messen den versprochenen Puls von 65ms:

Das ist in der Tat ein schöner sauberer Puls. Der leichte over-/undershoot dürfte daher rühren, dass ich zu faul war, den Tastkopf vorher abzugleichen. D.h. wie es aussieht, müssen wir uns keine Gedanken um Debouncing machen, wie es bei einem normalen Reed-Kontakt der Fall wäre.

Hardware

Nun gilt es, das Signal irgendwie zu registrieren. Dazu brauchen wir einen Mikrocontroller, der sich leicht an HASSIO anbinden lässt. Ich habe noch einen nodeMCU-Klon in der Bastelkiste:

Hier der Plan:

Unseren Sensor klemmen wir zwischen GPIO5 (Pin D1 des nodeMCU ) und GND und verwenden den internen Pull-Up Widerstand, damit der Pin einen sauberen high-Level hat solang der Sensor offen ist. Damit wir im Falle eines Stromausfalls keine Pulse verpassen, versorgen wir das ESP-Modul mit einer Lithiumzelle mit Strom und laden diese wiederum über ein USB-Ladegerät. Auch wäre es nicht übel, wenn man die Zellenspannung wüsste. Einfach damit man mitkriegt, wenn sich der Akku aus Altersgründen nicht mehr so super laden lässt. Und auch für die Bastler-Ehre. Auch das lässt sich machen, denn der ESP8266 hat einen ADC Pin. Dieser misst 0-1V mit einer Auflösung von 10 bit (also 0-1023). So eine Lithium-Zelle hat eine maximale Spannung von 4.2V. Das ist zuviel. Allerdings befindet sich auf dem Modul schon ein Spannungsteiler der die Spannung vom Modul-Pin A0 vor dem ADC des ESP8266 teilt:

Die SMD-Widerstände sind mit "224" bzw. "104" beschriftet. Das bedeutet im Klartext \(22\cdot 10^4Ω = 220kΩ\) bzw. \(10\cdot 10^4Ω = 100kΩ\).

D.h. der Spannungsteiler hat das Verhältnis \(\frac{320}{100} = 3.2\). Für unsere Zellenspannung von 4.2V ist das noch nicht ausreichend und wir müssen das Teilerverhältnis ändern. Dazu schalten wir einfach einen 110kΩ Widerstand in Serie vor den A0 Pin und bekommen so ein Teilungsverhältnis von \(\frac{430}{100} = 4.3\). Das ist perfekt.

Mein nodeMCU kam mit eingelöteten Pin-Headern (Stiftleisten). Das ist super, wenn man das Modul auf ein Breadboard stecken möchte, aber ich will ja ein Gadget bauen, das auf Dauer zum Einsatz kommt. Ich könnte natürlich trotzdem Steckverbinder verwenden, aber eigentlich möchte ich alle Kontakte lieber anlöten, damit sich auch im Dauerbetrieb nichts losrütteln kann. Also zuerst die Header Pins auslöten. Um mir das zu erleichtern habe ich zunächst die Plastikleiste vorsichtig entfernt und so die Stiftleiste in einen Haufen einzelner Pins verwandelt. Zuerst ein bisschen mit einer Spitzzange am Plastik ziehen und dann mit einem Haken drunter gehen und aushebeln:

Danach kann man die einzelnen Pins schön auslöten. Ich fasse die Pins dazu mit einer medizinischen Klemme, die einrastet, wenn man sie schließt:

Um die Lithiumzelle zu laden verwenden wir ein einfaches TP5400 USB-Lademodul, das über Mikro-USB gespeist wird und dann einen USB-A Lade-Port zur Verfügung stellt – zusammen mit einem Akku hat man dann quasi eine einfache Powerbank:

Nun ist es an der Zeit, das ganze provisorisch zusammenzubauen: Batteriehalter für eine 18650 Zelle an das Lademodul anlöten und gleich auch noch zwei weitere Litzen, die wir dann mit GND bzw. über den 110kΩ Widerstand mit A0 des nodeMCU verbinden (Messung der Zellenspannung).

Dann ein USB-A auf Mikro-USB-Kabel vom Lademodul zum nodeMCU. Und so sieht das in echt aus:

Zuguterletzt das Lademodul noch an einen USB-Ladestecker anschließen und das Ganze in eine einfache Abzweigdose mit klarem Deckel einbauen. Das Gehäuse selbst war schnell gefunden, aber das führt uns sofort zu einer der lästigsten Fragen bei Selbstbauprojekten: Wie befestigen wir die Platine und andere Komponenten darin? Den Batteriehalter konnte ich mit einer kleinen Selbstschneideschraube an einem der vorhandenen Löcher festschrauben (ein tropfen Plastikkleber unter dem Halter schadet auch nicht.) . Und bei der Platine (nodeMCU Modul) nehmen wir meine Lieblingsmethode: Vier Plastikdistanzhülsen (Sechskant mit M3 Gewinde, 6mm Höhe) werden mit M3x5 Maschinenschrauben an den vier Ecken (Löchern) der Platine festgeschraubt. Und dann geben wir je einen Tropfen Plastikkleber auf den Gehäuseboden und setzen die Platine mit den Plastikfüßen in diese kleinen Pfützen. Kurz andrücken und dann ein paar Stunden aushärten lassen. Statt der Distanzhülsen mit Gewinde kann man auch welche ohne nehmen und dazu passende Selbstschneideschrauben. So schauen meine Füßchen dann aus:

Sehr gut. Nun alles verkabeln und nochmal testen. Und dabei feststellen, das ich das nodeMCU Modul zu weit an den Rand gepappt habe und der Micro-USB Stecker nun nicht mehr reingeht. Und außerdem feststellen, das die beiden USB-Buchsen am Lademodul so nah beieinander liegen, dass man bei normalen Steckern nicht beide zugleich rein bringt. Grumpf!

Egal – dann anders: Wir löten die Verbindungen einfach und nehmen keine/weniger Stecker: Am Lademodul gibt es vorgesehene Lötstellen für \(V_{in}\), d.h. wir sparen uns den Mikro-USB-Stecker. Für den Spannungs-Output nehmen wir ein normales USB-Kabel und schneiden den Mikro-USB-Stecker ab. Die Leiter für GND(schwarz) und VCC(rot) löten wir dann direkt auf den Spannungsregler (Pins 1, 3) auf dem Node-MCU. Das ist ein AMS1117:

Pin Funktion
1 Ground/Adjust
2 VOUT
3 VIN

Das Laderegler-Modul hat keine Löcher an denen man es montieren könnte. Also pappen wir es einfach an der Gehäusewand fest. Dafür nehme ich so einen chinesischen Kleber, namens B7000 der dafür gedacht ist, Die Rückseiten oder Displays von Handys festzukleben. Den mag ich gerne, weil er einigermaßen elastisch bleibt und notfalls mit etwas Hitze wieder zu lösen ist. Noch je ein Loch für Lade- und Sensorkabel ins Gehäuse machen und mit je zwei Kabelbindern eine Zugentlastung basteln. Fertig:

ESPhome

Als nächstes brauchen wir eine Firmware für das ESP-Modul. Ich mag aktuell ESPhome sehr gerne und so nehmen wir das. TASMOTA würde bestimmt auch gehen, aber das behandeln wir heute nicht. Mein Ziel ist es, den eigentlichen Pulszähler im ESP Modul abzubilden, so dass dieses immer den aktuellen Zählerstand weiß. Im Modul ist das besser aufgehoben, als im Homeassistant. Letzter kann dann Statistik treiben, Momentanverbrauch ermitteln, Tankfüllstand berechnen etc.

Im Folgenden brauchen wir immer wieder Pin-Bezeichnungen, um verschiedene Sachen anzusteuern. Für meinen nodeMCU gibt es diverse Mappings, sodass man die Bezeichnungen des Moduls verwenden kann. Eine Übersicht dazu findet Ihr hier.

ESPhome bringt schon ein paar Sachen mit, die genau passend klingen: pulse_counter, oder pulse_meter. Damit habe ich zunächst ein bisschen rumgespielt, aber am Ende erschien es mir einfacher und klarer, das selbst aus simplen Komponenten aufzubauen.

Zunächst brauchen wir eine globale Variable für den Zählerstand (Anzahl Pulse) und einen binary_sensor, für die Impulse vom Lesekopf:

globals:
  - id: count_reading
    type: int

binary_sensor:
  - platform: gpio
    id: pulse_sensor
    name: "Pulse Sensor"
    pin:
      number: D1  # GPIO5
      inverted: true
      mode:
        input: true
        pullup: true
    on_press:
      then:
        - lambda: id(count_reading) += 1;

Wenn Ihr statt eines richtigen Lesekopfs einen Reed-Kontakt habt, dann müsst Ihr Euch um Debouncing kümmern. Idealerweise mit einem Hardware-Modul. Notfalls aber auch in Software:

binary_sensor:
- platform: gpio
  [...]
  filters:
    - delayed_on: 100ms

Und außerdem fände ich es hübsch, wenn die on-board LED des Moduls jedesmal kurz blinken würde wenn ein Puls kommt. Also definieren wir erstmal die LED...

output:
  - platform: gpio
    id: 'LED'
    pin: D4
    inverted: True

und ergänzen die on_press Sektion für den binary_sensor noch um die Ansteuerung:

on_press:
  then:
    - lambda: id(count_reading) += 1;
    - output.turn_on: LED
    - delay: 0.5s
    - output.turn_off: LED

Zählen sollte es jetzt, aber ich fände es gut, wenn wir hier Gleichstand mit der Anzeige des Gaszählers hätten. Das macht es leichter zu prüfen, ob unser ESP Zähler korrekt arbeitet. Dazu muss ESPhome aber wissen bei welchem Zählerstand wir starten. Außerdem ist das alles noch nicht 100% idiotensicher, denn sollte unser Modul doch mal den Strom verlieren, gehen nicht nur die Pulse verloren, die während des Blackouts kamen, sondern das Modul verliert auch den bisherigen Stand. Damit das nicht passiert, müssen wir noch konfigurieren, dass der Messwert im Flash Speicher gesichert und bei einem reboot wieder ausgelesen wird. Dazu rüsten wir unsere globals Sektion ein bisschen auf:

globals:
  - id: count_reading
    type: int
    restore_value: true      # restore from flash
    initial_value: '40900'  # initial meter reading in counts, 10l = 1 count

Außerdem muss restore_from_flash noch gesetzt werden:

esp8266:
  board: nodemcuv2
  restore_from_flash: true

Zum Schluss müssen wir noch unseren Akku-Spannungs-Sensor einrichten:

sensor:
  - platform: adc
    name: "Battery Voltage"
    pin: A0
    unit_of_measurement: 'V'
    filters:
      - multiply: 4.3
    update_interval: 600s

Jetzt sollte alles funktionieren und wir können die Firmware in das Modul flashen und schon kann es weitergehen. Zunächst mal schauen, was unser Eigenbau-Zähler so von sich gibt:

Sehr schön – alles scheint zu funktionieren.

Einbindung in HASSIO

Die Einbindung in HASSIO erfolgt dann über die ESPhome Integration, so dass wir sehr simpel an unsere Werte kommen. An dieser Stelle haben wir dann schon mal den aktuellen Zählerstand und die Akkuspannung:

Und wenn man drauf klickt auch den Verlauf:

Und wir binden das noch schnell ins Energy-Dashboard ein: Settings > Dashboards > Energy > Add gas source. Und dort den passenden Sensor auswählen. Nun heißt es eine Weile warten, denn es kann auch mal ein paar Stunden dauern, bis der Gasverbrauch nun im Energy-Dashboard auftaucht. Doch dann werden wir mit einer schönen Verbrauchskurve belohnt:

Tankfüllstand – oder 1 Liter ≠ 1 Liter

Wenn Euer Gas aus der Leitung kommt ist das Folgende nicht mehr relevant für Euch. Aber ich habe einen Gastank und so muss ich erstmal herausfinden, wieviel Gas eigentlich in selbigem ist. Also schauen wir mal in das Datenblatt:

Totalvolumen [l] 4850
Füllmenge [l] 4120
Füllmenge [kg] 2100

D.h. man kann den Tank auf maximal 85% füllen. Und so passiert das auch jedes mal wenn wir neues Gas tanken. Ein Prozentpunkt entspricht dabei \(\frac{4850l}{100} = 48.5l\).

Und wenn man kein Datenblatt hat kann man das auch sehr schön anhand der letzten Lieferbelege nachrechnen, die typischerweise angeben, wieviel Liter Gas geliefert wurden (\(V\)) und was der prozentuale Stand vor (\(P_0\)) und nach Füllung (\(P_1\)) war. Also beträgt der Faktor:

$$ f = \frac{V}{P_1 - P_0} $$

Wenn wir also bei 22% Füllstand tanken und dann 3059l geliefert bekommen und anschließend bei 84% sind:

$$ f = \frac{3059l}{84\% - 22\%} $$
$$ f = \frac{3059l}{62\%} $$
$$ f = 49.34l/\% $$

Das liegt tatsächlich sehr nah an den weiter oben berechneten 48.5l/%. Da v.a. der prozentuale Füllstand ziemlich fehlerbehaftet ist, sollte man das mal mit den letzten 5 Gasrechnungen durchexerzieren, um den Faktor korrekt zu berechnen.

Nun muss ich also nur noch am Gaszähler ablesen, wieviel Gas seit der Lieferung verbraucht wurde und das vom maximalen Tank-Füllstand abziehen und schon kenne ich das aktuelle Gasvolumen. Falsch!!!

Wieso? Weil das Propan in meinem Tank nicht gasförmig, sondern flüssig ist! Deshalb heißt das ja auch Flüssiggas. Bei der Entnahme aus dem Tank wird es verdampft und kommt entsprechend gasförmig am Zähler an. Also müssen wir das Ganze umrechnen. Aber wieviel Liter Gas werden aus einem Liter flüssigem Propan?

Unser Datenblatt sagt, dass ich bei maximalem Füllstand von 85% 2100kg Flüssiggas habe und das einem Volumen von 4120l entspricht. Im flüssigen Zustand beträgt die Dichte also:

$$ \varrho_{fl} = 2100kg / 4120l = 0.510 kg/l $$

Im gasförmigen Zustand hingegen beträgt die Dichte \(\varrho_{gas} = 2.01 kg/m³ = 0.00201 kg/l\) (Quelle). Und aus einem Liter Flüssiggas werden dann im gasförmigen Zustand:

$$ 0.51kg / 0.00201 kg/l = 254l$$

Diesen Faktor müssen wir berücksichtigen, wenn wir ausrechnen wieviel Gas noch im Tank ist. Aus meinem Gastank bekomme ich also maximal \(4120l \cdot 254 = 1047000l = 1047 m³\) gasförmiges Propan. Bei diesen Rechnungen sollte man es mit der Anzahl der signifikanten Stellen nicht übertreiben, denn alle Dichte-Werte sind temperaturabhängig! Aber hier geht es ja auch nicht darum, Aussagen im Milliliterbereich zu machen, sondern grob mitzubekommen was man verbraucht und v.a. wann Tanken angesagt ist.

Tankfüllstand in HASSIO

Nun wollen wir mal Berechnung des Tankfüllstands umsetzen.

Ablesung nach Füllung

Als erstes brauchen wir einen Weg, den Tankfüllstand (%) und den aktuellen Zählerstand unmittelbar nach der Füllung einzugeben. Dazu erstellen wir zwei input_number helpers in configuration.yaml:

input_number:
  gas_percent_fill:
    name: Initial Gas Tank Level  # Typically 85% after fill
    unit_of_measurement: "%"
    icon: mdi:storage-tank
    min: 0
    max: 100
    step: 1
    mode: box
  gas_reading_fill:
    name: Initial Gas Meter Reading # Gas meter reading after last fill
    unit_of_measurement: "m³"
    icon: mdi:meter-gas-outline
    min: 0
    max: 99999.99
    step: 0.01
    mode: box

Und binden diese nun in das Interface ein:

Den Gaszähler habe ich erst nach dem letzten Tanken bekommen und meine erste Ablesung war dann später, als der Tank-Füllstand schon auf 68% gesunken war.

Template Sensoren für berechnete Werte

Als nächstes brauchen wir dann ein paar Sensoren der Kategorie template sensor in denen wir unser ganzes Wissen zu Dichte und Füllständen verwursten:

template:
  - sensor:
    - name: "Gas used since fill" # liters of gas used since last fill
      unit_of_measurement: "m³"
      icon: mdi:meter-gas-outline
      state: >
        {% set v0 = states('input_number.gas_reading_fill') | float %}
        {% set v1 = states('sensor.gas_meter_reading') | float %}
        {{ v1 - v0 }}
    - name: "Liquid gas used since fill"  # volume of liquid gas used up since last fill
      unit_of_measurement: "l"
      icon: mdi:storage-tank-outline
      state: >
        {% set v0 = states('input_number.gas_reading_fill') | float %}
        {% set v1 = states('sensor.gas_meter_reading') | float %}
        {{ ((v1 - v0) * 1000 / 254.0) | round(0) }}
    - name: "Liquid gas remaining"  # Volume of liquid gas left in the tank
      unit_of_measurement: "l"
      icon: mdi:storage-tank
      state: >
        {% set maxvol = 4850 | float %}
        {% set p0 = states('input_number.gas_percent_fill') | float %}
        {% set v0 = p0 * maxvol / 100 | float %}
        {% set v1 = v0 - states('sensor.liquid_gas_used_since_fill') | float %}
        {{ v1 | float }}
    - name: "Gas remaining" # volume of gaseous gas left
      unit_of_measurement: "m³"
      icon: mdi:meter-gas
      state: >
        {% set v1 = states('sensor.liquid_gas_remaining') | float %}
        {{ (v1 / 1000 * 254) | round(0) }}
    - name: "Gas remaining percent" # Percent gas left wrt to maximum fill level of 85%
      unit_of_measurement: "%"
      icon: mdi:storage-tank
      state: >
        {% set maxvol = (4850 * 0.85) | float %}
        {% set v1 = states('sensor.liquid_gas_remaining') | float %}
        {{ (v1 / maxvol * 100) | round(0) }}
   - name: "Gas Tank Level"  # Percent fill level of gas tank. 85% max.
      unit_of_measurement: "%"
      icon: mdi:storage-tank
      state: >
        {% set maxvol = 4850 | float %}
        {% set p0 = states('input_number.gas_percent_fill') | float %}
        {% set v0 = p0 * maxvol / 100 | float %}
        {% set v1 = v0 - states('sensor.liquid_gas_used_since_fill') | float %}
        {{ (v1 / maxvol * 100) | round(0) }}

Und die können wir dann ins User Interface einbinden und bekommen dann zusammen mit den zuvor konfigurierten Sachen eine tolles Gas Panel:

Preisfrage

Und was mache ich, wenn der ESP-Pulszähler doch mal außer Takt kommt, oder ich einen neuen Gaszähler bekomme, mich einfach vertippt habe, oder sowas? Dann muss ich den Startwert ändern. Aber das alleine hilft nicht, denn das Modul liest ja den letzten Stand aus dem Flash-Speicher und verwendet den fixen Startwert nur, wenn das fehlschlägt. Zuerst dachte ich ich könnte einfach temporär restore_values auf false setzen, so dass einmalig der Startwert verwendet wird. Aber das hilft nicht, denn dann werden die Werte auch nicht im Flash Speicher gesichert. D.h. es scheint keinen Ausweg aus diesem Teufelskreis zu geben. Gibt es zum Glück aber doch.

Ich habe zwei verschiedene Ansätze gefunden:

Der erste verwendet einen factory_reset switch in unserer ESPhome Firmware:

switch:
  - platform: factory_reset
    name: factory reset

In Homeassistant kann man dann auf das entsprechende Element zugreifen und den "Factory reset" in der GUI auslösen. D.h. ich kann dann einen Startwert in die ESPhome Firmware einbauen, der noch nicht ganz erreicht ist und dann am Gaszähler lauern, bis er genau erreicht ist und den Factory-Reset auslösen, so dass der eingebaute Startwert nun verwendet wird. Damit wäre das Problem eigentlich gelöst – wenn auch nicht sehr elegant. Der Reset ist eigentlich dann perfekt, wenn man immer auf den selben Startwert zurück will – typischerweise auf Null.

Für meinen Anwendungsfall gibt es aber etwas besseres: den homeassistant sensor. Dabei handelt es sich um einen Mechanismus, Werte über die API direkt aus HASSIO zu bekommen. Z.B. einem input_number Element. Das gefällt mir besser und so wird's gemacht:

Zunächst den Sensor in die ESPhome-Konfiguration einbauen:

sensor:
  - platform: homeassistant
    id: set_gas_counter
    entity_id: input_number.gas_initial_value
    on_value:
      then:
        - lambda: id(count_reading) = int(x * 100);

Und dann das dazugehörige input_number Feld in HASSIO anlegen:

input_number:
  gas_initial_value:
    name: Initial value for ESPhome counter
    unit_of_measurement: "m³"
    icon: mdi:meter-gas-outline
    min: 0
    max: 99999.99
    step: 0.00
    mode: box

Und schon können wir das Input-Feld in ein Dashboard einbauen:

Jedesmal, wenn man diesen Wert anfasst, wird er dann automatisch im ESP gesetzt. D.h. dieses Element sollte man evtl. nicht allen HASSIO-Nutzern freigeben, sondern es auf eine Admin-Seite legen.

Alles in allem sieht die Logik nun also so aus:

  1. ESP startet zum allerersten mal: Zählerstand wird aus initial_value genommen
  2. ESP rebootet später mal: Zählerstand wird aus dem Flash-Speicher wiederhergestellt
  3. User gibt in HASSIO einen neuen Zählerstand ein: Der Zähler wird im ESP auf den eingegebenen Wert gesetzt (und im Flash gesichert). Ab hier geht es dann wieder bei 2. weiter.

Fazit

Das war eine schöne Bastelei und ich freue mich schon auf die nun mögliche Übersicht des Gasverbrauchs und vor allem, dass ich immer im Blick habe wann ich wieder tanken muss. Der Pulszähler scheint zuverlässig zu funktionieren ich habe ihn über viele Tage beobachtet und er hat im Vergleich zum Zähler keinen Impuls verloren.

So praktisch ich Homeassistant finde, ist dennoch zu sagen, dass die Konfiguration in den YAML Files eine sehr hakelige Sache ist. Bis die obigen Konfigurationen fertig waren und tatsächlich funktionierten ist echt viel Zeit ins Land gegangen. Für simple Dinge ist die YAML config ok, aber sobald es etwas komplexer wird, wäre es tausendmal einfacher ein paar Zeilen Python zu schreiben, als durch endlose Google-Suchen herauszufinden, wie man das nun wieder in die YAML Syntax presst...

Und weil die obige Konfiguration inzwischen relativ komplex geworden ist und wir auf dem Weg auch immer mal wieder was geändert haben gibt es meine endgültige Konfiguration hier zum download: ESPhome config und hassio configuration.yaml (only the parts relevant to gas emter plus general framework). Die ESPhome config enthält auch noch den oben erwähnten factory_reset sensor, auch wenn er eigentlich nicht gebraucht wird.

Und mit meinem Laderegler-Modul bin ich nicht ganz glücklich. Das Modul hat entgegen der Beschreibung keinen Tiefentladeschutz. Beim ersten Versuch ohne Ladestrom hat es meine Zelle bis auf 2.2V geleert. Erlaubt sind aber minimal 2.5V. D.h. das Modul tötet Lithiumzellen. Zudem scheint es, dass es nicht in der Lage ist, den Akku zu laden, während es den Verbraucher mit Strom versorgt. Ich werde es mittelfristig wohl gegen ein besseres tauschen. Sollte jemand eine Empfehlung haben – immer her damit.

Updates

2023-12-02

OK – gestern Nacht gab es einen Stromausfall. Und mein tolles Konzept hat versagt: Der ESP musste neu starten – offenbar hat mein Akkumodul es nicht geschafft, die Versorgungsspannung unterbrechungsfrei sicherzustellen. Zudem hat das Modul dann nicht den letzten Zählerstand aus dem Flash wiederhergestellt, sondern den letzten Wert aus dem homeassistant Sensor. Das war zwar leicht wieder zu korrigieren, aber mir ist gerade nicht klar, warum das passiert. Grumpf.

2024-01-12

Ich habe das Phänomen inzwischen in einem neuen Post genauer untersucht und musste feststellen, dass ich vollkommen unrecht habe und der Grund ein ganz anderer ist...

2024-02-27

Die Aussetzer sind durch die Änderungen im letzen Post viel seltener geworden. Aber wenn es doch mal zu einem Reboot von HA oder ESPhome kommt, dann wird er Zählerstand nach wie vor jedes mal auf den letzten manuell eingegeben Stand zurückgesetzt. das ist suboptimal. Also habe ich mich in einem dritten Post damit befasst, das endgültig zu lösen.