00_FBAHAHTTP.pm: new Module

git-svn-id: https://svn.fhem.de/fhem/trunk@11408 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
rudolfkoenig
2016-05-08 13:58:47 +00:00
parent bb17c13709
commit 8da1095602
5 changed files with 415 additions and 32 deletions

View File

@@ -1,5 +1,6 @@
# 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.
- feature: FBAHAHTTP module as replacement for the deprecated FBAHA
- feature: 50_TelegramBot reply set command / allowedCommands as restriction
- change: 49_SSCam: get "snapfileinfo" will get back an Infomessage if
Reading "LastSnapId" isn't available

257
fhem/FHEM/00_FBAHAHTTP.pm Normal file
View File

@@ -0,0 +1,257 @@
##############################################
# $Id$
package main;
# Documentation: AHA-HTTP-Interface.pdf, AVM_Technical_Note_-_Session_ID.pdf
use strict;
use warnings;
use Time::HiRes qw(gettimeofday);
use FritzBoxUtils;
sub
FBAHAHTTP_Initialize($)
{
my ($hash) = @_;
$hash->{WriteFn} = "FBAHAHTTP_Write";
$hash->{DefFn} = "FBAHAHTTP_Define";
$hash->{SetFn} = "FBAHAHTTP_Set";
$hash->{AttrFn} = "FBAHAHTTP_Attr";
$hash->{AttrList} = "dummy:1,0 fritzbox-user polltime";
}
#####################################
sub
FBAHAHTTP_Define($$)
{
my ($hash, $def) = @_;
my @a = split("[ \t][ \t]*", $def);
return "wrong syntax: define <name> FBAHAHTTP hostname"
if(@a != 3);
$hash->{Clients} = ":FBDECT:";
my %matchList = ( "1:FBDECT" => ".*" );
$hash->{MatchList} = \%matchList;
for my $d (devspec2array("TYPE=FBDECT")) {
if($defs{$d}{IODev} && $defs{$d}{IODev}{TYPE} eq "FBAHA") {
my $n = $defs{$d}{IODev}{NAME};
CommandAttr(undef, "$d IODev $hash->{NAME}");
CommandDelete(undef, $n) if($defs{$n});
}
$defs{$d}{IODev} = $hash
}
return undef if($hash->{DEF} eq "none"); # DEBUGGING
InternalTimer(1, "FBAHAHTTP_Poll", $hash);
$hash->{STATE} = "defined";
return undef;
}
#####################################
sub
FBAHAHTTP_Poll($)
{
my ($hash) = @_;
my $name = $hash->{NAME};
my $dr = sub {
$hash->{STATE} = $_[0];
Log 2, $hash->{STATE};
return $hash->{STATE};
};
if(!$hash->{".SID"}) {
my $fb_user = AttrVal($name, "fritzbox-user", '');
return $dr->("MISSING: attr $name fritzbox-user") if(!$fb_user);
my ($err, $fb_pw) = getKeyValue("FBAHAHTTP_PASSWORD_$name");
return $dr->("ERROR: $err") if($err);
return $dr->("MISSING: set $name password") if(!$fb_pw);
my $sid = FB_doCheckPW($hash->{DEF}, $fb_user, $fb_pw);
return $dr->("ERROR: cannot get SID, check hostname/fritzbox-user/password")
if(!$sid);
$hash->{".SID"} = $sid;
$hash->{STATE} = "connected";
}
my $sid = $hash->{".SID"};
HttpUtils_NonblockingGet({
url=>"http://$hash->{DEF}/webservices/homeautoswitch.lua?sid=$sid".
"&switchcmd=getdevicelistinfos",
loglevel => AttrVal($name, "verbose", 4),
callback => sub {
if($_[1]) {
Log3 $name, 3, "$name: $_[1]";
delete $hash->{".SID"};
return;
}
if($_[2] !~ m,^<devicelist.*</devicelist>$,s) {
Log3 $name, 3, "$name: unexpected reply from device: $_[2]";
delete $hash->{".SID"};
return;
}
$_[2] =~ s,</devicelist>,,;
my @a = split("<device ", $_[2]);
for(my $i=1; $i<int(@a); $i++) {
Dispatch($hash, "<device ".$a[$i], undef);
}
}
});
my $polltime = AttrVal($name, "polltime", 300);
RemoveInternalTimer($hash);
InternalTimer(gettimeofday()+$polltime, "FBAHAHTTP_Poll", $hash);
return;
}
#####################################
sub
FBAHAHTTP_Attr($@)
{
my ($type, $devName, $attrName, @param) = @_;
my $hash = $defs{$devName};
if($attrName eq "fritzbox-user") {
return "Cannot delete fritzbox-user" if($type eq "del");
if($init_done) {
delete($hash->{".SID"});
FBAHAHTTP_Poll($hash);
}
}
return undef;
}
#####################################
sub
FBAHAHTTP_Set($@)
{
my ($hash, @a) = @_;
my $name = shift @a;
my %sets = (password=>2, refreshstate=>1);
return "set $name needs at least one parameter" if(@a < 1);
my $type = shift @a;
return "Unknown argument $type, choose one of refreshstate:noArg password"
if(!defined($sets{$type}));
return "Missing argument for $type" if(int(@a) < $sets{$type}-1);
if($type eq "password") {
setKeyValue("FBAHAHTTP_PASSWORD_$name", $a[0]);
delete($hash->{".SID"});
FBAHAHTTP_Poll($hash);
return;
}
if($type eq "refreshstate") {
FBAHAHTTP_Poll($hash);
return;
}
return undef;
}
#####################################
sub
FBAHAHTTP_Write($$$)
{
my ($hash,$fn,$msg) = @_;
my $name = $hash->{NAME};
my $sid = $hash->{".SID"};
if(!$sid) {
Log 1, "$name: Not connected, wont execute $msg";
return;
}
HttpUtils_NonblockingGet({
url=>"http://$hash->{DEF}/webservices/homeautoswitch.lua?sid=$sid".
"&ain=$fn&switchcmd=$msg",
loglevel => AttrVal($name, "verbose", 4),
callback => sub {
if($_[1]) {
Log3 $name, 3, "$name: $_[1]";
delete $hash->{".SID"};
return;
}
Log 1, "FBAHAHTTP_Write reply for $name: $_[2]";
}
});
}
1;
=pod
=begin html
<a name="FBAHAHTTP"></a>
<h3>FBAHAHTTP</h3>
<ul>
This module connects to the AHA server (AVM Home Automation) on a FRITZ!Box
via HTTP, it is a successor/drop-in replacement for the FBAHA module. It is
necessary, as the FBAHA interface is deprecated by AVM. Since the AHA HTTP
interface do not offer any notification mechanism, the module is regularly
polling the FRITZ!Box.<br>
Important: For an existing installation with an FBAHA device, defining a
new FBAHAHTTP device will change the IODev of all FBDECT devices from the
old FBAHA to this FBAHAHTTP device, and it will delete the FBAHA device.<br>
This module serves as the "physical" counterpart to the <a
href="#FBDECT">FBDECT</a> devices. Note: you have to enable the access to
Smart Home in the FRITZ!Box frontend for the fritzbox-user.
<br><br>
<a name="FBAHAHTTPdefine"></a>
<b>Define</b>
<ul>
<code>define &lt;name&gt; FBAHAHTTP &lt;hostname&gt;</code><br>
<br>
&lt;hostnamedevice&gt; is most probably fritz.box.
Example:
<ul>
<code>define fb1 FBAHAHTTP fritz.box</code><br>
</ul>
</ul>
<br>
<a name="FBAHAHTTPset"></a>
<b>Set</b>
<ul>
<li>password &lt;password&gt;<br>
This is the only way to set the password
</li>
<li>refreshstate<br>
The state of all devices is polled every &lt;polltime&gt; seconds (default
is 300). This command forces a state-refresh.
</li>
</ul>
<br>
<a name="FBAHAHTTPget"></a>
<b>Get</b>
<ul>N/A</ul>
<br>
<a name="FBAHAHTTPattr"></a>
<b>Attributes</b>
<ul>
<li><a href="#dummy">dummy</a></li>
<li><a href="#fritzbox-user">fritzbox-user</a></li>
<li><a href="#polltime">polltime</a><br>
measured in seconds, default is 300 i.e. 5 minutes
</li>
</ul>
<br>
</ul>
=end html
=cut

