sphings-dev.de

Raumklima-Alarme mit Nachprüfung und Entwarnung in Home Assistant

Zwölf Räume mit Temperatur- und Feuchtesensoren, vier Arten von Alarm (zu feucht, zu trocken, zu kalt, zu warm) und mehrere Handys als Empfänger: Das ist schnell eine Flut aus Meldungen, wenn man die Sache naiv baut. Ich habe die Aufgabe auf drei Automationen verteilt. Eine meldet, eine gibt Entwarnung, und eine dritte prüft regelmäßig nach, was die ersten beiden verpasst haben könnten.

Was du brauchst

  • Temperatur- und Feuchtesensoren in den Räumen (bei mir Wandthermostate, Zigbee o. ä.)
  • Pro Meldekategorie einen input_number für Alarmschwelle und einen für Entwarnungsschwelle, dazu Haltezeiten
  • Pro Kategorie zwei input_text-Helfer: einen für bereits gemeldete Räume, einen für Kandidaten der Nachprüfung
  • Einen input_boolean zum Abschalten aller Alarme und einen Sensor für die Nachtruhe (binary_sensor.raumklima_nachtruhe)
  • Optional für den Lüftungstipp: die Integration thermal_comfort für die absolute Luftfeuchte innen und außen sowie einen input_number für den Luftwechsel pro Raum

So funktioniert es

Alle Schwellen und Haltezeiten stehen in Helfern, nicht in der Automation. Bei mir gelten 70 % als zu feucht (Entwarnung bei 65 %), 25 % als zu trocken (Entwarnung bei 30 %), 17 °C als zu kalt (Entwarnung bei 18 °C) und 26 °C als zu warm (Entwarnung bei 25 °C). Die Entwarnungsschwellen liegen bewusst innerhalb der Alarmschwellen. Mit dieser Hysterese flattert die Meldung nicht in Gegenrichtung. Die Haltezeit liegt bei 60 Minuten für Feuchte und 30 Sekunden für Temperatur, die Entwarnung wartet 30 Minuten.

Die Alarm-Automation hat vier Trigger, einen pro Kategorie, jeweils über alle Sensoren zugleich. Aus der auslösenden Entität errechnet sie einen Kurzcode für den Raum (kue, wz, beg). Bereits gemeldete Räume stehen als kommaseparierte Liste in einem input_text je Kategorie. Steht der Code dort, passiert nichts. Das ist der Wiederholungsschutz. Die Entwarnung entfernt den Code wieder, wenn der Wert die Entwarnungsschwelle erreicht hat. Die Nachtruhe unterdrückt alles außer Kälte. Beim Bad im Erdgeschoss gibt es noch eine Ausnahme: Läuft nach dem Duschen die Lüftungsfrist mit offenem Fenster, bleibt die Feuchtemeldung stumm.

Eine Bedingung verwirft Flanken aus unavailable und unknown. Dort zeigt sich der Nachteil flankengesteuerter Alarme: Fällt ein Sensor während der Haltezeit kurz aus, geht die Flanke verloren. Genau dafür gibt es die Nachprüfung. Sie läuft alle 30 Minuten, fünf Minuten nach jedem Start und am Ende der Nachtruhe und prüft alle Räume direkt gegen die Schwellen. Ein Raum wird erst gemeldet, wenn er bei zwei aufeinanderfolgenden Durchläufen auffällig war. Das ersetzt dort die Haltezeit. In der Nachtruhe sendet sie nichts, merkt sich den Zustand aber und holt die Meldung nach Ende der Ruhezeit nach, sofern er dann noch besteht.

Bei „zu feucht“ hängt an der Meldung ein Lüftungstipp: Er rechnet aus der absoluten Feuchte im Raum und im Garten, der Raumtemperatur und einem Luftwechsel pro Raum (Vorgabe 2,0 pro Stunde), wie lange das Fenster offen sein müsste. Ziel ist die Entwarnungsschwelle, wenn Lüften sie erreichen kann, sonst der halbe Weg zum erreichbaren Wert. Bringt Lüften weniger als fünf Prozentpunkte, entfällt der Zusatz ganz: Dann ist es draußen genauso feucht, und der Rat wäre falsch. Bei „zu trocken“ wäre Lüften der falsche Rat, deshalb gibt es dort keinen Tipp.

Die Meldung selbst nennt die Richtung im Klartext (zu feucht, zu trocken, zu kalt, zu warm) und den Messwert, nicht die Schwelle. Der Schwellwert neben dem Messwert war nur Rauschen. Die Warnung „zu warm“ geht nur an ein Handy, die anderen Arten an alle Empfänger.

