Bisher war nur das Filtern nach Zahlen dokumentiert:
[<Device>:<Reading>:d]
Es gab immer schon intern die Möglichkeit nach beliebigen Ausdrücken zu filtern. Diese Funktionalität war jedoch undokumentiert.
Ich habe das Filtern nach Ausdrücken, um die Möglichkeit die Ausgabe gleichzeitig zu formatieren, erweitert. Die Syntax lautet:
[<Device>:<Reading>:"<Regex>",<Output>]
Output ist Optional. Hier kann $1, $2, usw. aus Regex genutzt werden oder z. B. eine Formatierungsfunktion wie z. B. sprintf
Bsp. 1:
Im Reading steht "11:22"
Beide Zahlen sollen vertauscht werden:
[mydevice:myreading:"(\d\d):(\d\d)":"$2:$1")]
Bsp. 2:
Eine Zahl soll herausgefiltert werden und in einen Text eingebunden werden:
[mydevice:myreading:"(\d+)":"Die Zahl lautet $1")]
Bsp. 3
Es soll die Zahl im Reading auf 2 Nachkommastellen formatiert werden:
[mydevice:myreading:"(.*)":sprintf("%.2f",$1)]
Das Filtern nach Zahlen:
[mydevice:myreading:d]
entspricht
[mydevice:myreading:"(-?\d+(\.\d+)?)"]
oder
[mydevice:myreading:"(-?\d+(\.\d+)?)":"$1"]
Für die Nutzung des neuen Features ist das Verständnis von regulären Ausdrücken mit Speicherung der Ergebnisse in $1, $2 usw. erforderlich.
Da im Output-Parameter beliebige Perl-Funktionen genutzt werden können, sind die Möglichkeiten die Ausgabe zu formatieren unbegrenzt.
Wenn meine Tests abgeschlossen sind, werde ich die Version einchecken.
Gruß
Damian
Hallo Damian,
das ist genau das, was ich brauche! :)
Beim Auslesen der Tag-/Nacht-Temperatur (R-dayTemp, R-nightTemp) aus einem Heizkörperventil HM-CC-RT-DN kommt diese als z.B. "17" oder "21.5". Die ".0" wird nicht ausgegeben, beim Setzen der Temperatur aber erwartet, also "17.0" oder "21.5".
Mit den Info's aus Deinem Bsp. 3 sollte das dann klappen.
Ich würde es gerne vorab mal ausprobieren, kämpfe aber gerade mit massiven disconnect/init meines HMLAN.
Viele Grüße,
Nik
Zitat von: Mr. Flash am 14 Februar 2016, 19:05:12
Hallo Damian,
das ist genau das, was ich brauche! :)
Beim Auslesen der Tag-/Nacht-Temperatur (R-dayTemp, R-nightTemp) aus einem Heizkörperventil HM-CC-RT-DN kommt diese als z.B. "17" oder "21.5". Die ".0" wird nicht ausgegeben, beim Setzen der Temperatur aber erwartet, also "17.0" oder "21.5".
Mit den Info's aus Deinem Bsp. 3 sollte das dann klappen.
Ich würde es gerne vorab mal ausprobieren, kämpfe aber gerade mit massiven disconnect/init meines HMLAN.
Viele Grüße,
Nik
Ich muss noch bisschen testen, bevor ich es veröffentliche.
Du kannst aber jetzt schon mit DOIF folgendes definieren:
set blabla {(sprintf ("%.1f",[mydevice:R-dayTemp:d]))}
Gruß
Damian
Moin Damian,
<OT an>
ist es Dir möglich, Deine neuste Version anzupinnen?
Eventuell auch Threads mit Tipps bzw. Erklärungen?
Vielleicht hilft es bei der Übersichtlichkeit?
<OT aus>
Danke & viele Grüße
Sunny
Ich habe meine Tests abgeschlossen. Neue Version im Anhang.
Hier ein kleiner Vorgeschmack von neuen syntaktischen Möglichkeiten (semantisch nicht gerade sinnig an dieser Stelle)
DOIF ([FS:state:"(.*)"])(set bla {(sprintf("%s","[FS:state:"(.*)":$1]"))}, set bla2 [FS:state:"(.*)",sprintf("%s",$1)])
Man beachte, dass beliebige Hierarchien möglich sind. Nebenbei habe ich das Problem der Kommata als Trennzeichen minimiert. Hier ist z. B. keine doppelte Klammerung mehr nötig. Dazu hat mich dieser reguläre Ausdruck zum Suchen nach einem Komma als Trennzeichen etwas gefordert:
/^[^,^\[^(\{\()]*,|((\[.*\])|(\{\(.*\)\}))[^,]*,/g
Edit: Wer das versteht, der weiß wann er keine doppelten runden Klammern mehr verwenden muss ;)
Viel Spaß beim Ausprobieren.
Gruß
Damian
ZitatEdit: Wer das versteht, der weiß wann er keine doppelten runden Klammern mehr verwenden muss ;)
Herausforderung angenommen! ;)
Also in [] und {} sind die Kommata schon mal "safe".
Aber ich scheitere am ersten Teil Deiner Regex, was sollen denn da die ^ #3 und #4? Die sind doch nicht "special" in einer bracketed character class? Und die ganzen Klammern auch nicht?
^[^,^\[^(\{\()]*,
wäre also genau dasselbe wie
^[^,^[({()]*,
Da denke ich lieber heute Abend in Ruhe noch mal drüber nach. Und dann auch über den "g" Modifier. :-\
Gruss,
turo
Zitat von: turo am 18 Februar 2016, 16:34:34
Herausforderung angenommen! ;)
Also in [] und {} sind die Kommata schon mal "safe".
Aber ich scheitere am ersten Teil Deiner Regex, was sollen denn da die ^ #3 und #4? Die sind doch nicht "special" in einer bracketed character class? Und die ganzen Klammern auch nicht? ^[^,^\[^(\{\()]*,
wäre also genau dasselbe wie ^[^,^[({()]*,
Da denke ich lieber heute Abend in Ruhe noch mal drüber nach. Und dann auch über den "g" Modifier. :-\
Gruss,
turo
Safe sollen sie in [] und in {()} sein.
Ich musste allerdings noch feststellen, dass damit bei "[...], [...]," das erste Komma zwischen den Klammern nicht erkannt wird. Die Regex wird also noch etwas länger werden müssen.
Gruß
Damian
Zitat von: Damian am 18 Februar 2016, 16:56:13
Safe sollen sie in [] und in {()} sein.
Ich musste allerdings noch feststellen, dass damit bei "[...], [...]," das erste Komma zwischen den Klammern nicht erkannt wird. Die Regex wird also noch etwas länger werden müssen.
Gruß
Damian
/^[^,^\[^\{^\(]*,|(((\[[^\]]*\])[^,^\[]*)|((\([^\)]*\))[^,^\(]*)|((\{[^\}]*\})[^,^\{]*))*|[^,]*,/
Damit wird alles innerhalb von () {} [] überlesen. Allerdings werden noch keine ausmaskierten Klammern entdeckt.
Edit:
Hier noch mal die aktualisierte Version:
Für Regex-Experten und die, die es werden wollen, hier mein optimiertes Suchen nach dem "richtigen" Komma:
/^(([^,^\[^\{^\(]*)((\[[^\]]*\])|(\([^\)]*\))|(\{[^\}]*\}))?)*,/
Damit wird man zukünftig recht selten doppelte Klammerung benutzen müssen.
Gruß
Damian
Das kann aber mit verschachtelten Klammern nicht richtig umgehen und matched z.B. auch auf "(tralala,(hihi,hohoho),bububu)". Sollte man die Klammern da nicht rekursiv matchen, so wie etwa:
/^(([^,^\[^\{^\(]*)((\[[^\]]*\])|(?<p>\((?:[^()]++|(?&p))*+\))|(\{[^\}]*\}))?)*,/
(Ich habe die Rekursion jetzt mal nur für die runden Klammern eingebaut und der Code ist angepasst aus dem Perlbuch.)
Gruss,
turo
Zitat von: turo am 19 Februar 2016, 13:41:38
Das kann aber mit verschachtelten Klammern nicht richtig umgehen und matched z.B. auch auf "(tralala,(hihi,hohoho),bububu)". Sollte man die Klammern da nicht rekursiv matchen, so wie etwa:
/^(([^,^\[^\{^\(]*)((\[[^\]]*\])|(?<p>\((?:[^()]++|(?&p))*+\))|(\{[^\}]*\}))?)*,/
(Ich habe die Rekursion jetzt mal nur für die runden Klammern eingebaut und der Code ist angepasst aus dem Perlbuch.)
Gruss,
turo
Das braucht es nicht, denn es soll alles überlesen was geklammert ist, egal in welcher Hierarchiestufe. Für die äußeren Klammen habe ich eine eigene Funktion.
({[,]}) funktioniert genauso wie auch [({,})].
Gruß
Damian
Und was ist mit "((),())"? Deine Expression matched da - meine nicht. Du stoppst ja nach einer öffnenden Klammer bei der ersten schließenden.
Gruss,
turo
Zitat von: turo am 19 Februar 2016, 14:17:51
Und was ist mit "((),())"? Deine Expression matched da - meine nicht. Du stoppst ja nach einer öffnenden Klammer bei der ersten schließenden.
Gruss,
turo
ja, ich brauche ja nur die erste Position des Kommas zwischen den Klammern, mehr nicht.
z. B.
(slkfjskl,llksdjfl ) (ksjfk, skldfjs), ((((jskdfjsljk),),),),
oder
(slkfjskl,llksdjfl ), (ksjfk, skldfjs), ((((jskdfjsljk),),),),
oder
(slkfjskl,llksdjfl ) (ksjfk, skldfjs) ((((jskdfjsljk),),),),
usw.
Gruß
Damian
Die neue Version werde ich heute einchecken. Auszug aus der neuen Doku (hier unformatiert):
ZitatFiltern nach Ausdrücken mit Ausgabeformatierung
Syntax: [<Device>:<Reading>|<Internal>:d|"<Regex>":<Output>]
d - Der Buchstabe "d" ist ein Synonym für das Filtern nach Dezimalzahlen, es entspricht intern dem regulären Ausdruck "(-?\d+(\.\d+)?)"
<Regex>- Der reguläre Ausdruck muss in Anführungszeichen angegeben werden. Dabei werden Perl-Mechanismen zu regulären Ausdrücken mit Speicherung der Ergebnisse in Variablen $1, $2 usw. genutzt.
<Output> - ist ein optionaler Parameter, hier können die in den Variablen $1, $2, usw. aus der Regex-Suche gespeicherten Informationen für die Aufbereitung genutzt werden. Sie werden in Anführungzeichen bei Texten oder in Perlfunktionen angegeben. Wird kein Output-Parameter angegeben, so wird automatisch $1 genutzt.
Das Features ist sowohl in der Bedingung wie auch im Ausführungsteil eines DOIF-Moduls nutzbar.
Beispiel:
Es soll aus einem Reading, das z. B. ein Prozentzeichen beinhaltet, nur der Zahlenwert für den Vergleich genutzt werden:
define di_heating DOIF ([adjusting:actuator:d] < 10) (set heating off) DOELSE (set heating on)
Alternativen für die Nutzung der Syntax am Beispiel des Filterns nach Zahlen:
[mydevice:myreading:d]
entspricht:
[mydevice:myreading:"(-?\d+(\.\d+)?)"]
entspricht:
[mydevice:myreading:"(-?\d+(\.\d+)?)":$1]
entspricht:
[mydevice:myreading:"(-?\d+(\.\d+)?)":"$1"]
entspricht:
[mydevice:myreading:"(-?\d+(\.\d+)?)":sprintf("%s":$1)]
Weitere Beispiele:
Es soll aus einem Text eine Zahl herausgefiltert werden und anschließend die Einheit °C angehängt werden:
... (set mydummy [mydevice:myreading:d:"$1 °C"])
Es soll die Zahl aus einem Reading auf 2 Nachkommastellen formatiert werden:
[mydevice:myreading:d:sprintf("%.2f",$1)]
Es sollen aus einem Reading der Form "HH:MM:SS" die Stunden, Minuten und Sekunden separieret werden:
[mydevice:myreading:"(\d\d):(\d\d):(\d\d)":"hours: $1, minutes $2, seconds: $3"]
Der Inhalt des Dummys Alarm soll in einem Text eingebunden werden:
[alarm:state:"(.*)":"state of alarm is $1"]
Für eine ausgebiege Nutzung des Features ist das Verständnis von regulären Ausdrücken mit Speicherung der Ergebnisse in Perl-Variablen $1, $2 usw. erforderlich.
Gruß
Damian
@Damian:
Sag mir bitte, wenn ich nerve und Du musst mir natürlich nicht Deinen Code erklären, aber ich genieße gerade die geistige Herausforderung ;-).
Deinen Ausdruck habe mal kurz in ein kleines Perlskript gepackt und
(slkfjskl,llksdjfl ) (ksjfk, skldfjs) ((((jskdfjsljk),),),),
matched bei mir so:
(slkfjskl,llksdjfl ) (ksjfk, skldfjs) ((((jskdfjsljk),),),),
(Wenn das natürlich trotzdem funktioniert: Um so besser und s.o. ;-)
Gruss,
turo
Zitat von: turo am 19 Februar 2016, 16:31:01
@Damian:
Sag mir bitte, wenn ich nerve und Du musst mir natürlich nicht Deinen Code erklären, aber ich genieße gerade die geistige Herausforderung ;-).
Deinen Ausdruck habe mal kurz in ein kleines Perlskript gepackt und
(slkfjskl,llksdjfl ) (ksjfk, skldfjs) ((((jskdfjsljk),),),),
matched bei mir so:
(slkfjskl,llksdjfl ) (ksjfk, skldfjs) ((((jskdfjsljk),),),),
(Wenn das natürlich trotzdem funktioniert: Um so besser und s.o. ;-)
Gruss,
turo
Du hast Recht. Es funktioniert nur in der ersten Tiefe, ich habe offenbar immer mit verschiedenen geschachtet Klammern getestet.
Die Rekursion mit regulären Ausdrücken abzufangen, wird dann aber für alle drei Klammern recht aufwändig. Ich denke, ich werde es dann mit eigenen Funktionen realisieren, diese zählen die Klammern.
Gruß
Damian
Zitat von: Damian am 19 Februar 2016, 16:59:22
Die Rekursion mit regulären Ausdrücken abzufangen, wird dann aber für alle drei Klammern recht aufwändig. Ich denke, ich werde es dann mit eigenen Funktionen realisieren, diese zählen die Klammern.
So schlimm sollte das nicht werden: Den Fall, dass sich verschiedene Klammern ineinander verschachteln, kann man ja ignorieren: Es zählt der Typ der äußersten Klammer. Wenn Du es brauchen kannst, mache ich meinen Ausdruck noch fertig. (Und das wäre dann als Nebeneffekt für mich der erste Einsatz von recursive matching in freier Wildbahn!)
Gruss,
turo
Zitat von: turo am 19 Februar 2016, 17:07:58
So schlimm sollte das nicht werden: Den Fall, dass sich verschiedene Klammern ineinander verschachteln, kann man ja ignorieren: Es zählt der Typ der äußersten Klammer. Wenn Du es brauchen kannst, mache ich meinen Ausdruck noch fertig. (Und das wäre dann als Nebeneffekt für mich der erste Einsatz von recursive matching in freier Wildbahn!)
Gruss,
turo
Kannst du gerne machen. Ich bastle währenddessen eine Routine in Perl, dann schauen wir welche Lösung performanter ist.
Zitat von: Damian am 19 Februar 2016, 17:35:33
Kannst du gerne machen. Ich bastle währenddessen eine Routine in Perl, dann schauen wir welche Lösung performanter ist.
Ich habe aktuelle Version eingecheckt inkl. "safe" Komma in beliebiger Schachtelungstiefe :).
Gruß
Damian
In Verbinung mit Internals funktioniert das bei mir nicht :(.
Version vom 13/03 (https://forum.fhem.de/index.php/topic,49109.msg424219.html#msg424219)
set test3 [$DEVICE:STATUS:"(.+)":"$1"])
macht nix (eventMonitor: "reading does not exist")
set test3 [$DEVICE:status:"(.+)":"$1"])
setzt text3.
Statt status $EVENT (oder $event oder [$EVENT]) zu nehmen geht auch nicht, man kann die Events nur direkt benennen.
Oder ich habe die passende Syntax noch nicht rausbekommen.
Zitat von: Per am 18 März 2016, 21:14:12
In Verbinung mit Internals funktioniert das bei mir nicht :(.
Version vom 13/03 (https://forum.fhem.de/index.php/topic,49109.msg424219.html#msg424219)
set test3 [$DEVICE:STATUS:"(.+)":"$1"])
macht nix (eventMonitor: "reading does not exist")
set test3 [$DEVICE:status:"(.+)":"$1"])
setzt text3.
Statt status $EVENT (oder $event oder [$EVENT]) zu nehmen geht auch nicht, man kann die Events nur direkt benennen.
Oder ich habe die passende Syntax noch nicht rausbekommen.
Internals werden mit & angegeben, daher:
set test3 [$DEVICE:&STATE:"(.+)":"$1"])
Gruß
Damian
Was soll ich sagen, du hast natürlich Recht.
Gilt das mit dem & pauschal immer, wenn im DOIF statt Readings Internals genutzt werden werden sollen?
Zitat von: Per am 19 März 2016, 16:01:43
Was soll ich sagen, du hast natürlich Recht.
Gilt das mit dem & pauschal immer, wenn im DOIF statt Readings Internals genutzt werden werden sollen?
Hier hat Ellert alles noch mal zusammengefasst:
http://fhem.de/commandref_DE.html#DOIF_Kurzreferenz
Gruß
Damian