View File

@@ -2,8 +2,6 @@
# $Id$
package main;
# TODO: test multi-dev, test on the FB
use strict;
use warnings;
use SetExtensions;
@@ -73,8 +71,6 @@ FBDECT_Define($$)
if($ioNameAndId =~ m/^([^:]*):(.*)$/) {
$ioName = $1; $id = $2;
}
return "define $name: wrong id ($id): need a number"
if( $id !~ m/^\d+$/i );
$hash->{id} = $id;
$hash->{props} = shift @a;
@@ -84,14 +80,57 @@ FBDECT_Define($$)
}
###################################
my %sets = ("on"=>1, "off"=>1, "msgInterval"=>1);
sub
FBDECT_SetHttp($@)
{
my ($hash, @a) = @_;
my %cmd;
my $p = $hash->{props};
if($p =~ m/switch/) {
$cmd{off} = $cmd{on} = $cmd{toggle} = "noArg";
}
if($p =~ m/actuator/) {
$cmd{"desired-temp"} = "slider,8,0.5,28,1";
$cmd{open} = $cmd{closed} = "noArg";
}
if(!$cmd{$a[1]}) {
my $cmdList = join(" ", map { "$_:$cmd{$_}" } sort keys %cmd);
return SetExtensions($hash, $cmdList, @a)
}
my $cmd = $a[1];
if($cmd =~ m/^(on|off|toggle)$/) {
IOWrite($hash, ReadingsVal($hash->{NAME},"AIN",0), "setswitch$cmd");
my $state = ($cmd eq "toggle" ? ($hash->{state} eq "on" ? "off":"on"):$cmd);
readingsSingleUpdate($hash, "state", $state, 1);
return undef;
}
if($cmd =~ m/^(open|closed|desired-temp)$/) {
if($cmd eq "desired-temp") {
return "Usage: set $hash->{NAME} desired-temp value" if(int(@a) != 3);
return "desired-temp must be between 8 and 28"
if($a[2] !~ m/^[\d.]+$/ || $a[2] < 8 || $a[2] > 28)
}
my $val = ($cmd eq "open" ? 254 : ($cmd eq "closed" ? 253: int(2*$a[2])));
IOWrite($hash, ReadingsVal($hash->{NAME},"AIN",0),"sethkrtsoll&param=$val");
return undef;
}
}
###################################
sub
FBDECT_Set($@)
{
my ($hash, @a) = @_;
my %sets = ("on"=>1, "off"=>1, "msgInterval"=>1);
return FBDECT_SetHttp($hash, @a)
if($hash->{IODev} && $hash->{IODev}{TYPE} eq "FBAHAHTTP");
my $ret = undef;
my $cmd = $a[1];
if(!$sets{$cmd}) {
my $usage = join(" ", sort keys %sets);
return SetExtensions($hash, $usage, @a);
@@ -118,17 +157,17 @@ FBDECT_Set($@)
return undef;
}
my %gets = ("devInfo"=>1);
sub
FBDECT_Get($@)
{
my ($hash, @a) = @_;
my $ret = undef;
my $cmd = ($a[1] ? $a[1] : "");
my %gets = ("devInfo"=>1);
if(!$gets{$cmd}) {
return "Unknown argument $cmd, choose one of ".join(" ", sort keys %gets);
}
my $cmdList = ($hash->{IODev} && $hash->{IODev}{TYPE} eq "FBAHA") ?
join(" ", sort keys %gets) : "";
return "Unknown argument $cmd, choose one of $cmdList" if(!$gets{$cmd});
if($cmd eq "devInfo") {
my @answ = FBAHA_getDevList($hash->{IODev}, $hash->{id});
@@ -159,14 +198,87 @@ FBDECT_Get($@)
return undef;
}
my %fbhttp_readings = (
absenk => 'sprintf("night-temp:%.1f C", $val/2)',
celsius => 'sprintf("temperature:%.1f C (measured)", $val/10)',
energy => 'sprintf("energy:%d Wh", $val)',
functionbitmask => '"FBPROP:$fbprop"',
fwversion => '"fwversion:$val"',
id => '"ID:$val"',
identifier => '"AIN:$val"',
komfort => 'sprintf("day-temp:%.1f C", $val/2)',
lock => '"locked:".($val ? "yes":"no")',
mode => '"mode:$val"',
name => '"FBNAME:$val"',
offset => 'sprintf("tempadjust:%.1f C", $val/10)', # ??
power => 'sprintf("power:%.2f W", $val/1000)',
present => '"present:".($val?"yes":"no")',
productname => '"FBTYPE:$val"',
state => '"state:".($val?"on":"off")',
tist => 'sprintf("temperature:%.1f C (measured)", $val/2)',
tsoll => 'sprintf("desired-temp:%.1f C", $val/2)',
);
sub
FBDECT_ParseHttp($$)
{
my ($iodev, $msg, $local) = @_;
my $ioName = $iodev->{NAME};
my %h;
$msg =~ s,<([^/>]+?)>([^<]+?)<,$h{$1}=$2,ge; # Quick & Dirty: Tags
$msg =~ s, ([a-z]+?)="([^"]+)",$h{$1}=$2,ge; # Quick & Dirty: Attributes
my $ain = $h{identifier};
$ain =~ s/[: ]/_/g;
my %ll = (6=>"actuator", 7=>"powerMeter", 8=>"tempSensor",
9=>"switch", 10=>"repeater");
my $lsn = int($h{functionbitmask});
my @fb;
map { push @fb, $ll{$_} if((1<<$_) & $lsn) } sort keys %ll;
my $fbprop = join(",", @fb);
my $dp = $modules{FBDECT}{defptr};
my $hash = $dp->{"$ioName:$ain"};
$hash = $dp->{$ain} if(!$hash);
$hash = $dp->{"$ioName:$h{id}"} if(!$hash);
$hash = $dp->{$h{id}} if(!$hash);
if(!$hash) {
my $ret = "UNDEFINED FBDECT_${ioName}_$ain FBDECT $ioName:$ain $fbprop";
Log3 $ioName, 3, "$ret, please define it";
DoTrigger("global", $ret);
return "";
}
$hash->{props} = $fbprop; # replace values from define
readingsBeginUpdate($hash);
Log3 $hash, 5, $hash->{NAME};
foreach my $n (keys %h) {
Log3 $hash, 5, " $n = $h{$n}";
next if(!$fbhttp_readings{$n});
my $val = $h{$n};
my ($ptyp,$pyld) = split(":", eval $fbhttp_readings{$n}, 2);
readingsBulkUpdate($hash, $ptyp, $pyld);
readingsBulkUpdate($hash, "state", "desired-temp: ".($val/2))
if($n eq "tsoll"); # Exception
}
readingsEndUpdate($hash, 1);
return $hash->{NAME};
}
###################################
sub
FBDECT_Parse($$@)
{
my ($iodev, $msg, $local) = @_;
my $ioName = $iodev->{NAME};
my $mt = substr($msg, 0, 2);
return FBDECT_ParseHttp($iodev, $msg) if($mt eq "<d");
my $ioName = $iodev->{NAME};
if($mt ne "07" && $mt ne "04") {
Log3 $ioName, 1, "FBDECT: unknown message type $mt";
return ""; # Nobody else is able to handle this
@@ -361,16 +473,15 @@ FBDECT_Undef($$)
<h3>FBDECT</h3>
<ul>
This module is used to control AVM FRITZ!DECT devices via FHEM, see also the
<a href="#FBAHA">FBAHA</a> module for the base.
<a href="#FBAHA">FBAHA</a> or <a href="#FBAHAHTTP">FBAHAHTTP</a> module for
the base.
<br><br>
<a name="FBDECTdefine"></a>
<b>Define</b>
<ul>
<code>define &lt;name&gt; FBDECT [&lt;FBAHAname&gt;:]&lt;homeId&gt; &lt;id&gt; [classes]</code>
<code>define &lt;name&gt; FBDECT [&lt;FBAHAname&gt;:]&lt;id&gt; props</code>
<br>
<br>
&lt;id&gt; is the id of the device, the classes argument ist ignored for now.
<br>
Example:
<ul>
<code>define lamp FBDECT 16 switch,powerMeter</code><br>
@@ -387,11 +498,18 @@ FBDECT_Undef($$)
<b>Set</b>
<ul>
<li>on/off<br>
set the device on or off.</li>
<li>
<a href="#setExtensions">set extensions</a> are supported.</li>
set the device on or off.
</li>
<li>desired-temp &lt;value&gt;<br>
set the desired temp on a Comet DECT (FBAHAHTTP IOdev only)
</li>
<li><a href="#setExtensions">set extensions</a> are supported.
</li>
<li>msgInterval &lt;sec&gt;<br>
Number of seconds between the sensor messages.
Number of seconds between the sensor messages (FBAHA IODev only).
</li>
</ul>
<br>
@@ -400,7 +518,8 @@ FBDECT_Undef($$)
<b>Get</b>
<ul>
<li>devInfo<br>
report device information</li>
report device information (FBAHA IODev only)
</li>
</ul>
<br>
@@ -448,24 +567,23 @@ FBDECT_Undef($$)
<h3>FBDECT</h3>
<ul>
Dieses Modul wird verwendet, um AVM FRITZ!DECT Ger&auml;te via FHEM zu
steuern, siehe auch das <a href="#FBAHA">FBAHA</a> Modul f&uumlr die
Anbindung an das FRITZ!Box.
steuern, siehe auch das <a href="#FBAHA">FBAHA</a> oder <a
href="#FBAHAHTTP">FBAHAHTTP</a> Modul f&uumlr die Anbindung an das FRITZ!Box.
<br><br>
<a name="FBDECTdefine"></a>
<b>Define</b>
<ul>
<code>define &lt;name&gt; FBDECT [&lt;FBAHAname&gt;:]&lt;homeId&gt; &lt;id&gt; [classes]</code>
<code>define &lt;name&gt; FBDECT [&lt;FBAHAname&gt;:]&lt;id&gt; props</code>
<br>
<br>
&lt;id&gt; ist das Ger&auml;te-ID, das Argument wird z.Zt ignoriert.
<br>
Beispiel:
<ul>
<code>define lampe FBDECT 16 switch,powerMeter</code><br>
</ul>
<b>Achtung:</b>FBDECT Eintr&auml;ge werden noralerweise per
<a href="#autocreate">autocreate</a> angelegt. Falls sie die zugeordnete FBAHA
Instanz umbenennen, dann muss die FBDECT Definition manuell angepasst werden.
<b>Achtung:</b>FBDECT Eintr&auml;ge werden normalerweise per
<a href="#autocreate">autocreate</a> angelegt. Falls sie die zugeordnete
FBAHA oder FBAHAHTTP Instanz umbenennen, dann muss die FBDECT Definition
manuell angepasst werden.
</ul>
<br>
<br
@@ -475,11 +593,17 @@ FBDECT_Undef($$)
<ul>
<li>on/off<br>
Ger&auml;t einschalten bzw. ausschalten.</li>
<li>desired-temp &lt;value&/gt;<br>
Gew&uuml;nschte Temperatur beim Comet DECT setzen (nur mit FBAHAHTTP als
IODev).
</li>
<li>
Die <a href="#setExtensions">set extensions</a> werden
unterst&uuml;tzt.</li>
unterst&uuml;tzt.
</li>
<li>msgInterval &lt;sec&gt;<br>
Anzahl der Sekunden zwischen den Sensornachrichten.
Anzahl der Sekunden zwischen den Sensornachrichten (nur mit FBAHA als
IODev).
</li>
</ul>
<br>
@@ -488,7 +612,7 @@ FBDECT_Undef($$)
<b>Get</b>
<ul>
<li>devInfo<br>
meldet Ger&auml;te-Informationen.</li>
meldet Ger&auml;te-Informationen (nur mit FBAHA als IODev)</li>
</ul>
<br>

View File

@@ -18,6 +18,7 @@ configDB.pm betateilchen http://forum.fhem.de Sonstiges
FHEM/00_CM11.pm borisneubert http://forum.fhem.de SlowRF
FHEM/00_CUL.pm rudolfkoenig http://forum.fhem.de SlowRF
FHEM/00_FBAHA.pm rudolfkoenig http://forum.fhem.de FRITZ!Box
FHEM/00_FBAHAHTTP.pm rudolfkoenig http://forum.fhem.de FRITZ!Box
FHEM/00_FHZ.pm rudolfkoenig http://forum.fhem.de SlowRF
FHEM/00_HMLAN.pm martinp876 http://forum.fhem.de HomeMatic
FHEM/00_HXB.pm borisneubert http://forum.fhem.de Sonstige Systeme

View File

@@ -2518,7 +2518,7 @@ CommandAttr($$)
}
if($attrName eq "IODev" && (!$a[2] || !defined($defs{$a[2]}))) {
push @rets,"$sdev: unknown IODev specified";
push @rets,"$sdev: unknown IODev $a[2] specified";
next;
}