Die Automation

Im Beispiel stehen drei Räume und zwei Empfänger. Für jeden weiteren Raum ergänzt du Sensor, Kurzcode, Name und die Zuordnungen in den Variablen. Die Rechnung des Lüftungstipps steht nur in der Alarm-Automation vollständig, die Nachprüfung verwendet dieselbe.

alias: "Raumklima: Alarme"
description: >-
  Meldet zu hohe/niedrige Luftfeuchte und zu kalte/warme Räume. Bereits gemeldete
  Räume stehen als Kurzcode in input_text.raumklima_gemeldet_* (Wiederholungsschutz),
  die Entwarnung entfernt sie wieder. Nachtruhe unterdrückt alles außer Kälte.
triggers:
  - trigger: numeric_state
    id: feuchte_hoch
    entity_id: [sensor.kueche_luftfeuchte, sensor.wohnzimmer_luftfeuchte, sensor.bad_eg_luftfeuchte]
    above: input_number.raumklima_schwelle_feuchte_hoch
    for: "{{ '%02d:%02d:00' % ((states('input_number.raumklima_haltezeit_feuchte') | int(60)) // 60, (states('input_number.raumklima_haltezeit_feuchte') | int(60)) % 60) }}"
  - trigger: numeric_state
    id: feuchte_tief
    entity_id: [sensor.kueche_luftfeuchte, sensor.wohnzimmer_luftfeuchte, sensor.bad_eg_luftfeuchte]
    below: input_number.raumklima_schwelle_feuchte_tief
    for: "{{ '%02d:%02d:00' % ((states('input_number.raumklima_haltezeit_feuchte') | int(60)) // 60, (states('input_number.raumklima_haltezeit_feuchte') | int(60)) % 60) }}"
  - trigger: numeric_state
    id: kalt
    entity_id: [sensor.kueche_temperatur, sensor.wohnzimmer_temperatur, sensor.bad_eg_temperatur]
    below: input_number.raumklima_schwelle_temperatur_kalt
    for: {seconds: "{{ states('input_number.raumklima_haltezeit_temperatur') | int(30) }}"}
  - trigger: numeric_state
    id: warm
    entity_id: [sensor.kueche_temperatur, sensor.wohnzimmer_temperatur, sensor.bad_eg_temperatur]
    above: input_number.raumklima_schwelle_temperatur_warm
    for: {seconds: "{{ states('input_number.raumklima_haltezeit_temperatur') | int(30) }}"}
conditions:
  - {condition: state, entity_id: input_boolean.raumklima_alarme_aus, state: "off"}
  - alias: Nur echte Flanken – nicht aus unavailable/unknown heraus
    condition: template
    value_template: >-
      {{ trigger is defined and trigger.platform == 'numeric_state'
         and trigger.from_state is not none and is_number(trigger.from_state.state) }}
actions:
  - variables:
      codes: "{{ states(liste_entity).split(',') | reject('eq', '') | list }}"
  - alias: Raum bekannt und noch nicht gemeldet
    condition: template
    value_template: "{{ code != '' and code not in codes }}"
  - alias: Nachtruhe – nur Kälte darf durch
    condition: or
    conditions:
      - {condition: trigger, id: kalt}
      - {condition: state, entity_id: binary_sensor.raumklima_nachtruhe, state: "off"}
  - alias: "Bad EG: nach dem Duschen bei offenem Fenster keine Feuchtemeldung"
    condition: or
    conditions:
      - condition: not
        conditions: [{condition: trigger, id: feuchte_hoch}]
      - {condition: template, value_template: "{{ code != 'beg' }}"}
      - {condition: state, entity_id: input_boolean.raumklima_bad_eg_dusch_sperre, state: "off"}
  - action: input_text.set_value
    target: {entity_id: "{{ liste_entity }}"}
    data: {value: "{{ (codes + [code]) | join(',') }}"}
  - repeat:
      for_each: "{{ ['notify.mobile_app_handy_1', 'notify.mobile_app_handy_2'] if tid != 'warm' else ['notify.mobile_app_handy_1'] }}"
      sequence:
        - action: "{{ repeat.item }}"
          continue_on_error: true
          data:
            title: "{{ 'Luftfeuchtigkeitsalarm' if tid in ['feuchte_hoch', 'feuchte_tief'] else 'Temperaturalarm' }}"
            message: "{{ raum }} ist bei {{ wert }} {{ einheit }} – {{ lage_map.get(tid, '') }}.{{ lueftungstipp }}"
