Neues Feature: Filtern nach Ausdrücken mit Ausgabeformatierung

Begonnen von Damian, 14 Februar 2016, 17:59:32

Vorheriges Thema - Nächstes Thema

Damian

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
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Mr. Flash

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

RPi 4; Bullseye; FHEM 6.3; S.USV basic; BME280; TSL25911.
HM: CFG-LAN,CFG-USB-2,CC-RT-DN,Dis-(EP-)WM55,ES-PMSw1-Pl,ES-TX-WM,LC-Dim1T-FM,LC-Sw1-FM,LC-Sw2-FM,MOD-RPI-PCB,OU-CFM-Pl,RC-Dis-H-x-EU,SCI-3-FM,Sec-MDIR-2,-RHS,-SD,-SC-2,-SCo,-Sir-WM,-TiS,-WDS-2,TC-IT-WM-W-EU,WDS10-TH-O,WDS30-OT2-SM.
Shelly's.

Damian

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
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Sunny

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
FHEM 6.0 (RPi's 1b-4,CeleronM,Odroid C1+)
1-Wire (DS18B20,DS2406) |miniCUL|miniCUL868WLAN|HM|IT(-1500,LR-3500) |FB6591,FB7490,FB7580|DECT200|Powerline546E|520E|openwrt
Anfänger: Linux,FHEM+Perl

Damian

#4
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

Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

turo

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
3xRaspberry PI, Homematic, SELVE Rollos, 1-wire, Logitech Harmony, Alexa, Fussbodenheizung (ESP8266), Netatmo

Damian

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
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Damian

#7
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
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

turo

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
3xRaspberry PI, Homematic, SELVE Rollos, 1-wire, Logitech Harmony, Alexa, Fussbodenheizung (ESP8266), Netatmo

Damian

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
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

turo

#10
Und was ist mit "((),())"? Deine Expression matched da - meine nicht. Du stoppst ja nach einer öffnenden Klammer bei der ersten schließenden.

Gruss,
turo
3xRaspberry PI, Homematic, SELVE Rollos, 1-wire, Logitech Harmony, Alexa, Fussbodenheizung (ESP8266), Netatmo

Damian

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
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Damian

#12
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



Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

turo

@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
3xRaspberry PI, Homematic, SELVE Rollos, 1-wire, Logitech Harmony, Alexa, Fussbodenheizung (ESP8266), Netatmo

Damian

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
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF