Vorsicht viel viel Text und Details …
Einleitung
Am Anfang wußte ich – wie so oft – erst einmal gar nichts
Mich hatte aber interessiert, was im HA-Umfeld mit der App (Addon) RTL-HAOS und der 433 MHz-Funkanalyse überhaupt möglich ist.
Nach der ersten Daten-überwältigenden Erkundungsphase habe ich mich auf die Auswertung von Reifendaten sowie einigen Funkthermometern konzentriert. Themen wie Grillthermometer, Fernbedienungen oder „zufällige“ Funksignale aus der Nachbarschaft waren für mich weniger relevant (Wetterstationen wären interessant gewesen, sind aber nicht vorhanden). Zwischendurch gab es auch die Spaßidee wie das Erkennen von Amazon- oder DHL-Fahrzeugen und entsprechende Hinweise über Lautsprecher im Haus. Das habe ich aber wieder verworfen, weil zu aufwendig.
Dieser Post richtet sich in erster Linie an RTL-HAOS Neugierige und Experimentierfreudige mit Fokus auf Bestimmung eines Autozustandes (parking, leaving, moving, arriving, transit) sowie Reifendruckwarnung und Darstellung auf einem Dashboard. Im besten Fall hilft er ein paar Stunden an eigener Feldforschung und Entwicklungsarbeit einzusparen.
Ja, Copy/Paste Fans finden auch den kompletten Code aber Ihr müßt die Logik verstehen und individuell anpassen können. Gerade im Bereich Funkverhalten und beim Auto-Beispiel kann es lokale bedingte Unterschiede geben.
Nein, jemand der sein Auto bereits in irgendeiner Form integriert hat, wird das Thema als zu aufwendig einordnen. Für eine reine An-/Abfahrt Historie könnte man ebenfalls auch einen Tracker verwenden oder einfach einen Sensor anbringen, der erkennt wann Auto wegfährt oder ankommt.
Screenshots
Da viele von uns erst einmal Bilder sehen wollen, hier ein paar Eindrücke.
Produktives Dashboard bzw. Abschnitt
oder aufgeklappt
Entwicklungsdashboard
Voraussetzungen
RTL-HAOS ist installiert und liefert über einen angeschlossenen Dongle / Antenne bereits verwertbare 433 MHz-Funksignale. In meinem Fall nutze ich dafür das „Nooelec RTL-SDR v5 Bündel“ (Amazon), das per USB über Proxmox an HA durchgereicht wird.
Zusätzlich ist ein MQTT-Broker eingerichtet, ursprünglich für Zigbee2MQTT, inzwischen aber auch für RTL-HAOS genutzt. Zur Analyse der ankommenden Daten verwende ich den MQTT Explorer. Dieser ist nicht zwingend erforderlich, erleichtert jedoch die Auswertung deutlich.
Es ist wichtig, sein HA System verstanden zu haben und einigermaßen beherrschen zu können; ebenso das Verhalten bzw. die mögliche Konfiguration von RTL-HAOS. Ohne beidem wird es schnell zu Ärger über die Folgen eines zugemüllten HA Systems kommen. Es treffen kontinuierlich Funksignale ein, und neue Geräte / Entitäten werden automatisch angelegt.
Ihr müßt durch Beobachten/Testen die MQQT ID’s zu den Reifen Eures Auto erkannt haben bzw. die Namen der automatisch generierten Geräte/Entities.
Im Nachhinein aber hier vorweg
RTL-HAOS ist ein Docker, der im Unterbau das Original rtl_433 GitHub - merbanan/rtl_433: Program to decode radio transmissions from devices on the ISM bands (and other frequencies) · GitHub verwendet und gewohnte HA Entitäten bereitstellt. Als Anfänger erleichtert dies den Einstieg in die Funkwelt. Heute jedoch frage ich mich, ob ich nicht doch noch nur zum Original wechseln sollte. Ich könnte dann das MQTT Autodiscovery abschalten und selbst entscheiden welche Topics ich nach HA zur dort von mir eingerichteten Template Sensoren schicke. Das erinnert mich etwas an die HA Paperless Diskussionen in den letzten Jahren: Einfach und schnell im HA das Paperless Addon installieren oder nicht lieber doch separat weil die eingescannten Dokumente sonst das HA Backup aufblähen.
Eine Sache würde ich im Nachhinein auch noch ändern und vielleicht mache ich das auch noch: Anstelle Text-Helfer und deren 255 Zeichen Begrenzung zu verwenden, würde ich die Information als Attribute über Template Sensoren speichern. Eine History wäre demnach viel länger.
Beobachtungen, Herausforderungen, Ansätze
Anfangs dachte ich alles easy – Reifen entfernt sich und so wird das RSSI Signal schwächer und umgekehrt beim Ankommen des Autos. Das stimmt zwar immer noch generell aber eigene Feldversuche und Beobachtungen zeigen eine etwas differenziertere Realität.
-
Für jedes empfangene Reifenfunksignal legt RTL-HAOS mehrere HA Entitäten an. Ich habe alle deaktiviert bis auf:
RSSI → Signalstärke zur groben Distanzabschätzung oder Richtungsbestimmung (entfernt sich das Auto oder nähert es sich)
ACC → Beschleunigung als Bewegungsindikator (und tlw. als Richtungsindikator)
Reifendruck und Temperatur → Unregelmäßigkeiten erkennen -
Die absoluten ACC-Beschleunigungswerte haben sich selten alleinig als verläßliche Werte zur Zustandsbestimmung erwiesen. Ich behelfe mich über Mittelwerte zumindest beim Wechsel von parking → leaving.
-
Das Delta der RSSI-Werte hat sich hingegen als deutlich brauchbareres Indiz zur Unterscheidung zwischen ankommendem und wegfahrendem Fahrzeug erwiesen. Für eine sinnvolle Auswertung müssen ein, idealerweise mehrere Reifen innerhalb einer „Session“ mindestens zweimal senden. Das ist manchmal der Fall, aber nicht zuverlässig genug, um sich ausschließlich darauf zu verlassen.
-
Erschwerend wirkt sich das in RTL-HAOS konfigurierbare „Throttle Intervall“ (Standard: 30 Sekunden) aus. Innerhalb dieses Zeitraums werden Funksignale und deren Werte aggregiert. Eine Reduzierung verbessert zwar die Aktualität der Daten und erzeugt mehr Deltas, führt im Gegenzug jedoch zu einer größeren Recorder- bzw. Datenbanklast. Alle!! Funksignale werden öfters an HA weitergereich. Ich habe den Wert aktuell bei 30 Sekunden belassen und werde ihn voraussichtlich erst anpassen, wenn ich vom Blacklist- auf ein Whitelist-Konzept umstelle.
-
Nicht immer senden alle vier Reifen gleichzeitig oder komplett. Um zu vermeiden, daß Werte aus unterschiedlichen „Sessions“ miteinander verglichen werden, berücksichtigt die Zustandslogik ausschließlich Daten innerhalb eines Zeitfensters von 3 Minuten.
-
Bei einem stehenden Fahrzeug senden einzelne Reifen sporadisch neue Werte. Diese enthalten in der Regel Beschleunigungswerte ≤ 10, was sich als ein eher zuverlässiger Indikator für ein parkendes Auto erwiesen hat. Aus diesem Grund lasse ich bei Erkennen solcher Werte immer den aktuellen Zustand mit parking übeschreiben.
-
Ein echter Sonderfall tritt auf, wenn ein Fahrzeug im Empfangsbereich wendet und anschließend wieder zurück- oder weiterfährt. Das sauber zu erkennen ist schwer, da innerhalb einer Session sowohl positive als auch negative RSSI-Deltas auftreten. Ich versuche den neuen Zustand aus dem vorherigen (z. B. „parking“) abzuleiten und widersprüchliche Deltas entsprechend zu ignorieren.
Dieser Ansatz fängt einige Fälle ab aber andere individuelle nicht, z.B. Auto fährt noch einmal kurz raus in den Wendehammer um besser einzuparken … und erzeugt dadurch widersprüchliche RSSI / ACC Werte, die die Logik als leaving interpretiert. Glücklicherweise korrigiert sich der Status wieder zu parking sobald ein Reifen einen entsprechenden sicheren Wert liefert. Da können aber Stunden dazwischen liegen. Ich fand bis heute keine Idee diesen Fall abzufangen ohne andere Logiken wieder kaputt zu machen. Mich nervt so etwas aber es bleibt am Ende ein Spaßprojekt. Zumindest führte es dazu, daß ich zur Dashboard Statuskarte noch 2 Buttons zur individuellen Korrektur hinzufügte. -
Ein weiterer Sonderfall tritt auf, wenn ein baugleiches Fahrzeug vorbeifährt und dadurch das eigene Fahrzeug zur Übertragung neuer Reifenwerte „anregt“. Ich vermute eher, daß RTL-HAOS einige Funksignale mißinterpretiert. Nicht immer handelt es sich um echte neue Werte sondern vorher unavailable HA Sensoren werden reaktiviert mit Vorgängerwerten. Dies löst fälschlicherweise Automatisationen aus.
Ein ähnliches Verhalten zeigt sich nach einem Neustart von RTL-HAOS, bei dem dieses Mal alle Entitäten so erscheinen, als wären sie frisch empfangen worden.
Ich fange diese Effekte aktuell durch einen Vergleich der Mittelwerte der Beschleunigungswerte aus der letzten und der davorliegenden Session ab. Sind diese annährend gleich, wird der alte Auto Zustand nicht verändert. -
Zum Speichern von Werten und Verlaufsdaten (History) verwende ich mehrere HA Standard input_text Helfer, die leider auf 255 Zeichen begrenzt sind. Die Daten werden dort im JSON-Format abgelegt. Beispiel:
[{"t":1777393470,"r":"r3","acc":70},{"t":1777392124,"r":"r1","acc":91},{"t":1777392123,"r":"r3","acc":93},{"t":1777392121,"r":"r2","acc":85},{"t":1777392120,"r":"r4","acc":78},{"t":1777281205,"r":"r2","acc":5},{"t":1777238242,"r":"r3","acc":5}]
Dies erleichtert den standardisierten Zugriff auf bestimmte gespeicherte Inhalte. Ich sagte bereits oben, daß ich zukünftig zumindestens die Text-Helfer für History in Template-Helfer wandele und die History als Attribute schreiben werde. -
Beim Reifendruck und der Temperatur stellt sich die Frage, ab wann eine Warnung sinnvoll ist und wie zuverlässig die Werte tatsächlich sind. Die gefunkten Reifendrücke sind teilweise so abnormal, daß der Bordcomputer im Auto eigentlich dauerhaft Alarm auslösen müßte. Vermutlich haben die Ingenieure dort bereits eine entsprechende Filterung oder Bewertung vorgenommen um genau solch Fehlalarme zu verhindern (Anzeige erst nach ca. 2 min Fahrt). Im stehenden und abgekühlten Zustand stimmen die Werte jedoch weitgehend mit denen des Bordcomputers überein.
Anfangs hatte ich eine einfache Warnung: IF Reifendruck < 180 THEN Warnung. Dies produzierte jedoch viele Fehlalarme.
Ich habe mir folgendes erdacht: Nur wenn die Reifentemperatur nahe der Außentemperatur am Carport sind, sind die Reifendruckwerte auch valide, alle anderen werden ignoriert. Im Falle z.B. des Ankommens (theoretisch eher valide Funkwerte) gibt es bei niedrigem Reifendruck eine Warnung sofern die Mittelwerte aller gefunkten Reifentemperaturen nur max. 15 Grad von der Temperatur im Carport abweicht.
Die visuelle Einordnung in der Email erfolgt über Symbole, die vom gleichen Makro wie die auf dem Dashboard.
-----Ursprüngliche Nachricht-----
Von: Homeassi <xxx@xxx>
Gesendet: Dienstag, 21. April 2026 16:29
An: xxx
Betreff: 🚨 AUTO1 / Reifen 2 hat mit 152 kPa kritischen Druck!
Auto: Auto1
Ausgelöst durch Reifen 2
HA Warngrenze: 180 kPa
- Reifen 1 | 21.04. 16:28:42 Uhr | 🟢 228 kPa | 🟠 68.3 °C
- Reifen 2 | 21.04. 16:28:43 Uhr | 🔴 152 kPa | ⚪ - °C
- Reifen 3 | 21.04. 10:13:41 Uhr | 🟡 268 kPa | 🟢 23.0 °C
- Reifen 4 | 21.04. 16:12:42 Uhr | 🟢 239 kPa | 🟢 19.0 °C
- Zur Darstellung verwende ich 2 Dashboards. Ein Dashboard dient ausschließlich der Entwicklung und dem Debugging, in dem ich Tabellen, Zwischenwerte und verschiedene Testkarten nutze, von denen ein Teil im Nachhinein nicht mehr notwendig wäre. Im anderen Fall verwende ich auf dem produktiven Dashboard einen Abschnitt, wo ich zustandsabhängig Werte und andere Informationen darstelle. Die Entwicklungsseite ist überladen aber historisch so gewachsen und alle Karten hatten Ihren Zweck auf dem Weg zum Wesentlichen. Sucht Euch im Code aus was Ihr gebrauchen könnt.
Insgesamt war ich überrascht wie individuell das Funkverhalten, die Werte und Situationen sein können. Ob diese Erfahrungen für Eurer Auto bzw. Räumlichkeit bzw. Straßenverlauf 1:1 übertragen läßt, kann ich nicht sagen. Auf jeden Fall habt Ihr mit diesen Bemerkungen und dem Code ein anpassbares Grundgerüst.
Der Code
Zu aller erst müßt Ihr durch Beobachten/Testen die MQTT ID’s zu den Reifen Eures Auto erkannt haben bzw. die Namen der automatisch generierten Geräte/Entities.
Dann solltet Ihr diese nach diesem Muster umbenennen damit Ihr die Automatisationen / Scripte / Karten einfacher übernemen könnt. Wenn der Code einmal läuft, könnt Ihr immer noch die Namen anpassen.
Exemplarisch für einen! Reifen eines! Renaults sind:
sensor.renault1_reifen1_centrifugal_acc_fixed
sensor.renault1_reifen1_signal_rssi_fixed
sensor.renault1_reifen1_temperature_fixed
sensor.renault1_reifen1_model
sensor.renault1_reifen1_type
sensor.renault1_reifen1_pressure
sensor.renault1_reifen1_temperature
sensor.renault1_reifen1_centrifugal_acc
sensor.renault1_reifen1_integrity_check
sensor.renault1_reifen1_signal_rssi
Insgesamt beobachte ich renault1 (-> auto1) und renault2 (-> auto2) mit jeweils 4 Reifen.
Die Entity Namen werden später nach der renault/auto und Reifen Nummer aufgeteilt und weiterverarbeitet. Der Code wird zwar komplizierter aber auch deutlich kürzer/erweiterbarer um ein weiteres Auto.
Um sicherzustellen, daß mathematische Deltas berechnet werden zu können habe ich folgende Template Sensoren angelegt. Sie stellen ein nummerisches Format von RSSI, ACC; Temperatur sicher.
Zusammenfassung
- name: "Renault1 Reifen1 Centrifugal Acc Fixed"
unique_id: "renault1_reifen1_centrifugal_acc_fixed"
unit_of_measurement: "m/s"
device_class: speed
state_class: measurement
state: >
{% set v = states('sensor.renault1_reifen1_centrifugal_acc') %}
{{ v | float(0) if v not in ['unknown','unavailable',''] else 0 }}
- name: "Renault1 Reifen1 Signal RSSI Fixed"
unique_id: "renault1_reifen1_signal_rssi_fixed"
unit_of_measurement: "dB"
device_class: SIGNAL_STRENGTH
state_class: measurement
state: >
{% set v = states('sensor.renault1_reifen1_signal_rssi') %}
{{ v | float(0) if v not in ['unknown','unavailable',''] else 0 }}
- name: "Renault1 Reifen1 Temperature Fixed"
unique_id: "renault1_reifen1_temperature_fixed"
unit_of_measurement: "°C"
device_class: temperature
state_class: measurement
state: >
{% set v = states('sensor.renault1_reifen1_temperature') %}
{{ v | float | round(1) if v not in ['unknown','unavailable',''] else none }}
Danach 13 Text-Helfer pro Auto anlegen und 255 Zeichen als Länge erlauben:
input_text.auto1_reifen1",
input_text.auto1_reifen2",
input_text.auto1_reifen3",
input_text.auto1_reifen4",
input_text.auto1_reifen1_delta",
input_text.auto1_reifen2_delta",
input_text.auto1_reifen3_delta",
input_text.auto1_reifen4_delta",
input_text.auto1_status_history",
input_text.auto1_reifendruck_history",
input_text.auto1_zustand",
input_text.auto1_reifentemperatur_history",
input_text.auto1_reifenbeschleunigung_history"
In diesen werden die Informationen mit Timestamp im JSON Format gespeichert.
Folgende Automatisation reagiert auf neue Funksignale und stößt ein Script zur Speicherung der Werte in obere Helfer an.
Ich ließ mir auch gerne Mails mit entsprechenden Informationen zusenden. Diese sind im Code deaktiviert, können aber leicht an Eure Bedürfnisse angepaßt werden. Deaktiviert z.B. die Trigger von Auto2 wenn Ihr nur ein Auto1 tracken wollt.
Zusammenfassung
alias: om_aut_draussen_auto_reifenwerte_speichern
description: >-
## Autos: Stößt das Speichern von Werte und Zeitstempel an
- Modus Warteschlange
- hier werden bewußt als Trigger die Wechsel Wert -> unavailable
ausgeschlossen über "not_to:" Ein | float(0) würde beim Delta Berechnen zu
einem inhaltlichen Fehler bei der Distanzberechnung führen.
triggers:
- trigger: state
entity_id: sensor.renault1_reifen1_signal_rssi
not_to:
- unavailable
- unknown
- ""
id: Auto1_Reifen1
- trigger: state
entity_id: sensor.renault1_reifen2_signal_rssi
not_to:
- unavailable
- unknown
- ""
id: Auto1_Reifen2
- trigger: state
entity_id: sensor.renault1_reifen3_signal_rssi
not_to:
- unavailable
- unknown
- ""
id: Auto1_Reifen3
- trigger: state
entity_id: sensor.renault1_reifen4_signal_rssi
not_to:
- unavailable
- unknown
- ""
id: Auto1_Reifen4
- trigger: state
entity_id: sensor.renault2_reifen1_signal_rssi
not_to:
- unavailable
- unknown
- ""
id: Auto2_Reifen_1
- trigger: state
entity_id: sensor.renault2_reifen2_signal_rssi
not_to:
- unavailable
- unknown
- ""
id: Auto2_Reifen_2
- trigger: state
entity_id: sensor.renault2_reifen3_signal_rssi
not_to:
- unavailable
- unknown
- ""
id: Auto2_Reifen_3
- trigger: state
entity_id: sensor.renault2_reifen4_signal_rssi
not_to:
- unavailable
- unknown
- ""
id: Auto2_Reifen_4
conditions:
- condition: template
value_template: >-
{{ trigger.from_state is not none and trigger.to_state.state !=
trigger.from_state.state }}
actions:
- action: notify.gmxolaf3
data:
title: |
HA-Debug aut_reifen: {{ trigger.id }} feuerte ->
(From {{ trigger.from_state.state }} → To {{ trigger.to_state.state }})
message: om_aut_draussen_auto_reifenwerte_speichern
enabled: false
- choose:
- conditions:
- condition: trigger
id:
- Auto1_Reifen1
sequence:
- action: script.om_scr_draussen_auto_werte_speichern
data:
entity_id: input_text.auto1_reifen1
- conditions:
- condition: trigger
id:
- Auto1_Reifen2
sequence:
- action: script.om_scr_draussen_auto_werte_speichern
data:
entity_id: input_text.auto1_reifen2
- conditions:
- condition: trigger
id:
- Auto1_Reifen3
sequence:
- action: script.om_scr_draussen_auto_werte_speichern
data:
entity_id: input_text.auto1_reifen3
- conditions:
- condition: trigger
id:
- Auto1_Reifen4
sequence:
- action: script.om_scr_draussen_auto_werte_speichern
data:
entity_id: input_text.auto1_reifen4
- choose:
- conditions:
- condition: trigger
id:
- Auto2_Reifen_1
sequence:
- action: script.om_scr_draussen_auto_werte_speichern
data:
entity_id: input_text.auto2_reifen1
- conditions:
- condition: trigger
id:
- Auto2_Reifen_2
sequence:
- action: script.om_scr_draussen_auto_werte_speichern
data:
entity_id: input_text.auto2_reifen2
- conditions:
- condition: trigger
id:
- Auto2_Reifen_3
sequence:
- action: script.om_scr_draussen_auto_werte_speichern
data:
entity_id: input_text.auto2_reifen3
- conditions:
- condition: trigger
id:
- Auto2_Reifen_4
sequence:
- action: script.om_scr_draussen_auto_werte_speichern
data:
entity_id: input_text.auto2_reifen4
mode: queued
trace:
stored_traces: 20
max: 16
Und das eangestoßene Script.
Zusammenfassung
alias: om_scr_draussen_auto_werte_speichern
description: >
## Verarbeitung von Funk-Reifenwerten und Speicherung in entsprechende
Auto-Helfer zur Anzeige im Dashboard.
#### Ablauf
Das Script extrahiert aus der eingehenden Entity die relevanten Informationen
(Fahrzeug und Reifen) und bestimmt über ein Mapping-Dictionary (sensor_map),
in welche Text-Helfer die Werte geschrieben werden.
Für jedes Fahrzeug und jeden Reifen werden drei Datentypen gepflegt:
- Aktuelle Werte
- Delta-Werte (Differenz zum vorherigen Zustand → zur Erkennung von Bewegung /
Ankunft / Abfahrt)
- Historie (Reifendruck, Temperatur, Beschleunigung)
- Jeder Text Helfer hat eine Beschränkung von max. 255 Zeichen
- Gespeichert wird im Text Helfer ein JSON Format.
#### Beispiele
##### Mapping
| Quelle | Auto | Reifen | Ziel Text
Helfer |
|--------------------------------------|------|--------|------------------------------|
| sensor.renault1_reifen1_pressure | 1 | 1 |
input_text.auto1_reifen1_* |
| sensor.renault2_reifen3_pressure | 2 | 3 |
input_text.auto2_reifen3_* |
##### In Text-Helfern gespeicherte Daten
- Delta
{"autoname": "Familie 1", "delta_acc_m_s2": null, "delta_pressure_kbar": null,
"delta_rssi_db": null, "reifen_nr": "reifen1", "timestamp": 1775156583}
- Aktueller Wert
{"acc_m_s2": null, "autoname": "Familie 1", "pressure_kbar": null,
"reifen_nr": "reifen1", "rssi_db": null, "timestamp": 1775238231}
- Historie
[{"t":1775240899,"r":"r2","p":219},{"t":1775217018,"r":"r3","p":209}]
fields:
entity_id:
description: Aus dieser wird das Auto und die Reifennummer abgeleitet
example: input_text.auto1_reifen1
sequence:
- variables:
Zweck: Zuordnung / Mapping der Autosensoren
comment1: >-
Ermittelt aus der übergebenen Entity das Fahrzeug und den Reifen und
bildet daraus einen einheitlichen Schlüssel für das sensor_map(ing)
comment1_1: >-
Unterstützt zwei Eingabeformate - sensor.renault1_reifen1_pressure →
parts = ['renault1','reifen1','pressure'] - input_text.auto1_reifen1 →
parts = ['auto1','reifen1'] In beiden Fällen - Extraktion der
Autonummer (renault1/auto1 → 1) Reifenkennung bleibt unverändert
(reifen1) Ergebnis auto_key = auto1 und key = auto1_reifen1
entity: "{{ entity_id }}"
name: "{{ entity.split('.')[1] }}"
parts: "{{ name.split('_') }}"
auto_nr: |
{% if parts[0].startswith('renault') %}
{{ parts[0] | regex_replace('^renault', '') }}
{% elif parts[0].startswith('auto') %}
{{ parts[0] | regex_replace('^auto', '') }}
{% else %}
0
{% endif %}
auto_key: "{{ 'auto' ~ auto_nr }}"
reifen_nr: "{{ parts[1] }}"
key: "{{ auto_key ~ '_' ~ reifen_nr }}"
namen:
auto1: OS1
auto2: OS2
sensor_map:
auto1_reifen1:
pressure: sensor.renault1_reifen1_pressure
rssi: sensor.renault1_reifen1_signal_rssi_fixed
acc: sensor.renault1_reifen1_centrifugal_acc_fixed
temperature: sensor.renault1_reifen1_temperature_fixed
auto1_reifen2:
pressure: sensor.renault1_reifen2_pressure
rssi: sensor.renault1_reifen2_signal_rssi_fixed
acc: sensor.renault1_reifen2_centrifugal_acc_fixed
temperature: sensor.renault1_reifen2_temperature_fixed
auto1_reifen3:
pressure: sensor.renault1_reifen3_pressure
rssi: sensor.renault1_reifen3_signal_rssi_fixed
acc: sensor.renault1_reifen3_centrifugal_acc_fixed
temperature: sensor.renault1_reifen3_temperature_fixed
auto1_reifen4:
pressure: sensor.renault1_reifen4_pressure
rssi: sensor.renault1_reifen4_signal_rssi_fixed
acc: sensor.renault1_reifen4_centrifugal_acc_fixed
temperature: sensor.renault1_reifen4_temperature_fixed
auto2_reifen1:
pressure: sensor.renault2_reifen1_pressure
rssi: sensor.renault2_reifen1_signal_rssi_fixed
acc: sensor.renault2_reifen1_centrifugal_acc_fixed
temperature: sensor.renault2_reifen1_temperature_fixed
auto2_reifen2:
pressure: sensor.renault2_reifen2_pressure
rssi: sensor.renault2_reifen2_signal_rssi_fixed
acc: sensor.renault2_reifen2_centrifugal_acc_fixed
temperature: sensor.renault2_reifen2_temperature_fixed
auto2_reifen3:
pressure: sensor.renault2_reifen3_pressure
rssi: sensor.renault2_reifen3_signal_rssi_fixed
acc: sensor.renault2_reifen3_centrifugal_acc_fixed
temperature: sensor.renault2_reifen3_temperature_fixed
auto2_reifen4:
pressure: sensor.renault2_reifen4_pressure
rssi: sensor.renault2_reifen4_signal_rssi_fixed
acc: sensor.renault2_reifen4_centrifugal_acc_fixed
temperature: sensor.renault2_reifen4_temperature_fixed
- variables:
Zweck: DELTA schreiben (RSSI, Druck, ACC)
comment1: >-
Ermittelt die zugehörigen Sensoren anhand des berechneten key und liest
den zuletzt gespeicherten Zustand aus dem input_text Helper.
s: "{{ sensor_map.get(key, {}) }}"
old_raw: "{{ states(entity) }}"
delta_entity: "{{ 'input_text.' ~ key ~ '_delta' }}"
comment2: >-
Liest aktuelle Sensorwerte (RSSI, Druck, ACC) und wandelt sie in Zahlen
um. Ungültige Zustände (unknown, unavailable, none) werden zu 0
konvertiert, um Berechnungsfehler zu vermeiden.
rssi_new: >
{% set v = states(s.rssi) if s.get('rssi') else none %} {{ v | float(0)
if v not in ['unknown','unavailable', none] else 0 }}
pressure_new: >
{% set v = states(s.pressure) if s.get('pressure') else none %} {{ v |
float(0) if v not in ['unknown','unavailable', none] else 0 }}
acc_new: >
{% set v = states(s.acc) if s.get('acc') else none %} {{ v | float(0) if
v not in ['unknown','unavailable', none] else 0 }}
temp_new: >
{% set v = states(s.temperature) if s.get('temperature') else none %} {{
v | float(0) if v not in ['unknown','unavailable', none] else 0 }}
comment3: >-
Wandelt den gespeicherten JSON-String aus dem Helper in ein Dictionary
um. Falls der Inhalt ungültig oder kein Mapping ist, wird ein leeres
Objekt {} verwendet.
old: |
{% if old_raw is string %}
{% set tmp = old_raw | from_json(default={}) %}
{{ tmp if tmp is mapping else {} }}
{% elif old_raw is mapping %}
{{ old_raw }}
{% else %}
{}
{% endif %}
comment4: >-
Datenfluss sensor_map → aktuelle Werte → alter Helper-Wert → Parsing →
Basis für Delta-Berechnung
comment4_1: >-
neue und zu alte RSSI und ACC Werte sollen kein Delta bilden (Druck
schon)
comment4_2: 999999 soll Fehler abfangen wie JSON kaputt oder fehlerhafter Timespamp
gap: >
{% if old is mapping and 'timestamp' in old and old['timestamp'] is
number %}
{{ (now().timestamp() - old['timestamp']) | int }}
{% else %}
{{ 999999 }}
{% endif %}
comment5: Berechnet Differenz zwischen aktuellen und gespeicherten Werten
allowed_gap_sec: >-
{{states('input_number.auto_reifen_valide_zeitspanne_in_sekunden_zw_2_funkwerten')
| int(180)}}
delta_rssi: |
{% if old is mapping
and 'rssi_db' in old
and old['rssi_db'] is number
and gap < allowed_gap_sec %}
{{ (rssi_new - old['rssi_db']) | round(1) }}
{% else %}
{{ none }}
{% endif %}
delta_pressure: |
{% if old is mapping
and 'pressure_kbar' in old
and old['pressure_kbar'] is number
and pressure_new > 0 %}
{{ (pressure_new - old['pressure_kbar']) | round(0) }}
{% else %}
{{ none }}
{% endif %}
delta_acc: |
{% if old is mapping
and 'acc_m_s2' in old
and old['acc_m_s2'] is number
and acc_new > 0
and gap < allowed_gap_sec %}
{{ (acc_new - old['acc_m_s2']) | round(0) }}
{% else %}
{{ none }}
{% endif %}
delta_temp: |
{% if old is mapping
and 'temperature_c' in old
and old['temperature_c'] is number
and temp_new is number %}
{{ (temp_new - old['temperature_c']) | round(1) }}
{% else %}
{{ none }}
{% endif %}
- action: input_text.set_value
target:
entity_id: "{{ delta_entity }}"
data:
value: |
{{ {
"autoname": namen.get(auto_key, auto_key),
"reifen_nr": parts[1],
"timestamp": now().timestamp() | int,
"delta_rssi_db":
(delta_rssi | float(0) | round(1))
if delta_rssi is not none
else none,
"delta_pressure_kbar":
(delta_pressure | float(0) | round(0))
if delta_pressure is not none
else none,
"delta_acc_m_s2":
(delta_acc | float(0) | round(0))
if delta_acc is not none
else none,
"delta_temperature_c":
(delta_temp | float(0) | round(1))
if delta_temp is not none
else none
} | tojson }}
- variables:
Zweck: Neue Reifenwerte schreiben (RSSI, Druck, ACC)
Wichtig: Darf nicht vor dem Delta schreiben passieren sonst ist Delta immer 0 :)
- action: input_text.set_value
target:
entity_id: "{{ entity }}"
data:
value: >
{% set rssi_state = states(s.rssi) if s.get('rssi') else none %}
{% set pressure_state = states(s.pressure) if s.get('pressure') else
none %}
{% set acc_state = states(s.acc) if s.get('acc') else none %}
{% set temp_state = states(s.temperature) if s.get('temperature') else
none %}
{{ {
"timestamp": now().timestamp() | int,
"autoname": namen.get(auto_key, auto_key),
"reifen_nr": parts[1],
"rssi_db":
(rssi_new | round(1))
if rssi_state not in ['unknown','unavailable', none]
else none,
"pressure_kbar":
(pressure_new | round(0))
if pressure_state not in ['unknown','unavailable', none]
else none,
"acc_m_s2":
(acc_new | round(0))
if acc_state not in ['unknown','unavailable', none]
else none,
"temperature_c":
(temp_new | round(1))
if temp_state not in ['unknown','unavailable', none]
else none
} | tojson }}
- variables:
Zweck: History Reifendruck schreiben
comment: >-
Bisherige History laden - Hole mir die Helfer Entity und deren Inhalt
und die Reifenkurbezeichnung wie r1...4
history_entity_pressure: "{{ 'input_text.' ~ auto_key ~ '_reifendruck_history' }}"
history_entity_temp: "{{ 'input_text.' ~ auto_key ~ '_reifentemperatur_history' }}"
history_entity_acc: "{{ 'input_text.' ~ auto_key ~ '_reifenbeschleunigung_history' }}"
raw_history_pressure: "{{ states(history_entity_pressure) }}"
raw_history_temp: "{{ states(history_entity_temp) }}"
raw_history_acc: "{{ states(history_entity_acc) }}"
history_pressure: >
{% set raw = states(history_entity_pressure) %} {{ raw |
from_json(default=[]) if raw not in ['unknown','unavailable',''] else []
}}
history_temp: >
{% set raw = states(history_entity_temp) %} {{ raw |
from_json(default=[]) if raw not in ['unknown','unavailable',''] else []
}}
history_acc: >
{% set raw = states(history_entity_acc) %} {{ raw |
from_json(default=[]) if raw not in ['unknown','unavailable',''] else []
}}
r_short: "{{ 'r' ~ parts[1].replace('reifen','') }}"
comment1: >-
Nur Druckwerte über 0 berücksichtigen {% if pressure_new is not none and
pressure_new > 0 %} {{ pressure_new | round(0) }} {% else %} {{ none }}
{% endif %}
pressure_current: |
{% if pressure_new is number %}
{{ pressure_new | round(0) }}
{% else %}
{{ none }}
{% endif %}
comment2: >-
letzten Wert des momentan funkenden Reifens holen und prüfen ob
Veränderung vorliegt
last_value_pressure: |
{% set vals = history_pressure
| selectattr('r','equalto', r_short)
| sort(attribute='t', reverse=True)
| selectattr('p','defined')
| map(attribute='p')
| list %}
{{ vals[0] if vals | length > 0 else none }}
comment3: neuen Reifendruck nur speichern wenn Änderung vorliegt
changed_pressure: >-
{{ pressure_current is not none and last_value_pressure !=
pressure_current }}
Zweck2: History Reifentemperatur schreiben
temperature_current: |
{% if temp_new is number %}
{{ temp_new | round(1) }}
{% else %}
{{ none }}
{% endif %}
comment4: >-
letzten Temperaturwert dieses Reifens holen und prüfen ob Veränderung
vorliegt
last_value_temp: |
{% set vals = history_temp
| selectattr('r','equalto', r_short)
| sort(attribute='t', reverse=True)
| selectattr('temp','defined')
| map(attribute='temp')
| list %}
{{ vals[0] if vals | length > 0 else none }}
Zweck3: History Reifenbeschleunigung schreiben
acc_current: |
{% if acc_new is number %}
{{ acc_new | round(0) }}
{% else %}
{{ none }}
{% endif %}
changed_temp: >-
{{ temperature_current is not none and last_value_temp !=
temperature_current }}
comment5: letzten ACC Wert dieses Reifens holen und prüfen ob Veränderung vorliegt
last_value_acc: |
{% set vals = history_acc
| selectattr('r','equalto', r_short)
| sort(attribute='t', reverse=True)
| selectattr('acc','defined')
| map(attribute='acc')
| list %}
{{ vals[0] if vals | length > 0 else none }}
comment6: nur neue History schreiben wenn eine Änderung vorliegt Änderung vorliegt
changed_acc: "{{ acc_current is not none and last_value_acc != acc_current }}"
changed_any: "{{ changed_pressure or changed_temp or changed_acc}}"
- action: input_text.set_value
target:
entity_id: "{{ history_entity_pressure }}"
data:
value: |-
{% if changed_pressure and pressure_current is not none %}
{% set new_entry = {
"t": now().timestamp() | int,
"r": r_short,
"p": pressure_current
} %}
{#
Länge begrenzen (255 Zeichen) -> ältester wird entfernt -> Liste ist immer [neu, älter, älter ...]
#}
{% set ns = namespace(list = ([new_entry] + history_pressure) | sort(attribute='t', reverse=True)) %}
{% for i in range(ns.list | length) %}
{% set json = ns.list | to_json %}
{% if json | length > 255 %}
{% set ns.list = ns.list[:-1] %}
{% endif %}
{% endfor %}
{{ ns.list | to_json }}
{% else %}
{{ raw_history_pressure }}
{% endif %}
- action: input_text.set_value
target:
entity_id: "{{ history_entity_temp }}"
data:
value: |-
{% if changed_temp and temperature_current is not none %}
{% set new_entry = {
"t": now().timestamp() | int,
"r": r_short,
"temp": temperature_current
} %}
{% set ns = namespace(list = ([new_entry] + history_temp) | sort(attribute='t', reverse=True)) %}
{% for i in range(ns.list | length) %}
{% set json = ns.list | to_json %}
{% if json | length > 255 %}
{% set ns.list = ns.list[:-1] %}
{% endif %}
{% endfor %}
{{ ns.list | to_json }}
{% else %}
{{ raw_history_temp }}
{% endif %}
- action: input_text.set_value
target:
entity_id: "{{ history_entity_acc }}"
data:
value: |-
{% if changed_acc and acc_current is not none %}
{% set new_entry = {
"t": now().timestamp() | int,
"r": r_short,
"acc": acc_current
} %}
{% set ns = namespace(list = ([new_entry] + history_acc) | sort(attribute='t', reverse=True)) %}
{% for i in range(ns.list | length) %}
{% set json = ns.list | to_json %}
{% if json | length > 255 %}
{% set ns.list = ns.list[:-1] %}
{% endif %}
{% endfor %}
{{ ns.list | to_json }}
{% else %}
{{ raw_history_acc }}
{% endif %}
- variables:
Zweck: Zum Debuggen / Logik überprüfen -> später deaktivieren
- action: notify.gmxolaf3
data:
title: |
HA Reifen SCR Debug: {{ namen.get(auto_key, auto_key) }} {{ parts[1] }}
message: >-
Timestamp: {{ now() }}
==============================
📦 ROHDATEN
==============================
Sensor Map : {{ s }}
Alt (raw) : {{ old_raw }}
RAW STRING? : {{ old_raw is string }}
Delta Entity : {{ delta_entity }}
==============================
🚗 FAHRZEUG
==============================
Auto : {{ auto_key }}
Name : {{ namen.get(auto_key, auto_key) }}
Reifen : {{ parts[1] }} ({{ r_short }})
==============================
📡 SENSORWERTE (NEU)
==============================
RSSI : {{ rssi_new }} dB
Druck : {{ pressure_new }} kbar
Beschleunigung : {{ acc_new }} m/s²
Temperatur : {{ temp_new }} °C
==============================
🧮 DELTA
==============================
Δ RSSI : {{ delta_rssi }}
Δ Druck : {{ delta_pressure }}
Δ Acc : {{ delta_acc }}
Δ Temperatur : {{ delta_temp }}
==============================
🔍 ALTWERTE (PARSED)
==============================
{{ old }}
pressure_old : {{ old.get('pressure_kbar', 'MISSING') }}
rssi_old : {{ old.get('rssi_db', 'MISSING') }}
acc_old : {{ old.get('acc_m_s2', 'MISSING') }}
temp_old : {{ old.get('temperature_c', 'MISSING') }}
==============================
💾 HISTORY DRUCK
==============================
Entity : input_text.{{ auto_key }}_reifendruck_history
Letzter Wert : {{ last_value_pressure }}
Neuer Wert : {{ pressure_current }}
Änderung : {{ pressure_current is not none and
last_value_pressure != pressure_current }}
{% if pressure_current is not none and last_value_pressure !=
pressure_current %}
➜ Neuer Eintrag:
{{ {
"t": now().timestamp() | int,
"r": r_short,
"p": pressure_current
} | tojson }}
{% else %}
➜ Kein neuer Eintrag
{% endif %}
==============================
🌡️ HISTORY TEMPERATUR
==============================
Entity : input_text.{{ auto_key }}_reifentemperatur_history
Temperatur aktuell : {{ temperature_current }}
Letzter Wert :
{% set hist_temp = states('input_text.' ~ auto_key ~
'_reifentemperatur_history') | from_json(default=[]) %}
{% set vals_temp = hist_temp
| selectattr('r','equalto', r_short)
| sort(attribute='t', reverse=True)
| map(attribute='temp')
| list %}
{{ vals_temp[0] if vals_temp | length > 0 else none }}
Änderung :
{{ temperature_current is not none and
(vals_temp[0] if vals_temp | length > 0 else none) != temperature_current }}
{% if temperature_current is not none and
(vals_temp[0] if vals_temp | length > 0 else none) != temperature_current %}
➜ Neuer Eintrag:
{{ {
"t": now().timestamp() | int,
"r": r_short,
"temp": temperature_current
} | tojson }}
{% else %}
➜ Kein neuer Eintrag
{% endif %}
enabled: false
trace:
stored_traces: 20
mode: queued
max: 16
Nachdem Speichern der aktuellen Werte wird anhand verschiedener Kriterien der Zustand des Autos ermittelt.
Die Automatisation wird diesmal nur getriggert wenn die RSSI Werte valide Zustände haben. Um eine Überschneidung mit voriger Automatisation und dem Speichern der Infos zu gewährleisten, wartet die Automatisation noch 5 s bevor es das entsprechende Script startet. Desweiteren erzwingt die Automatisation bestimmte Stati nach gewisser Zeit, z.B. Auto verläßt Funkantennenbereich und so wird aus leaving → transit.
(Wichtig! Bei RTL-HAOS habe ich den Parameter „Sensor Expiry“ von 600 auf 300 Sekunden reduziert. Demzufolge erzwinge ich in der Automatisation nach 6 min)
Zusammenfassung
alias: om_aut_draussen_auto_letzten_zustand_speichern
description: >-
## Auto: Speichert bzw. triggert Speicherung letzten Autozustandes und die
History (max 255 Zeichen)
- Modus Warteschlange
- Trigger Wechsel Wert -> unavailable wird ausgeschlossen über "not_to:"
Unnötig DB Ballast und die Delta Berechnung, auf die hier auch das Makro
zugreift, findet woanders statt.
- Diese Automatisation ist nachrangig und läuft mit einer xxx s Verzögerung
pro Durchgang damit es nicht zuviel plötzlichen Streß gibt und Werte ggf.
nicht richtig gespeichert werden.
### Wichtig!
Ab wann wechselt eine Entity auf unavailable?
In RTL-HAOS sind per default 600 s = 10 min eingestellt. Ich habe die Zeit
halbiert auf 5.
Demzufolge soll der Wechsel des Autozustandes ab der 6. Minute von LEAVING
oder ARRIVING bestimmt werden.
Ich setze 1 min warten drauf bevor ich die Prüfung vollziehe.
triggers:
- trigger: state
entity_id:
- sensor.renault1_reifen1_signal_rssi
not_to:
- unavailable
- unknown
- ""
id: Auto1Reifen1
- trigger: state
entity_id:
- sensor.renault1_reifen2_signal_rssi
not_to:
- unavailable
- unknown
- ""
id: Auto1Reifen2
- trigger: state
entity_id:
- sensor.renault1_reifen3_signal_rssi
not_to:
- unavailable
- unknown
- ""
id: Auto1Reifen3
- trigger: state
entity_id:
- sensor.renault1_reifen4_signal_rssi
not_to:
- unavailable
- unknown
- ""
id: Auto1Reifen4
- trigger: state
entity_id:
- input_text.auto1_zustand
id: Auto1Zustand
- trigger: state
entity_id:
- input_text.auto1_zustand
to:
- leaving
id: Auto1LeavingSeitXXXmin
for:
hours: 0
minutes: 6
seconds: 0
- trigger: state
entity_id:
- input_text.auto1_zustand
to:
- arriving
id: Auto1ArrivingSeitXXXmin
for:
hours: 0
minutes: 6
seconds: 0
- trigger: state
entity_id:
- sensor.renault2_reifen1_signal_rssi
not_to:
- unavailable
- unknown
- ""
id: Auto2Reifen1
- trigger: state
entity_id:
- sensor.renault2_reifen2_signal_rssi
not_to:
- unavailable
- unknown
- ""
id: Auto2Reifen2
- trigger: state
entity_id:
- sensor.renault2_reifen3_signal_rssi
not_to:
- unavailable
- unknown
- ""
id: Auto2Reifen3
- trigger: state
entity_id:
- sensor.renault2_reifen4_signal_rssi
not_to:
- unavailable
- unknown
- ""
id: Auto2Reifen4
- trigger: state
entity_id:
- input_text.auto2_zustand
id: Auto2Zustand
- trigger: state
entity_id:
- input_text.auto2_zustand
to:
- leaving
id: Auto2LeavingSeitXXXmin
for:
hours: 0
minutes: 6
seconds: 0
- trigger: state
entity_id:
- input_text.auto2_zustand
to:
- arriving
id: Auto2ArrivingSeitXXXmin
for:
hours: 0
minutes: 6
seconds: 0
conditions:
- condition: template
value_template: |
{{ trigger.from_state is not none and
trigger.from_state.state != trigger.to_state.state }}
actions:
- variables:
GrundVerzoegerung: >-
Das Funken triggert auch die Automatisation
om_aut_draussen_auto_reifenwerte_speichern und mit dessen Werten wird
hier der Zustanmd des Autos bestimmt. Je mehr Reifenwerte
vorhanden/verarbeitet, desto genau der Auto Zustand. Es wird zwar in
Warteschlange abgearbeitet aber nicht zuviel Aktionen auf einmal.
option1: Auto1 Reifen funken und Autozustand wird über Script geprüft
option2: Auto1 Zustand war länger auf leaving -> Änderung zu transit
option3: Auto1 Zustand war länger auf arriving -> Änderung zu parking
comment: Sobald eine Bedingung zutrifft, werden andere nicht mehr geprüft
option4: Auto1 Zustand hat sich geändert -> Speicherung in History
option5: Auto2 Reifen funken und Autozustand wird über Scriptgeprüft
option6: Auto2 Zustand war länger auf leaving -> Änderung zu transit
option7: Auto2 Zustand war länger auf arriving -> Änderung zu parking
option8: Auto2 Zustand hat sich geändert -> Speicherung in History
- delay:
hours: 0
minutes: 0
seconds: 5
milliseconds: 0
- choose:
- conditions:
- condition: or
conditions:
- condition: trigger
id:
- Auto1Reifen1
- condition: trigger
id:
- Auto1Reifen2
- condition: trigger
id:
- Auto1Reifen3
- condition: trigger
id:
- Auto1Reifen4
sequence:
- action: script.turn_on
target:
entity_id: script.om_scr_draussen_auto_zustand_tlw_schreiben
data:
variables:
auto_prefix: auto1
- conditions:
- condition: trigger
id:
- Auto1LeavingSeitXXXmin
- condition: template
value_template: >-
{# Invalide Zusstände -> 0 und bei Summe 0 wird derzeit kein
Funksignal empfangen #}
{{
(
states('sensor.renault1_reifen1_signal_rssi_fixed')|int +
states('sensor.renault1_reifen2_signal_rssi_fixed')|int +
states('sensor.renault1_reifen3_signal_rssi_fixed')|int +
states('sensor.renault1_reifen4_signal_rssi_fixed')|int
) == 0
}}
sequence:
- action: input_text.set_value
target:
entity_id: input_text.auto1_zustand
data:
value: transit
- conditions:
- condition: trigger
id:
- Auto1ArrivingSeitXXXmin
- condition: template
value_template: >-
{# Invalide Zusstände -> 0 und bei Summe 0 wird derzeit kein
Funksignal empfangen #}
{{
(
states('sensor.renault1_reifen1_signal_rssi_fixed')|int +
states('sensor.renault1_reifen2_signal_rssi_fixed')|int +
states('sensor.renault1_reifen3_signal_rssi_fixed')|int +
states('sensor.renault1_reifen4_signal_rssi_fixed')|int
) == 0
}}
sequence:
- action: input_text.set_value
target:
entity_id: input_text.auto1_zustand
data:
value: parking
- conditions:
- condition: trigger
id:
- Auto1Zustand
- condition: template
value_template: >-
{# Nur speichern wenn Zustand sich änderte #}
{{ trigger.from_state.state | lower != trigger.to_state.state |
lower }}
sequence:
- action: input_text.set_value
target:
entity_id: input_text.auto1_status_history
data:
value: |
{% set raw = states('input_text.auto1_status_history') %}
{# --- History sicher laden --- #}
{% set tmp = raw | from_json(default=[]) %}
{% set history = tmp if tmp is iterable else [] %}
{# --- letzter Zustand sicher --- #}
{% set last_state =
history[0]['s']
if history | length > 0
and history[0] is mapping
and 's' in history[0]
else ''
%}
{% set new_state = trigger.to_state.state %}
{% set now_ts = now().timestamp() | int %}
{# --- neuer Eintrag --- #}
{% set new_entry = {
"t": now_ts,
"s": new_state
} %}
{# --- Duplikat-Handling --- #}
{% if new_state == last_state and history | length > 0 %}
{% set ns = namespace(list = [new_entry] + history[1:]) %}
{% else %}
{% set ns = namespace(list = [new_entry] + history) %}
{% endif %}
{# --- Längenbegrenzung (255 Zeichen) --- #}
{% set ns = namespace(list = ns.list) %}
{% for i in range(ns.list | length) %}
{% set json = ns.list | to_json %}
{% if json | length > 255 %}
{% set ns.list = ns.list[:-1] %}
{% endif %}
{% endfor %}
{{ ns.list | to_json }}
- conditions:
- condition: or
conditions:
- condition: trigger
id:
- Auto2Reifen1
- condition: trigger
id:
- Auto2Reifen2
- condition: trigger
id:
- Auto2Reifen3
- condition: trigger
id:
- Auto2Reifen4
sequence:
- action: script.turn_on
target:
entity_id: script.om_scr_draussen_auto_zustand_tlw_schreiben
data:
variables:
auto_prefix: auto2
- conditions:
- condition: trigger
id:
- Auto2LeavingSeitXXXmin
- condition: template
value_template: >-
{# Invalide Zusstände -> 0 und bei Summe 0 wird derzeit kein
Funksignal empfangen #}
{{
(
states('sensor.renault2_reifen1_signal_rssi_fixed')|int +
states('sensor.renault2_reifen2_signal_rssi_fixed')|int +
states('sensor.renault2_reifen3_signal_rssi_fixed')|int +
states('sensor.renault2_reifen4_signal_rssi_fixed')|int
) == 0
}}
sequence:
- action: input_text.set_value
target:
entity_id: input_text.auto2_zustand
data:
value: transit
- conditions:
- condition: trigger
id:
- Auto2ArrivingSeitXXXmin
- condition: template
value_template: >-
{# Invalide Zusstände -> 0 und bei Summe 0 wird derzeit kein
Funksignal empfangen #}
{{
(
states('sensor.renault2_reifen1_signal_rssi_fixed')|int +
states('sensor.renault2_reifen2_signal_rssi_fixed')|int +
states('sensor.renault2_reifen3_signal_rssi_fixed')|int +
states('sensor.renault2_reifen4_signal_rssi_fixed')|int
) == 0
}}
sequence:
- action: input_text.set_value
target:
entity_id: input_text.auto2_zustand
data:
value: parking
- conditions:
- condition: trigger
id:
- Auto2Zustand
- condition: template
value_template: >-
{# Nur speichern wenn Zustand sich änderte #}
{{ trigger.from_state.state | lower != trigger.to_state.state |
lower }}
sequence:
- action: input_text.set_value
target:
entity_id: input_text.auto2_status_history
data:
value: |
{% set raw = states('input_text.auto2_status_history') %}
{# --- History sicher laden --- #}
{% set tmp = raw | from_json(default=[]) %}
{% set history = tmp if tmp is iterable else [] %}
{# --- letzter Zustand sicher --- #}
{% set last_state =
history[0]['s']
if history | length > 0
and history[0] is mapping
and 's' in history[0]
else ''
%}
{% set new_state = trigger.to_state.state %}
{% set now_ts = now().timestamp() | int %}
{# --- neuer Eintrag --- #}
{% set new_entry = {
"t": now_ts,
"s": new_state
} %}
{# --- Duplikat-Handling --- #}
{% if new_state == last_state and history | length > 0 %}
{% set ns = namespace(list = [new_entry] + history[1:]) %}
{% else %}
{% set ns = namespace(list = [new_entry] + history) %}
{% endif %}
{# --- Längenbegrenzung (255 Zeichen) --- #}
{% set ns = namespace(list = ns.list) %}
{% for i in range(ns.list | length) %}
{% set json = ns.list | to_json %}
{% if json | length > 255 %}
{% set ns.list = ns.list[:-1] %}
{% endif %}
{% endfor %}
{{ ns.list | to_json }}
- action: notify.gmxolaf3
metadata: {}
data:
message: om_aut_draussen_auto_letzten_status_speichern
title: >-
HA Debug aut_status: {{ trigger.id }} ({{ trigger.from_state.state}} →
{{ trigger.to_state.state }})
enabled: false
mode: queued
trace:
stored_traces: 20
max: 16
und das angestoßene Script.
Zusammenfassung
alias: om_scr_draussen_auto_zustand_tlw_schreiben
description: >
## Definiert den Zustand aufgrund verschiedener gespeicherter Reifenwerte
- Modus Warteschlange
- "tlw" im Namen weil das nicht der einzige Ort ist von dem gespecihert wird
-> om_aut_draussen_auto_letzten_status_speichern
- Hatte vorher alles als Makro aber Script liefert per Standard trace Werte
zum Debuggen
---
Mit diesem Script wird der Zustand des Autos bestimmt, z.B. Steht es vor der
Tür oder fährt es gerade weg?
Dafür werden folgende Informationen herangezogen
- Funk RSSI Signal (Mastersignal)
- Beschleunigungswert ACC
- Voriger Zustand des Autos
Herausforderungen:
- Nicht jeder der 4 Reifen funkt immer sofort
- Was ist eine valide Zeitspanne zwischen 2 Werten?
- Innerhalb einer valiuden Zeitspanne funkt ein Reifen selten 2x um daraus
einen RSSI Trend zu erkennen (abnhemen -> entfernt sich vs zunehmend -> kommt
an)
Genereller Ansatz:
- RSSI ist das führende Signal: nur wenn RSSI vorhanden ist, stehen auch ACC
und weitere Werte zur Verfügung
- RSSI Delta wird verwendet, um die Richtung zu bestimmen (arriving / leaving)
-> dafür muß es 2 Signale deselben Reifens geben
- Absolute ACC Werte werden verwendet, um Bewegung zu erkennen (fährt / steht)
(z.B. "parking" -> 1 Reifen meldet ACC = 5)
- Kombination aus RSSI (Richtung) und ACC (Bewegung) ergibt den aktuellen
Zustand
- Sofern kein verwertbares RSSI Delta vorhanden ist, wird der neue Status aus
dem vorherigen Zustand abgeleitet
- Zeitspanne wird über Helfer input_number.zentrale
auto_reifen_valide_zeitspanne_in_sekunden_zw_2_funkwerten gesteuert, derzeit
180 s
Beispiele für geladene Helfer / JSON Inhalt
- Delta: {"autoname": "OS1", "delta_acc_m_s2": 55, "delta_pressure_kbar": 230,
"delta_rssi_db": -5.12, "reifen_nr": "reifen1", "timestamp": 1775156583}
-
History:[{"t":1775469229,"s":"parking"},{"t":1775241060,"s":"no_signal"},{"t":1775240899,"s":"parking"},{"t":1775226430,"s":"unavailable"},{"t":1775226324,"s":"no_signal"},{"t":1775226324,"s":"unavailable"},{"t":1775217180,"s":"no_signal"}]
mode: queued
max: 10
fields:
auto_prefix:
description: Prefix des Autos
example: auto1
sequence:
- variables:
comment: Zum Debugging alle momentanen Eingangwerte
reifenwerte: >
{% set state_entities = [
'input_text.' ~ auto_prefix ~ '_reifen1',
'input_text.' ~ auto_prefix ~ '_reifen2',
'input_text.' ~ auto_prefix ~ '_reifen3',
'input_text.' ~ auto_prefix ~ '_reifen4'
] %}
{% set delta_entities = [
'input_text.' ~ auto_prefix ~ '_reifen1_delta',
'input_text.' ~ auto_prefix ~ '_reifen2_delta',
'input_text.' ~ auto_prefix ~ '_reifen3_delta',
'input_text.' ~ auto_prefix ~ '_reifen4_delta'
] %}
{% set history_entity = 'input_text.' ~ auto_prefix ~ '_status_history'
%} {% set history = states(history_entity) | from_json(default=[]) %} {%
set last_status = (history | sort(attribute='t', reverse=true) |
first).s if history else 'unknown' %}
{% set acc_history_entity = 'input_text.' ~ auto_prefix ~
'_reifenbeschleunigung_history' %} {% set acc_history =
states(acc_history_entity) | from_json(default=[]) %}
{% set ns = namespace(
state_map={},
delta_map={},
acc_list=[],
sum_pos_delta_rssi_abs=0,
sum_neg_delta_rssi_abs=0
) %}
{% for e in delta_entities %}
{% set raw = states(e) %}
{% set ns.delta_map = ns.delta_map | combine({ e: raw }) %}
{% if raw not in ['unknown','unavailable',''] %}
{% set d = raw | from_json(default={}) %}
{% if d.delta_rssi_db is number %}
{% if d.delta_rssi_db > 0 %}
{% set ns.sum_pos_delta_rssi_abs = ns.sum_pos_delta_rssi_abs + d.delta_rssi_db %}
{% elif d.delta_rssi_db < 0 %}
{% set ns.sum_neg_delta_rssi_abs = ns.sum_neg_delta_rssi_abs + (d.delta_rssi_db | abs) %}
{% endif %}
{% endif %}
{% endif %}
{% endfor %}
{% for e in delta_entities %}
{% set ns.delta_map = ns.delta_map | combine({ e: states(e) }) %}
{% endfor %}
{% for e in state_entities %}
{% set raw_state = states(e) %}
{% set ns.state_map = ns.state_map | combine({ e: raw_state }) %}
{% endfor %}
{# --- aktueller Mittelwert ACC --- #} {% set current_mw_acc =
(ns.acc_list | sum / ns.acc_list | length)
if ns.acc_list | length > 0 else 0
%}
{# --- letzter gespeicherter Mittelwert --- #} {% set ts_list =
acc_history | map(attribute='t') | list %} {% set acc_last_ts = ts_list
| max if ts_list | length > 0 else 0 %}
{% set last_session = acc_history
| selectattr('t', 'eq', acc_last_ts)
| list
%}
{% set acc_values = last_session | map(attribute='acc') | list %}
{% set last_mw_acc =
(acc_values | sum / acc_values | length)
if acc_values | length > 0 else 0
%}
{{ {
"state_entities": ns.state_map,
"delta_entities": ns.delta_map,
"history": history,
"last_status": last_status,
"current_mw_acc": current_mw_acc,
"last_mw_acc": last_mw_acc
} }}
- variables:
comment: komplette Logik in einem Block
comment1: >-
Ich hatte erst versucht, in mehrere Blöcke aufzuteilen um mehr Trace zu
erzeugen. Aber dann werden Objekte zum String serialisert und der Code
scheitert.
auto_zustand: >
{#
Im Folgenden wird versucht aufgrund der vorhanden neuen und alten Reifendaten, den mommentanen Zustand des Autos zu bestimmen.
- parking
- moving
- arriving
- leaving
PS: Die Übergänge von leaving -> transit und arriving -> parking werden seperat über einer Automatisation nach Ausbleiben eines weiteren Funksignals nach xxx Minuten gesetzt.
#}
{#--- Helfer Entities auffgrund des Autoprefix zusammenbauen ---#}
{% set state_entities = [
'input_text.' ~ auto_prefix ~ '_reifen1',
'input_text.' ~ auto_prefix ~ '_reifen2',
'input_text.' ~ auto_prefix ~ '_reifen3',
'input_text.' ~ auto_prefix ~ '_reifen4'
] %}
{% set delta_entities = [
'input_text.' ~ auto_prefix ~ '_reifen1_delta',
'input_text.' ~ auto_prefix ~ '_reifen2_delta',
'input_text.' ~ auto_prefix ~ '_reifen3_delta',
'input_text.' ~ auto_prefix ~ '_reifen4_delta'
] %}
{#--- Einlesen der Zustands-History ---#}
{% set history_entity = 'input_text.' ~ auto_prefix ~ '_status_history'
%}
{% set history_status = states(history_entity) | from_json(default=[])
%}
{% set history_last_state = (history_status | sort(attribute='t',
reverse=true) | first).s if history_status else 'unknown' %}
{#--- Bestimmung von Zeit-Variablen ---#}
{% set max_age_sec =
states('input_number.auto_reifen_valide_zeitspanne_in_sekunden_zw_2_funkwerten')
| int(180)
%}
{% set now_ts = now().timestamp() | int %}
{% set history_last_ts = (history_status | sort(attribute='t',
reverse=true) | first).t if history_status else 0 %}
{% set timediff_now_lastStatusHistory = now_ts - history_last_ts %}
{#--- Einlesen von jedem momentanen RSSI, ACC Reifenwerts und dessen
ggf. vorhandenen Deltas zu Vorgängern und entsprechendes Zählen von
Entscheidungskriterien für die Zustandsbestimmung ---#}
{% set ns_current = namespace(
pos_delta_rssi_count=0,
neg_delta_rssi_count=0,
acc_high_count=0,
acc_low_count=0,
acc_parking_count=0,
sum_pos_delta_rssi_abs=0,
sum_neg_delta_rssi_abs=0,
list_rssi=[],
list_acc=[],
timestamps=[]
) %}
{% for i in range(4) %}
{% set raw_delta = states(delta_entities[i]) %}
{% set raw_state = states(state_entities[i]) %}
{% if raw_state not in ['unknown','unavailable',''] %}
{% set s = raw_state | from_json(default={}) %}
{% set d = raw_delta | from_json(default={}) %}
{% set age_state = now_ts - (s.timestamp | int(0)) %}
{% set age_delta = now_ts - (d.timestamp | int(0)) %}
{# --- Nur State entscheidet ob Daten gültig sind --- #}
{% if age_state <= max_age_sec %}
{% set ns_current.timestamps = ns_current.timestamps + [s.timestamp | int] %}
{# Absolutes RSSI #}
{% if s.rssi_db is number %}
{% set ns_current.list_rssi = ns_current.list_rssi + [s.rssi_db] %}
{% endif %}
{# Delta RSSI (sofern Delta zeitlich valide) #}
{% if age_delta <= max_age_sec and d.delta_rssi_db is number %}
{% set val_delta = d.delta_rssi_db %}
{# Auto nähert sich #}
{% if val_delta > 0 %}
{% set ns_current.pos_delta_rssi_count = ns_current.pos_delta_rssi_count + 1 %}
{% set ns_current.sum_pos_delta_rssi_abs = ns_current.sum_pos_delta_rssi_abs + val_delta %}
{# Auto entfernt sich #}
{% elif val_delta < 0 %}
{% set ns_current.neg_delta_rssi_count = ns_current.neg_delta_rssi_count + 1 %}
{% set ns_current.sum_neg_delta_rssi_abs = ns_current.sum_neg_delta_rssi_abs + (val_delta | abs) %}
{% endif %}
{% endif %}
{# Absolute Beschleunigung #}
{% if s.acc_m_s2 is number %}
{% set acc = s.acc_m_s2 %}
{% set ns_current.list_acc = ns_current.list_acc + [acc] %}
{# ACC > 58 klares Indiz für wegfahrendes Auto #}
{% if acc >= 58 %}
{% set ns_current.acc_high_count = ns_current.acc_high_count + 1 %}
{# ACC > 5 ein sich bewegendes Auto #}
{% elif acc > 5 %}
{% set ns_current.acc_low_count = ns_current.acc_low_count + 1 %}
{# ACC <= 10 ein parkendes Auto #}
{% else %}
{% set ns_current.acc_parking_count = ns_current.acc_parking_count + 1 %}
{% endif %}
{% endif %}
{% endif %}
{% endif %}
{% endfor %}
{% set pos_delta_rssi_count = ns_current.pos_delta_rssi_count %}
{% set neg_delta_rssi_count = ns_current.neg_delta_rssi_count %}
{#--- Feststellen falscher identischer Signale über
Durchschnittsvergleich ---#}
{# Bebobachtungen:
- Manchmal triggert das Vorbeifahren von Auto1 das "Neu"-Senden von Auto2 mit dessen vorigen Werten
- Ein Neustart von RTL HAOS führt zu einem "Neu"senden aller Reifenwerte
- Es ist kein Neusenden sondern HA holt kurz die unavailable Entites mit den letzten Werten in den active Status
- Führt zu falscher Zustandsbestimmung des Autos
-> Meine Idee: Wenn Vor- und Nachfolger annährend gleich sind, werden die Werte ignoriert.
Vorgehen:
- Ich hole mir aus der ACC History alle zeitlich zusammenhängenden Werte der letzten Session.
- Dann bilde ich Mittelwerte aus momentanen ACC Werten und denen der vorigen Session
- Sind die Mittelwerte annährend gleich wird die Variable is_false_signal_mw = true gesetzt
#}
{% set is_false_signal_mw = false %}
{% set acc_history_entity = 'input_text.' ~ auto_prefix ~
'_reifenbeschleunigung_history' %}
{% set acc_history = states(acc_history_entity) | from_json(default=[])
%}
{% set sorted = acc_history | sort(attribute='t') %}
{% set ns_last = namespace(list=[], last_t=0) %}
{% for entry in sorted | reverse %}
{% if ns_last.last_t == 0 %}
{% set ns_last.last_t = entry.t %}
{% set ns_last.list = ns_last.list + [entry] %}
{% elif (ns_last.last_t - entry.t) <= max_age_sec %}
{% set ns_last.list = ns_last.list + [entry] %}
{% set ns_last.last_t = entry.t %}
{% else %}
{% break %}
{% endif %}
{% endfor %}
{% set last_session = ns_last.list %}
{% set last_mw_acc =
(last_session | map(attribute='acc') | list | sum /
(last_session | length))
if last_session | length > 0 else 0
%}
{% set current_mw_acc =
(ns_current.list_acc | sum / (ns_current.list_acc | length))
if ns_current.list_acc | length > 0 else 0
%}
{% set acc_diff = (current_mw_acc - last_mw_acc) | abs %}
{# Vorgänger muß vorhanden sein %}
{% set has_history = last_session | length > 0 %}
{% set is_false_signal_mw =
has_history
and acc_diff <= 3
%}
{# Bei falschen Signalen bleibt der neue Zustand der alte ELSE weitere
Bereinigung #}
{% if is_false_signal_mw %}
{% set current_new = history_last_state %}
{% else %}
{# -------------------------------------------------------- #}
{# --------------Start Zustandsbestimmung------------------ #}
{# -------------------------------------------------------- #}
{% set current_candidate = history_last_state %}
{#--- Bereinigen / Nullen falscher RSSI Deltas (Ausreißer) nach letztem Zustand parking oder leaving ---#}
{#
Es gibt ab und zu RSSI Delta Ausreißer, die im Kontext fachlich falsch und zu einer falschen Zustandsbestimmung führen.
z.B. leaving: Ein Auto fährt ab und sendet neben richtigen negativen Deltas auch ein positives.
Ich könnte komromißlos Ausreißer Nullen, würde damit aber ein Wenden nie erkennen können.
Ich versuche den Kompromiß über nur bei wenigen Außreißern wird genullt
#}
{# parking → erwartete Richtung: negativ (wegfahren) aber kein Zeitvergleich wegen längeren Funkpausen #}
{% if history_last_state == 'parking'
and pos_delta_rssi_count <= 2
and pos_delta_rssi_count < neg_delta_rssi_count %}
{% set pos_delta_rssi_count = 0 %}
{% endif %}
{# leaving → erwartete Richtung: negativ (wegfahren) wieder mit Zeitvergleich #}
{% if history_last_state == 'leaving'
and timediff_now_lastStatusHistory <= max_age_sec
and pos_delta_rssi_count <= 1
and pos_delta_rssi_count < neg_delta_rssi_count %}
{% set pos_delta_rssi_count = 0 %}
{% endif %}
{# arriving → erwartete Richtung: positiv (annähern) #}
{% if history_last_state == 'arriving'
and timediff_now_lastStatusHistory <= max_age_sec
and neg_delta_rssi_count <= 2
and neg_delta_rssi_count < pos_delta_rssi_count %}
{% set neg_delta_rssi_count = 0 %}
{% endif %}
{# transit → erwartete Richtung: positiv (annähern) aber kein Zeitvergleich wegen längeren Funkpausen #}
{% if history_last_state == 'transit'
and neg_delta_rssi_count <= 1
and neg_delta_rssi_count < pos_delta_rssi_count %}
{% set neg_delta_rssi_count = 0 %}
{% endif %}
{#--- Neutrales MOVING ---#}
{#
- Moving bedeutet, daß ACC Werte eine Bewegung signalisieren
- aber aufgrund fehlender Deltas keine Richtung erkennbar ist
- current_candidate = moving wird später überschrieben sollte eine Richtung erkennbar werden
#}
{% if pos_delta_rssi_count == 0
and neg_delta_rssi_count == 0
and current_candidate == history_last_state
%}
{% set current_candidate = 'moving' %}
{% endif %}
{# --- Auch ohne Delta können ACC Werte zu parking -> leaving führen --- #}
{#
Beobachtungen der ACC Werte beim AN-Abfahrt ergaben
- Mehrere gleichzeitig hohe ACC-Werte (typisch >= 3 Reifen mit ACC >= 58)
deuten zuverlässig auf eine Abfahrt hin.
- Typische Werte:
- Summe ca. 300–450
- Mittelwert ca. 75–110 (nur grober Orientierungswert weil es gibt Ausreißer)
- Uneinheitliche oder gemischte Werte ohne klare Dominanz sprechen eher
für arriving oder moving.
- Die abgeleitete Regel ist nicht wasserdicht aber Zustand ist treffsicherer als ohne
- Ich nehme auch mit hinein, daß keien Deltas vorhanden sein dürfen obwohl die Regel erst später kommt
#}
{% set acc_count = ns_current.list_acc | length %}
{% set acc_sum = ns_current.list_acc | sum %}
{% set acc_mw = (acc_sum / acc_count) if acc_count > 0 else 0 %}
{% if history_last_state == 'parking'
and pos_delta_rssi_count == 0
and neg_delta_rssi_count == 0
and ns_current.acc_high_count >= 3
and acc_mw > 70 %}
{% set current_candidate = 'leaving' %}
{% endif %}
{#--- Auch ohne Deltas: Wiedereintritt nach transit -> arriving ---#}
{#
- Nach dem Wiedereintritt in den Funkbereich kommen Signale an, die in den meisten Fällen für arriving sprechen
- Ob bereits verwertbare Deltas vorliegen, ist zu diesem Zeitpunkt oft noch offen.
- Einzelne verspätete Restsignale der vorigen Session können nicht vorliegen weil Zustand trasnit erst nach 6 min leaving erreicht wird
#}
{% if history_last_state == 'transit'
and (pos_delta_rssi_count + neg_delta_rssi_count) == 0
and ns_current.acc_high_count >= 1
and ns_current.acc_parking_count == 0 %}
{% set current_candidate = 'arriving' %}
{% endif %}
{#--- Mit Deltas: Richtungsbestimmung ---#}
{#
Bewegungsrichtung:
- Beim Wenden oder durch Rauschen können gemischte RSSI-Deltas auftreten
- Kleine, unterlegene Gegenbewegungen wurden zuvor bereits gefiltert (genullt)
- Die Richtung wird primär über die Anzahl gleichgerichteter Deltas bestimmt
- Bei Gleichstand entscheidet die größere Summenstärke der Delta-Beträge
- Bleibt auch dann Gleichstand, erfolgt kein Richtungsentscheid (Zustand bleibt unverändert)
#}
{% if neg_delta_rssi_count > pos_delta_rssi_count %}
{% set current_candidate = 'leaving' %}
{% elif pos_delta_rssi_count > neg_delta_rssi_count %}
{% set current_candidate = 'arriving' %}
{% elif neg_delta_rssi_count == pos_delta_rssi_count and neg_delta_rssi_count > 0 %}
{% if sum_neg_delta_rssi_abs > sum_pos_delta_rssi_abs %}
{% set current_candidate = 'leaving' %}
{% elif sum_pos_delta_rssi_abs > sum_neg_delta_rssi_abs %}
{% set current_candidate = 'arriving' %}
{% endif %}
{% endif %}
{# --- Mit Deltas: Richtungswechsel innerhalb einer Session --- #}
{# leaving → arriving
- Auto fährt weg (leaving erkannt)
- Innerhalb der gültigen Zeitspanne treten mehrere dominante positive RSSI-Deltas auf
- Vorgehen: history_last_state wird auf "moving" gesetzt,
sodaß die Transitionstabelle anschließend korrekt "arriving" ergibt
#} {% if history_last_state == 'leaving'
and current_candidate == 'arriving'
and pos_delta_rssi_count >= 2
and pos_delta_rssi_count > neg_delta_rssi_count %}
{% set history_last_state = 'moving' %}
{% endif %}
{# arriving → leaving
- Auto kommt an (arriving erkannt)
- Innerhalb der gültigen Zeitspanne treten mehrere dominante negative RSSI-Deltas auf
- Vorgehen: history_last_state wird auf "moving" gesetzt,
sodaß die Transitionstabelle anschließend korrekt "leaving" ergibt
#} {% if history_last_state == 'arriving'
and current_candidate == 'leaving'
and neg_delta_rssi_count >= 2
and neg_delta_rssi_count > pos_delta_rssi_count %}
{% set history_last_state = 'moving' %}
{% endif %}
{#--- Feststellen von echtem parking ---#}
{#
- Mindestens ein Reifen liefert einen gültigen ACC-Wert <= 10 (in 99 % der Fälle nur 5)
- Es ist höchstens ein einzelner RSSI-Delta-Ausreißer vorhanden (egal in welche Richtung)
- Mehrere oder gemischte Deltas (pos + neg > 1) werden als Bewegung interpretiert
#}
{% if ns_current.acc_parking_count >= 1
and (pos_delta_rssi_count + neg_delta_rssi_count) <= 1 %}
{% set current_candidate = 'parking' %}
{% endif %}
{#--- Zustandslogik unter Einbeziehung logisch möglicher Zustandswechsel ---#}
{#
Diese Tabelle diente ursprünglich der nachgelagerten Prüfung logischer Zustandsübergänge.
Inzwischen werden viele Zustandsübergänge bereits durch die vorgelagerte Logik
(RSSI-Deltas, ACC-Auswertung, Spezialfälle) bestimmt, wodurch die Bedeutung dieser
Tabelle reduziert ist. Man könnte die Zustandsübergänge auch zusammenstreichen.
Die Tabelle bleibt jedoch bewußt bestehen, da sie weiterhin als nachgelagerter
Konsistenzfilter wirkt:
- verhindert potenziell widersprüchliche oder sprunghafte Zustandswechsel
- stabilisiert das Verhalten bei Grenzfällen und unvollständigen Daten
Funktionsweise:
Es wird geprüft, ob der Übergang von history_last_state zu current_candidate
logisch zulässig ist. Nur definierte Übergänge werden übernommen,
ansonsten bleibt der bisherige Zustand bestehen.
#}
{% set key = history_last_state ~ '|' ~ current_candidate %}
{% set transitions = {
'parking|moving': 'moving',
'parking|leaving': 'leaving',
'moving|moving': 'moving',
'moving|leaving': 'leaving',
'moving|arriving': 'arriving',
'moving|parking': 'parking',
'leaving|moving': 'leaving',
'leaving|arriving': 'moving',
'transit|arriving': 'arriving',
'transit|parking': 'parking',
'transit|moving': 'moving',
'arriving|moving': 'arriving',
'arriving|leaving': 'moving',
'arriving|parking': 'parking'
} %}
{# parking hat höchste Priorität #}
{% if current_candidate == 'parking' %}
{% set current_new = 'parking' %}
{% else %}
{% set key = history_last_state ~ '|' ~ current_candidate %}
{% set current_new = transitions.get(key, history_last_state) %}
{% endif %}
{% endif %}
{{ current_new }}
- if:
- condition: template
value_template: >-
{# Nur wenn Zustand sich vom vorigem unterscheidet, soll auch
specihert werden #}
{% set entity_zustand = 'input_text.' ~ auto_prefix ~ '_zustand' %}
{{ states(entity_zustand) | lower != auto_zustand | lower }}
then:
- action: input_text.set_value
target:
entity_id: input_text.{{ auto_prefix }}_zustand
data:
value: "{{ auto_zustand }}"
trace:
stored_traces: 20
Es folgt die Automatisation zur Warnung bei zu niedrigen Reifenwerten. Ich lasse mir pro plausiblen niedrigem Reifen eine Mail schicken.
Wichtig! Das Mail / Jinja Layout ist sehr anfällig für kleinste Änderungen. Ihr solltet vorher an einem statischen Beispiel mit fixen Werten probieren.
Zusammenfassung
alias: om_aut_draussen_auto_reifen_warnung
description: >-
## Warnt bei zu niedrigem Reifendruck
- Modus Warteschlange
- jeder Reifen bekommt eine Mail sobald
- gefunkt
- Reifendruck > 90 (Rest wird als Außreißer betrachtet)
- Mittelwerte der Reifentemperaturen max um 15 Grad von der Außentemperatur abweicht (Rest wird als Außreißer betrachtet)
- 10 s Verzögerung eingebaut weil RSSI Werte triggern bereits andere und Werte
sollen Konfliktfrei geschrieben werden
triggers:
- trigger: numeric_state
entity_id: sensor.renault1_reifen1_pressure
below: 180
id: Auto1_Reifen1
- trigger: numeric_state
entity_id: sensor.renault1_reifen2_pressure
below: 180
id: Auto1_Reifen2
- trigger: numeric_state
entity_id: sensor.renault1_reifen3_pressure
below: 180
id: Auto1_Reifen3
- trigger: numeric_state
entity_id: sensor.renault1_reifen4_pressure
below: 180
id: Auto1_Reifen4
- trigger: numeric_state
entity_id: sensor.renault2_reifen1_pressure
below: 180
id: Auto2_Reifen1
- trigger: numeric_state
entity_id: sensor.renault2_reifen2_pressure
below: 180
id: Auto2_Reifen2
- trigger: numeric_state
entity_id: sensor.renault2_reifen3_pressure
below: 180
id: Auto2_Reifen3
- trigger: numeric_state
entity_id: sensor.renault2_reifen4_pressure
below: 180
id: Auto2_Reifen4
conditions: []
actions:
- variables:
GrundVerzoegerung: Konfliktvermeidung - dies Mail hat Nachrang Charakter
- delay:
hours: 0
minutes: 0
seconds: 10
milliseconds: 0
- variables:
pressure_limit_low: 180
auto: "{{ trigger.id.split('_')[0] }}"
tire_map:
Auto1_Reifen1: r1
Auto1_Reifen2: r2
Auto1_Reifen3: r3
Auto1_Reifen4: r4
Auto2_Reifen1: r1
Auto2_Reifen2: r2
Auto2_Reifen3: r3
Auto2_Reifen4: r4
tire: "{{ tire_map[trigger.id] }}"
raw_pressure: "{{ states('input_text.' ~ auto | lower ~ '_reifendruck_history') }}"
pressure_history: |
{% if raw_pressure is string %}
{{ raw_pressure | from_json(default=[]) }}
{% else %}
{{ raw_pressure }}
{% endif %}
raw_temp: "{{ states('input_text.' ~ auto | lower ~ '_reifentemperatur_history') }}"
temp_history: |
{% if raw_temp is string %}
{{ raw_temp | from_json(default=[]) }}
{% else %}
{{ raw_temp }}
{% endif %}
pressure_entries: |
{{ pressure_history
| selectattr('r','eq', tire)
| list
| sort(attribute='t', reverse=True)
}}
latest_pressure: "{{ pressure_entries[0] if pressure_entries else none }}"
- if:
- condition: template
value_template: >-
{# Untersten Werte sind oftmals Müll #}
{{ latest_pressure is not none and (latest_pressure.p | float(0)) > 90
}}
- condition: template
value_template: >-
{# Warnung wegen niedrigem Reifendruck nur wenn
- Datensatz zeitlich valide (180 s)
- und Reifentemperaturen maximal um 15 °C von Außentemperatur abweichen
Hintergrund: Reifendrücke und Reifentemperaturen nehmen manchmal beim Abfahren abstruse Werte ein
Risiko des Ansatzes: Temp eines Reifens darf nur 15 Grad von Außen abweichen um eine Reifenwarnung durchzulassen
#}
{% set outside =
states('sensor.om_temperatur_draussen_unten_durchschnitt') | float %}
{% set span =
states('input_number.auto_reifen_valide_zeitspanne_in_sekunden_zw_2_funkwerten')
| int(180) %}
{% set now_ts = as_timestamp(now()) | int %}
{% set temp_limit_grad = 15 %}
{% set valid = temp_history
| selectattr('t', '>=', now_ts - span)
| list %}
{% set temps = valid
| map(attribute='temp')
| list %}
{% set count = temps | length %}
{% set mw = (temps | sum / count)
if count > 0 else none %}
{{ count > 0 and (mw - outside) | abs <= temp_limit_grad }}
then:
- data:
title: "1"
message: >-
{% from 'autos.jinja' import reifendruck_icon %}
{% from 'autos.jinja' import reifentemperatur_icon %}
Auto: VW Golf 7
Ausgelöst durch Reifen 2
HA eingestellte Warngrenze: 180 kPa
{% from 'autos.jinja' import reifendruck_icon -%}
{% for r in ['r1','r2','r3','r4'] -%}
{# feste Druckdaten -#}
{% set pressure_history = [
{'r':'r1','t':1713270720,'p':220},
{'r':'r2','t':1713270660,'p':175},
{'r':'r3','t':1713270600,'p':215},
{'r':'r4','t':1713270540,'p':218}
] -%}
{# feste Temperaturdaten -#}
{% set temp_history = [
{'r':'r1','t':1713270715,'temp':32},
{'r':'r2','t':1713270655,'temp':35},
{'r':'r3','t':1713270595,'temp':30},
{'r':'r4','t':1713270535,'temp':31}
] -%}
{# Druck -#}
{% set p_entries = pressure_history
| selectattr('r','eq', r)
| list
| sort(attribute='t', reverse=True)
-%}
{% set latest_p = p_entries[0] if p_entries else none -%}
{# Temperatur passend suchen -#}
{% set temps = temp_history | selectattr('r','eq', r) | list -%}
{% set best = namespace(val=None, diff=999999999) -%}
{% for t in temps -%}
{% if latest_p -%}
{% set d = (latest_p.t | int - t.t | int) | abs -%}
{% if d <= 14 and d < best.diff -%}
{% set best.val = t -%}
{% set best.diff = d -%}
{% endif -%}
{% endif -%}
{% endfor -%}
{% set temp = best.val.temp -%}
- {{ as_datetime(latest_p.t | int).astimezone().strftime('%d.%m.
%H:%M') if latest_p else 'n/a' }} Uhr | {{
reifendruck_icon(latest_p.p) | trim}} kPa Reifendruck {{
r.replace('r','') }} | {{ reifentemperatur_icon(temp) | trim}} °C
{% endfor -%}
action: notify.gmxolaf3
enabled: false
- action: notify.gmxolaf3
data:
title: >
🚨 {{ auto | upper }} / Reifen {{ tire.replace('r','') }} hat mit {{
latest_pressure.p }} kPa kritischen Druck!
message: >-
{% from 'autos.jinja' import reifendruck_icon -%}
{% from 'autos.jinja' import reifentemperatur_icon -%}
Auto: {{ auto }}
Ausgelöst durch Reifen {{ tire.replace('r','') }}
HA Warngrenze: {{ pressure_limit_low }} kPa
{% for r in ['r1','r2','r3','r4'] -%}
{% set p_entries = pressure_history
| selectattr('r', 'eq', r)
| list
| sort(attribute='t', reverse=True) -%}
{% set latest_p = p_entries[0] if p_entries else none -%}
{% set temps = temp_history
| selectattr('r', 'eq', r)
| list -%}
{% set best = namespace(val=None, diff=999999999) -%}
{% for t in temps -%}
{% if latest_p -%}
{% set d = (latest_p.t | int - t.t | int) | abs -%}
{% if d <= 14 and d < best.diff -%}
{% set best.val = t -%}
{% set best.diff = d -%}
{% endif -%}
{% endif -%}
{% endfor -%}
{% set temp = best.val.temp if best.val else 'n/a' -%}
- Reifen {{ r.replace('r','') }} | {{ as_datetime(latest_p.t |
int).astimezone().strftime('%d.%m. %H:%M:%S') if latest_p else 'n/a'
}} Uhr | {{ reifendruck_icon(latest_p.p) | trim if latest_p else ''
}} kPa | {{ reifentemperatur_icon(temp) | trim if temp != 'n/a' else
'' }} °C
{% endfor -%}
mode: queued
max: 8
Ich habe auch noch eine Automatisation zur Ansage auf Sonos Lautsprechern (kurz Auto kommt an und Auto fährt weg) . Zumindest die Trigger möchte ich hier zeigen.
Zusammenfassung
alias: om_aut_draussen_auto_ansage_info
description: >-
## Informiert akustisch über Autoankunft oder Abfahrt
- Eingebaute Verzögerung nach Ansage in Kombination mit Warteschlange dient
dazu bei Richtungswechsel beide Ansagen über arriving und leaving zu hören
triggers:
- trigger: state
entity_id: input_text.auto1_zustand
from: transit
to: arriving
id: Auto1KommtAn
- trigger: state
entity_id: input_text.auto1_zustand
from: moving
to: arriving
id: Auto1KommtAn
- trigger: state
entity_id: input_text.auto1_zustand
from: transit
to: parking
id: Auto1KommtAn
- trigger: state
entity_id: input_text.auto1_zustand
from: parking
to: leaving
id: Auto1FaehrtAb
- trigger: state
entity_id: input_text.auto1_zustand
from: parking
to: moving
id: Auto1FaehrtAb
- trigger: state
entity_id: input_text.auto1_zustand
from: parking
to: transit
id: Auto1FaehrtAb
- trigger: state
entity_id: input_text.auto1_zustand
from: arriving
to: leaving
id: Auto1FaehrtAb
- trigger: state
entity_id: input_text.auto2_zustand
from: transit
to: arriving
id: Auto2KommtAn
- trigger: state
entity_id: input_text.auto2_zustand
from: moving
to: arriving
id: Auto2KommtAn
- trigger: state
entity_id: input_text.auto2_zustand
from: transit
to: parking
id: Auto2KommtAn
- trigger: state
entity_id: input_text.auto2_zustand
from: parking
to: leaving
id: Auto2FaehrtAb
- trigger: state
entity_id: input_text.auto2_zustand
from: parking
to: moving
id: Auto2FaehrtAb
- trigger: state
entity_id: input_text.auto2_zustand
from: parking
to: transit
id: Auto2FaehrtAb
- trigger: state
entity_id: input_text.auto2_zustand
from: arriving
to: leaving
id: Auto2FaehrtAb
conditions:
- condition: state
entity_id: input_boolean.jemand_zu_hause
state:
- "on"
actions:```
Die Reifendruck Mail und auch nachfolgender Dashboard Code verwenden Makros zusammengefaßt in /homeassistant/custom_templates/autos.jinja
Zusammenfassung
{% macro pkw_reifendruck(v) -%}
{% set p = v | float(0) %}
{% if p == 0 %}
gray|rgba(128,128,128,0.05)
{% elif p < 120 %}
red|rgba(255,0,0,0.15)
{% elif p < 180 %}
orange|rgba(255,165,0,0.15)
{% elif p <= 250 %}
green|rgba(0,128,0,0.15)
{% elif p <= 300 %}
orange|rgba(255,165,0,0.15)
{% else %}
red|rgba(255,0,0,0.15)
{% endif %}
{%- endmacro %}
{% macro pkw_signalstaerke_farbe(v, min=-10, max=0) -%}
{% set x = v | float(0) %}
{% if x == 0 %}
none|none
{% else %}
{% set max = 0.01 %}
{% set min = -10 %}
{% set norm = (x - min) / (max - min) %}
{% if norm < 0 %}
{% set norm = 0 %}
{% elif norm > 1 %}
{% set norm = 1 %}
{% endif %}
{% set hue = norm * 120 %}
{{ "hsl(" ~ hue ~ ", 100%, 35%)" }}|{{ "hsla(" ~ hue ~ ", 100%, 50%, 0.15)" }}
{% endif %}
{%- endmacro %}
{% macro pkw_beschleunigung(v) -%}
{% set p = v | float(0) %}
{% if p <= 5 %}
gray|rgba(128,128,128,0.05)
{% elif p < 50 %}
orange|rgba(255,165,0,0.15)
{% else %}
dodgerblue|rgba(30,144,255,0.15)
{% endif %}
{%- endmacro %}
{% macro reifentemperatur_icon(temp) %}
{% if temp is none or temp < -20 %}
⚪ -
{% elif temp < 0 %}
🔵 {{ temp }}
{% elif temp < 25 %}
🟢 {{ temp }}
{% elif temp < 50 %}
🟡 {{ temp }}
{% elif temp <= 80 %}
🟠 {{ temp }}
{% elif temp > 80 %}
⚪ -
{% else %}
❓ {{ temp }}
{% endif %}
{% endmacro %}
{% macro reifendruck_icon(p) %}
{% if p is none or p < 90 %}
⚪ - {# Sensorfehler / kein Signal #}
{% elif p < 120 %}
🔴🔴 {{ p }}
{% elif p < 170 %}
🔴 {{ p }}
{% elif p < 180 %}
🟡 {{ p }}
{% elif p <= 250 %}
🟢 {{ p }}
{% elif p <= 290 %}
🟡 {{ p }}
{% elif p <= 320 %}
🔴 {{ p }}
{% else %}
❓ {{ p }}
{% endif %}
{% endmacro %}
Produktive Dashboard / Abschnitt mit Darstellung von Zuständen und Histories
Beide Fotos sind KI bearbeitet. Ich ließ den Hintergrund anonymisieren. Man sieht im rechten Foto schön den KI Knick im Bordstein aber mir hat es gereicht.
Zusammenfassung
type: grid
cards:
- type: heading
heading: Unser Auto
heading_style: title
tap_action:
action: perform-action
perform_action: input_boolean.toggle
target:
entity_id: input_boolean.dashboard_raum_menue_auto
icon: mdi:car-estate
- type: picture
aspect_ratio: "0:1"
image:
media_content_id: media-source://image_upload/70be5bee6bb5ec20f603880602b0e406
media_content_type: image/jpeg
metadata:
title: carport_mit_auto.jpg
thumbnail: /api/image/serve/70be5bee6bb5ec20f603880602b0e406/256x256
media_class: image
navigateIds:
- {}
- media_content_type: app
media_content_id: media-source://image_upload
tap_action:
action: call-service
service: input_boolean.toggle
target:
entity_id: input_boolean.dashboard_raum_menue_auto
visibility:
- condition: state
entity: input_text.auto1_zustand
state: parking
grid_options:
columns: 6
rows: auto
- type: picture
visibility:
- condition: state
entity: input_text.auto1_zustand
state_not: parking
grid_options:
columns: 6
rows: auto
image:
media_content_id: media-source://image_upload/0ba18aa2e7da5834fe055d26a8047d21
media_content_type: image/jpeg
metadata:
title: carport_ohne_auto.jpg
thumbnail: /api/image/serve/0ba18aa2e7da5834fe055d26a8047d21/256x256
media_class: image
children_media_class: null
navigateIds:
- {}
- media_content_type: app
media_content_id: media-source://image_upload
tap_action:
action: call-service
service: input_boolean.toggle
target:
entity_id: input_boolean.dashboard_raum_menue_auto
- type: custom:button-card
show_state: false
show_label: true
show_icon: false
tap_action:
action: more-info
entity: input_text.auto1_zustand
custom_fields:
seit: |
[[[
/* Nimm nicht last_changed weil bei Neustart sich dieses Attribut immer ändert */
const raw = states['input_text.auto1_status_history']?.state;
if (!raw || raw === 'unknown' || raw === 'unavailable') return '';
let history;
try {
history = JSON.parse(raw);
} catch (e) {
return '';
}
if (!history.length) return '';
/* neuesten Timestamp holen */
const lastTs = history[0].t;
const lastChanged = new Date(lastTs * 1000);
const now = new Date();
const diff = (now - lastChanged) / 1000;
let text = '';
if (diff < 60) {
text = '< 1 min';
} else if (diff < 3600) {
text = Math.round(diff / 60) + ' min';
} else if (diff < 86400) {
text = Math.round(diff / 3600) + ' h';
} else {
const days = Math.round(diff / 86400);
text = days + (days === 1 ? ' Tag' : ' Tage');
}
return `( seit ${text} )`;
]]]
label: |
[[[
const current = states['input_text.auto1_zustand']?.state || 'unknown';
// optional Debug
const debug = false;
const debugText = debug
? `<br><font color=red>[curr=${current}]</font>`
: '';
if (current === 'parking') {
return "🚗💤 🅿️🟩<br><b>Auto parkt vor der Tür</b>" + debugText;
}
if (current === 'moving') {
return "🚗💨❓🔁🟨<br><b>Auto bewegt sich</b> " + debugText;
}
if (current === 'leaving') {
return "🚗💨💨 ⬅️🅿️🟧<br><b>Auto entfernt sich</b>" + debugText;
}
if (current === 'transit') {
return "🚗💨💨💨 ↔️🟥<br><b>Auto ist unterwegs</b> " + debugText;
}
if (current === 'arriving') {
return "🚗💨💨 ➡️🅿️🟧<br><b>Auto kommt an</b>" + debugText;
}
return "🚗❓ ⬜<br><b>Status unklar</b>" + debugText;
]]]
styles:
card:
- background-color: |
[[[
/* Idee: Hintergrund farblich wenn letzter Zustand innerhalb eines Zeitraumes gespeichert wurde
Nimm nicht last_changed weil bei Neustart sich dieses Attribut immer ändert
*/
const backgroundzeit_min = 60;
const raw = states['input_text.auto1_status_history']?.state;
if (!raw || raw === 'unknown' || raw === 'unavailable') return '';
let history;
try {
history = JSON.parse(raw);
} catch (e) {
return '';
}
if (!history.length) return '';
const lastTs = history[0].t; // neuester Eintrag
const last = new Date(lastTs * 1000);
const now = new Date();
const diff = (now - last) / 1000;
if (diff < backgroundzeit_min * 60) {
return '#FFD8BC';
}
return '';
]]]
grid:
- grid-template-areas: |
"n"
"l"
"seit"
- grid-template-columns: 1fr
- grid-template-rows: min-content min-content min-content min-content
label:
- font-weight: bold
- font-size: 20px
- padding-top: 10px
custom_fields:
seit:
- justify-self: center
- text-align: center
- font-size: 18px
- padding-top: 5px
history:
- justify-self: center
- text-align: center
- font-size: 14px
- padding-top: 6px
- line-height: 1.1em
korrektur:
- text-align: center
- color: gray
- padding-top: 8px
- margin-left: 10px
- margin-right: 10px
buttons:
- justify-self: center
- padding-top: 10px
layout: vertical
visibility:
- condition: or
conditions:
- condition: state
entity: input_boolean.dashboard_raum_menue_auto
state: "off"
- type: custom:button-card
show_state: false
show_label: true
show_icon: false
tap_action:
action: more-info
entity: input_text.auto1_zustand
custom_fields:
seit: |
[[[
/* Nimm nicht last_changed weil bei Neustart sich dieses Attribut immer ändert */
const raw = states['input_text.auto1_status_history']?.state;
if (!raw || raw === 'unknown' || raw === 'unavailable') return '';
let history;
try {
history = JSON.parse(raw);
} catch (e) {
return '';
}
if (!history.length) return '';
/* neuesten Timestamp holen */
const lastTs = history[0].t;
const lastChanged = new Date(lastTs * 1000);
const now = new Date();
const diff = (now - lastChanged) / 1000;
let text = '';
if (diff < 60) {
text = '< 1 min';
} else if (diff < 3600) {
text = Math.round(diff / 60) + ' min';
} else if (diff < 86400) {
text = Math.round(diff / 3600) + ' h';
} else {
const days = Math.round(diff / 86400);
text = days + (days === 1 ? ' Tag' : ' Tage');
}
return `( seit ${text} )`;
]]]
history: |
[[[
const raw = states['input_text.auto1_status_history']?.state;
if (!raw || raw === 'unknown' || raw === 'unavailable') return '';
let history;
try {
history = JSON.parse(raw);
} catch (e) {
return 'JSON Fehler';
}
if (!history.length) return '';
const max = 10;
const translate = (state) => {
if (state === 'parking') return 'parkte Auto vor der Tür';
if (state === 'leaving') return 'entfernte sich Auto';
if (state === 'transit') return 'war Auto unterwegs';
if (state === 'arriving') return 'kam Auto an';
if (state === 'moving') return 'bewegte sich Auto';
return 'Status unklar';
};
return `
<table style="margin-top:16px; font-size:14px;">
<!-- Wichtig, nicht den ersten Wert [0] sondern ab zweitem [1] beginnen ansonsten wird momentaner Status bereits als History angezeigt -->
${history.slice(1, max).map((e) => {
const d = new Date(e.t * 1000);
const now = new Date();
const isToday =
d.getDate() === now.getDate() &&
d.getMonth() === now.getMonth() &&
d.getFullYear() === now.getFullYear();
const color = isToday ? '#199BFF' : 'black';
const weight = isToday ? 'bold' : 'normal';
const day = String(d.getDate()).padStart(2, '0');
const month = String(d.getMonth() + 1).padStart(2, '0');
const hours = String(d.getHours()).padStart(2, '0');
const mins = String(d.getMinutes()).padStart(2, '0');
return `
<tr style="color:${color}; font-weight:${weight}; text-align:left">
<td style="padding:1px;">Am </td>
<td style="padding:1px;"><b>${day}.${month}.</b></td>
<td style="padding:1px;"><b>${hours}:${mins} Uhr </b></td>
<td style="padding:2px; padding-left:3px; text-align:left">
${translate(e.s)}
</td>
</tr>
`;
}).join('')}
</table>
`;
]]]
korrektur: |
[[[
return `
<hr>
<div style="line-height:1.1; margin-top:15px; width:100%; box-sizing:border-box;">
<div style="font-size:14px; font-weight:bold;">
Manuelle Korrektur
</div>
<div style="
font-size:12px;
margin-top:8px;
width:100%;
max-width:100%;
box-sizing:border-box;
white-space:normal;
overflow-wrap:normal;
">
Der Status des Autos wird mit jedem Senden eines Funksignals neu bestimmt.
In den meisten Fällen erlauben die Werte eine Zuordnung; in wenigen Fällen nicht.
Sofern ein Fehler bemerkt wird, können mit den unteren Buttons die zentralen Stati manuell gesetzt werden.
</div>
</div>
`;
]]]
buttons:
card:
type: horizontal-stack
cards:
- type: custom:button-card
name: parking
tap_action:
action: perform-action
perform_action: input_text.set_value
target:
entity_id: input_text.auto1_zustand
data:
value: parking
styles:
card:
- padding: 8px
- background-color: "#F2F2F2"
- height: 25px
- width: 100px
name:
- font-size: 12px
- type: custom:button-card
name: transit
tap_action:
action: perform-action
perform_action: input_text.set_value
target:
entity_id: input_text.auto1_zustand
data:
value: leaving
styles:
card:
- padding: 8px
- background-color: "#F2F2F2"
- height: 25px
- width: 100px
name:
- font-size: 12px
label: |
[[[
const current = states['input_text.auto1_zustand']?.state || 'unknown';
// optional Debug
const debug = false;
const debugText = debug
? `<br><font color=red>[curr=${current}]</font>`
: '';
if (current === 'parking') {
return "🚗💤 🅿️🟩<br><b>Auto parkt vor der Tür</b>" + debugText;
}
if (current === 'moving') {
return "🚗💨❓🔁🟨<br><b>Auto bewegt sich</b> " + debugText;
}
if (current === 'leaving') {
return "🚗💨💨 ⬅️🅿️🟧<br><b>Auto entfernt sich</b>" + debugText;
}
if (current === 'transit') {
return "🚗💨💨💨 ↔️🟥<br><b>Auto ist unterwegs</b> " + debugText;
}
if (current === 'arriving') {
return "🚗💨💨 ➡️🅿️🟧<br><b>Auto kommt an</b>" + debugText;
}
return "🚗❓ ⬜<br><b>Status unklar</b>" + debugText;
]]]
styles:
card:
- background-color: |
[[[
/* Idee: Hintergrund farblich wenn letzter Zustand innerhalb eines Zeitraumes gespeichert wurde
Nimm nicht last_changed weil bei Neustart sich dieses Attribut immer ändert
*/
const backgroundzeit_min = 60;
const raw = states['input_text.auto1_status_history']?.state;
if (!raw || raw === 'unknown' || raw === 'unavailable') return '';
let history;
try {
history = JSON.parse(raw);
} catch (e) {
return '';
}
if (!history.length) return '';
const lastTs = history[0].t; // neuester Eintrag
const last = new Date(lastTs * 1000);
const now = new Date();
const diff = (now - last) / 1000;
if (diff < backgroundzeit_min * 60) {
return '#FFD8BC';
}
return '';
]]]
grid:
- grid-template-areas: |
"n"
"l"
"seit"
"history"
"korrektur"
"buttons"
- grid-template-columns: 1fr
- grid-template-rows: min-content min-content min-content min-content
name:
- color: "#6898FF"
- padding-bottom: 8px
- margin-bottom: 6px
- border-bottom: 1px solid rgba(104,152,255,0.00)
label:
- font-weight: bold
- font-size: 20px
- padding-top: 10px
custom_fields:
seit:
- justify-self: center
- text-align: center
- font-size: 18px
- padding-top: 5px
history:
- justify-self: center
- text-align: center
- font-size: 14px
- padding-top: 6px
- line-height: 1.1em
korrektur:
- text-align: center
- color: gray
- padding-top: 8px
- margin-left: 10px
- margin-right: 10px
buttons:
- justify-self: center
- padding-top: 10px
layout: vertical
visibility:
- condition: or
conditions:
- condition: state
entity: input_boolean.dashboard_raum_menue_auto
state: "on"
- condition: state
entity: input_boolean.dashboard_raum_menue_sonstiges
state: "on"
- type: markdown
content: >
{# --- Text und Farben --- #}
{% set title = '🚗🛞 Reifendruck und Temperatur<br>(Olaf+Susi 1)' %}
{% set farbe1 ='#199BFF' %}
{% set farbe2 ='#727272' %}
{# --- Unicodezeichen und Skala in Makros --- #}
{% from 'autos.jinja' import reifentemperatur_icon %}
{% from 'autos.jinja' import reifendruck_icon %}
{# --- Einlesen Inhalte von Texthelfern --- #}
{% set raw_temp = states('input_text.auto1_reifentemperatur_history') %}
{% set raw_press = states('input_text.auto1_reifendruck_history') %}
{% set parsed_temp = raw_temp | from_json(default=[]) %}
{% set parsed_press = raw_press | from_json(default=[]) %}
{% if parsed_temp is iterable and parsed_temp is not string %}
{% set temp_list = parsed_temp %}
{% else %}
{% set temp_list = [] %}
{% endif %}
{% if parsed_press is iterable and parsed_press is not string %}
{% set press_list = parsed_press %}
{% else %}
{% set press_list = [] %}
{% endif %}
{% set ns = namespace(keys=[], merged=[]) %}
{# --- Erstellen von Keys aus vorhandenen Daten --- #}
{#
- Die Inhalte beider Texthelfer durchgehen und alle vorhandenen Keys (t timestamp + r Reifen) in einer Liste sammeln
- Ergebnisbeispiel: z.B. ['1775669616_r4', '1775665777_r3', '1775660530_r2', '1775658484_r4', '1775629928_r4', '1775629926_r1', '1775629925_r3', '1775629923_r2']
#}
{# Keys Temperatur #}
{% for e in temp_list %}
{% set key = e.t ~ '_' ~ e.r %}
{# Key hinzufügen sofern noch nicht in Liste, z.B. '1775669616_r4', '1775665777_r3', '1775660530_r2'] #}
{% if key not in ns.keys %}
{% set ns.keys = ns.keys + [key] %}
{% endif %}
{% endfor %}
{# Keys Reifendruck #}
{% for e in press_list %}
{% set key = e.t ~ '_' ~ e.r %}
{# Liste oben ermittelten Keys fortsetzen - es kann keine Duplikate geben #}
{% if key not in ns.keys %}
{% set ns.keys = ns.keys + [key] %}
{% endif %}
{% endfor %}
{# --- Ermittlung der Daten entsprechend der vorher ermittelten Keys ---
#}
{#
- Ergebnisbeispiel: [{'t': 1775669616, 'r': 'r4', 'temp': 10.0, 'p': 200}, {'t': 1775665777, 'r': 'r3', 'temp': 11.0, 'p': 203}, {'t': 1775660530, 'r': 'r2', 'temp': 72.0, 'p': None}, {'t': 1775658484, 'r': 'r4', 'temp': None, 'p': 215}, {'t': 1775629928, 'r': 'r4', 'temp': None, 'p': 201}, {'t': 1775629926, 'r': 'r1', 'temp': None, 'p': 205}, {'t': 1775629925, 'r': 'r3', 'temp': None, 'p': 200}, {'t': 1775629923, 'r': 'r2', 'temp': None, 'p': 208}]
#}
{% for k in ns.keys %}
{% set parts = k.split('_') %}
{% set t = parts[0] | int %}
{% set r = parts[1] %}
{# Wert Temperatur entsprechend des Keys #}
{% set temp_match = temp_list
| selectattr('t','equalto', t)
| selectattr('r','equalto', r)
| list
| first | default(None) %}
{# Wert Reifendruck entsprechend des Keys #}
{% set press_match = press_list
| selectattr('t','equalto', t)
| selectattr('r','equalto', r)
| list
| first | default(None) %}
{# Zusammenführen zu einer Liste #}
{% set ns.merged = ns.merged + [{
't': t,
'r': r,
'temp': (temp_match.temp | round(0)) if temp_match and temp_match.temp is not none else none,
'p': press_match.p if press_match else none
}] %}
{% endfor %}
{% set sorted = ns.merged | sort(attribute='t', reverse=True) %}
{# --- Ausgabe/Darstellung --- #}
<center>
<font size=4><b>
{{title}}
</b></font>
<br>
<br>
<table>
<tr align='center'>
<td><font size=2>Datum/Uhrzeit</font></td>
<td><font size=2>Reifen</font></td>
<td><font size=2>Temp (°C)</font></td>
<td><font size=2>Druck (kPa)</font></td>
</tr>
{% for e in sorted %}
{% set temp = e.temp %}
{% set p = e.p %}
{# --- Zeilenfarbe (heute = fabe 1 und rest farbe 2 --- #}
{% set entry_date = as_datetime(e.t).astimezone().date() %}
{% set today = now().date() %}
{% if entry_date == today %}
{% set color = farbe1 %}
{% else %}
{% set color = farbe2 %}
{% endif %}
<tr>
<td>
<font color="{{ color }}" >
{{ as_datetime(e.t).astimezone().strftime('%d.%m. %H:%M') }}
</font>
</td>
<td align='center'>
<font color="{{ color }}">
{{ e.r.replace('r','') }}
</font>
</td>
<td align='left'>
<font color="{{ color }}">
{{ reifentemperatur_icon(temp) | trim }}
</font>
</td>
<td align='left'>
<font color="{{ color }}">
{{ reifendruck_icon(p) | trim}}
</font>
</td>
</tr>
{% else %}
<tr>
<td colspan="4">Keine Daten</td>
</tr>
{% endfor %}
</table>
</center>
<center>
<br>
<font size=2>Momentan draußen
<b>{{states('sensor.om_temperatur_draussen_unten_durchschnitt')}}
°C</b></font>🌡️🌤️
<br>
<font size=1 color=gray>
Die Werte ändern sich sobald neue Funksignale des Reifens empfangen bzw.
gespeichert wurden.</font>
</center>
card_mod:
style:
ha-markdown $:
ha-markdown-element: |
td {
border: 0 !important;
padding: 2px 3px !important;
line-height: 1.3 !important;
}
.: |
ha-card {
{# In dieser Zeit werden Veränderungen mit eingefärbtem Hintergrund hervorgehoben #}
{% set backgroundzeit_min = 60 %}
{% set raw_temp = states('input_text.auto1_reifentemperatur_history') %}
{% set raw_press = states('input_text.auto1_reifendruck_history') %}
{% set t_list = raw_temp | from_json(default=[]) %}
{% set p_list = raw_press | from_json(default=[]) %}
{% set all = (t_list + p_list) | sort(attribute='t', reverse=True) %}
{% if all | count > 0 %}
{% set last = all | first %}
{% set diff = now().timestamp() - last.t %}
{% if diff < backgroundzeit_min * 60 %}
background: #FFD8BC;
{% endif %}
{% endif %}
}
visibility:
- condition: or
conditions:
- condition: state
entity: input_boolean.dashboard_raum_menue_auto
state: "on"
- condition: state
entity: input_boolean.dashboard_raum_menue_sonstiges
state: "on"
- type: heading
heading_style: subtitle
tap_action:
action: perform-action
perform_action: input_boolean.toggle
target:
entity_id: input_boolean.dashboard_raum_menue_sonstiges
icon: mdi:information
heading: Info zu Reifendruck/Temperatur
visibility:
- condition: or
conditions:
- condition: state
entity: input_boolean.dashboard_raum_menue_auto
state: "on"
- condition: state
entity: input_boolean.dashboard_raum_menue_sonstiges
state: "on"
- type: markdown
content: >-
### Reifentemperatur
Die Reifentemperatur hängt von der Fahrdauer, Fahrweise, Außentemperatur
und Fahrzeugbelastung ab. Während der Fahrt steigt sie an und kann auch
nach dem Abstellen noch einige Zeit erhöht bleiben.
Im normalen Straßenbetrieb gelten Temperaturen bis etwa 70–80 °C als
üblich.
### Reifendruck
Der Reifendruck wird durch Temperatur, Fahrdauer und Fahrzeugbelastung
beeinflusst und steigt während der Fahrt an.
Maßgeblich ist der vom Hersteller vorgegebene Sollwert, der in der Regel
bei etwa 1,8–2,6 bar (180–260 kPa) liegt – abhängig von Fahrzeug und
Beladung.
❗⚠️ Für eine realitätsnahe Beurteilung sollte der Reifendruck im kühlerem
Zustand gemessen werden.
Deutlich niedrigere Werte als der Sollbereich sollten überprüft werden, da
sie auf Druckverlust oder eine falsche Befüllung hinweisen können.
### Auffällige Abweichungen bei einem einzelnen Reifen 🛞
bei Druck oder Temperatur sollten auf technische Probleme überprüft
werden:
- schleichenden Luftverlust
- ungleichmäßige Belastung
- Bremsprobleme
- fehlerhafte Messwerte
grid_options:
columns: full
visibility:
- condition: state
entity: input_boolean.dashboard_raum_menue_sonstiges
state: "on"
card_mod:
style: |
ha-card {
--ha-card-background: #E5E5E5;
font-size: 10pt;
color: #595959;
line-height: normal;
border: 1px solid white;
}
Ich schrieb zwar oben man sollte sich mit RTL-HAOS auskennen aber ich geb auch gerne den Code des Dasboardes den ich auf der RTL-HAOS Erst-Erkundungsphase viel verwendet und angepaßt hatte um überhaupt Geräte und Autos zu erkennen/filtern. Zwischenzeitlich habe ich über 250 verschieden Funksignale aber fast alle auf der RTL-HAOS Blacklist. Um eine frühere Bekannte zu zitieren: “Da würste ja blöde in der Rübe” ![]()
Zusammenfassung
type: sections
max_columns: 6
title: funk
path: funk
icon: mdi:radio-tower
sections:
- type: grid
cards:
- type: markdown
content: >-
- Alle aktiven Geräte durch gehen und prüfen ob Du wirklich alle
Entites brauchst - wegen der DB
- Brauchst Du wirkjlich von allem eine History? Recorder
- Gehe nochmal auf Entwicklerwekrzeuge -> Statistik Probleme -> Sind
alle behoben
title: Still DoDo
- type: heading
heading: Info
heading_style: title
icon: mdi:information
tap_action:
action: perform-action
perform_action: input_boolean.toggle
target:
entity_id: input_boolean.dashboard_raum_menue_sonstiges
- type: markdown
content: >+
### Herausforderung
RTL-HAOS erkennt sehr sehr viele Geräte sobald diese auch nur für
Sekunden funken. Die meisten brauchst Du nicht und sie müllen HA zu
und führen beim "unavailable" Monitoring zu überquellenden
"Falsch"-Meldungen.
Ein weiteres Problem erzeugen Batteriewechsel, z.B. eines
Funkthermometers. Danach sendet das gleiche Gerät mit einer anderer
ID, was manuelle Anpassungen notwendig macht, sehr nervend.
### Ansätze / Erfahrungen
- Zuerst muß man durch viel Beobachtung Rückschlüsse ziehen und
überprüfen bevor man bspw. weiß, welche Reifen zu welchem Auto gehören
und welches Auto zu wem gehört. Das ist ein längerer Prozeß aber so
lernt man gleich die Blacklist kennen und wie RTL-HAOS das HA System
zukleistert.
- RTL-HAOS kann entweder Black- oder Whitelist. Man kann auch mit *
filtern. Das reicht in vielen Fällen. Leider nicht, wenn Du nur ein
bestimmtes Auto einer Marke durchlassen willst. Hier sind die Device
Namen einfach zu global. Ich habe mir die MQTT ID's über MQTT-Explorer
und über KI aus Screenshots ID-Listen generieren lassenund in die
Blacklist händisch eingepflegt. Sobald dann irgendwann die Neugierde
über fremde Funksignale nachläßt, wechselt man zum Whitelist-Konzept
und läßt nur gewollte ID's durch.
- Bei dem Kennenlernen von neu erfaßten Geräten gegenüber bereits
früheren half die custom:auto-entities Karte, Du kannst sowohl
zeitlich eingrenzen als auch bereits bekannte per Wildcard
ausschließen. Ein Klick auf den Eintrag zeigt schnell die hilfreiche
History.
- Das RSSI Signal hilft bei der Einschätzung der Distanz zwischen
Gerät und Antenne. Es kann auch für die Unterscheidung zwischen einem
ankommenden und ab fahrenden Auto benutzt werden.
- Neben dem RSSI Signal ist auch die Entity Filterung "entity_id:
sensor.*model" nützlich, zu erkennen welche Geräte neu, alt etc sind.
Als secondary_info ist last-updated hilfreich.
- Am Ende wartet das Aufräumen, sei es aus technischer Ordnungsliebe
oder unnötig wachsender Datenbank/Recorder. Gelöschte Geräte oder
später deaktivierte Entities verbleiben in der Statistik.
Entwicklerwerkzeuge -> Statistik -> Probleme beheben
- Einige Funkthermometer liefern eine Batterie-Status Entity.
Entsprechend habe ich eine Warnautomatisation angelegt.
- Einmal war der Stick abgestürzt und ich merkte es erst 1 Tag später.
Seitedem habe ich meine verläßlichen 4 Funktthermometer genommmen und
sobald mindestens 2 für 1 min auf unavailable gehen, gibt es eine
Warn-Email.
## Ausblick / Alternative
Ich leibäugele etwas mit der Idee rtl_433 HA-gesondert aufzusetzen,
Auto-Discovery zu deaktivieren und dann für jedes gewolltes! Topic
eigene Sensoren anzulegen.
title: Spreu vom Weizen trennen
visibility:
- condition: state
entity: input_boolean.dashboard_raum_menue_sonstiges
state: "on"
card_mod:
style: |
ha-card {
--ha-card-background: #E5E5E5;
font-size: 10pt;
color: #595959;
line-height: normal;
border: 1px solid white;
}
- type: markdown
content: >-
### Herausforderung
Jedesmal wenn ein Gerät(*) vom Strom getrennt wird, sendet das Gerät
über MQTT mit einer neuen ID
Damit passiert Folgendes:
- Alte Entity und Statistik (vorerst) bleiben erhalten
- Automatisationen, Sensoren, die alte Entities verwenden, werden
nicht mehr getriggert oder aktualisiert
- Das Gleiche für den Recorder, im Falle von gesetzten Excludes.
- Neues Gerät mit neuen ID's hat alle Entities aktiviert und deren
Statuswechsel werden durch den Recorder aufgezeichnet und müllen die
Datenbank zu
(*) Ich las im Internet, daß insbesondere preiswertere, ältere Geräte
davon betroffen sind. Meine Funkthermometer gehören dazu.
### Manuelle Schritte nach jedem Batteriewechsel
1. Namen der alten Entities notieren, z.B. "funkthermometer_u" für
- sensor.funkthermometer_u_humidity
- sensor.funkthermometer_u_temperature
- binary_sensor.funkthermometer_u_battery_low
- sensor.funkthermometer_u_channel
- sensor.funkthermometer_u_signal_rssi
2. Altes Gerät in HA löschen (Statistik bleibt vorerst erhalten)
3. Neues Gerät umbenennen in Namen vom alten, z.B. funkthermometer_u
4. Neuem Gerät gleichen Metadaten (Bereich, Tags) geben wie dem alten
5. Unnötige Geräte-Entities deaktivieren
4. Oben rechts "Entities neu erstellen" (Damit werden alle Entities
mit neuem (altem) Namen erstellt)
6. Jeden neuen Sensor überprüfen ob er auch Statistik vor dem
Batteriewechsel anzeigt. In diesem Fall funktionieren auch wieder
Automatisationen, Sensoren und Recorder-Excludes.
7. In der Zeit zwichen Batterie-Einsetzen und umbennen wurde Statistik
mit den neuen Entity-Namen geschrieben. Die kann man unter
Entwicklerwerkzeuge -> Statistik -> Pobleme beheben löschen. Dieser
Zeitraum fehlt natürlich in der alten/neuen Statistik.
### Tlw. Automatisierung
https://community.simon42.com/t/mqtt-rtl-433-topic-umschreiben-um-batteriewechsel-leichter-abzufangen/79237
Die Idee ist, eingehende MQTT-Daten nicht direkt zu verwenden, sondern
sie zuerst umzuschreiben und unter einem festen, selbst gewählten
Topic neu zu veröffentlichen, wodurch die wechselnde Sensor-ID durch
einen stabilen Namen ersetzt wird. Finde ich richtig gut aber leider
geht das nicht mit RTL-HAOS, zumindestens weiß ich noch nicht wie.
title: Problem bei Batteriewechsel
visibility:
- condition: state
entity: input_boolean.dashboard_raum_menue_sonstiges
state: "on"
card_mod:
style: |
ha-card {
--ha-card-background: #E5E5E5;
font-size: 10pt;
color: #595959;
line-height: normal;
border: 1px solid white;
}
- type: heading
icon: mdi:home-assistant
heading: Homeassi Standardfunktionen
heading_style: title
- type: custom:button-card
name: Zur Funk Geräteübersicht
icon: mdi:radio-tower
tap_action:
action: navigate
navigation_path: /config/devices/device/26ec9303ea7a9c6b2c561d6011ef96e4
styles:
card:
- height: 50px
- padding: 0px 10px
grid:
- grid-template-areas: "\"i n\""
- grid-template-columns: 60px 1fr
- align-items: center
icon:
- margin-right: 10px
- width: 40px
- height: 40px
name:
- font-size: 11pt
- font-weight: bold
- justify-self: start
- align-self: center
grid_options:
columns: full
- type: custom:button-card
name: Zu MQTT Geräten (deaktivierte erkennbar)
icon: mdi:key-wireless
tap_action:
action: navigate
navigation_path: /config/integrations/integration/mqtt
styles:
card:
- height: 50px
- padding: 0px 10px
grid:
- grid-template-areas: "\"i n\""
- grid-template-columns: 60px 1fr
- align-items: center
icon:
- margin-right: 10px
- width: 40px
- height: 40px
name:
- font-size: 11pt
- font-weight: bold
- justify-self: start
- align-self: center
grid_options:
columns: full
- type: custom:button-card
name: Zum RTL-HAOS Addon
icon: mdi:radiobox-blank
tap_action:
action: navigate
navigation_path: /config/app/fb0025cd_rtl-haos/info
styles:
card:
- height: 50px
- padding: 0px 10px
grid:
- grid-template-areas: "\"i n\""
- grid-template-columns: 60px 1fr
- align-items: center
icon:
- margin-right: 10px
- width: 40px
- height: 40px
name:
- font-size: 11pt
- font-weight: bold
- justify-self: start
- align-self: center
grid_options:
columns: full
- type: custom:button-card
name: Zu Entwicklertools -> Statistik
icon: mdi:tooltip-edit-outline
tap_action:
action: navigate
navigation_path: /config/developer-tools/statistics
styles:
card:
- height: 50px
- padding: 0px 10px
grid:
- grid-template-areas: "\"i n\""
- grid-template-columns: 60px 1fr
- align-items: center
icon:
- margin-right: 10px
- width: 40px
- height: 40px
name:
- font-size: 11pt
- font-weight: bold
- justify-self: start
- align-self: center
grid_options:
columns: full
- type: custom:button-card
name: MQTT Explorer
icon: mdi:history
tap_action:
action: navigate
navigation_path: /config/app/9cf1ea8f_mqtt_explorer/info
styles:
card:
- height: 50px
- padding: 0px 10px
grid:
- grid-template-areas: "\"i n\""
- grid-template-columns: 60px 1fr
- align-items: center
icon:
- margin-right: 10px
- width: 40px
- height: 40px
name:
- font-size: 11pt
- font-weight: bold
- justify-self: start
- align-self: center
grid_options:
columns: full
- type: custom:button-card
entity: sensor.rtl_haos_bridge_42_active_devices
name: |
[[[ return entity.state + ' von <br>RTL-HAOS erkannte Geräte'; ]]]
show_icon: false
show_state: false
tap_action:
action: more-info
styles:
card:
- height: 60px
name:
- font-size: 22px
- color: red
- text-align: center
- width: 100%
- type: markdown
content: >+
#### Direkt zu Automatisationen/Scripten
[⚙️ om_aut_tech_funk_rtlhaos_probleme](
/config/automation/edit/1775394391651 )
[⚙️ om_aut_tech_funk_battery_low](
/config/automation/edit/1775395193649 )
card_mod:
style:
ha-markdown$: |
a {
color: var(--primary-text-color) !important;
text-decoration: none !important;
}
- type: grid
cards:
- type: custom:auto-entities
card:
type: entities
title: Neue Renaults
filter:
include:
- entity_id: sensor.renault*acc
options:
secondary_info: last-updated
card_mod:
style: |
:host {
{% set v = states(config.entity) | float(0) %}
{% from 'autos.jinja' import pkw_beschleunigung %}
{% set res = pkw_beschleunigung(v).split('|') %}
{% set col = res[0] %}
{% set bg = res[1] %}
--card-mod-icon-color: {{ col }};
}
hui-generic-entity-row {
color: {{ col }};
background: {{ bg }};
border-radius: 8px;
padding: 2px 6px;
}
- entity_id: sensor.renault*pressure
options:
secondary_info: last-updated
card_mod:
style: |
:host {
{% set v = states(config.entity) | float(0) %}
{% from 'autos.jinja' import pkw_reifendruck %}
{% set res = pkw_reifendruck(v).split('|') %}
{% set col = res[0] %}
{% set bg = res[1] %}
--card-mod-icon-color: {{ col }};
}
hui-generic-entity-row {
color: {{ col }};
background: {{ bg }};
border-radius: 8px;
padding: 2px 6px;
}
exclude:
- state: 1_unavailable
- state: 1_unknown
- name: "*Renault1*"
- name: "*Renault2*"
sort:
method: last_updated
reverse: true
grid_options:
columns: 18
rows: auto
- type: custom:auto-entities
card:
type: entities
title: NEUE verfügbare Models
filter:
include:
- entity_id: sensor.*model
options:
secondary_info: last-updated
exclude:
- state: 1unavailable
- state: 1unknown
- state: Ambientweather-F007TH
- state: Acurite-606TX
- name: Funkthermometer*
- name: "*Renault1*"
- name: "*Renault2*"
sort:
method: last_updated
reverse: true
grid_options:
columns: 18
rows: auto
- type: custom:auto-entities
card:
type: entities
title: Momentane RSSI Signale
filter:
include:
- entity_id: sensor.*_signal_rssi
options:
secondary_info: last-updated
card_mod:
style: |
:host {
{% set v = states(config.entity) | float(0) %}
{% from 'autos.jinja' import pkw_signalstaerke_farbe %}
{% set res = pkw_signalstaerke_farbe(v).split('|') %}
{% set col = res[0] %}
{% set bg = res[1] %}
--card-mod-icon-color: {{ col }};
color: {{ col }};
}
hui-generic-entity-row {
background: {{ bg }};
border-radius: 8px;
padding: 2px 6px;
}
exclude:
- state: unavailable
- state: unknown
- name: 1Funkthermometer*U*
- name: 1Funkthermometer*M*
- name: 1Funkthermometer*S*
sort:
method: state
numeric: true
reverse: true
- type: grid
cards:
- type: custom:auto-entities
card:
type: entities
title: Die letzten ? min (mit excludes)
show_header_toggle: false
filter:
include:
- entity_id: sensor.*model
options:
secondary_info: last-updated
exclude:
- state: unavailable
- state: unknown
- name: Funkthermo*
sort:
method: last_updated
reverse: true
grid_options:
columns: full
- type: custom:auto-entities
card:
type: entities
title: Momentan verfügbare Models
show_header_toggle: false
filter:
include:
- entity_id: sensor.*model
options:
secondary_info: last-updated
name: Updated
exclude:
- state: unavailable
- state: unknown
sort:
method: last_updated
reverse: true
- type: custom:auto-entities
card:
type: entities
title: Aktive Geräte
filter:
include:
- entity_id: sensor.*_signal_rssi
options:
secondary_info: last-updated
exclude:
- state: unavailable
- state: unknown
sort:
method: last_updated
reverse: true
column_span: 2
header:
card:
type: markdown
text_only: true
content: "# RTL HAOS Funk Geräte erkennen, filtern und zurordnen"
EDITS:
Nur kleine Rechtschreibfehler die ich im Nachhinein sah.