mode: queued
max: 15
max_exceeded: silent
variables:
  namen: {kue: Küche, wz: Wohnzimmer, beg: Bad EG}
  code_von_sensor:
    sensor.kueche_luftfeuchte: kue
    sensor.wohnzimmer_luftfeuchte: wz
    sensor.bad_eg_luftfeuchte: beg
    sensor.kueche_temperatur: kue
    sensor.wohnzimmer_temperatur: wz
    sensor.bad_eg_temperatur: beg
  liste_map:
    feuchte_hoch: input_text.raumklima_gemeldet_feuchte_hoch
    feuchte_tief: input_text.raumklima_gemeldet_feuchte_tief
    kalt: input_text.raumklima_gemeldet_kalt
    warm: input_text.raumklima_gemeldet_warm
  einheit_map: {feuchte_hoch: "%", feuchte_tief: "%", kalt: "°C", warm: "°C"}
  tid: "{{ trigger.id if trigger is defined and trigger.id is defined else '' }}"
  sensor_id: "{{ trigger.entity_id if trigger is defined and trigger.entity_id is defined else '' }}"
  code: "{{ code_von_sensor.get(sensor_id, '') }}"
  raum: "{{ namen.get(code, sensor_id) }}"
  wert: "{{ trigger.to_state.state if trigger is defined and trigger.to_state is defined else '' }}"
  liste_entity: "{{ liste_map.get(tid, '') }}"
  einheit: "{{ einheit_map.get(tid, '') }}"
  lage_map: {feuchte_hoch: zu feucht, feuchte_tief: zu trocken, kalt: zu kalt, warm: zu warm}
  abs_von_code:
    kue: sensor.thermal_comfort_kueche_absolute_luftfeuchtigkeit
    wz: sensor.thermal_comfort_wohnzimmer_absolute_luftfeuchtigkeit
    beg: sensor.thermal_comfort_bad_eg_absolute_luftfeuchtigkeit
  temp_von_code:
    kue: sensor.kueche_temperatur
    wz: sensor.wohnzimmer_temperatur
    beg: sensor.bad_eg_temperatur
  lw_von_code:
    kue: input_number.raumklima_luftwechsel_kueche
    wz: input_number.raumklima_luftwechsel_wohnzimmer
    beg: input_number.luftentfeuchter_luftwechsel
  lueftungstipp: >-
    {% set ai = states(abs_von_code.get(code, 'unknown')) | float(0) %}
    {% set ao = states('sensor.thermal_comfort_garten_absolute_luftfeuchtigkeit') | float(0) %}
    {% set t = states(temp_von_code.get(code, 'unknown')) | float(-100) %}
    {% set n = states(lw_von_code.get(code, 'unknown')) | float(0) %}
    {% set zrh = states('input_number.raumklima_entwarnung_feuchte_hoch') | float(65) %}
    {% set mg = states('input_number.raumklima_lueften_mindestgewinn') | float(5) %}
    {% if tid == 'feuchte_hoch' and ai > 0 and ao > 0 and t > -50 and n > 0 and ai > ao %}
      {% set sd = 6.112 * e ** (17.62 * t / (243.12 + t)) %}
      {% set saet = 216.7 * sd / (273.15 + t) %}
      {% set gewinn = (ai - ao) / saet * 100 %}
      {% set zabs = zrh / 100 * saet %}
      {% set zhalb = ai - 0.5 * (ai - ao) %}
      {% set ziel = zabs if zabs > ao else zhalb %}
      {% set dauer = ([[ -60 / n * log((ziel - ao) / (ai - ao)), 0 ] | max, 60 ] | min | round(0) | int) if ai > ziel else 0 %}
      {% set erreicht = (ao + (ai - ao) * e ** (-1 * n * dauer / 60)) / saet * 100 %}
      {% if gewinn > mg and dauer >= 1 %} Fenster etwa {{ dauer }} Minuten weit auf, das bringt dich auf rund {{ erreicht | round(0) | int }} %.{% endif %}
    {% endif %}
alias: "Raumklima: Entwarnung"
description: >-
  Gegenstück zu "Raumklima: Alarme". Meldet, wenn ein gemeldeter Raum die
  Entwarnungsschwelle erreicht hat, und nimmt ihn aus input_text.raumklima_gemeldet_*.
  Die Entwarnungsschwellen liegen innerhalb der Alarmschwellen (Hysterese).
triggers:
  - trigger: numeric_state
    id: feuchte_hoch
    entity_id: [sensor.kueche_luftfeuchte, sensor.wohnzimmer_luftfeuchte, sensor.bad_eg_luftfeuchte]
    below: input_number.raumklima_entwarnung_feuchte_hoch
    for: "{{ '%02d:%02d:00' % ((states('input_number.raumklima_haltezeit_entwarnung') | int(30)) // 60, (states('input_number.raumklima_haltezeit_entwarnung') | int(30)) % 60) }}"
  - trigger: numeric_state
    id: feuchte_tief
    entity_id: [sensor.kueche_luftfeuchte, sensor.wohnzimmer_luftfeuchte, sensor.bad_eg_luftfeuchte]
    above: input_number.raumklima_entwarnung_feuchte_tief
    for: "{{ '%02d:%02d:00' % ((states('input_number.raumklima_haltezeit_entwarnung') | int(30)) // 60, (states('input_number.raumklima_haltezeit_entwarnung') | int(30)) % 60) }}"
  - trigger: numeric_state
    id: kalt
    entity_id: [sensor.kueche_temperatur, sensor.wohnzimmer_temperatur, sensor.bad_eg_temperatur]
    above: input_number.raumklima_entwarnung_temperatur_kalt
    for: "{{ '%02d:%02d:00' % ((states('input_number.raumklima_haltezeit_entwarnung') | int(30)) // 60, (states('input_number.raumklima_haltezeit_entwarnung') | int(30)) % 60) }}"
  - trigger: numeric_state
    id: warm
    entity_id: [sensor.kueche_temperatur, sensor.wohnzimmer_temperatur, sensor.bad_eg_temperatur]
    below: input_number.raumklima_entwarnung_temperatur_warm
    for: "{{ '%02d:%02d:00' % ((states('input_number.raumklima_haltezeit_entwarnung') | int(30)) // 60, (states('input_number.raumklima_haltezeit_entwarnung') | int(30)) % 60) }}"
actions:
  - variables:
      codes: "{{ states(liste_entity).split(',') | reject('eq', '') | list }}"
  - alias: Nur für Räume, die als gemeldet vorgemerkt sind
    condition: template
    value_template: "{{ code != '' and code in codes }}"
  - alias: Nachtruhe – nur Kälte darf durch
    condition: or
    conditions:
      - {condition: trigger, id: kalt}
      - {condition: state, entity_id: binary_sensor.raumklima_nachtruhe, state: "off"}
  - action: input_text.set_value
    target: {entity_id: "{{ liste_entity }}"}
    data: {value: "{{ (codes | reject('eq', code) | list) | join(',') }}"}
  - repeat:
      for_each: "{{ ['notify.mobile_app_handy_1', 'notify.mobile_app_handy_2'] if tid != 'warm' else ['notify.mobile_app_handy_1'] }}"
      sequence:
        - action: "{{ repeat.item }}"
          continue_on_error: true
          data:
            title: "{{ 'Luftfeuchtigkeit wieder normal' if tid in ['feuchte_hoch', 'feuchte_tief'] else 'Temperatur wieder normal' }}"
            message: "Entwarnung: {{ raum }} ist wieder bei {{ wert }} {{ einheit }}"
mode: queued
max: 15
max_exceeded: silent
variables:
  namen: {kue: Küche, wz: Wohnzimmer, beg: Bad EG}
  code_von_sensor:
    sensor.kueche_luftfeuchte: kue
    sensor.wohnzimmer_luftfeuchte: wz
    sensor.bad_eg_luftfeuchte: beg
    sensor.kueche_temperatur: kue
    sensor.wohnzimmer_temperatur: wz
    sensor.bad_eg_temperatur: beg
  liste_map:
    feuchte_hoch: input_text.raumklima_gemeldet_feuchte_hoch
    feuchte_tief: input_text.raumklima_gemeldet_feuchte_tief
    kalt: input_text.raumklima_gemeldet_kalt
    warm: input_text.raumklima_gemeldet_warm
  einheit_map: {feuchte_hoch: "%", feuchte_tief: "%", kalt: "°C", warm: "°C"}
  tid: "{{ trigger.id if trigger is defined and trigger.id is defined else '' }}"
  sensor_id: "{{ trigger.entity_id if trigger is defined and trigger.entity_id is defined else '' }}"
  code: "{{ code_von_sensor.get(sensor_id, '') }}"
  raum: "{{ namen.get(code, sensor_id) }}"
  wert: "{{ trigger.to_state.state if trigger is defined and trigger.to_state is defined else '' }}"
  liste_entity: "{{ liste_map.get(tid, '') }}"
  einheit: "{{ einheit_map.get(tid, '') }}"
alias: "Raumklima: Nachprüfung"
description: >-
  Sicherheitsnetz für "Raumklima: Alarme". Läuft alle 30 Minuten, nach jedem Start
  und am Ende der Nachtruhe. Ein Raum wird erst gemeldet, wenn er bei zwei
  aufeinanderfolgenden Durchläufen auffällig war (input_text.raumklima_kandidat_*).
triggers:
  - {trigger: time_pattern, id: turnus, minutes: "/30"}
  - {trigger: homeassistant, id: start, event: start}
  - {trigger: state, id: ruhe_ende, entity_id: binary_sensor.raumklima_nachtruhe, to: "off"}
conditions:
  - {condition: state, entity_id: input_boolean.raumklima_alarme_aus, state: "off"}
actions:
  - if: [{condition: trigger, id: start}]
    then:
      - delay: "00:05:00"   # Sensoren nach dem Neustart erst melden lassen
  - repeat:
      for_each: [feuchte_hoch, feuchte_tief, kalt, warm]
      sequence:
        - variables:
            kat: "{{ repeat.item }}"
            k: "{{ kategorien[repeat.item] }}"
            sens: "{{ sensoren_map[kategorien[repeat.item].art] }}"
            sw: "{{ states(kategorien[repeat.item].schwelle) | float(0) }}"
            ew: "{{ states(kategorien[repeat.item].entwarnung) | float(0) }}"
            ueber: "{{ kategorien[repeat.item].ueber }}"
        - variables:
            aktuell: >-
              {% set ns = namespace(l=[]) %}
              {% for c, e in sens.items() %}
                {% if is_number(states(e)) and ((states(e) | float(0) > sw) if ueber else (states(e) | float(0) < sw)) %}
                  {% set ns.l = ns.l + [c] %}
                {% endif %}
              {% endfor %}
              {{ ns.l }}
            gemeldet: "{{ states(k.liste).split(',') | reject('eq', '') | list }}"
            kandidaten: "{{ states(k.kandidat).split(',') | reject('eq', '') | list }}"
            ruhe: "{{ kat != 'kalt' and is_state('binary_sensor.raumklima_nachtruhe', 'on') }}"
        - variables:
            entwarnt: >-
              {% set ns = namespace(l=[]) %}
              {% for c in gemeldet %}
                {% set e = sens.get(c, '') %}
                {% if e != '' and is_number(states(e)) and ((states(e) | float(0) < ew) if ueber else (states(e) | float(0) > ew)) %}
                  {% set ns.l = ns.l + [c] %}
                {% endif %}
              {% endfor %}
              {{ ns.l }}
            neu: >-
              {% set ns = namespace(l=[]) %}
              {% for c in aktuell %}
                {% if c not in gemeldet and c in kandidaten
                      and not (kat == 'feuchte_hoch' and c == 'beg' and is_state('input_boolean.raumklima_bad_eg_dusch_sperre', 'on')) %}
                  {% set ns.l = ns.l + [c] %}
                {% endif %}
              {% endfor %}
              {{ [] if ruhe else ns.l }}
        - variables:
            raus: "{{ [] if ruhe else entwarnt }}"
        - alias: Kandidaten für den nächsten Durchlauf merken
          action: input_text.set_value
          target: {entity_id: "{{ k.kandidat }}"}
          data: {value: "{{ aktuell | join(',') }}"}
        - alias: Gemeldet-Liste fortschreiben
          action: input_text.set_value
          target: {entity_id: "{{ k.liste }}"}
          data: {value: "{{ ((gemeldet + neu) | unique | reject('in', raus) | list) | join(',') }}"}
        - repeat:
            for_each: "{{ neu }}"
            sequence:
              - variables:
                  msg: "{{ namen.get(repeat.item, repeat.item) }} ist bei {{ states(sens[repeat.item]) }} {{ k.einheit }} – {{ k.lage }}."
                  # hier folgt derselbe Lüftungstipp wie in "Raumklima: Alarme", Ziel ist die Entwarnungsschwelle ew
              - action: notify.mobile_app_handy_1
                continue_on_error: true
                data: {title: "{{ k.titel }}", message: "{{ msg }}"}
              - if: "{{ kat != 'warm' }}"
                then:
                  - action: notify.mobile_app_handy_2
                    continue_on_error: true
                    data: {title: "{{ k.titel }}", message: "{{ msg }}"}
        - repeat:
            for_each: "{{ raus }}"
            sequence:
              - variables:
                  msg2: "Entwarnung: {{ namen.get(repeat.item, repeat.item) }} ist wieder bei {{ states(sens[repeat.item]) }} {{ k.einheit }}"
              - action: notify.mobile_app_handy_1
                continue_on_error: true
                data: {title: "{{ k.titel_ok }}", message: "{{ msg2 }}"}
              - if: "{{ kat != 'warm' }}"
                then:
                  - action: notify.mobile_app_handy_2
                    continue_on_error: true
                    data: {title: "{{ k.titel_ok }}", message: "{{ msg2 }}"}
mode: single
max_exceeded: silent
variables:
  namen: {kue: Küche, wz: Wohnzimmer, beg: Bad EG}
  sensoren_map:
    feuchte:
      kue: sensor.kueche_luftfeuchte
      wz: sensor.wohnzimmer_luftfeuchte
      beg: sensor.bad_eg_luftfeuchte
    temp:
      kue: sensor.kueche_temperatur
      wz: sensor.wohnzimmer_temperatur
      beg: sensor.bad_eg_temperatur
  kategorien:
    feuchte_hoch:
      art: feuchte
      ueber: true
      einheit: "%"
      schwelle: input_number.raumklima_schwelle_feuchte_hoch
      entwarnung: input_number.raumklima_entwarnung_feuchte_hoch
      liste: input_text.raumklima_gemeldet_feuchte_hoch
      kandidat: input_text.raumklima_kandidat_feuchte_hoch
      titel: Luftfeuchtigkeitsalarm
      titel_ok: Luftfeuchtigkeit wieder normal
      lage: zu feucht
    feuchte_tief:
      art: feuchte
      ueber: false
      einheit: "%"
      schwelle: input_number.raumklima_schwelle_feuchte_tief
      entwarnung: input_number.raumklima_entwarnung_feuchte_tief
      liste: input_text.raumklima_gemeldet_feuchte_tief
      kandidat: input_text.raumklima_kandidat_feuchte_tief
      titel: Luftfeuchtigkeitsalarm
      titel_ok: Luftfeuchtigkeit wieder normal
      lage: zu trocken
    kalt:
      art: temp
      ueber: false
      einheit: "°C"
      schwelle: input_number.raumklima_schwelle_temperatur_kalt
      entwarnung: input_number.raumklima_entwarnung_temperatur_kalt
      liste: input_text.raumklima_gemeldet_kalt
      kandidat: input_text.raumklima_kandidat_kalt
      titel: Temperaturalarm
      titel_ok: Temperatur wieder normal
      lage: zu kalt
    warm:
      art: temp
      ueber: true
      einheit: "°C"
      schwelle: input_number.raumklima_schwelle_temperatur_warm
      entwarnung: input_number.raumklima_entwarnung_temperatur_warm
      liste: input_text.raumklima_gemeldet_warm
      kandidat: input_text.raumklima_kandidat_warm
      titel: Temperaturalarm
      titel_ok: Temperatur wieder normal
      lage: zu warm

Anpassen

  • Trage deine Räume und Sensoren ein und vergib pro Raum einen eindeutigen Kurzcode.
  • Schwellen, Hysterese und Haltezeiten ändern sich über die Helfer, ohne Neustart.
  • Brauchst du die Nachtruhe nicht, lässt du die Bedingung weg und den Trigger ruhe_ende der Nachprüfung. Ohne thermal_comfort entfällt der Lüftungstipp.

Stolpersteine

Die Zuordnung Sensor → Kurzcode muss in allen drei Automationen übereinstimmen. Eine Änderung nur an einer Stelle führt dazu, dass Räume gemeldet, aber nie entwarnt werden. Das Kandidaten-Verfahren der Nachprüfung kostet im Zweifel eine Runde (30 Minuten) Verzögerung, ist dafür aber unempfindlich gegen kurze Ausreißer. Außerdem erlauben input_text-Helfer höchstens 255 Zeichen. Bei zwölf Räumen mit Kurzcodes von drei Zeichen ist das genug.

Tags

Unterstützen

Diese Tools entstehen in meiner Freizeit und bleiben kostenlos, offen und cloud-frei. Wenn dir eins davon einen Nachmittag gespart hat, freue ich mich über einen Kaffee oder ein Stück Pizza.

Kaffee oder Pizza

Weitere Beiträge