MQTT Datenflut filtern.

Begonnen von tomleitner, 26 Oktober 2020, 13:36:46

Vorheriges Thema - Nächstes Thema

tomleitner

Hallo,

Ich habe meinen Aiibot Air Purifier A500 erfolgreich über MQTT angebunden (Tuya-Api und Tuya-MQTT).

Problem nun: das Gerät sendet ca. alle 2-3 Sekunden einen neuen PM2.5 Messwert. Das ist viel zu viel für eine vernünftigte Grafik und es müllt mir auch das DbLog voll.  Ideal wäre eine Minute lang einen Mittelwert zu bilden und diesen dann erst ins DbLog aufzunehmen.

Frage daher: Wie kann ich das in FHEM realisieren. Meine initiale Idee wäre ein externe Script das per mosquitto_sub die Daten empfängt, Mittelwert rechnet und diesen per Mosquitto_Pub wieder ausgibt. Leider ist das nicht die schönste Lösung.  Gibt es etwas mit FHEM Bordmitteln?   Ich habe bereits eine "runningMean" Routine, diese aber setzt voraus das die Ursprungsdaten in der Dblog sind ... aber genau das will ich NICHT.

Danke // Tom

tomleitner

#1
Ok -- habe nun dieses Script gebastelt -- sollte sich leicht für ähnliche Fälle anpassen lassen. Wenn man den Median anstatt des Mittelwertes braucht kann man calc_mean auch leicht anpassen ...

#!/bin/bash

function calc_mean() {
    arr=("$@")
    m=0
    for v in "${arr[@]}"; do
        m=`expr $m + $v`
    done
    n=${#arr[@]}
    mean=`echo $m $n | awk '{ print $1 / $2 }'`
}

while true ; do
    start=`date +%s`
    arr=()
    mosquitto_sub -t 'tuya/aiibot_air_purifier/dps/2/state' -T tuya | ( while read v ; do
        arr+=($v)
        curr=`date +%s`
        diff=`expr $curr - $start`
        echo $diff ${arr[@]}
        if [ $diff -ge 60 ]; then
            calc_mean ${arr[@]}
            echo mean=$mean
            mosquitto_pub -t tuya/aiibot_air_purifier/pm25mean -m $mean
            arr=()
            start=`date +%s`
        fi
    done )
    sleep 1
done


Gestartet und am Leben gehalten wird das Ganze per Crontab Eintrag:

* * * * * /home/fhem/pm25mean/startpm25mean.sh >/dev/null

und startpm25mean.sh ist:

#!/bin/sh
cd /home/fhem/pm25mean
pidof -x pm25mean.sh >/dev/null
if [ $? -ne 0 ]; then
    nohup ./pm25mean.sh </dev/null >/dev/null 2>&1 &
    echo pm25mean.sh started
fi

Vielleicht hilfts ja jemandem ...



Wzut

wenn ich so ein Probleme habe , setze ich gleitende Mittelwerte via userreading ein -> https://wiki.fhem.de/wiki/Gleitende_Mittelwerte_berechnen_und_loggen
Der Ursprungswert wird im Log excludet und das usereading dafür aufgenommen und dient als Basis für die Plots
Maintainer der Module: MAX, MPD, UbiquitiMP, UbiquitiOut, SIP, BEOK, readingsWatcher

tomleitner

Zitat von: Wzut am 30 Oktober 2020, 10:11:42
wenn ich so ein Probleme habe , setze ich gleitende Mittelwerte via userreading ein -> https://wiki.fhem.de/wiki/Gleitende_Mittelwerte_berechnen_und_loggen
Der Ursprungswert wird im Log excludet und das usereading dafür aufgenommen und dient als Basis für die Plots

Ich kenne diese Funktion. Aber mein Eindruck ist dass die Routine die Original Daten aus der dbLog liest oder? Oder wie sonst sammelt die Routine die Daten zur Berechnung zusammen? Bildet sie dazu ein globales Array das die Aufrufe überlebt? Weil wenn das so geht werde ich das bei mir auch einsetzen.

Danke.

Wzut

#4
Zitat von: tomleitner am 30 Oktober 2020, 11:44:25
Aber mein Eindruck ist dass die Routine die Original Daten aus der dbLog liest oder?
dein Eindruck täuscht dich , lies doch mal was ich dir verlinkt habe, da steht z.B.
attr devicename userReading readingname.av {movingAverage("devicename","readingname",zeitspanne)}
also nix mit Log oder Db lesen sondern direkt am Event verarbeiten und berechnen, darum schrieb ich ja auch die Ur Daten erst gar nicht loggen und nur noch das userReading.
Wobei die Wiki Zeile in sofern etwas unschön ist da kein Trigger am neuen Reading Namen hängt, aber den muss der user eh nach seinen Gegebenheiten festlegen.

Edit : Ich habe das Wiki gerade nochmal überflogen, da stehen zwei Beispiele für eine sub in der 99_myUtils, das untere kleinere nimmt die Daten aus einem Log File (insofern hast du Recht) , aber ich verwende nur das erste Beispiel.
Maintainer der Module: MAX, MPD, UbiquitiMP, UbiquitiOut, SIP, BEOK, readingsWatcher

tomleitner


Wzut

achso habe ich noch vergessen, wenn du das Beispiel aus dem Wiki nimmst achte auf die Zeile
if( ($num < 25)&&( ($ctime-$stime)<$avtime) ){
je nachdem wie oft deine Daten kommen und welches Intervall für den Mitelwert du möchstest kann die 25 zu klein sein.
Bsp deine Minute und Daten alle 2,5 Sekunden : 60/2,5 = 24
geh in diesem Fall einfach auf die sichere Seite und nimm statt der 25 einen größeren Wert.
Maintainer der Module: MAX, MPD, UbiquitiMP, UbiquitiOut, SIP, BEOK, readingsWatcher

tomleitner

Ok. Danke für den Hinweis ...

tomleitner

Hallo,

Habe mir nun die movingAverage routine im Zusammenhang mit userReadings noch einmal angeschaut als Alternative zu meinem Script. Die movingAverage Routine macht genau das NICHT was ich brauche. Wenn die Routine z.B. alle 2 Sekunden einen Messwert bekommt, kriege ich auch alle 2 Sekunden einen moving Average ... das ist nett wenn man das will ... ich will es aber nicht. Ich will nur alle 60 Sekunden einen Mittelwert, egal wie oft Input Daten kommen -- und ich glaube mit "userReadings" geht das auch nicht weil das auf jeden Fall einen Wert erzeugt .... also schon zwei Gründe warum diese Lösung nicht klappt für mich ... ich muss daher bis auf Weiteres bei meinem externen Script bleiben ...

Danke // Tom

rudolfkoenig

Zitat...  mit "userReadings" geht das auch nicht weil das auf jeden Fall einen Wert erzeugt
Wenn das perl-Skript undef zurueckliefert, dann wird kein Wert gesetzt / Event erzeugt.

tomleitner

Zitat von: rudolfkoenig am 03 November 2020, 15:19:28
Wenn das perl-Skript undef zurueckliefert, dann wird kein Wert gesetzt / Event erzeugt.

Ah, ok ... so könnte ich die Routine entsprechend umbauen ...

Danke.