Neue Features: Aggregationsfunktion, Filtern nach Zahl mit Nachkommastellen

Begonnen von Damian, 15 April 2017, 21:25:06

Vorheriges Thema - Nächstes Thema

Damian

Pünktlich zu Ostern gibt es wieder neue Features. Wie bereits angekündigt, habe ich DOIF eine Aggregationsfunktion spendiert.

Die Aggregationsfunktion kann sowohl in den Bedingungen als auch im Ausführungsteil angegeben werden.

Syntax:

[<Funktion>:"<regex Device>:<regex Event>":<reading>:<condition>,<default>]

<Funktion> :

# Anzahl der Treffer
@ kommagetrennte Liste Devices
#sum Summe
#max:d<Number> höchster Wert
#min:d<Number> niedrigster Wert
#average:d<Number> Durchschnitt
@max Device des höchsten Wertes
@min Device de niedrigsten Wertes

Der danach folgende Doppelpunkt als Trennzeichen kann weggelassen werden, das bietet sich allerdings zwecks besserer Lesbarkeit nur bei den beiden einstelligen Angaben #,@ an.

"<regex Device>:<regex Event>" spezifiziert sowohl den Trigger, als auch die betroffenen Devices, die Syntax entspricht der DOIF-Syntax für Ereignistrigger.
Die Angabe <regex Event> ist im Ausführungsteil nicht sinnvoll und sollte weggelassen werden.

<reading> Reading, welches überprüft werden soll

<condition>  Aggregations-Bedingung, $_ ist der Platzhalter für den aktuellen Wert des internen Schleifendurchlaufs, Angaben in Anführungszeichen der Art "<value>" entsprechen $_ =~ "<value>" , hier sind alle Perloperatoren möglich.

<default> Default-Wert, falls kein Device gefunden wird, entspricht der Syntax des Default-Wertes bei Readingangaben

<reading>, <condition>  <default> sind optional

Syntax-Beispiele im Ausführungteil

Anzahl der Devices, die mit "window" beginnen:
[#"^window"]

Liste der Devices, die mit "window" beginnen:
[@"^window"]

Liste der Devices, die mit "windows" beginnen und ein Reading "myreading" beinhalten:
[@"^window":myreading]

Liste der Devices, die mit "windows" beginnen und im Status das Wort "opened" vorkommt:
[@"^window":state:"opened"]
entspricht:
[@"^window":state:$_ =~ "opened"]
siehe Aggregationsbedingung.

In der Aggregationsbedingung <condition> können alle in FHEM definierten Perlfunktionen genutzt werden. Folgende Variablen sind vorbelegt und können ebenfalls benutzt werden:

$_ Inhalt des angegebenen Readings (s.o.)
$number Nach Zahl gefilteres Reading
$name Name des Devices
$TYPE Devices-Typ
$STATE Status des Devices (nicht das Reading state)
$room Raum des Devices
$group Gruppe des Devices

Beispiele für Definition der Aggregationsbedingung <condition>

Liste der Devices, die mit "rooms" enden und im Reading "temperature" einen Wert größer 20 haben.
[@"rooms$":temperature:$_ > 20]

Liste der Devices im Raum "livingroom", die mit "rooms" enden und im Reading "temperature" einen Wert größer 20 haben:
[@"rooms$":temperature:$_ > 20 and $room eq "livingroom"]

Liste der Devices in der Gruppe "windows", die mit "rooms" enden, deren Status (nicht state-Reading) "on" ist:
[@"rooms$"::$STATE eq "on" and $group eq "windows"]

Liste der Devices, deren state-Reading "on" ist und das Attribut disable nicht auf "1" gesetzt ist:
[@"":state:$_ eq "on" and AttrVal($name,"disable","") ne "1"]


Aggregationsangaben in der Bedingung reagieren zusätzlich auf Trigger, hier sollte die regex-Angabe für das Device um eine regex-Angabe für das zu triggernde Event erweitert werden.

Anzahl der Devices, die mit "window" beginnen. Getriggert wird, wenn ein Eventzeile beginnend mit "window" und dem Wort "opened" vorkommt:
[#"^window:opened"]

Anwendungsbeispiele


Fenster Status/Meldung:

define di_Fenster DOIF ([#"^Window:open":state:"open"] != 0) (push "Fenster $DEVICE wurde geöffnet. Es sind folgende Fenster offen: [@"^Window":state:"open"]")
DOELSEIF ([#"^Window:closed":state:"open"] == 0) (push "alle Fenster geschlossen")

attr di_Fenster do always
attr di_Fenster cmdState $DEVICE zuletzt geöffnet|alle geschlossen


Raumtemperatur-Überwachung:

define di_Raumtemp DOIF (["^Rooms:temperature"])
  (push "In folgenden Zimmern ist zu kalt [@"^Rooms":temperature: $_ < 20]")
DOELSEIF ([#"^Rooms:temperature":temperature: $_ >= 20] == 0)
  (push "alle Zimmmer sind warm")

attr di_Raumtemp cmdState es muss geheizt werden|alle Räume sind warm


Durchschnittstemperatur:

define di_average DOIF ([08:00] or [12:00]) (push "Die durchschnittliche Temperatur aller Zimmer beträgt [#average:"^Rooms":temperature]")

attr di_average do always


Es soll beim Öffnen eines Fenster eine Meldung über alle geöffneten Fenster erfolgen:

define di_Fenster DOIF (["^Window:open"]) (push "Folgende Fenster: [@"^Window:state:"open"] sind geöffnet")

attr di_Fenster do always


Wenn im Wohnzimmer Lampe ausgeschaltet wird, sollen alle anderen Lampen ausgeschaltet werden, die noch an sind:

define di_lamp DOIF (["livingroom_lamp: off"]) (set [@"room_lamp",state,"on"] off)

attr di_lamp DOIF do always



Für Perl-Anwender:

Für reine Perlangaben gibt es eine entsprechende Perlfunktion namens AggrDoIf(<function>,<regex Device>,<reading>,<condition>) diese liefert bei der Angabe @ ein Array statt einer Stringliste,  dadurch lässt sie sich gut bei foreach-Schleifen verwenden.

Beispiele

define di_Fenster DOIF (["^Window:open"]) {foreach (AggrDoIf('@','^windows','state','"open"')) {Log3 "di_Fenster",3,"Das Fenster $_ ist noch offen"}}

define di_Temperature DOIF (["^room:temperature"]) {foreach (AggrDoIf('@','^room','temperature','$_ < 15')) {Log3 "di_Temperatur",3,"im Zimmer $_ ist zu kalt"}}

Es handelt sich um eine Testversion. Sie ist zur bisherigen Version abwärtskompatibel.

Intern wurde die Aggregationsfunktion durch die Nutzung des grep-Befehls optimiert, daher gehe ich nicht von Performance-Problemen aus.

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

Damian

#1
Damit das Interesse für die neue Aggregationsfunktion geweckt wird, bedarf es natürlich einer Killer-Anwendung :)

Hier mal eine als Zweizeiler:

defmod di_Battery DOIF
attr di_Battery state [@":battery":battery:$_ ne "ok"]



Dieses Modul zeigt immer aktuell im Status alle Devices des Systems, deren battery-Reading nicht "ok" ist.
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Ralli

Ein lang gehegter Wunsch wird wahr  :) .

Danke!
Gruß,
Ralli

Proxmox 8.1 Cluster mit HP ED800G2i7, Intel NUC11TNHi7+NUC7i5BNH, virtualisiertes fhem 6.3 dev, virtualisierte RaspberryMatic (3.75.6.20240316) mit HB-RF-ETH 1.3.0 / RPI-RF-MOD, HM-LAN-GW (1.1.5) und HMW-GW, FRITZBOX 7490 (07.57), FBDECT, Siri und Alexa

Damian

#3
Zitat von: Ralli am 17 April 2017, 12:30:47
Ein lang gehegter Wunsch wird wahr  :) .

Danke!

Man kann es auch als Structure-Ersatz nutzen, ohne diese pflegen zu müssen. Ebenfalls ist es als universeller Filter nutzbar. Ich habe gerade noch den TYPE (Version v0.4) ergänzt. Damit könnte man z. B. alle HM-Lampen im Wohnzimmer einschalten die noch aus sind:

set [@"":state:$_ eq "off" and $TYPE eq "CUL_HM" and $room eq "livingroom")] on

Da man in der Bedingung beliebigen Perl-Code mit allen erdenklichen FHEM-Funktionen definieren kann, dürften die Filter-Möglichkeiten vielfältiger sein als beim set-FILTER.
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

OliS.

Danke für die neuen Features!

Ich habe gerade mal etwas rumgespielt. Kann es sein, dass sich die Funktion "#average" noch leicht verrechnet? Ich wollte mir die durchschnittliche Helligkeit meiner drei HM-Bewegungsmelder anzeigen lassen.

Internals:
   CFGFN
   NAME       brightness_average
   NR         12437
   NTFY_ORDER 50-brightness_average
   STATE      17.875
   TYPE       DOIF
   Readings:
     2017-04-17 20:58:45   Device          BMelder1
     2017-04-17 20:34:32   cmd             0
     2017-04-17 20:58:45   state           17.875
   Condition:
   Devices:
   Do:
     0:
   Helper:
     event      brightness: 68
     globalinit 1
     last_timer 0
     sleeptimer -1
     triggerDev BMelder1
     triggerEvents:
       brightness: 68
     triggerEventsState:
       brightness: 68
   Internals:
   Itimer:
   Readings:
   Regexp:
     All:
   State:
     State:
       0          ^BMelder
   Trigger:
Attributes:
   state      [#average"^BMelder":brightness]


Hier ein exemplarischer Melder.

Internals:
   DEF        2EECD0
   HMLAN1_MSGCNT 391
   HMLAN1_RAWMSG E2EECD0,0000,14F53E53,FF,FFBA,7184102EECD029A2F706014F00
   HMLAN1_RSSI -70
   HMLAN1_TIME 2017-04-17 20:53:09
   IODev      HMLAN1
   LASTInputDev HMLAN1
   MSGCNT     391
   NAME       BMelder1
   NOTIFYDEV  global
   NR         399
   NTFY_ORDER 50-BMelder1
   STATE      79
   TYPE       CUL_HM
   lastMsg    No:71 - t:10 s:2EECD0 d:29A2F7 06014F00
   peerList   Terrassenlicht,
   protLastRcv 2017-04-17 20:53:09
   rssi_at_HMLAN1 cnt:391 max:-63 avg:-69.28 min:-85 lst:-70
   Readings:
     2017-04-16 14:38:12   Activity        alive
     2016-02-19 20:57:32   CommandAccepted yes
     2016-02-19 20:55:37   D-firmware      1.6
     2016-02-19 20:55:37   D-serialNr      LEQ0657403
     2016-02-19 20:57:32   PairedTo        0x29A2F7
     2016-02-19 20:57:34   R-Terrassenlicht_chn-01-peerNeedsBurst off
     2016-02-19 15:12:28   R-brightFilter  7
     2016-02-19 15:12:28   R-captInInterval off
     2016-02-19 15:11:40   R-evtFltrNum    1
     2016-02-19 15:11:40   R-evtFltrPeriod 1 s
     2016-02-19 15:12:28   R-minInterval   15
     2016-02-19 15:11:40   R-pairCentral   0x29A2F7
     2017-04-17 20:53:09   battery         ok
     2017-04-17 20:53:09   brightness      79
     2017-04-17 20:53:09   cover           closed
     2017-04-17 18:14:42   motion          off
     2017-04-17 18:14:25   motionCount     45_next:15s
     2017-04-17 18:14:42   motionDuration  17
     2017-04-16 14:28:10   peerList        Terrassenlicht,
     2016-02-26 10:36:49   powerOn         2016-02-26 10:36:49
     2017-04-17 20:53:09   recentStateType info
     2017-04-17 18:14:42   state           noMotion
     2017-01-28 10:18:11   state_toggle    188
     2016-02-19 20:57:30   trigDst_29A2F7  noConfig
     2017-04-17 18:14:25   trigger_cnt     45
   Helper:
     HM_CMDNR   113
     mId        00C1
     rxType     28
     supp_Pair_Rep 0
     Ack:
     Expert:
       def        1
       det        0
       raw        1
       tpl        0
     Io:
       newChn     +2EECD0,00,00,00
       nextSend   1492455189.44373
       prefIO
       rxt        2
       vccu
       p:
         2EECD0
         00
         00
         00
     Mrssi:
       mNo        71
       Io:
         HMLAN1     -68
     Prt:
       bErr       0
       sProc      0
       sleeping   1
     Q:
       qReqConf   00
       qReqStat
     Role:
       chn        1
       dev        1
     Rssi:
       At_hmlan1:
         avg        -69.2864450127877
         cnt        391
         lst        -70
         max        -63
         min        -85
     Tmpl:
Attributes:
   IODev      HMLAN1
   actCycle   000:10
   actStatus  alive
   autoReadReg 4_reqStatus
   devStateIcon alive:message_ok@green
   event-on-change-reading Activity,battery,brightness,motion
   expert     2_raw
   firmware   1.6
   icon       people_sensor
   model      HM-Sen-MDIR-O-2
   peerIDs    00000000,3ECCDB01,
   room       CUL_HM
   serialNr   LEQ0657403
   stateFormat brightness
   subType    motionDetector


Der berechnete Wert liegt leider deutlich unter dem tatsächlichen durchschnittlichen Helligkeitswert.

Frohe Restostern!

Oli


FHEM in Debian VM auf DS720+, HMLAN und HMUARTLGW, RFXTRX, Conbee II, Homebridge, Alexa
Geräte: Homematic, Tradfri, Shelly, IT, ESA2000, VU+, Denon-AVR, Sonos, Fritz!Box, Harmony Hub, IP-Cams, Roborock, Automower

Damian

Kann ich bei mir nicht feststellen. Möglicherweise hat er mehr Sensoren erwischt, als du erwartest.

Du kannst mal folgendes im Status definieren und dann mal selber nachrechnen sum/anzahl. Zusätzlich werden alle gezählten Sensoren angezeigt:

[#average"^BMelder":brightness] [#"^BMelder":brightness] [#sum"^BMelder":brightness] [@"^BMelder":brightness]

Man kann zwar mit sprintf die Nochkommastellen formatieren, aber ich werde noch eine Formatierungsfunktion für Dezimalzahlen einbauen.
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Damian

Version V.05

Ich habe das allgemeine Filtern nach Zahl mit dem Buchstaben "d" um das Runden auf Nachkommastellen erweitert:

Syntax:

[<device>:<reading>:d<Number>]

<Number> ist eine einstellige Ziffer für die Anzahl der Nachkommastellen, Mit 0 wird auf ganze Zahlen gerundet.

Auch die Aggregationsfunktion hat das Runden als Option bekommen, sinnvoll bei #average, #sum, #min, #max

Beispiel:

Durchschnitt auf zwei Nachkommastellen runden:

[#average:d2:"...]

Desweiteren gibt es jetzt die Variable $number als "nach Zahl gefiltertes Reading" für die Aggregationsbedingung. Die Nutzung ist sinnvoll bei Vergleichen von Zahlen, die Einheiten beinhalten.

Beispiel:

[#"":power:$number > 10]

Jetzt fehlt noch die Doku für die Commandref und noch etwas Testen, dann kann man die Sache eintüten und ich kann mich wieder anderen Sachen widmen. :)
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Newbie

Hallo Damian,

ich hab mal diesen Code bei mir probiert

defmod di_Battery DOIF
attr di_Battery state [@":battery":battery:$_ ne "ok"]


jetzt haben alle Device und Kanäle ein battery-Reading.  :)

Ist das so gewollt?

vg Jesn
fhem-6.1 (configDB+DbLog)  auf ODROID-XU4

OliS.

Zitat von: Damian am 17 April 2017, 21:32:20
Kann ich bei mir nicht feststellen. Möglicherweise hat er mehr Sensoren erwischt, als du erwartest.

Du hattest natürlich Recht. Das RegEx hat noch auf einige DOIF gematcht, welche nach dem Schema benannt wurden. Daraufhin habe ich versucht, Deine neuen Filter zu kombinieren, was leider nicht funktioniert hat.

attr brightness_average state [#average:"BMelder":brightness and $TYPE eq "CUL_HM"]

Hier bekomme ich als Ausgabe in state "0".

Durch Umbenennung der mitgematchten DOIF funktioniert es nun. Auch die neuere Version mit dem Kürzen der Nachkommastellen. Allerdings ist mir noch nicht so ganz klar, warum die Kombination aus der Funktion #average und dem Filter $TYPE nicht funktioniert.

Oli

FHEM in Debian VM auf DS720+, HMLAN und HMUARTLGW, RFXTRX, Conbee II, Homebridge, Alexa
Geräte: Homematic, Tradfri, Shelly, IT, ESA2000, VU+, Denon-AVR, Sonos, Fritz!Box, Harmony Hub, IP-Cams, Roborock, Automower

Ellert

Zitat von: Newbie am 18 April 2017, 10:35:09
Hallo Damian,

ich hab mal diesen Code bei mir probiert

defmod di_Battery DOIF
attr di_Battery state [@":battery":battery:$_ ne "ok"]


jetzt haben alle Device und Kanäle ein battery-Reading.  :)

Ist das so gewollt?

vg Jesn

Kann ich bestätigen, alle Geräte haben jetzt ein "battery" Reading, die zugefügten Readings haben Wert 0 .

Falls es keine Geräte gibt, für die ein Reading "battery" mit dem Wert 0 ein gültiger Wert ist, könnte man die Readings über die Befehlszeile löschen mit
{foreach my $item (keys %defs) {delete $defs{$item}{READINGS}{battery} if ($defs{$item}{READINGS}{battery}{VAL} == 0)}}

Damian

Zitat von: Ellert am 18 April 2017, 17:08:00
Kann ich bestätigen, alle Geräte haben jetzt ein "battery" Reading, die zugefügten Readings haben Wert 0 .

Falls es keine Geräte gibt, für die ein Reading "battery" mit dem Wert 0 ein gültiger Wert ist, könnte man die Readings über die Befehlszeile löschen mit
{foreach my $item (keys %defs) {delete $defs{$item}{READINGS}{battery} if ($defs{$item}{READINGS}{battery}{VAL} == 0)}}

tja, komisch.

Ich habe das auch mal beobachtet, dachte aber, dass es von irgendwelchen set-Experimenten in Verbindung mit der Aggregationsfunktion zusammen hing.

Ich kann keinen schreibenden Zugriff auf das vermeintliche Reading in der Aggregationsfunktion erkennen.

Ich kann es auch mit der aktuellen Version v0.6, die ich hier gleich hochladen werde, nicht mehr nachstellen.
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Damian

Mit Version v0.6 ist das Problem des Anlegens von Readings mit Wert 0 behoben (mal zur Info: wer hätte es gedacht: alleine die Abfrage if (defined $defs{$name}{READINGS}{$reading}{VAL}) führt zum Anlegen des Readings!!!)

Man kann jetzt auch einen Defaultwert vorgeben, wenn kein Treffer da ist (siehe erster Post).

Sinnvoll bei Angaben der Art:

set [@"room_lamp",state,"on","defaultdummy"] off

Ansonsten würde set ohne Device-Angabe einen Fehler produzieren.

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

OliS.

Guten Morgen,

Damian, kannst Du bestätigen, dass eine Kombi der Filter nach folgendem Muster

[#average:"BMelder":brightness and $TYPE="CUL_HM"]

nicht funktioniert?

Oli
FHEM in Debian VM auf DS720+, HMLAN und HMUARTLGW, RFXTRX, Conbee II, Homebridge, Alexa
Geräte: Homematic, Tradfri, Shelly, IT, ESA2000, VU+, Denon-AVR, Sonos, Fritz!Box, Harmony Hub, IP-Cams, Roborock, Automower

Ellert

Zitat von: OliS. am 19 April 2017, 06:40:31
Guten Morgen,

Damian, kannst Du bestätigen, dass eine Kombi der Filter nach folgendem Muster

[#average:"BMelder":brightness and $TYPE="CUL_HM"]

nicht funktioniert?

Oli
Dein Vergleichsoperator ist entweder unvollständig oder falsch, was Du geschrieben hast ist eine Zuweisung, siehe https://wiki.selfhtml.org/wiki/Perl/Operatoren oder https://fhem.de/commandref_DE.html#DOIF_Ereignissteuerung.

Damian

Zitat von: OliS. am 19 April 2017, 06:40:31
Guten Morgen,

Damian, kannst Du bestätigen, dass eine Kombi der Filter nach folgendem Muster

[#average:"BMelder":brightness and $TYPE="CUL_HM"]

nicht funktioniert?

Oli

Die Syntax passt nicht. Die Aggregationsbedingung ist reines Perl, daher ist dort brightness nicht bekannt. Du musst erst mal sagen, was das Reading ist und dann könntest du über $_ oder gefiltert nach Zahl über $number in der Aggregationsbedingung darauf zugreifen. Wenn du brightness nicht abfragen willst - ist auch ok, trotzdem muss die Aggregationsfunktion wissen um welches Reading es sich handelt um nur die Devices, die auch das Reading beinhalten über die Bedingung abzufragen, daher:

[#average:"BMelder":brightness:$TYPE eq "CUL_HM"]


und mit Version v0.6 auch möglich:

[#average:"BMelder":brightness:$TYPE eq "CUL_HM","keine Melder vorhanden"]
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF