NEWS
[Gelöst] EspHome: Zeit seit letztem State Wechsel
-
Wie könnte man das realisieren? Ich hätte gerne einen Datenpunkt (State) in dem z.B. alle Minute aktualisiert wird, wann der Zustand von BinarySensor X zuletzt geändert worden ist.
-
@wolfgangfb Kommt darauf an was du genau mit dem Datenpunkt/Daten anstellen willst?
Die letzte Änderung steht dir im entsprechenden Datenpunkt jederzeit zur Verfügung:
Da kommst du per "LC" (=last change) dran, zB. Javascript
console.log (getState("esphome.0.xxxxxx.Sensor.xxxxx.state").lc);
Du kannst jetzt natürlich auch ein Blockly nutzen welches auf "Änderung" triggert und dann den "LC" wieder in einen weiteren Datenpunkt schreibt.
-
@sborg
HiIch möchte eigentlich gerade vermeiden, mit JS darauf reagieren zu müssen. Deshalb hätte ich gerne direkt aus dem ESP heraus die Info, wann z.B. ein Bewegungsmelder zuletzt ausgelöst hat (am besten in Sekunden).
Im Moment scheiter ich aber gerade an der Syntax von Time_component und lambdas -
@wolfgangfb
So, ich habe es geschafft, vielleicht interessiert es ja auch andere, deshalb hier meine ausführliche Lösung.Mein Ziel war es, in der Vis auf einen Blick zu erkennen, wann z.B. eine Bewegungsmelder oder ein Türkontakt zuletzt ausgelöst hat.
Das ganze hatte ich ursprünglich in Javaskript realisiert, immerhin soll ja in de Regel auch irgendwas passieren, wenn der Bewegungsmelder auslöst Aber mein Ehrgeiz war es, das ganze zu vereinfachen so dass der Sensor direkt die gewünschte Zeit ausgibt.
Meine Sensoren sind in der Regel ESP8266 besiert, das ganze wird in ESPHome gemacht. Als Hardware verwende ich für den Bewegungmelder AM312 Sensor, der kostet bei Ali 40 Cent und ist meiner Meinung nach zuverlässiger als der HC-SR501Ein "nromaler" Bewegungsmelder sieht dort ja wie folgt aus:
binary_sensor: - platform: gpio name: "Bewegung" pin: number: D5
Damit wird ein Datenpunkt angelegt, der immer dann für ein paar Sekunden true wird, wenn der Bewegugsmelder auslöst. Damit kann man dann ein Licht steuern oder was auch immer.
Jetzt zur Zeiterfassung und da habe ich eben gemerkt, dass C++ nicht meine (Programmier) Muttersprache ist. Hat mich einiges an Kopfzerbrechen gekostet bis ich da immer am Ende auf die richtige Syntax und die richtigen Typumwandlungen gekommen bin. C++ Experten dürfen mir hier auch gerne Vorschläge machen, wie der Code besser oder suaberer wird.
Zunächst definiere ich mir eine Variale, in der die Sekunden seit dem letzten Auslösen gespeichert werden:
globals: - id: D5Last type: int restore_value: yes initial_value: '0'
(id ist der Name der Variable, der Type eben, was darin gespeichert werden soll, restore_value sorgt dafür, dass der Wert bei einem Neustart erhalten bleibt und initial_value ist der Ausgangswert).
Jetzt zur Zeit, hierfür wird die Time Komponente benötig:
time: - platform: sntp timezone: Etc/GMT+1 id: MySntp on_time: - seconds: /5 then: - lambda: |- id(D5Last) += 5;
Was macht dieser Teil: Es wird eine Echtzeituhr gestartet (die eigentliche Zeit werte ich hier garnicht aus) und alle 5 Sekunden wird ein Trigger ausgelöst, der auf die bisherige Zeit seit dem letzten Auslösen 5 (Sekunden) draufaddiert. Man könnte das ganze natürlich auch Sekündlich machen, bringt aber meiner Meinung nach nichts.
Als nächstes muss die Zeit wieder auf 0 zurückgesetzt werden, wenn der Bewegungsmelder auslöst: Also den den Bewegungsmelder erweitern.binary_sensor: - platform: gpio name: "Bewegung" pin: number: D5 on_state: then: - lambda: id(D5Last) = 0;
Jetzt zum scvhwierigsten Teil, das ganze als fertig lesbaren Text mit Einheit "Sek" zu liefern so dass es dirket in die VIS eingebunden werden kann.
Das ganze wird realisiert über einen sogenannten Textsensor. Die habe ich bisher dafür verwendet um beispielsweise die IP eines ESP anzuzeigen.
text_sensor: - platform: template name: "IP" lambda: 'return {WiFi.localIP().toString().c_str()};'
Jetzt musste ich ertmal kapieren, wie das mit den Lambdas und dem c_str etc. funktioniert.
aber zunächst mal einen Datenpunkt anlegen:
- platform: template name: "D5MotionLast" id: D5MotionLastID update_interval: never
Das ist dann der Datenpunkt, der in Vis als "Text" eingebunden wird.
(normalerweise wird so ein Textsensor alle 60 Seekunden aktualisiert, hier wird mit "never aber dafür gesorgt, dass er selbständig nie aktualisier, das will ich ja selbst steuern.)Also die Zeitkomponente erweitert:
time: - platform: sntp timezone: Etc/GMT+1 id: MySntp on_time: - seconds: /5 then: - lambda: |- id(D5Last) += 5; - text_sensor.template.publish: id: D5LastId state: !lambda return MyTime(id(D5Last));
Mit text_sensor.template.publish kann man den Textsensor von außen mit einem neuen Wert versehen. Die Aufgabe war jetzt noch aus der nakten Zehl (int) eine schöne Ausgabe zu machen.
Gehen würde das z.B. mitreturn str_sprintf("%02.0f sec",float(D5Last));
aber ich wollte es schöner haben und 13565 Sekunden finde ich nicht sonderlich aussagekräftig.
Also in der ersten Minute die Sekunden anzeigen, inder ersten Stunde die Minuten und am ersten Tag die Stunden, dann nur noch die Tage (wer will darf das ganze gerne auf Wochen, Monate etc. erweitern).
Da ich das ganze nicht nur für einen Sensor mahen wollte habe ich mir überlegt, wie ich diese Funktion nicht bei jedem Sensor neu reinkopieren muss.
Also die Funktion ausgelagert.
Im gleichen Verzeichnis in dem die yaml Dateien sind eine Date "MyTime.h" angelgt und diese am Anfan in das yaml Projekt eingebunden:esphome: name: Testraum platform: ESP8266 board: nodemcuv2 includes: - MyTime.h
die Datei "MyTime.h" sieht wie folgt aus:
std::string MyTime(int secs){ char Plural = ' '; if (secs > 60 * 60 * 24 *2 ) { Plural = 'e'; } if (secs < 60){ return str_sprintf("%02.0f Sek", float(secs)); } else { if (secs < 60 * 60){ return str_sprintf("%2.0f Min", abs(float(secs/60))); } else { if (secs < 60 * 60 * 24){ return str_sprintf("%2.0f Std", abs(float(secs/(60 * 60)))); } else { return str_sprintf("%2.0f Tag%c", abs(float(secs/(60 * 60 * 24))), Plural); } } } }
(Das geht mit Sicherheit eleganter, Verbesserungsvorschläge nehme ich aber gerne an).
Auf jeden Fall funktioniert das ganze und es wird sogar bei einem Tag nur "Tag" und ab dem zweiten Tag "Tage" ausgegeben
Hier das ganze nochmal als komplettes yaml File das sich so auch übersetzen lassen sollte (wenn ich beim Copy&Paste keinen Fehler gemacht habe
esphome: name: Testraum platform: ESP8266 board: nodemcuv2 includes: - MyTime.h # Enable logging logger: # Enable Home Assistant API api: ota: password: "xxxx" wifi: ssid: xxxx" password: "xxxx" # Enable fallback hotspot (captive portal) in case wifi connection fails ap: ssid: "xxxx" password: "xxxx" captive_portal: web_server: port: 80 globals: - id: D5Last type: int restore_value: yes initial_value: '0' text_sensor: - platform: template name: D5Last id: D5LastId update_interval: never binary_sensor: - platform: gpio pin: number: D5 on_state: then: - lambda: id(D5Last) = 0; time: - platform: sntp timezone: Etc/GMT+1 id: MySntp on_time: - seconds: /5 then: - lambda: |- id(D5Last) += 5; - text_sensor.template.publish: id: D5LastId state: !lambda return MyTime(id(D5Last));
-
@wolfgangfb Servus, kann man auch z.B. bei
time: - platform: sntp timezone: Etc/GMT+1 id: MySntp on_time: - seconds: /5 # Wert aus einer Variable nehmen???? then: