diff --git a/fhem/FHEM/98_HTTPMOD.pm b/fhem/FHEM/98_HTTPMOD.pm
index cec048208..407eff816 100755
--- a/fhem/FHEM/98_HTTPMOD.pm
+++ b/fhem/FHEM/98_HTTPMOD.pm
@@ -1,7 +1,7 @@
#########################################################################
# 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
@@ -18,15 +18,15 @@
# along with fhem. If not, see .
#
##############################################################################
-# Changelog:
+# 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
-#
-
+# 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
+# 2014-3-13 added noShutdown and disable attributes
+
package main;
use strict;
@@ -60,21 +60,23 @@ my %HTTPMOD_gets = (
#########################################################################
sub HTTPMOD_Initialize($)
{
- my ($hash) = @_;
+ 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;
+ $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.* " .
+ "disable:0,1 " .
+ "noShutdown:0,1 " .
+ $readingFnAttributes;
}
#
@@ -84,35 +86,35 @@ sub HTTPMOD_Initialize($)
#########################################################################
sub HTTPMOD_Define($$)
{
- my ( $hash, $def ) = @_;
- my @a = split( "[ \t][ \t]*", $def );
+ my ( $hash, $def ) = @_;
+ my @a = split( "[ \t][ \t]*", $def );
- return "wrong syntax: define HTTPMOD URL interval"
- if ( @a < 3 );
+ 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";
- }
- }
+ 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);
+ $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;
+ return undef;
}
#
@@ -120,10 +122,10 @@ sub HTTPMOD_Define($$)
#########################################################################
sub HTTPMOD_Undef($$)
{
- my ( $hash, $arg ) = @_;
- DevIo_CloseDev($hash);
- RemoveInternalTimer($hash);
- return undef;
+ my ( $hash, $arg ) = @_;
+ DevIo_CloseDev($hash);
+ RemoveInternalTimer($hash);
+ return undef;
}
@@ -133,40 +135,40 @@ sub HTTPMOD_Undef($$)
sub
HTTPMOD_Attr(@)
{
- my ($cmd,$name,$aName,$aVal) = @_;
+ my ($cmd,$name,$aName,$aVal) = @_;
# $cmd can be "del" or "set"
- # $name is device name
- # aName and aVal are Attribute name and value
+ # $name is device name
+ # aName and aVal are Attribute name and value
- # Attributes are readingsRegexp.*, requestHeader.* and requestData.*
+ # 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?
+ # 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;
+ # 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;
}
@@ -177,20 +179,20 @@ HTTPMOD_Attr(@)
#########################################################################
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);
- }
+ 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;
+ return undef;
}
#
@@ -199,19 +201,19 @@ sub HTTPMOD_Set($@)
#########################################################################
sub HTTPMOD_Get($@)
{
- my ( $hash, @a ) = @_;
- return "\"get HTTPMOD\" needs at least an argument" if ( @a < 2 );
+ 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;
+ # @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;
}
@@ -221,31 +223,40 @@ sub HTTPMOD_Get($@)
###################################
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 ($hash) = @_;
+ my $name = $hash->{NAME};
+
+ InternalTimer(gettimeofday()+$hash->{Interval}, "HTTPMOD_GetUpdate", $hash, 1);
+
+ return if(AttrVal($name, "disable", undef));
- 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};
- }
+ Log3 $name, 4, "HTTPMOD: GetUpdate called, hash = $hash, name = $name";
+
+ if ( $hash->{url} eq "none" ) {
+ return 0;
+ }
- 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);
+ 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};
+ }
+
+ if (AttrVal($name, "disable", undef)) {
+ $hash->{noshutdown} = 1;
+ } else {
+ delete $hash->{noshutdown};
+ };
+
+ HttpUtils_NonblockingGet($hash);
}
#
@@ -254,54 +265,54 @@ sub HTTPMOD_GetUpdate($)
###################################
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 ($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;
-
+ 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;
+
}
@@ -315,102 +326,106 @@ sub HTTPMOD_Read($$$)
HTTPMOD
- This module provides a generic way to retrieve information from devices with an HTTP Interface and store them in Readings.
- It queries a given URL with Headers and data defined by attributes.
- From the HTTP Response it extracts Readings named in attributes using Regexes also defined by attributes.
-
- Prerequisites
-
-
- -
- This Module uses the non blocking HTTP function HttpUtils_NonblockingGet provided by FHEM's HttpUtils in a new Version published in December 2013.
- If not already installed in your environment, please update FHEM or install it manually using appropriate commands from your environment.
-
-
-
-
+ This module provides a generic way to retrieve information from devices with an HTTP Interface and store them in Readings.
+ It queries a given URL with Headers and data defined by attributes.
+ From the HTTP Response it extracts Readings named in attributes using Regexes also defined by attributes.
+
+ Prerequisites
+
+
+ -
+ This Module uses the non blocking HTTP function HttpUtils_NonblockingGet provided by FHEM's HttpUtils in a new Version published in December 2013.
+ If not already installed in your environment, please update FHEM or install it manually using appropriate commands from your environment.
+
+
+
+
-
- Define
-
-
- define <name> HTTPMOD <URL> <Interval>
-
- The module connects to the given URL every Interval seconds, sends optional headers and data and then parses the response
-
- Example:
-
- define PM HTTPMOD http://MyPoolManager/cgi-bin/webgui.fcgi 60
-
-
+
+ Define
+
+
+ define <name> HTTPMOD <URL> <Interval>
+
+ The module connects to the given URL every Interval seconds, sends optional headers and data and then parses the response
+
+ Example:
+
+ define PM HTTPMOD http://MyPoolManager/cgi-bin/webgui.fcgi 60
+
+
-
- Configuration of HTTP Devices
-
- Specify optional headers as attr requestHeader1
to attr requestHeaderX
,
- optional POST data as attr requestData
and then
- pairs of attr readingNameX
and attr readingRegexX
to define which readings you want to extract from the HTTP
- response and how to extract them.
-
- Example for a PoolManager 5:
-
- define PM HTTPMOD http://MyPoolManager/cgi-bin/webgui.fcgi 60
- attr PM readingsName1 PH
- attr PM readingsName2 CL
- attr PM readingsName3 TEMP
- attr PM readingsRegex1 34.4001.value":[ \t]+"([\d\.]+)"
- attr PM readingsRegex2 34.4008.value":[ \t]+"([\d\.]+)"
- attr PM readingsRegex3 34.4033.value":[ \t]+"([\d\.]+)"
- attr PM requestData {"get" :["34.4001.value" ,"34.4008.value" ,"34.4033.value", "14.16601.value", "14.16602.value"]}
- attr PM requestHeader1 Content-Type: application/json
- attr PM requestHeader2 Accept: */*
- attr PM stateFormat {sprintf("%.1f Grad, PH %.1f, %.1f mg/l Chlor", ReadingsVal($name,"TEMP",0), ReadingsVal($name,"PH",0), ReadingsVal($name,"CL",0))}
-
- If you need to do some calculation on a raw value before it is used as a reading you can define the attribute readingsExprX
- which can use the raw value from the variable $val
-
- Example:
-
- attr PM readingsExpr3 $val * 10
-
-
-
+
+ Configuration of HTTP Devices
+
+ Specify optional headers as attr requestHeader1
to attr requestHeaderX
,
+ optional POST data as attr requestData
and then
+ pairs of attr readingNameX
and attr readingRegexX
to define which readings you want to extract from the HTTP
+ response and how to extract them.
+
+ Example for a PoolManager 5:
+
+ define PM HTTPMOD http://MyPoolManager/cgi-bin/webgui.fcgi 60
+ attr PM readingsName1 PH
+ attr PM readingsName2 CL
+ attr PM readingsName3 TEMP
+ attr PM readingsRegex1 34.4001.value":[ \t]+"([\d\.]+)"
+ attr PM readingsRegex2 34.4008.value":[ \t]+"([\d\.]+)"
+ attr PM readingsRegex3 34.4033.value":[ \t]+"([\d\.]+)"
+ attr PM requestData {"get" :["34.4001.value" ,"34.4008.value" ,"34.4033.value", "14.16601.value", "14.16602.value"]}
+ attr PM requestHeader1 Content-Type: application/json
+ attr PM requestHeader2 Accept: */*
+ attr PM stateFormat {sprintf("%.1f Grad, PH %.1f, %.1f mg/l Chlor", ReadingsVal($name,"TEMP",0), ReadingsVal($name,"PH",0), ReadingsVal($name,"CL",0))}
+
+ If you need to do some calculation on a raw value before it is used as a reading you can define the attribute readingsExprX
+ which can use the raw value from the variable $val
+
+ Example:
+
+ attr PM readingsExpr3 $val * 10
+
+
+
-
- Set-Commands
-
-
-
- Get-Commands
-
-
-
- Attributes
-
- - do_not_notify
- - readingFnAttributes
-
- - requestHeader.*
- Define an additional HTTP Header to set in the HTTP request
- - requestData
- POST Data to be sent in the request. If not defined, it will be a GET request as defined in HttpUtils used by this module
- - readingsName.*
- the name of a reading to extract with the corresponding readingRegex
- - readingsRegex.*
- defines the regex to be used for extracting the reading. The value to extract should be in a sub expression e.g. ([\d\.]+) in the above example
- - readingsExpr.*
- defines an expression that is used in an eval to compute the readings value. The raw value will be in the variable $val.
-
-
-
- Author's notes
-
- - If you don't know which URLs, headers or POST data your web GUI uses, you might try a local proxy like BurpSuite to track requests and responses
-
+
+ Set-Commands
+
+
+
+ Get-Commands
+
+
+
+ Attributes
+
+ - do_not_notify
+ - readingFnAttributes
+
+ - requestHeader.*
+ Define an additional HTTP Header to set in the HTTP request
+ - requestData
+ POST Data to be sent in the request. If not defined, it will be a GET request as defined in HttpUtils used by this module
+ - readingsName.*
+ the name of a reading to extract with the corresponding readingRegex
+ - readingsRegex.*
+ defines the regex to be used for extracting the reading. The value to extract should be in a sub expression e.g. ([\d\.]+) in the above example
+ - readingsExpr.*
+ defines an expression that is used in an eval to compute the readings value. The raw value will be in the variable $val.
+ - noShutdown
+ pass the noshutdown flag to HTTPUtils for webservers that need it (some embedded webservers only deliver empty pages otherwise)
+ - disable
+ stop doing HTTP requests while this attribute is set to 1
+
+
+
+ Author's notes
+
+ - If you don't know which URLs, headers or POST data your web GUI uses, you might try a local proxy like BurpSuite to track requests and responses
+
=end html