mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-02-01 07:19:24 +00:00
00_MQTT2_SERVER.pm: Fixing bugs (Forum #90145)
git-svn-id: https://svn.fhem.de/fhem/trunk@17126 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
76c54ca4cf
commit
ac3067c14a
@ -9,11 +9,10 @@ use warnings;
|
|||||||
use TcpServerUtils;
|
use TcpServerUtils;
|
||||||
use MIME::Base64;
|
use MIME::Base64;
|
||||||
|
|
||||||
sub MQTT2_SERVER_Parse($$$);
|
|
||||||
sub MQTT2_SERVER_Read($@);
|
sub MQTT2_SERVER_Read($@);
|
||||||
sub MQTT2_SERVER_Write($$$);
|
sub MQTT2_SERVER_Write($$$);
|
||||||
sub MQTT2_SERVER_Undef($@);
|
sub MQTT2_SERVER_Undef($@);
|
||||||
sub MQTT2_SERVER_doPublish($$$;$$);
|
sub MQTT2_SERVER_doPublish($$$$;$);
|
||||||
|
|
||||||
|
|
||||||
# See also:
|
# See also:
|
||||||
@ -25,6 +24,7 @@ MQTT2_SERVER_Initialize($)
|
|||||||
my ($hash) = @_;
|
my ($hash) = @_;
|
||||||
|
|
||||||
$hash->{Clients} = ":MQTT2_DEVICE:";
|
$hash->{Clients} = ":MQTT2_DEVICE:";
|
||||||
|
$hash->{MatchList}= { "1:MQTT2_DEVICE" => "^.*" },
|
||||||
$hash->{ReadFn} = "MQTT2_SERVER_Read";
|
$hash->{ReadFn} = "MQTT2_SERVER_Read";
|
||||||
$hash->{DefFn} = "MQTT2_SERVER_Define";
|
$hash->{DefFn} = "MQTT2_SERVER_Define";
|
||||||
$hash->{AttrFn} = "MQTT2_SERVER_Attr";
|
$hash->{AttrFn} = "MQTT2_SERVER_Attr";
|
||||||
@ -61,7 +61,7 @@ MQTT2_SERVER_Define($$)
|
|||||||
Log3 $hash, 1, "$ret. Exiting.";
|
Log3 $hash, 1, "$ret. Exiting.";
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
$hash->{NRCLIENTS} = 0;
|
readingsSingleUpdate($hash, "nrclients", 0, 0);
|
||||||
$hash->{clients} = {};
|
$hash->{clients} = {};
|
||||||
$hash->{retain} = {};
|
$hash->{retain} = {};
|
||||||
InternalTimer(1, "MQTT2_SERVER_keepaliveChecker", $hash, 0);
|
InternalTimer(1, "MQTT2_SERVER_keepaliveChecker", $hash, 0);
|
||||||
@ -73,13 +73,16 @@ MQTT2_SERVER_keepaliveChecker($)
|
|||||||
{
|
{
|
||||||
my ($hash) = @_;
|
my ($hash) = @_;
|
||||||
my $now = gettimeofday();
|
my $now = gettimeofday();
|
||||||
|
my $multiplier = AttrVal($hash, "keepaliveFactor", 1.5);
|
||||||
|
if($multiplier) {
|
||||||
foreach my $clName (keys %{$hash->{clients}}) {
|
foreach my $clName (keys %{$hash->{clients}}) {
|
||||||
my $cHash = $defs{$clName};
|
my $cHash = $defs{$clName};
|
||||||
next if(!$cHash || !$cHash->{keepalive} ||
|
next if(!$cHash || !$cHash->{keepalive} ||
|
||||||
$now < $cHash->{lastMsgTime}+$cHash->{keepalive}*1.5 );
|
$now < $cHash->{lastMsgTime}+$cHash->{keepalive}*$multiplier );
|
||||||
Log3 $hash, 3, "$hash->{NAME}: $clName left us (keepalive check)";
|
Log3 $hash, 3, "$hash->{NAME}: $clName left us (keepalive check)";
|
||||||
CommandDelete(undef, $clName);
|
CommandDelete(undef, $clName);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
InternalTimer($now+10, "MQTT2_SERVER_keepaliveChecker", $hash, 0);
|
InternalTimer($now+10, "MQTT2_SERVER_keepaliveChecker", $hash, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,11 +96,12 @@ MQTT2_SERVER_Undef($@)
|
|||||||
|
|
||||||
my $shash = $defs{$sname};
|
my $shash = $defs{$sname};
|
||||||
delete($shash->{clients}{$hash->{NAME}});
|
delete($shash->{clients}{$hash->{NAME}});
|
||||||
$shash->{NRCLIENTS}--;
|
readingsSingleUpdate($shash, "nrclients",
|
||||||
|
ReadingsVal($sname, "nrclients", 1)-1, 1);
|
||||||
|
|
||||||
if($hash->{lwt}) { # Last will
|
if($hash->{lwt}) { # Last will
|
||||||
my ($tp, $val) = split(':', $hash->{lwt}, 2);
|
my ($tp, $val) = split(':', $hash->{lwt}, 2);
|
||||||
MQTT2_SERVER_doPublish($shash, $tp, $val, undef, $hash->{cflags} & 0x20);
|
MQTT2_SERVER_doPublish($shash, $shash, $tp, $val, $hash->{cflags} & 0x20);
|
||||||
}
|
}
|
||||||
return $ret;
|
return $ret;
|
||||||
}
|
}
|
||||||
@ -133,7 +137,7 @@ MQTT2_SERVER_Set($@)
|
|||||||
return "Usage: publish -r topic [value]" if(@a < 1);
|
return "Usage: publish -r topic [value]" if(@a < 1);
|
||||||
my $tp = shift(@a);
|
my $tp = shift(@a);
|
||||||
my $val = join(" ", @a);
|
my $val = join(" ", @a);
|
||||||
MQTT2_SERVER_doPublish($hash, $tp, $val, undef, $retain);
|
MQTT2_SERVER_doPublish($hash->{CL}, $hash, $tp, $val, $retain);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,7 +171,8 @@ MQTT2_SERVER_Read($@)
|
|||||||
my $nhash = TcpServer_Accept($hash, "MQTT2_SERVER");
|
my $nhash = TcpServer_Accept($hash, "MQTT2_SERVER");
|
||||||
return if(!$nhash);
|
return if(!$nhash);
|
||||||
$nhash->{CD}->blocking(0);
|
$nhash->{CD}->blocking(0);
|
||||||
$hash->{NRCLIENTS}++;
|
readingsSingleUpdate($hash, "nrclients",
|
||||||
|
ReadingsVal($hash->{NAME}, "nrclients", 0)+1, 1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -280,7 +285,7 @@ MQTT2_SERVER_Read($@)
|
|||||||
$val = substr($pl, $off);
|
$val = substr($pl, $off);
|
||||||
Log3 $sname, 4, "$cname $hash->{cid} $cpt $tp:$val";
|
Log3 $sname, 4, "$cname $hash->{cid} $cpt $tp:$val";
|
||||||
addToWritebuffer($hash, pack("CCnC*", 0x40, 2, $pid)) if($qos); # PUBACK
|
addToWritebuffer($hash, pack("CCnC*", 0x40, 2, $pid)) if($qos); # PUBACK
|
||||||
MQTT2_SERVER_doPublish($defs{$sname}, $tp, $val, $cname, $cf & 0x01);
|
MQTT2_SERVER_doPublish($hash, $defs{$sname}, $tp, $val, $cf & 0x01);
|
||||||
|
|
||||||
####################################
|
####################################
|
||||||
} elsif($cpt eq "SUBSCRIBE") {
|
} elsif($cpt eq "SUBSCRIBE") {
|
||||||
@ -328,25 +333,29 @@ MQTT2_SERVER_Read($@)
|
|||||||
######################################
|
######################################
|
||||||
# Call sendto for all clients + Dispatch + dotrigger if rawEvents is set
|
# Call sendto for all clients + Dispatch + dotrigger if rawEvents is set
|
||||||
sub
|
sub
|
||||||
MQTT2_SERVER_doPublish($$$;$$)
|
MQTT2_SERVER_doPublish($$$$;$)
|
||||||
{
|
{
|
||||||
my ($hash, $tp, $val, $src, $retain) = @_;
|
my ($src, $tgt, $tp, $val, $retain) = @_;
|
||||||
$val = "" if(!defined($val));
|
$val = "" if(!defined($val));
|
||||||
|
$src = $tgt if(!defined($src));
|
||||||
|
|
||||||
if($retain) {
|
if($retain) {
|
||||||
my $now = gettimeofday();
|
my $now = gettimeofday();
|
||||||
my %h = ( ts=>$now, val=>$val );
|
my %h = ( ts=>$now, val=>$val );
|
||||||
$hash->{retain}{$tp} = \%h;
|
$tgt->{retain}{$tp} = \%h;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach my $clName (keys %{$hash->{clients}}) {
|
foreach my $clName (keys %{$tgt->{clients}}) {
|
||||||
MQTT2_SERVER_sendto($defs{$clName}, $tp, $val) if(!$src || $src ne $clName);
|
MQTT2_SERVER_sendto($defs{$clName}, $tp, $val) if($src->{NAME} ne $clName);
|
||||||
}
|
}
|
||||||
|
|
||||||
Dispatch($hash, "$tp:$val", undef, 1);
|
if($src->{cid}) { # "real" MQTT client
|
||||||
|
my $cid = $src->{cid};
|
||||||
my $re = AttrVal($hash->{NAME}, "rawEvents", undef);
|
$cid =~ s,[^a-z0-9._],_,gi;
|
||||||
DoTrigger($hash->{NAME}, "$tp:$val") if($re && $tp =~ m/$re/);
|
Dispatch($tgt, "$cid:$tp:$val", undef, 1);
|
||||||
|
my $re = AttrVal($tgt->{NAME}, "rawEvents", undef);
|
||||||
|
DoTrigger($tgt->{NAME}, "$tp:$val") if($re && $tp =~ m/$re/);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
######################################
|
######################################
|
||||||
@ -382,7 +391,7 @@ sub
|
|||||||
MQTT2_SERVER_Write($$$)
|
MQTT2_SERVER_Write($$$)
|
||||||
{
|
{
|
||||||
my ($hash,$topic,$msg) = @_;
|
my ($hash,$topic,$msg) = @_;
|
||||||
MQTT2_SERVER_doPublish($hash, $topic, $msg);
|
MQTT2_SERVER_doPublish($hash, $hash, $topic, $msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub
|
sub
|
||||||
@ -490,6 +499,18 @@ MQTT2_SERVER_getStr($$)
|
|||||||
Should only be used, if there is no MQTT2_DEVICE to process the topic.
|
Should only be used, if there is no MQTT2_DEVICE to process the topic.
|
||||||
</li><br>
|
</li><br>
|
||||||
|
|
||||||
|
<a name="keepaliveFactor"></a>
|
||||||
|
<li>keepaliveFactor<br>
|
||||||
|
the oasis spec requires a disconnect, if after 1.5 times the client
|
||||||
|
supplied keepalive no data or PINGREQ is sent. With this attribute you
|
||||||
|
can modify this factor, 0 disables the check.
|
||||||
|
Notes:
|
||||||
|
<ul>
|
||||||
|
<li>dont complain if you set this attribute to less or equal to 1.</li>
|
||||||
|
<li>MQTT2_SERVER checks the keepalive only every 10 second.</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
|
||||||
<a name="SSL"></a>
|
<a name="SSL"></a>
|
||||||
<li>SSL<br>
|
<li>SSL<br>
|
||||||
Enable SSL (i.e. TLS)
|
Enable SSL (i.e. TLS)
|
||||||
|
@ -2,8 +2,6 @@
|
|||||||
# $Id$
|
# $Id$
|
||||||
package main;
|
package main;
|
||||||
|
|
||||||
# TODO: autocreate
|
|
||||||
|
|
||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
use SetExtensions;
|
use SetExtensions;
|
||||||
@ -46,6 +44,7 @@ MQTT2_DEVICE_Define($$)
|
|||||||
my @a = split("[ \t][ \t]*", $def);
|
my @a = split("[ \t][ \t]*", $def);
|
||||||
my $name = shift @a;
|
my $name = shift @a;
|
||||||
my $type = shift @a; # always MQTT2_DEVICE
|
my $type = shift @a; # always MQTT2_DEVICE
|
||||||
|
shift(@a) if(@a && $a[0] eq "autocreated");
|
||||||
|
|
||||||
return "wrong syntax for $name: define <name> MQTT2_DEVICE" if(int(@a));
|
return "wrong syntax for $name: define <name> MQTT2_DEVICE" if(int(@a));
|
||||||
|
|
||||||
@ -72,10 +71,11 @@ MQTT2_DEVICE_Parse($$)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
my ($topic, $value) = split(":", $msg, 2);
|
my ($cid, $topic, $value) = split(":", $msg, 3);
|
||||||
my $dp = $modules{MQTT2_DEVICE}{defptr};
|
my $dp = $modules{MQTT2_DEVICE}{defptr};
|
||||||
foreach my $re (keys %{$dp}) {
|
foreach my $re (keys %{$dp}) {
|
||||||
next if($msg !~ m/^$re$/s);
|
next if(!("$topic:$value" =~ m/^$re$/s ||
|
||||||
|
"$cid:$topic:$value" =~ m/^$re$/s));
|
||||||
foreach my $dev (keys %{$dp->{$re}}) {
|
foreach my $dev (keys %{$dp->{$re}}) {
|
||||||
next if(IsDisabled($dev));
|
next if(IsDisabled($dev));
|
||||||
my @retData;
|
my @retData;
|
||||||
@ -86,6 +86,7 @@ MQTT2_DEVICE_Parse($$)
|
|||||||
if($code =~ m/^{.*}$/s) {
|
if($code =~ m/^{.*}$/s) {
|
||||||
$code = EvalSpecials($code, ("%TOPIC"=>$topic, "%EVENT"=>$value));
|
$code = EvalSpecials($code, ("%TOPIC"=>$topic, "%EVENT"=>$value));
|
||||||
my $ret = AnalyzePerlCommand(undef, $code);
|
my $ret = AnalyzePerlCommand(undef, $code);
|
||||||
|
if($ret && ref $ret eq "HASH") {
|
||||||
readingsBeginUpdate($hash);
|
readingsBeginUpdate($hash);
|
||||||
foreach my $k (keys %{$ret}) {
|
foreach my $k (keys %{$ret}) {
|
||||||
readingsBulkUpdate($hash, $k, $ret->{$k});
|
readingsBulkUpdate($hash, $k, $ret->{$k});
|
||||||
@ -93,6 +94,7 @@ MQTT2_DEVICE_Parse($$)
|
|||||||
checkForGet($hash, $k, $ret->{$k});
|
checkForGet($hash, $k, $ret->{$k});
|
||||||
}
|
}
|
||||||
readingsEndUpdate($hash, 1);
|
readingsEndUpdate($hash, 1);
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
readingsSingleUpdate($hash, $code, $value, 1);
|
readingsSingleUpdate($hash, $code, $value, 1);
|
||||||
@ -104,6 +106,27 @@ MQTT2_DEVICE_Parse($$)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# autocreate, init readingList if message is a json string
|
||||||
|
# deactivated, as there are a lot of messages to be catched
|
||||||
|
# if(!@ret) {
|
||||||
|
# my $nn = "MQTT2_$cid";
|
||||||
|
# if(!$defs{$nn} && $cid !~ m/mosqpub.*/) {
|
||||||
|
# PrioQueue_add(sub{
|
||||||
|
# return if(!$defs{$nn});
|
||||||
|
# if($value =~ m/^{.*}$/) {
|
||||||
|
# my %ret = MQTT2_JSON($msg);
|
||||||
|
# if(keys %ret) {
|
||||||
|
# CommandAttr(undef,
|
||||||
|
# "$nn readingList $cid:$topic:.* { MQTT2_JSON(\$EVENT) }");
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
# $defs{$nn}{autocreated_on} = $msg;
|
||||||
|
# }, undef);
|
||||||
|
# return "UNDEFINED $nn MQTT2_DEVICE autocreated"
|
||||||
|
# }
|
||||||
|
# return "";
|
||||||
|
# }
|
||||||
|
|
||||||
return @ret;
|
return @ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -274,6 +297,7 @@ MQTT2_DEVICE_Attr($$)
|
|||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return "$dev attr $attrName: more parameters needed" if(!$param); #90145
|
||||||
foreach my $el (split("\n", $param)) {
|
foreach my $el (split("\n", $param)) {
|
||||||
my ($par1, $par2) = split(" ", $el, 2);
|
my ($par1, $par2) = split(" ", $el, 2);
|
||||||
next if(!$par1);
|
next if(!$par1);
|
||||||
@ -394,11 +418,13 @@ MQTT2_DEVICE_Undef($$)
|
|||||||
<a name="readingList"></a>
|
<a name="readingList"></a>
|
||||||
<li>readingList <regexp> [readingName|perl-Expression] ...
|
<li>readingList <regexp> [readingName|perl-Expression] ...
|
||||||
<br>
|
<br>
|
||||||
If the regexp matches topic:message either set readingName to
|
If the regexp matches topic:message or cid:topic:message either set
|
||||||
the published message, or evaluate the perl expression, which has to
|
readingName to the published message, or evaluate the perl expression,
|
||||||
return a hash consisting of readingName=>readingValue entries.
|
which has to return a hash consisting of readingName=>readingValue
|
||||||
|
entries.
|
||||||
You can define multiple such tuples, separated by newline, the newline
|
You can define multiple such tuples, separated by newline, the newline
|
||||||
does not have to be entered in the FHEMWEB frontend.<br>
|
does not have to be entered in the FHEMWEB frontend. cid is the client-id
|
||||||
|
of the sending device.<br>
|
||||||
Example:<br>
|
Example:<br>
|
||||||
<code>
|
<code>
|
||||||
attr dev readingList\<br>
|
attr dev readingList\<br>
|
||||||
|
@ -59,9 +59,10 @@ FHEM2FHEM_Define($$)
|
|||||||
my $iodev = $defs{$rdev};
|
my $iodev = $defs{$rdev};
|
||||||
return "Undefined local device $rdev" if(!$iodev);
|
return "Undefined local device $rdev" if(!$iodev);
|
||||||
$hash->{rawDevice} = $rdev;
|
$hash->{rawDevice} = $rdev;
|
||||||
$hash->{Clients} = $iodev->{Clients};
|
|
||||||
$hash->{Clients} = $modules{$iodev->{TYPE}}{Clients}
|
my $iomod = $modules{$iodev->{TYPE}};
|
||||||
if(!$hash->{Clients});
|
$hash->{Clients} = $iodev->{Clients} ? $iodev->{Clients} :$iomod->{Clients};
|
||||||
|
$hash->{MatchList} = $iomod->{MatchList} if($iomod->{MatchList});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user