diff --git a/CHANGED b/CHANGED index 884d3cf57..3ff7d1ebf 100644 --- a/CHANGED +++ b/CHANGED @@ -1,6 +1,7 @@ # Add changes at the top of the list. Keep it in ASCII, and 80-char wide. # Do not insert empty lines here, update check depends on it. - SVN + - added: new module 98_HTTPMOD.pm (stefanstrobel) - added: new module 51_BBB_WATCHDOG.pm (betateilchen) - bugfix: SYSMON: Fix: uninitialized variable - feature: new modul 73_MPD added (Wzut) diff --git a/FHEM/98_HTTPMOD.pm b/FHEM/98_HTTPMOD.pm new file mode 100755 index 000000000..cec048208 --- /dev/null +++ b/FHEM/98_HTTPMOD.pm @@ -0,0 +1,417 @@ +######################################################################### +# fhem Modul für Geräte mit Web-Oberfläche +# wie z.B. Poolmanager Pro von Bayrol (PM5) +# +# This file is part of fhem. +# +# Fhem is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# Fhem is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with fhem. If not, see . +# +############################################################################## +# Changelog: +# +# 2013-12-25 initial version +# 2013-12-29 modified to use non blocking HTTP +# 2014-1-1 modified to use attr instead of set to define internal parameters +# 2014-1-6 extended error handling and added documentation +# 2014-1-15 added readingsExpr to allow some computation on raw values before put in readings +# + +package main; + +use strict; +use warnings; +use Time::HiRes qw(gettimeofday); +use HttpUtils; + +sub HTTPMOD_Initialize($); +sub HTTPMOD_Define($$); +sub HTTPMOD_Undef($$); +sub HTTPMOD_Set($@); +sub HTTPMOD_Get($@); +sub HTTPMOD_Attr(@); +sub HTTPMOD_GetUpdate($); +sub HTTPMOD_Read($$$); + +# +# lists of Set and Get Options for this module +# so far this is not used + +my %HTTPMOD_sets = ( +); + +my %HTTPMOD_gets = ( +); + + +# +# FHEM module intitialisation +# defines the functions to be called from FHEM +######################################################################### +sub HTTPMOD_Initialize($) +{ + my ($hash) = @_; + + $hash->{DefFn} = "HTTPMOD_Define"; + $hash->{UndefFn} = "HTTPMOD_Undef"; + #$hash->{SetFn} = "HTTPMOD_Set"; + #$hash->{GetFn} = "HTTPMOD_Get"; + $hash->{AttrFn} = "HTTPMOD_Attr"; + $hash->{AttrList} = + "do_not_notify:1,0 " . + "readingsName.* " . + "readingsRegex.* " . + "readingsExpr.* " . + "requestHeader.* " . + "requestData.* " . + $readingFnAttributes; +} + +# +# Define command +# init internal values, +# set internal timer get Updates +######################################################################### +sub HTTPMOD_Define($$) +{ + my ( $hash, $def ) = @_; + my @a = split( "[ \t][ \t]*", $def ); + + return "wrong syntax: define HTTPMOD URL interval" + if ( @a < 3 ); + + my $name = $a[0]; + my $url = $a[2]; + my $inter = 300; + + if(int(@a) == 4) { + $inter = $a[3]; + if ($inter < 5) { + return "interval too small, please use something > 5, default is 300"; + } + } + + $hash->{url} = $url; + $hash->{Interval} = $inter; + + # for non blocking HTTP Get + $hash->{callback} = \&HTTPMOD_Read; + $hash->{timeout} = 2; + #$hash->{loglevel} = 3; + + # initial request after 2 secs, there timer is set to interval for further update + InternalTimer(gettimeofday()+2, "HTTPMOD_GetUpdate", $hash, 0); + + return undef; +} + +# +# undefine command when device is deleted +######################################################################### +sub HTTPMOD_Undef($$) +{ + my ( $hash, $arg ) = @_; + DevIo_CloseDev($hash); + RemoveInternalTimer($hash); + return undef; +} + + +# +# Attr command +######################################################################### +sub +HTTPMOD_Attr(@) +{ + my ($cmd,$name,$aName,$aVal) = @_; + # $cmd can be "del" or "set" + # $name is device name + # aName and aVal are Attribute name and value + + # Attributes are readingsRegexp.*, requestHeader.* and requestData.* + + # requestHeader and requestData need no special treatment here + # however they have to be added to $hash later so HttpUtils + # an pick them up. Maybe later versions of HttpUtils could + # also pick up attributes? + + # readingsRegex.* needs validation though. + # ... to be implemented later here ... + # each readingsRegexX defines a pair of Reading and Regex + + if ($cmd eq "set") { + if ($aName =~ "readingsRegex") { + eval { qr/$aVal/ }; + if ($@) { + Log3 $name, 3, "HTTPOD: Invalid regex in attr $name $aName $aVal: $@"; + return "Invalid Regex $aVal"; + } + } elsif ($aName =~ "readingsExpr") { + my $val = 1; + eval $aVal; + if ($@) { + Log3 $name, 3, "HTTPOD: Invalid Expression in attr $name $aName $aVal: $@"; + return "Invalid Expression $aVal"; + } + } + } + + return undef; +} + + + +# +# SET command +# currently not used +######################################################################### +sub HTTPMOD_Set($@) +{ + my ( $hash, @a ) = @_; + return "\"set HTTPMOD\" needs at least an argument" if ( @a < 2 ); + + # @a is an array with DeviceName, SetName, Rest of Set Line + my $name = shift @a; + my $attr = shift @a; + my $arg = join("", @a); + + if(!defined($HTTPMOD_sets{$attr})) { + my @cList = keys %HTTPMOD_sets; + return "Unknown argument $attr, choose one of " . join(" ", @cList); + } + + return undef; +} + +# +# GET command +# currently not used +######################################################################### +sub HTTPMOD_Get($@) +{ + my ( $hash, @a ) = @_; + return "\"get HTTPMOD\" needs at least an argument" if ( @a < 2 ); + + # @a is an array with DeviceName and GetName + my $name = shift @a; + my $attr = shift @a; + + if(!defined($HTTPMOD_gets{$attr})) { + my @cList = keys %HTTPMOD_gets; + return "Unknown argument $attr, choose one of " . join(" ", @cList); + } + + return undef; +} + + + +# +# request new data from device +################################### +sub HTTPMOD_GetUpdate($) +{ + my ($hash) = @_; + my $name = $hash->{NAME}; + + InternalTimer(gettimeofday()+$hash->{Interval}, "HTTPMOD_GetUpdate", $hash, 1); + Log3 $name, 4, "HTTPMOD: GetUpdate called, hash = $hash, name = $name"; + + if ( $hash->{url} eq "none" ) { + return 0; + } + + my $header = join ("\r\n", map ($attr{$name}{$_}, sort grep (/requestHeader/, keys %{$attr{$name}}))); + if (length $header > 0) { + $hash->{header} = $header; + } else { + delete $hash->{header}; + } + + my $data = join ("\r\n", map ($attr{$name}{$_}, sort grep (/requestData/, keys %{$attr{$name}}))); + if (length $data > 0) { + $hash->{data} = $data; + } else { + delete $hash->{data}; + } + + HttpUtils_NonblockingGet($hash); +} + +# +# read / parse new data from device +# - callback for non blocking HTTP +################################### +sub HTTPMOD_Read($$$) +{ + my ($hash, $err, $buffer) = @_; + my $name = $hash->{NAME}; + + if ($err) { + Log3 $name, 3, "HTTPMOD got error in callback: $err"; + return; + } + Log3 $name, 5, "HTTPMOD: Callback called: Hash: $hash, Name: $name, buffer: $buffer\r\n"; + + my $msg = ""; + readingsBeginUpdate($hash); + foreach my $a (sort (grep (/readingsName/, keys %{$attr{$name}}))) { + $a =~ /readingsName(.*)/; + if (defined ($attr{$name}{'readingsName' . $1}) && + defined ($attr{$name}{'readingsRegex' . $1})) { + my $reading = $attr{$name}{'readingsName' . $1}; + my $regex = $attr{$name}{'readingsRegex' . $1}; + my $expr = ""; + if (defined ($attr{$name}{'readingsExpr' . $1})) { + $expr = $attr{$name}{'readingsExpr' . $1}; + } + Log3 $name, 5, "HTTPMOD: Trying to extract Reading $reading with regex /$regex/..."; + if ($buffer =~ /$regex/) { + my $val = $1; + if ($expr) { + $val = eval $expr; + Log3 $name, 5, "HTTPMOD: change value for Reading $reading with Expr $expr from $1 to $val"; + } + Log3 $name, 5, "HTTPMOD: Set Reading $reading to $val"; + readingsBulkUpdate( $hash, $reading, $val ); + } else { + if ($msg) { + $msg .= ", $reading"; + } else { + $msg = "$reading"; + } + } + } else { + Log3 $name, 3, "HTTPMOD: inconsitant attributes for $a"; + } + } + readingsEndUpdate( $hash, 1 ); + if ($msg) { + Log3 $name, 3, "HTTPMOD: Response didn't match Reading(s) $msg"; + Log3 $name, 4, "HTTPMOD: response was $buffer"; + } + return; + +} + + +1; + + +=pod +=begin html + + +

HTTPMOD

+ + + +=end html +=cut diff --git a/MAINTAINER.txt b/MAINTAINER.txt index 71974a8fc..b1dea91da 100644 --- a/MAINTAINER.txt +++ b/MAINTAINER.txt @@ -186,6 +186,7 @@ FHEM/98_CULflash.pm rudolfkoenig http://forum.fhem.de Sonstiges FHEM/98_GEOFANCY.pm loredo http://forum.fhem.de Unterstuetzende Dienste FHEM/98_HMinfo.pm martinp876 http://forum.fhem.de HomeMatic FHEM/98_Heating_Control.pm dietmar63 http://forum.fhem.de Unterstuetzende Dienste +FHEM/98_HTTPMOD.pm stefanstrobel http://forum.fhem.de Sonstiges FHEM/98_JsonList.pm mfr69bs http://forum.fhem.de Automatisierung FHEM/98_PID.pm betateilchen http://forum.fhem.de Automatisierung FHEM/98_RandomTimer.pm dietmar63 http://forum.fhem.de Unterstuetzende Dienste