Stoßlüften - Meldung zum Schutz vor Auskühlen

Begonnen von d.schoen, 03 November 2016, 13:36:01

Vorheriges Thema - Nächstes Thema

d.schoen

Hallo zusammen,

ich habe mir für unsere Wohnung ein kleines DOIF Konstrukt erstellt, das mir eine Telegram Nachricht sendet, wenn es sinnvoll ist das Fenster nach dem Stoßlüften wieder zu schließen.
Ich möchte damit den optimalen Zeitpunkt ausgeben, so dass die Fenster nur so lange offen bleiben, wie auch wirklich ein Luftaustausch stattfindet.

Dazu untersuche ich die Änderung der Luftfeuchtigkeit eines Homematic Thermostats (bei mir HM-TC-IT-WM-W-EU).

Schritt 1: Überwachen der Änderung der Luftfeuchtigkeit

Um die Änderung der Luftfeuchtigkeit zu überwachen lege ich mir zuerst ein UserReading an, das die Ableitung der Luftfeuchtigkeit speichert:
attr Thermostat_Chan_Climate userReadings humdiff:humidity.* differential { ReadingsVal("Thermostat_Chan_Climate", "humidity", 0);; }

Somit bekommt mein Climate-Channel des Thermostats ein Reading humdiff, das folgende Werte annehmen kann:

  • <0 wenn die Luftfeuchtigkeit abnimmt
  • =0 wenn die Luftfeuchtigkeit konstant bleibt
  • >0 wenn die Luftfeuchtigkeit zunimmt

Basierend auf diesem Wert kann ich nun feststellen, wenn ein Lüftvorgang beginnt (starker Abfall der Luftfeuchtigkeit)
humdiff < -0.01
und wann ein Lüftvorgang wieder beendet werden sollte (Luftfeuchtigkeit nimmt nicht weiter ab)
humdiff > -0.002


Schritt 2: Anlegen eines Dummy-Devices zum Speichern des aktuellen Status
define Stosslueften dummy
Hiermit werden wir dann speichern, in welcher "Phase" des Lüftvorgangs wir gerade sind.

Schritt 3: Anlegen des DOIF
define DOIF_Stosslueften DOIF ([Thermostat_Chan_Climate:humdiff] < -0.01 and [?Stosslueften ] ne "opened") (set Stosslueften opened)
DOELSEIF ([Thermostat_Chan_Climate:humdiff] > -0.002 and [?Stosslueften] eq "opened") (set Stosslueften reducing)
DOELSEIF ([Thermostat_Chan_Climate:humdiff] > -0.002 and [?Stosslueften] eq "reducing") (set Stosslueften reduced)
DOELSEIF ([Thermostat_Chan_Climate:humdiff] > -0.002 and [?Stosslueften] eq "reduced") (set Stosslueften sent; set tgbot message @... Fenster schließen)


Was mache ich nun damit konkret?

  • Luftfeuchtigkeit fällt stark ab (humdiff < -0.01): Stoßlüften hat begonnen. Fenster ist "opened".
  • Luftfeuchtigkeit fällt deutlich schwächer ab (humdiff > -0.002): Optimaler Luftaustausch geht zuende; Es wird "nur noch" kälter.
  • Status "reducing" und "reduced": Da es häufig vorkommt, dass die Luftfeuchtigkeit während des Stoßlüftens zwischen zwei Messwerten gleich bleibt (und humdiff damit 0 wird) sichere ich durch die zwei Zwischenstati "reducing" und "reduced" ab, dass erst beim dritten aufeinanderfolgenden Messwert mit humdiff > -0.002 der Lüftvorgang als beendet gilt. Erst dann wird die Nachricht versendet.

Viel Spaß mit dem Snippet.
FHEM 5.7 auf RasPi2
COC 868MHz, Jeelink Clone
Devices: IT, HomeMatic, LaCrosse, ENIGMA2, LG-TV, Thinkingcleaner (iRobot Roomba), LIFX Wifi-Bulbs
Helper: TelegramBot, Homebridge (Siri), Geofency

sash.sc

Gehe ich recht in der Annahme, dass die Erzeugen des userReading sich immer nur auf ein Gerät bezieht ???

Gruß
Sascha
Raspi 4B+ Bullseye ;LaCrosse; HomeMatic; MapleCUL; ZigBee; Signalduino ESP32 ; Shellys; MQTT2; Grafana mit Influxdb

sash.sc

Zitat von: d.schoen am 03 November 2016, 13:36:01


Um die Änderung der Luftfeuchtigkeit zu überwachen lege ich mir zuerst ein UserReading an, das die Ableitung der Luftfeuchtigkeit speichert:
[code]attr Thermostat_Chan_Climate userReadings humdiff:humidity.* differential { ReadingsVal("Thermostat_Chan_Climate", "humidity", 0);; }



Bezieht sich das "differential" nur auf HM Geräte oder lässt sich das auch auf andere Sensoren anwenden ??

Gruß
Sascha
Raspi 4B+ Bullseye ;LaCrosse; HomeMatic; MapleCUL; ZigBee; Signalduino ESP32 ; Shellys; MQTT2; Grafana mit Influxdb

jeti

Die Ableitung kannst du als userReading von jedem Wert erstellen lassen!
Ist also universell und kann für jeden device und reading verwendet werden.

locodriver

@d.schoen: danke für deine Idee. Ich habe sie noch mit der Außentemperatur und dem Fensterkontakt verknüpft, da bekomme ich die Meldung nur, wenn das Fenster noch offen ist bzw. gibt es keine Meldung, wenn aus anderen Gründen (offene Badtür) die Feuchtigkeit sinkt.

Wie hast du die Schwellwerte ermittelt - probiert oder hast du einen "fachlichen Hintergrund"? Welche Zeiträume ergeben sich bei dir in der Regel, bis die Meldung abgesetzt wird?
Ich kann dazu noch nichts sagen da ich es erst gestern umgesetzt habe  :) . Bisher hatte ich ein zweistufiges Verfahren, welches mich nach 10 bzw. 15 Minuten (unter 10 bzw. 15 °C Außentemperatur) warnt und dann in der Endkonsequenz zur "Not" noch den Rollladen schließen kann. Das hat bis jetzt auch gut funktioniert...

([BD_Regler_Climate:humdiff]<-0.01 and [?Lueften] ne "opened" and (ReadingsVal("Wetter_Borsdorf","temperature",0)<15))(set Lueften opened)
DOELSEIF
([BD_Regler_Climate:humdiff] > -0.002 and [?Lueften] eq "opened" and (ReadingsVal("Wetter_Borsdorf","temperature",0)<15)) (set Lueften reducing)
DOELSEIF
([BD_Regler_Climate:humdiff] > -0.002 and [?Lueften] eq "reducing" and (ReadingsVal("Wetter_Borsdorf","temperature",0)<15)) (set Lueften reduced)
DOELSEIF
([BD_Regler_Climate:humdiff] > -0.002 and [?Lueften] eq "reduced" and (ReadingsVal("Wetter_Borsdorf","temperature",0)<15)) ((set Lueften sent)
IF ([BD_Fenster] eq "offen" set Nachrichten message Badfenster schließen))


Noch eine Frage etwas OT: Wie bildet der interne Alghoritmus die Ableitung? Eigentlich benötige ich doch eine Funktion, die ich dann nach den bekannten Regeln ableite, das ist hier ja nicht gegeben. So sind das doch eigentlich Differenzenquotienten, die hier zwischen den einzelnen Werten gebildet werden?! Damit hängt die Genauigkeit vom zeitlichen Abstand der einzelnen Feuchtigkeitwerte ab, oder? Nicht falsch verstehen: ich will nicht bis auf die letzte signifikante Stelle einen exakten Wert (man kann es ja übertreiben), es geht mir um das Verständnis.

Danke nochmals,

Uwe
fhem 6.0 auf Rpi3 Bookworm
HM-LAN-CFG (FW 0.965), HM-MOD-UART, 2x HM-TC-IT-WM-W-EU, 4x HM-Sec-RHS und 3x HM-CC-RT-DN, 6x HM-LC-Bl1-FM mit je 1x Somfy-Motor,
2x HM-LC-SW2-FM für Licht und Lüfter, 2x HM-PB-6-WM55, Alexa, Jeelinkcross, CUL, CUNO2, IR-Blaster

sash.sc

Das würde ich auch gerne wissen, Info´s zum differential

Gruß
Sascha
Raspi 4B+ Bullseye ;LaCrosse; HomeMatic; MapleCUL; ZigBee; Signalduino ESP32 ; Shellys; MQTT2; Grafana mit Influxdb

d.schoen

Hallo zusammen,

danke für die Ergänzungen! Mangels Fensterkontakte habe ich das bei mir nicht integriert.

Die Schwellwerte habe ich probiert - und probiere immer noch. Mittlerweile hat sich mein benutzter Code noch etwas verändert: Ich nehme einen gleitenden Durchschnitt über die Ableitungswerte. Da gibt es weniger "Ausreißer".

Bzgl. der "Ableitung": Das sind definitiv Differenzquotienten und keine Ableitung im Sinne einer Kurvendiskussion. Wie das aber intern genau berechnet wird, weiß ich leider auch nicht - aber wie du schon sagst: Das wäre Mikrooptimierung. Mir ging es bei dem Snippet eher darum, mich ohne Fensterkontakt daran erinnern zu lassen, dass das Fenster wieder geschlossen werden sollte.

Grüße
Dominik
FHEM 5.7 auf RasPi2
COC 868MHz, Jeelink Clone
Devices: IT, HomeMatic, LaCrosse, ENIGMA2, LG-TV, Thinkingcleaner (iRobot Roomba), LIFX Wifi-Bulbs
Helper: TelegramBot, Homebridge (Siri), Geofency

locodriver

@Dominik:
Danke für die Ergänzungen, kannst du bitte die Änderung mit dem gleitenden Mittelwert noch posten?

Danke

Uwe.
fhem 6.0 auf Rpi3 Bookworm
HM-LAN-CFG (FW 0.965), HM-MOD-UART, 2x HM-TC-IT-WM-W-EU, 4x HM-Sec-RHS und 3x HM-CC-RT-DN, 6x HM-LC-Bl1-FM mit je 1x Somfy-Motor,
2x HM-LC-SW2-FM für Licht und Lüfter, 2x HM-PB-6-WM55, Alexa, Jeelinkcross, CUL, CUNO2, IR-Blaster

d.schoen

Klar, gerne.

Ich nutze dazu ein weiteres Userreading:
humdiff_avg:humidity.* {movingAverage("Device","humdiff",600)}

Der letzte Wert ist die Dauer (in Sekunden) über die der Mittelwert gebildet wird. Damit kann/muss man ein bisschen spielen um einen für sich passenden Mittelwert zu bekommen.

Zudem brauchts die Methode movingAverage in der myUtils (irgendwo hier aus dem Forum geklaut):

###############################################################################
#
#  Moving average
#
#  Aufruf: movingAverage(devicename,readingname,zeitspanne in s)
#
###############################################################################

sub movingAverage($$$){
   my ($name,$reading,$avtime) = @_;
   my $hash = $defs{$name};
   my @new = my ($val,$time) = ($hash->{READINGS}{$reading}{VAL},$hash->{READINGS}{$reading}{TIME});
   my ($cyear, $cmonth, $cday, $chour, $cmin, $csec) = $time =~ /(\d+)-(\d+)-(\d+)\s(\d+):(\d+):(\d+)/;
   my $ctime = $csec+60*$cmin+3600*$chour;
   my $num;
   my $arr;
   #-- initialize if requested
   if( ($avtime eq "-1") ){
     $hash->{READINGS}{$reading}{"history"}=undef;
   }
   #-- test for existence
   if( !$hash->{READINGS}{$reading}{"history"}){
      #Log 1,"ARRAY CREATED";
      push(@{$hash->{READINGS}{$reading}{"history"}},\@new);
      $num = 1;
      $arr=\@{$hash->{READINGS}{$reading}{"history"}};
   } else {
      $num = int(@{$hash->{READINGS}{$reading}{"history"}});
      $arr=\@{$hash->{READINGS}{$reading}{"history"}};
      my $starttime = $arr->[0][1];
      my ($syear, $smonth, $sday, $shour, $smin, $ssec) = $starttime =~ /(\d+)-(\d+)-(\d+)\s(\d+):(\d+):(\d+)/;
      my $stime = $ssec+60*$smin+3600*$shour;
      #-- correct for daybreak
      $stime-=86400
        if( $stime > $ctime);
      if( ($num < 25)&&( ($ctime-$stime)<$avtime) ){
        #Log 1,"ARRAY has $num elements, adding another one";
        push(@{$hash->{READINGS}{$reading}{"history"}},\@new);
      }else{
        shift(@{$hash->{READINGS}{$reading}{"history"}});
        push(@{$hash->{READINGS}{$reading}{"history"}},\@new);
      }
    }
    #-- output and average
    my $average = 0;
    for(my $i=0;$i<$num;$i++){
      $average+=$arr->[$i][0];
      Log 4,"[$name moving average] Value = ".$arr->[$i][0]." Time = ".$arr->[$i][1];
    }
    $average=sprintf( "%5.3f", $average/$num);
    #--average
    Log 4,"[$name moving average] calculated over $num values is $average"; 
    return $average;
}
FHEM 5.7 auf RasPi2
COC 868MHz, Jeelink Clone
Devices: IT, HomeMatic, LaCrosse, ENIGMA2, LG-TV, Thinkingcleaner (iRobot Roomba), LIFX Wifi-Bulbs
Helper: TelegramBot, Homebridge (Siri), Geofency

locodriver

fhem 6.0 auf Rpi3 Bookworm
HM-LAN-CFG (FW 0.965), HM-MOD-UART, 2x HM-TC-IT-WM-W-EU, 4x HM-Sec-RHS und 3x HM-CC-RT-DN, 6x HM-LC-Bl1-FM mit je 1x Somfy-Motor,
2x HM-LC-SW2-FM für Licht und Lüfter, 2x HM-PB-6-WM55, Alexa, Jeelinkcross, CUL, CUNO2, IR-Blaster

TheRisen

Falls es noch interessiert, ich habe bei mir das ganze über den Taupunkt geregelt. Dadurch stelle ich sicher, dass die Taupunkttemperatur im Raum immer kleiner ist als die Temperatur an der Innenseite des Fensters. So können die Fenster nicht von innen beschlagen.
Sowohl die Wettervorhersage als auch der Sensor im Bad bekommen den Taupunkt als Reading:

define dew_inside dewpoint dewpoint HM_Feuchte_Bad temperature humidity dewpoint
attr dew_inside max_timediff 600
define dew_outside dewpoint dewpoint Wetter temperature humidity dewp
attr dew_outside max_timediff 600


Ein Dummy berechnet die Temperatur innen an der Glasscheibe, 0,13 ist der Wärmeübergangskoeffizient und 1,2 ein typischer Wert für etwas ältere Fenster mit Doppelverglasung.

define dy_Fenster_Bad_Innen dummy
define nt_Temp_Bad notify (Wetter:temperature|HM_Feuchte_Bad:dewpoint) {fhem("set dy_Fenster_Bad_Innen ".(ReadingsVal("HM_Feuchte_Bad","temperature","0")-ReadingsVal("Wetter","temperature","0")*(-1,2*0,13)+ReadingsVal("HM_Feuchte_Bad","temperature","0")))}


Ein DOIF vergleicht den Tauounkt des Raumes mit der Temperatur am Fenster und gibt darüber Nachrichten ans Handy

define lueften_bad DOIF ([HM_Feuchte_Bad:dewpoint]<[dy_Fenster_Bad_Innen] and [HM_Fenster_Bad] eq "open" and [HM_Fenster_Bad:state:sec] > 480)(set pushoverjay msg "Bad" "Fenster im Bad kann geschlossen werden.\nTemperatur [HM_Feuchte_Bad:temperature] °C")\
DOELSEIF([HM_Feuchte_Bad:dewpoint]<[dy_Fenster_Bad_Innen] and [HM_Fenster_Bad] eq "closed")\
DOELSEIF([HM_Feuchte_Bad:dewpoint]>[dy_Fenster_Bad_Innen] and [HM_Fenster_Bad] eq "open")\
DOELSEIF([HM_Feuchte_Bad:dewpoint]>[dy_Fenster_Bad_Innen] and [HM_Fenster_Bad] eq "closed")(set pushoverjay msg "Bad" "Bitte Fenster öffnen.\nTaupunkt [HM_Feuchte_Bad:dewpoint] °C muss gesenkt werden")
attr lueften_bad alias Fenster Bad
attr lueften_bad devStateIcon cmd_1:fts_window_roof_open_2@red cmd_2:fts_window_roof@green cmd_3:fts_window_roof_open_2@green cmd_4:fts_window_roof@red
attr lueften_bad room Bad