Air Quality Index Berechnung als Funktion

Begonnen von maddhin, 14 Oktober 2017, 16:29:24

Vorheriges Thema - Nächstes Thema

maddhin

Hi,

ich möchte aus gemessenen Feinstaubwerten den bzw: einen Air Quality Index / AQI berechnen. Da gibt es bei Wiki (https://en.wikipedia.org/wiki/Air_quality_index) eine Formel mit der ich auch soweit zurechtkomme (density ist hier der Feinstaubwert):


###############################################################################
#
#  PM in ug/m3 to AQI (US)
#
#  Aufruf: PMtoAQIus(devicename,readingname)
#
###############################################################################

sub PMtoAQIus($$$){
   my ($name,$reading) = @_;


// AQI formula: https://en.wikipedia.org/wiki/Air_Quality_Index#United_States
int toAQI(int I_high, int I_low, int C_high, int C_low, int C) {
return (I_high - I_low) * (C - C_low) / (C_high - C_low) + I_low;
}

//thanks to https://gist.github.com/nfjinjing/8d63012c18feea3ed04e
int calculateAQI25(float density) {
int d10 = (int)(density * 10);
if (d10 <= 0) {
return 0;
} else if(d10 <= 120) {
return toAQI(50, 0, 120, 0, d10);
} else if (d10 <= 354) {
return toAQI(100, 51, 354, 121, d10);
} else if (d10 <= 554) {
return toAQI(150, 101, 554, 355, d10);
} else if (d10 <= 1504) {
return toAQI(200, 151, 1504, 555, d10);
} else if (d10 <= 2504) {
return toAQI(300, 201, 2504, 1505, d10);
} else if (d10 <= 3504) {
return toAQI(400, 301, 3504, 2505, d10);
} else if (d10 <= 5004) {
return toAQI(500, 401, 5004, 3505, d10);
} else if (d10 <= 10000) {
return toAQI(1000, 501, 10000, 5005, d10);
} else {
return 1001;
}
}


Aber voran ich verzweifle ist das in FHEM zu bekommen. Eigentlich ja eine recht simple Funktion, aber ich kann absolut kein Perl. Ich habe versucht das anhand von z.B. der "movingAverage" Funktion, die ich in FHEM nutze, analog irgendwie hinzubekommen, aber ich müsste mich jetzt erstmal ein paar Tage hinsetzen und Perl lernen um halbwegs zu verstehen, was da eigentlich was ist.

Könnte mir jemand helfen und hier die Funktion und Variablen definieren damit ich das in die 99_MyUtils machen kann? Dann sollte ich an der Formel, etc selbst weiterbasteln können:)

Das wäre genial, ganz lieben Dank im Voraus!

igami

Na das sieht doch mal interessant aus. Gucke ich mir mal an :)
Pi3 mit fhem.cfg + DbLog/logProxy
Komm vorbei zum FHEM Treffen im Kreis Gütersloh! Das nächste Mal im April 2020.

MAINTAINER: archetype, LuftdatenInfo, monitoring, msgDialog, Nmap, powerMap
ToDo: AVScene, FluxLED

maddhin

Zitat von: igami am 14 Oktober 2017, 18:08:53
Na das sieht doch mal interessant aus. Gucke ich mir mal an :)

Super! Ich wollte eine PM -> AQI und AQI -> PM Funktion machen, dann kann man Werte vergleichen. Ggf. kann man dann auch mit unterschiedlichen Indizes arbeiten.

http://www.aqicn.org einige gute Tools & Info (und auch API um Werte zu holen). Da kann man auch online umrechnen und die Formel testen:)


igami


sub myUtils_PM10_to_AQI(@_) {
  my ($C) = @_;
  my ($I_high, $I_low, $C_high, $C_low);

  return unless(looks_like_number($C));

  if($C < 55){
    $I_high =   0 ;
    $I_low  =  50 ;
    $C_high =   0 ;
    $C_low  =  54 ;
  }
  elsif($C < 155){
    $I_high =  51 ;
    $I_low  = 100 ;
    $C_high =  55 ;
    $C_low  = 154 ;   
  }
  elsif($C < 255){
    $I_high = 101 ;
    $I_low  = 150 ;
    $C_high = 155 ;
    $C_low  = 254 ;   
  }
  elsif($C < 355){
    $I_high = 151 ;
    $I_low  = 200 ;
    $C_high = 255 ;
    $C_low  = 354 ;   
  }
  elsif($C < 425){
    $I_high = 201 ;
    $I_low  = 300 ;
    $C_high = 355 ;
    $C_low  = 424  ;   
  }
  elsif($C < 505){
    $I_high = 301 ;
    $I_low  = 400 ;
    $C_high = 425 ;
    $C_low  = 504 ;   
  }
  elsif($C < 605){
    $I_high = 401 ;
    $I_low  = 500 ;
    $C_high = 505 ;
    $C_low  = 604 ;   
  }
  else{
    return;   
  }

  my $I = ($I_high - $I_low) / ($C_high - $C_low) * ($C - $C_low) + $I_low;

  return($I);
}


Im Feinstaubsensor dann noch folgende Attribute setzen:

attr <name> event-aggregator PM10_24h::linear:mean:86400
attr <name> userReadings AQI {myUtils_PM10_to_AQI(ReadingsNum($name, "PM10_24h", 0))},\
PM10_24h:PM10:.* {ReadingsNum($name, "PM10", 0)}
Pi3 mit fhem.cfg + DbLog/logProxy
Komm vorbei zum FHEM Treffen im Kreis Gütersloh! Das nächste Mal im April 2020.

MAINTAINER: archetype, LuftdatenInfo, monitoring, msgDialog, Nmap, powerMap
ToDo: AVScene, FluxLED

maddhin


maddhin

in dem ersten Skript waren die highs und lows vertauscht.

hier nun die fertigen Funktionen:



##########################################################
# PM10 to AQI(US)
# berechnet den Air Quality Index nach US Standard
# siehe https://en.wikipedia.org/wiki/Air_quality_index
##########################################################

sub myUtils_PM10_to_usAQI(@_) {
  my ($C) = @_;
  my ($I_high, $I_low, $C_high, $C_low);

  return unless(looks_like_number($C));

  if($C < 55){
    $I_high =  50 ;
    $I_low  =   0 ;
    $C_high =  54 ;
    $C_low  =   0 ;
  }
  elsif($C < 155){
    $I_high = 100 ;
    $I_low  =  51 ;
    $C_high = 154 ;
    $C_low  =  55 ;   
  }
  elsif($C < 255){
    $I_high = 150 ;
    $I_low  = 101 ;
    $C_high = 254 ;
    $C_low  = 155 ;   
  }
  elsif($C < 355){
    $I_high = 200 ;
    $I_low  = 151 ;
    $C_high = 354 ;
    $C_low  = 255 ;   
  }
  elsif($C < 425){
    $I_high = 300 ;
    $I_low  = 201 ;
    $C_high = 424 ;
    $C_low  = 355  ;   
  }
  elsif($C < 505){
    $I_high = 400 ;
    $I_low  = 301 ;
    $C_high = 504 ;
    $C_low  = 425 ;   
  }
  elsif($C < 605){
    $I_high = 500 ;
    $I_low  = 401 ;
    $C_high = 604 ;
    $C_low  = 505 ;   
  }
  else{
    return;   
  }

  my $I = ($I_high - $I_low) / ($C_high - $C_low) * ($C - $C_low) + $I_low;
  my $I = int($I);
  return($I);
}

##########################################################

##########################################################
# PM2.5 to AQI(US)
# berechnet den Air Quality Index nach US Standard
# siehe https://en.wikipedia.org/wiki/Air_quality_index
##########################################################

sub myUtils_PM25_to_usAQI(@_) {
  my ($C) = @_;
  my ($I_high, $I_low, $C_high, $C_low);

  return unless(looks_like_number($C));

  if($C < 12.1){
    $I_high =  50 ;
    $I_low  =   0 ;
    $C_high =  12 ;
    $C_low  =   0 ;
  }
  elsif($C < 35.5){
    $I_high = 100 ;
    $I_low  =  51 ;
    $C_high = 35.4 ;
    $C_low  = 12.1 ;   
  }
  elsif($C < 55.5){
    $I_high = 150 ;
    $I_low  = 101 ;
    $C_high = 55.4 ;
    $C_low  = 35.5 ;   
  }
  elsif($C < 150.5){
    $I_high = 200 ;
    $I_low  = 151 ;
    $C_high = 150.4 ;
    $C_low  = 55.5 ;   
  }
  elsif($C < 250.5){
    $I_high = 300 ;
    $I_low  = 201 ;
    $C_high = 250.4 ;
    $C_low  = 150.5 ;   
  }
  elsif($C < 350.5){
    $I_high = 400 ;
    $I_low  = 301 ;
    $C_high = 350.4 ;
    $C_low  = 250.5 ;   
  }
  elsif($C < 500.5){
    $I_high = 500 ;
    $I_low  = 401 ;
    $C_high = 500.4 ;
    $C_low  = 350.5 ;   
  }
  else{
    return;   
  }

  my $I = ($I_high - $I_low) / ($C_high - $C_low) * ($C - $C_low) + $I_low;
  my $I = int($I);
  return($I);
}

##########################################################


Ich habe das kurz getestet und es scheint alles zu funktionieren. Muss aber jetzt eine Weile damit spielen.

Den 24h Durchschnitt habe ich erstmal nicht genommen - das ist eine philosophische Frage, ob das Sinn macht. Denn wenn Wind aufkommt kann die Luft ja schon in ein paar Minuten sauber sein, aber der Index würde Stunden(?) brauchen, um das abzubilden...

Mir ging es hier primär darum, Werte vergleichen zu können.

Ich beschäftige mich weiter damit und poste ggf. Weiteres. Ansonsten PM an mich. Wenn das generell von Interesse ist kann ich das alles auch mal irgendwie posten.

@igami: super, herzlichen Dank für Deine Hilfe!


Ich versuche im Moment auch, externe AQI Werte via JSON zu bekommen (https://forum.fhem.de/index.php/topic,38463.msg699578.html#msg699578), ab er das ist nicht so einfach.

Ggf. kann man das Ganze auch mit LuftdatenInfo verbinden, da muss ich aber auch erst gucken, was/wie das Sinn macht. Aber sicherlich haben die doch auch eine Art Index mit schönen Farbdefinitionen :)


maddhin

Etwas off topic aber:

AQIcn.org (z.B. http://aqicn.org/city/germany/berlin/) hat wirklich sehr schöne Widgets (siehe http://aqicn.org/faq/2015-07-28/air-quality-widget-new-improved-feed/, die kann man toll in TabletUI einbauen, etc.

Die sind Teil des World Air Quality Index Projects. Wäre eigentlich genial, wenn man so auch die LuftdatenInfo Daten bekommen könnte.

maddhin

zu den Funktionen bekomme ich noch ein paar Fehlermeldungen:

2017.10.15 15:44:48 1: PERL WARNING: Prototype after '@' for main::myUtils_PM10_to_usAQI : @_ at /usr/share/fhem/FHEM/99_MyUtils.pm line 110.
2017.10.15 15:44:48 1: PERL WARNING: "my" variable $I masks earlier declaration in same scope at /usr/share/fhem/FHEM/99_MyUtils.pm line 163.
2017.10.15 15:44:48 1: PERL WARNING: Prototype after '@' for main::myUtils_PM25_to_usAQI : @_ at /usr/share/fhem/FHEM/99_MyUtils.pm line 175.
2017.10.15 15:44:48 1: PERL WARNING: "my" variable $I masks earlier declaration in same scope at /usr/share/fhem/FHEM/99_MyUtils.pm line 228.


Scheint nicht kritisch zu sein, aber macht wohl Sinn das noch aufzuräumen;)

igami

mach mal "sub myUtils_PM10_to_AQI($)" draus und lass das my vor dem zweiten $I weg.
Pi3 mit fhem.cfg + DbLog/logProxy
Komm vorbei zum FHEM Treffen im Kreis Gütersloh! Das nächste Mal im April 2020.

MAINTAINER: archetype, LuftdatenInfo, monitoring, msgDialog, Nmap, powerMap
ToDo: AVScene, FluxLED

maddhin

Danke! Da habe ich heute ja doch etwas Perl gelernt! ;)

Habe noch die entsprechenden Kategorien als Funktion hinzugefügt:

##########################################################
# AQI(US) Category
#
# siehe https://en.wikipedia.org/wiki/Air_quality_index
##########################################################

sub myUtils_usAQI_cat($) {
  my ($AQI) = @_;
  my ($cat);

  return unless(looks_like_number($AQI));

  if($AQI < 50) { $cat = "Good"}
  elsif($AQI < 100) { $cat = "Moderate"}
  elsif($AQI < 150) { $cat = "Unhealthy for Sensitive Groups"}
  elsif($AQI < 200) { $cat = "Unhealthy"}
  elsif($AQI < 300) { $cat = "Very Unhealthy"}
  elsif($AQI < 400) { $cat = "Hazardous"}
  elsif($AQI < 500) { $cat = "Hazardous"}

  else{return;}

  return($cat);
}

##########################################################


und rufe das Ganze über ein Userreading mit:

usAQI25 {myUtils_PM25_to_usAQI(ReadingsNum($name, "PM2.5", 0))},
usAQI25_cat {myUtils_usAQI_cat(ReadingsNum($name, "usAQI25", 0))},
usAQI10 {myUtils_PM10_to_usAQI(ReadingsNum($name, "PM10", 0))},
usAQI10_cat {myUtils_usAQI_cat(ReadingsNum($name, "usAQI10", 0))}


ab.

maddhin

Ein kurzer Tip falls jemand die AQIcn.org JSON API (http://www.aqicn.org/api/) nutzen möchte um Feinstaubwerte abzufragen:

Zuerst einen Token holen und dann super komfortabel mit dem Modul JSONREADINGS.PM (https://forum.fhem.de/index.php/topic,38463.0.html) via

https://api.waqi.info/feed/@YYYY/?token=XXX
die Daten abfragen.

Zum Testen kann man https://api.waqi.info/feed/shanghai/?token=demo nehmen. Aber Achtung: mit dem Token scheint dann eine Abfrage via Städtenamen nicht zu funktionieren!

@YYYY ist der IDX Code der Stadt (Berlin beispielsweise @6132) - der IDX Code ist nicht ganz einfach zu finden, ich habe die gesuchte Stadt via Browser aufgerufen und mir mit  "Seitenquelltext anzeigen" den HTML Code angesehen und nach "idx" gesucht.

XXX ist der token, den ihr nach Anmeldung via Email bekommt.

Mal abgesehen von dem Gefummel mit dem IDX Code funktioniert das super, alle PM2.5, PM10, O3, NO2, CO2, etc kommen in Fhem und man kann sie weiterverarbeiten.



CoolTux

Ich habe mir erlaubt daraus ein Modul zu machen. Hier gibt es eine erste Alpha Version
https://github.com/LeonGaultier/fhem-Aqicn


define aqicnWebBridge Aqicn token=<YOUR-TOKEN>


Der Rest ist selbst erklärend
Du musst nicht wissen wie es geht! Du musst nur wissen wo es steht, wie es geht.
Support me to buy new test hardware for development: https://www.paypal.com/paypalme/MOldenburg
My FHEM Git: https://git.cooltux.net/FHEM/
Das TuxNet Wiki:
https://www.cooltux.net

maddhin


CoolTux

Du musst nicht wissen wie es geht! Du musst nur wissen wo es steht, wie es geht.
Support me to buy new test hardware for development: https://www.paypal.com/paypalme/MOldenburg
My FHEM Git: https://git.cooltux.net/FHEM/
Das TuxNet Wiki:
https://www.cooltux.net

harway2007

Messungen der Städte in meinem Umfeld haben Werte
die mehr als 5 Monate alt sind ?
z.B.