00_MQTT2_SERVER.pm: $DEVICETOPIC and autocreate (Forum #90145)

git-svn-id: https://svn.fhem.de/fhem/trunk/fhem@17152 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
rudolfkoenig 2018-08-16 22:31:28 +00:00
parent a9562401dc
commit 8fc1796d44
2 changed files with 94 additions and 42 deletions

View File

@ -37,6 +37,7 @@ MQTT2_SERVER_Initialize($)
my @attrList = qw( my @attrList = qw(
disable:0,1 disable:0,1
disabledForIntervals disabledForIntervals
autocreate
rawEvents:0,1 rawEvents:0,1
SSL:0,1 SSL:0,1
); );
@ -364,7 +365,8 @@ MQTT2_SERVER_doPublish($$$$;$)
if($src->{cid}) { # "real" MQTT client if($src->{cid}) { # "real" MQTT client
my $cid = $src->{cid}; my $cid = $src->{cid};
$cid =~ s,[^a-z0-9._],_,gi; $cid =~ s,[^a-z0-9._],_,gi;
Dispatch($tgt, "$cid:$tp:$val", undef, 1); my $ac = AttrVal($tgt->{NAME}, "autocreate", undef) ? "autocreate:":"";
Dispatch($tgt, "$ac$cid:$tp:$val", undef, !$ac);
my $re = AttrVal($tgt->{NAME}, "rawEvents", undef); my $re = AttrVal($tgt->{NAME}, "rawEvents", undef);
DoTrigger($tgt->{NAME}, "$tp:$val") if($re && $tp =~ m/$re/); DoTrigger($tgt->{NAME}, "$tp:$val") if($re && $tp =~ m/$re/);
} }
@ -412,8 +414,10 @@ MQTT2_SERVER_calcRemainingLength($)
my ($l) = @_; my ($l) = @_;
my @r; my @r;
while($l > 0) { while($l > 0) {
unshift(@r, $l % 128); my $eb = $l % 128;
$l = int($l/128); $l = int($l/128);
$eb += 128 if($l);
push(@r, $eb);
} }
return pack("C*", @r); return pack("C*", @r);
} }
@ -527,6 +531,13 @@ MQTT2_SERVER_getStr($$)
<li>SSL<br> <li>SSL<br>
Enable SSL (i.e. TLS) Enable SSL (i.e. TLS)
</li><br> </li><br>
<a name="autocreate"></a>
<li>autocreate<br>
If set, MQTT2_DEVICES will be automatically created upon receiving an
unknown message.
</li><br>
</ul> </ul>
</ul> </ul>
=end html =end html

View File

@ -22,6 +22,7 @@ MQTT2_DEVICE_Initialize($)
no warnings 'qw'; no warnings 'qw';
my @attrList = qw( my @attrList = qw(
IODev IODev
devicetopic
disable:0,1 disable:0,1
disabledForIntervals disabledForIntervals
readingList:textField-long readingList:textField-long
@ -30,7 +31,8 @@ MQTT2_DEVICE_Initialize($)
); );
use warnings 'qw'; use warnings 'qw';
$hash->{AttrList} = join(" ", @attrList)." ".$readingFnAttributes; $hash->{AttrList} = join(" ", @attrList)." ".$readingFnAttributes;
$modules{MQTT2_DEVICE}{defptr} = (); my %h = ( re=>{}, cid=>{});
$modules{MQTT2_DEVICE}{defptr} = \%h;
} }
@ -42,9 +44,12 @@ 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"); $hash->{CID} = shift(@a) if(@a);
return "wrong syntax for $name: define <name> MQTT2_DEVICE" if(int(@a)); return "wrong syntax for $name: define <name> MQTT2_DEVICE [clientid]"
if(int(@a));
$hash->{DEVICETOPIC} = $name;
$modules{MQTT2_DEVICE}{defptr}{cid}{$hash->{CID}} = $hash if($hash->{CID});
AssignIoPort($hash); AssignIoPort($hash);
return undef; return undef;
@ -56,7 +61,7 @@ MQTT2_DEVICE_Parse($$)
{ {
my ($iodev, $msg) = @_; my ($iodev, $msg) = @_;
my $ioname = $iodev->{NAME}; my $ioname = $iodev->{NAME};
my @ret; my %fnd;
sub sub
checkForGet($$$) checkForGet($$$)
@ -69,21 +74,35 @@ MQTT2_DEVICE_Parse($$)
} }
} }
my $autocreate;
if($msg =~ m/^autocreate:(.*)/) {
$msg = $1;
$autocreate = 1;
}
my ($cid, $topic, $value) = split(":", $msg, 3); my ($cid, $topic, $value) = split(":", $msg, 3);
my $dp = $modules{MQTT2_DEVICE}{defptr}; my $dp = $modules{MQTT2_DEVICE}{defptr}{re};
foreach my $re (keys %{$dp}) { foreach my $re (keys %{$dp}) {
next if(!("$topic:$value" =~ m/^$re$/s || my $reAll = $re;
"$cid:$topic:$value" =~ m/^$re$/s)); $reAll =~ s/\$DEVICETOPIC/\.\*/g;
next if(!("$topic:$value" =~ m/^$reAll$/s ||
"$cid:$topic:$value" =~ m/^$reAll$/s));
foreach my $dev (keys %{$dp->{$re}}) { foreach my $dev (keys %{$dp->{$re}}) {
next if(IsDisabled($dev)); next if(IsDisabled($dev));
my $hash = $defs{$dev};
my $reRepl = $re;
$reRepl =~ s/\$DEVICETOPIC/$hash->{DEVICETOPIC}/g;
next if(!("$topic:$value" =~ m/^$reRepl$/s ||
"$cid:$topic:$value" =~ m/^$reRepl$/s));
my @retData; my @retData;
my $code = $dp->{$re}{$dev}; my $code = $dp->{$re}{$dev};
Log3 $dev, 4, "MQTT2_DEVICE_Parse: $dev $topic => $code"; Log3 $dev, 4, "MQTT2_DEVICE_Parse: $dev $topic => $code";
my $hash = $defs{$dev};
if($code =~ m/^{.*}$/s) { if($code =~ m/^{.*}$/s) {
$code = EvalSpecials($code, $code = EvalSpecials($code, ("%TOPIC"=>$topic, "%EVENT"=>$value,
("%TOPIC"=>$topic, "%EVENT"=>$value, "%NAME"=>$hash->{NAME})); "%DEVICETOPIC"=>$hash->{DEVICETOPIC}, "%NAME"=>$hash->{NAME}));
my $ret = AnalyzePerlCommand(undef, $code); my $ret = AnalyzePerlCommand(undef, $code);
if($ret && ref $ret eq "HASH") { if($ret && ref $ret eq "HASH") {
readingsBeginUpdate($hash); readingsBeginUpdate($hash);
@ -101,32 +120,35 @@ MQTT2_DEVICE_Parse($$)
checkForGet($hash, $code, $value); checkForGet($hash, $code, $value);
} }
push @ret, $dev; $fnd{$dev} = 1;
} }
} }
# autocreate, init readingList if message is a json string # autocreate and expand readingList
# deactivated, as there are a lot of messages to be catched if($autocreate && !%fnd) {
# if(!@ret) { return "" if($cid =~ m/mosqpub.*/);
# my $nn = "MQTT2_$cid"; my $cidHash = $modules{MQTT2_DEVICE}{defptr}{cid}{$cid};
# if(!$defs{$nn} && $cid !~ m/mosqpub.*/) { my $nn = $cidHash ? $cidHash->{NAME} : "MQTT2_$cid";
# PrioQueue_add(sub{ PrioQueue_add(sub{
# return if(!$defs{$nn}); return if(!$defs{$nn});
# if($value =~ m/^{.*}$/) { my $add;
# my %ret = json2nameValue($msg); if($value =~ m/^{.*}$/) {
# if(keys %ret) { my $ret = json2nameValue($value);
# CommandAttr(undef, $add = "{ json2nameValue(\$EVENT) }" if(keys %{$ret});
# "$nn readingList $cid:$topic:.* { json2nameValue(\$EVENT) }"); }
# } if(!$add) {
# } $topic =~ m,[^/]*/(.*),;
# $defs{$nn}{autocreated_on} = $msg; $add = ($1 ? $1 : $topic);
# }, undef); }
# return "UNDEFINED $nn MQTT2_DEVICE autocreated" my $rl = AttrVal($nn, "readingList", "");
# } $rl .= "\n" if($rl);
# return ""; CommandAttr(undef, "$nn readingList $rl$cid:$topic:.* $add");
# } }, undef);
return "UNDEFINED $nn MQTT2_DEVICE $cid" if(!$cidHash);
return "";
}
return @ret; return keys %fnd;
} }
sub sub
@ -170,6 +192,7 @@ MQTT2_DEVICE_Get($@)
$cmd .= " ".join(" ",@a) if(@a); $cmd .= " ".join(" ",@a) if(@a);
} }
$cmd =~ s/\$DEVICETOPIC/$hash->{DEVICETOPIC}/g;
IOWrite($hash, split(" ",$cmd,2)); IOWrite($hash, split(" ",$cmd,2));
return undef; return undef;
} }
@ -198,6 +221,8 @@ MQTT2_DEVICE_Set($@)
shift @a; shift @a;
$cmd .= " ".join(" ",@a) if(@a); $cmd .= " ".join(" ",@a) if(@a);
} }
$cmd =~ s/\$DEVICETOPIC/$hash->{DEVICETOPIC}/g;
IOWrite($hash, split(" ",$cmd,2)); IOWrite($hash, split(" ",$cmd,2));
return undef; return undef;
} }
@ -207,6 +232,12 @@ sub
MQTT2_DEVICE_Attr($$) MQTT2_DEVICE_Attr($$)
{ {
my ($type, $dev, $attrName, $param) = @_; my ($type, $dev, $attrName, $param) = @_;
my $hash = $defs{$dev};
if($attrName eq "devicetopic") {
$hash->{DEVICETOPIC} = ($type eq "del" ? $hash->{NAME} : $param);
return undef;
}
if($attrName =~ m/(.*)List/) { if($attrName =~ m/(.*)List/) {
my $atype = $1; my $atype = $1;
@ -217,6 +248,7 @@ MQTT2_DEVICE_Attr($$)
} }
return "$dev attr $attrName: more parameters needed" if(!$param); #90145 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);
@ -226,8 +258,9 @@ MQTT2_DEVICE_Attr($$)
if($atype eq "reading") { if($atype eq "reading") {
if($par2 =~ m/^{.*}$/) { if($par2 =~ m/^{.*}$/) {
my $ret = perlSyntaxCheck($par2, my $ret = perlSyntaxCheck($par2,
("%TOPIC"=>1, "%EVENT"=>"0 1 2 3 4 5 6 7 8 9", "%NAME"=>$dev)); ("%TOPIC"=>1, "%EVENT"=>"0 1 2 3 4 5 6 7 8 9",
"%NAME"=>$dev, "%DEVICETOPIC"=>$hash->{DEVICETOPIC}));
return $ret if($ret); return $ret if($ret);
} else { } else {
return "unsupported character in readingname $par2" return "unsupported character in readingname $par2"
@ -249,7 +282,7 @@ sub
MQTT2_DEVICE_delReading($) MQTT2_DEVICE_delReading($)
{ {
my ($name) = @_; my ($name) = @_;
my $dp = $modules{MQTT2_DEVICE}{defptr}; my $dp = $modules{MQTT2_DEVICE}{defptr}{re};
foreach my $re (keys %{$dp}) { foreach my $re (keys %{$dp}) {
if($dp->{$re}{$name}) { if($dp->{$re}{$name}) {
delete($dp->{$re}{$name}); delete($dp->{$re}{$name});
@ -264,7 +297,7 @@ MQTT2_DEVICE_addReading($$)
my ($name, $param) = @_; my ($name, $param) = @_;
foreach my $line (split("\n", $param)) { foreach my $line (split("\n", $param)) {
my ($re,$code) = split(" ", $line,2); my ($re,$code) = split(" ", $line,2);
$modules{MQTT2_DEVICE}{defptr}{$re}{$name} = $code; $modules{MQTT2_DEVICE}{defptr}{re}{$re}{$name} = $code;
} }
} }
@ -285,6 +318,7 @@ MQTT2_DEVICE_Undef($$)
{ {
my ($hash, $arg) = @_; my ($hash, $arg) = @_;
MQTT2_DEVICE_delReading($arg); MQTT2_DEVICE_delReading($arg);
delete $modules{MQTT2_DEVICE}{defptr}{cid}{$hash->{CID}} if($hash->{CID});
return undef; return undef;
} }
@ -331,6 +365,13 @@ MQTT2_DEVICE_Undef($$)
<b>Attributes</b> <b>Attributes</b>
<ul> <ul>
<a name="devicetopic"></a>
<li>devicetopic value<br>
replace $DEVICETOPIC in the topic part of readingList, setList and
getList with value. if not set, $DEVICETOPIC will be replaced with the
name of the device.
</li><br>
<li><a href="#disable">disable</a><br> <li><a href="#disable">disable</a><br>
<a href="#disabledForIntervals">disabledForIntervals</a></li><br> <a href="#disabledForIntervals">disabledForIntervals</a></li><br>
@ -352,10 +393,10 @@ MQTT2_DEVICE_Undef($$)
</code><br> </code><br>
Notes: Notes:
<ul> <ul>
<li>in the perl expression the variables $TOPIC and $EVENT are <li>in the perl expression the variables $TOPIC, $NAME, $DEVICETOPIC
available (the letter containing the whole message), as well as and $EVENT are available (the letter containing the whole message),
$EVTPART0, $EVTPART1, ... each containing a single word of the as well as $EVTPART0, $EVTPART1, ... each containing a single word of
message.</li> the message.</li>
<li>the helper function json2nameValue($EVENT) can be used to parse a <li>the helper function json2nameValue($EVENT) can be used to parse a
json encoded value. Importing all values from a Sonoff device with a json encoded value. Importing all values from a Sonoff device with a
Tasmota firmware can be done with: Tasmota firmware can be done with: