####################################################################### # # 95_PachLog.pm # # Logging to www.pachube.com # Autor: a[PUNKT]r[BEI]oo2p[PUNKT]net # Stand: 09.09.2009 # Version: 0.9 ####################################################################### # Vorausetzung: Account bei www.pachube.com mit API-Key ####################################################################### # # FHEM: Neues Pachube-Device erstelle: define PachLog API-Key # "define PACH001 PachLog 1234kliceee77hgtzuippkk99" # # PACHUBE: FEED erstellen -> FEED-NR: DATASTREAM-ID:TAGS # Beispiel: HMS_TF (Temperatur und Feuchte Sensor) # FEED-NR: 1234 # ID 0 => Temperatur (temperature) # ID 1 => rel. Luftfeuchte (humidity) # # FHEM: PachLog-Devices: PACH01 # HMS_DEVICE: HMS_TF01 # FEED-NR: 1234 # ID 0 => Temperatur (temperature) # ID 1 => rel. Luftfeuchte (humidity) # "set PACH01 ADD HMS_TF01 1234:0:temperature:1:humidity" # # Hinweise: # Ein FEED kann nur komplett upgedated werden: # FEED 3456 -> ID 0 -> DEVICE A # FEED 3456 -> ID 1 -> DEVICE B # => geht nicht # # Es werden nur READINGS mit einfach Werten und Zahlen unterst?tzt. # Beispiele: NICHT unterst?tze READINGS # cum_month => CUM_MONTH: 37.173 CUM: 108.090 COST: 0.00 # cum_day => 2009-09-09 00:03:19 T: 1511725.6 H: 4409616 W: 609.4 R: 150.4 # israining no => (yes/no) ####################################################################### # $Id$ package main; use strict; use warnings; use POSIX; use Data::Dumper; use LWP; use LWP::UserAgent; use HTTP::Request::Common; ####################################################################### sub PachLog_Initialize($) { my ($hash) = @_; $hash->{DefFn} = "PachLog_Define"; $hash->{SetFn} = "PachLog_Set"; $hash->{NotifyFn} = "PachLog_Notify"; $hash->{AttrList} = "do_not_notify:0,1"; } ####################################################################### sub PachLog_Define($@) { # define PachLog Pachube-X-API-Key my ($hash, @a) = @_; # X-API-Key steht im DEF %defs{}{DEF} # Alternativ nach $defs{}{XAPIKEY} my($package, $filename, $line, $subroutine) = caller(3); return "Unknown argument count " . int(@a) . " , usage set dataset value or set delete dataset" if(int(@a) != 1); return undef; } ####################################################################### sub PachLog_Set($@) { # set ADD/DEL FEED:STREAM:VALUE:STREAM:VALUE&FEED-2:STEAM,VALUE my ($hash, @a) = @_ ; # FHEMWEB Frage....Auswahliste return "Unknown argument $a[1], choose one of ". join(" ",sort keys %{$hash->{READINGS}}) if($a[1] eq "?"); # Pruefen Uebergabeparameter # @a => a[0]:; a[1]=ADD oder DEL; a[2]= DeviceName; # a[3]=FEED:STREAM:VALUE:STREAM:VALUE&FEED-2:STREAM,VALUE # READINGS setzten oder l?schen if($a[1] eq "DEL") { Log3 $hash, 2, "PACHLOG -> DELETE: A0= ". $a[0] . " A1= " . $a[1] . " A2=" . $a[2]; if(defined($hash->{READINGS}{$a[2]})) { delete($hash->{READINGS}{$a[2]}) } } if($a[1] eq "ADD") { if(!defined($defs{$a[2]})) {return "PACHLOG[". $a[2] . "] => Unkown Device";} # Mindestens 3 Parameter my @b = split(/:/, $a[3]); return "PACHLOG[". $a[2] . "] => Argumenete: " . $a[3] . " nicht eindeutig => mind. 3 => FEED-NR:DATASTREAM:READING" if(int(@b) < 3); my $feednr = shift(@b); #FEED-Nr darf nur Zahlen enthalten if($feednr !~ /^\d+$/) {return "PACHLOG[". $a[2] . "] => FEED-Nr >" . $feednr . "< ist ungueltig";} # ??? Pruefen ob READING vorhanden ??? my ($i,$j); for ($i=0;$i<@b;$i++) { #Stream nur Zahlen if($b[$i] !~ /^\d+$/) {return "PACHLOG => FEED-Nr[" . $feednr ."] Stream-ID >" . $b[$i] . "< ungueltig";} # Reading existiert $j = $i + 1; if(!defined($defs{$a[2]}{READINGS}{$b[$j]})) {return "PACHLOG[". $a[2] . "] => Unkown READING >" . $b[$j] . "<";} # READING-Value validieren my $r = $defs{$a[2]}{READINGS}{$b[$j]}{VAL}; my $rn = &ReadingToNumber($r); if(!defined($rn)) {return "PACHLOG[". $a[$i] . "] => READING not supported >" . $b[$j] . "<";} $i = $j; } $hash->{READINGS}{$a[2]}{TIME} = TimeNow(); $hash->{READINGS}{$a[2]}{VAL} = $a[3]; } $hash->{CHANGED}[0] = $a[1]; $hash->{STATE} = $a[1]; return undef; return "Unknown argument count " . int(@a) . " , usage set ADD/DEL FEED:STREAM:VALUE:STREAM:VALUE&FEED-2:STREAM,VALUE" if(int(@a) != 4); } ####################################################################### sub PachLog_Notify ($$) { my ($me, $trigger) = @_; my $d = $me->{NAME}; return "" if($attr{$d} && $attr{$d}{disable}); my $t = $trigger->{NAME}; # Eintrag fuer Trigger-Device vorhanden if(!defined($defs{$d}{READINGS}{$t})) { Log3 $d, 5, ("PACHLOG[INFO] => " . $t . " => Nicht definiert"); return undef;} # Umwandeln 1234:0:temperature:1:humidity => %feed # Struktur: # %feed{FEED-NR}{READING}{VAL} # %feed{FEED-NR}{READING}{DATASTREAM} my ($dat,@a,$feednr,$i,$j); my %feed = (); $dat = $defs{$d}{READINGS}{$t}{VAL}; @a = split(/:/, $dat); $feednr = shift(@a); for ($i=0;$i<@a;$i++) { $j = $i + 1; $feed{$feednr}{$a[$j]}{STREAM} = $a[$i]; $i = $j; } # Werte aus Trigger-Device foreach my $r (keys %{$feed{$feednr}}) { $i = $defs{$t}{READINGS}{$r}{VAL}; # Werte Normalisieren # Einheit -> 21,1 (celsius) -> 21,1 # FS20: VAL = on => 1 && VAL = off => 0 # @a = split(' ', $i); $feed{$feednr}{$r}{VAL} = ReadingToNumber($i,$d) ; } # CVS-Data my @cvs = (); foreach my $r (keys %{$feed{$feednr}}) { $cvs[$feed{$feednr}{$r}{STREAM}] = $feed{$feednr}{$r}{VAL}; } my $cvs_data = join(',',@cvs); Log3 $d, 5, "PACHLOG[CVSDATA] => $cvs_data"; # Aufbereiten %feed als EEML-Data my $eeml = "\n"; $eeml .= "\n"; $eeml .= "\n"; foreach my $r (keys %{$feed{$feednr}}) { $eeml .= "\n"; $eeml .= "" . $feed{$feednr}{$r}{VAL} . "\n"; # Unit fuer EEML: Celsius my ($u_name,$u_symbol,$u_type,$u_tag) = split(',',PachLog_ReadingToUnit($r,$d)); if(defined($u_name)) { $eeml .= "". $u_tag . "\n"; $eeml .= "" . $u_name . "<\/unit>\n"; } $eeml .= "\n"; } $eeml .= "\n"; $eeml .= "\n"; Log3 $d, 5, "PACHLOG -> " . $t . " EEML => " . $eeml; # Pachube-Update per EEML -> XML my ($res,$ret,$ua,$apiKey,$url); $apiKey = $defs{$d}{DEF}; $url = "http://www.pachube.com/api/feeds/" . $feednr . ".xml"; $ua = new LWP::UserAgent; $ua->default_header('X-PachubeApiKey' => $apiKey); #Timeout 3 sec ... default 180sec $ua->timeout(3); $res = $ua->request(PUT $url,'Content' => $eeml); # Ueberpruefen wir, ob alles okay war: if ($res->is_success()) { Log3 $d, 5,("PACHLOG => Update[" . $t ."]: " . $cvs_data . " >> SUCCESS\n"); # Time setzten $defs{$d}{READINGS}{$t}{TIME} = TimeNow(); } else { Log3 $d, 0,("PACHLOG => Update[" . $t ."] ERROR: " . ($res->as_string) . "\n");} } ################################################################################ sub PachLog_ReadingToUnit($$) { # Unit fuer EEML: Celsius # Input: READING z.B. temperature # Output: Name,symbol,Type,Tag z.B. Celsius,C,derivedSI # weiters => www.eeml.org # No Match = undef my ($in,$d) = @_; my %unit = (); %unit = ( 'temperature' => "Celsius,C,derivedSI,Temperature", 'dewpoint' => "Celsius,C,derivedSI,Temperature", 'current' => "Power,kW,derivedSI,EnergyConsumption", 'humidity' => "Humidity,rel%,contextDependentUnits,Humidity", 'rain' => "Rain,l/m2,contextDependentUnits,Rain", 'rain_now' => "Rain,l/m2,contextDependentUnits,Rain", 'wind' => "Wind,m/s,contextDependentUnits,Wind", ); if(defined($unit{$in})) { Log3 $d,5,("PACHLOG[ReadingToUnit] " . $in . " >> " . $unit{$in} ); return $unit{$in}; } else { return undef; } } ################################################################################ sub ReadingToNumber($$) { # Input: reading z.B. 21.1 (Celsius) oder dim10%, on-for-oldtimer etc. # Output: 21.1 oder 10 # ERROR = undef # Alles au?er Nummern loeschen $t =~ s/[^0123456789.-]//g; my ($in,$d) = @_; Log3 $d, 5, "PACHLOG[ReadingToNumber] => in => $in"; # Bekannte READINGS FS20 Devices oder FHT if($in =~ /^on|Switch.*on/i) {$in = 1;} if($in =~ /^off|Switch.*off|lime-protection/i) {$in = 0;} # Keine Zahl vorhanden if($in !~ /\d{1}/) { Log3 $d, 5, "PACHLOG[ReadingToNumber] No Number: $in"; return undef;} # Mehrfachwerte in READING z.B. CUM_DAY: 5.040 CUM: 334.420 COST: 0.00 my @b = split(' ', $in); if(int(@b) gt 2) { Log3 $d, 5, "PACHLOG[ReadingToNumber] Not Supportet Reading: $in"; return undef;} # Nur noch Zahlen z.B. dim10% = 10 oder 21.1 (Celsius) = 21.1 if (int(@b) eq 2){ Log3 $d, 5, "PACHLOG[ReadingToNumber] Split:WhiteSpace-0- $b[0]"; $in = $b[0]; } $in =~ s/[^0123456789.-]//g; Log3 $d, 5, "PACHLOG[ReadingToNumber] => out => $in"; return $in } 1; =pod =begin html

PachLog

    The PachLog-Module Logs SensorData like (temperature and humidity) to www.pachube.com.

    Note: this module needs the HTTP::Request and LWP::UserAgent perl modules.

    Define

      define <name> PachLog <Pachube-API-Key>

      <Pachube-API-Key>:
      The Pachube-API-Key however is what you need in your code to authenticate your application's access the Pachube service.
      Don't share this with anyone: it's just like any other password.
      www.pachube.com

    Set

      Add a new Device for Logging to www.pachube.com

      set <NAME> ADD <FHEM-DEVICENAME> FEED-NR:ID:READING:ID:READING

      Example: KS300-Weather-Data

      READINGS: temperature humidity wind rain

      1. Generate Input-Feed on www.pachube.com => Yout get your FEED-NR: 1234
      2. Add Datastreams to the Feed:
        ID0temperature
        ID1humidity
        ID2wind
        ID3rain

      3. Add the KS300 to your PachLog-Device

      set <NAME> ADD <My-KS300> 1234:0temperature:1:humidity:2:wind:3:rain

      Delete a Device form Logging to www.pachube.com

      set <NAME> DEL <FHEM-DEVICENAME>


    Get
      N/A

    Attributes
    • do_not_notify

    • disable
      Disables PachLog. Nor more Logging to www.pachube.com

=end html =cut