From 4e3f65a1ca88b9178fe6e0a4ddd827650be8d78d Mon Sep 17 00:00:00 2001 From: rudolfkoenig <> Date: Thu, 16 Aug 2018 22:31:28 +0000 Subject: [PATCH] 00_MQTT2_SERVER.pm: $DEVICETOPIC and autocreate (Forum #90145) git-svn-id: https://svn.fhem.de/fhem/trunk@17152 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/FHEM/00_MQTT2_SERVER.pm | 15 ++++- fhem/FHEM/10_MQTT2_DEVICE.pm | 121 +++++++++++++++++++++++------------ 2 files changed, 94 insertions(+), 42 deletions(-) diff --git a/fhem/FHEM/00_MQTT2_SERVER.pm b/fhem/FHEM/00_MQTT2_SERVER.pm index 01c55dbc2..243918a73 100644 --- a/fhem/FHEM/00_MQTT2_SERVER.pm +++ b/fhem/FHEM/00_MQTT2_SERVER.pm @@ -37,6 +37,7 @@ MQTT2_SERVER_Initialize($) my @attrList = qw( disable:0,1 disabledForIntervals + autocreate rawEvents:0,1 SSL:0,1 ); @@ -364,7 +365,8 @@ MQTT2_SERVER_doPublish($$$$;$) if($src->{cid}) { # "real" MQTT client my $cid = $src->{cid}; $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); DoTrigger($tgt->{NAME}, "$tp:$val") if($re && $tp =~ m/$re/); } @@ -412,8 +414,10 @@ MQTT2_SERVER_calcRemainingLength($) my ($l) = @_; my @r; while($l > 0) { - unshift(@r, $l % 128); + my $eb = $l % 128; $l = int($l/128); + $eb += 128 if($l); + push(@r, $eb); } return pack("C*", @r); } @@ -527,6 +531,13 @@ MQTT2_SERVER_getStr($$)
  • SSL
    Enable SSL (i.e. TLS)

  • + + +
  • autocreate
    + If set, MQTT2_DEVICES will be automatically created upon receiving an + unknown message. +

  • + =end html diff --git a/fhem/FHEM/10_MQTT2_DEVICE.pm b/fhem/FHEM/10_MQTT2_DEVICE.pm index 85e9d646d..53bbd66be 100644 --- a/fhem/FHEM/10_MQTT2_DEVICE.pm +++ b/fhem/FHEM/10_MQTT2_DEVICE.pm @@ -22,6 +22,7 @@ MQTT2_DEVICE_Initialize($) no warnings 'qw'; my @attrList = qw( IODev + devicetopic disable:0,1 disabledForIntervals readingList:textField-long @@ -30,7 +31,8 @@ MQTT2_DEVICE_Initialize($) ); use warnings 'qw'; $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 $name = shift @a; 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 MQTT2_DEVICE" if(int(@a)); + return "wrong syntax for $name: define MQTT2_DEVICE [clientid]" + if(int(@a)); + $hash->{DEVICETOPIC} = $name; + $modules{MQTT2_DEVICE}{defptr}{cid}{$hash->{CID}} = $hash if($hash->{CID}); AssignIoPort($hash); return undef; @@ -56,7 +61,7 @@ MQTT2_DEVICE_Parse($$) { my ($iodev, $msg) = @_; my $ioname = $iodev->{NAME}; - my @ret; + my %fnd; sub 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 $dp = $modules{MQTT2_DEVICE}{defptr}; + my $dp = $modules{MQTT2_DEVICE}{defptr}{re}; foreach my $re (keys %{$dp}) { - next if(!("$topic:$value" =~ m/^$re$/s || - "$cid:$topic:$value" =~ m/^$re$/s)); + my $reAll = $re; + $reAll =~ s/\$DEVICETOPIC/\.\*/g; + + next if(!("$topic:$value" =~ m/^$reAll$/s || + "$cid:$topic:$value" =~ m/^$reAll$/s)); foreach my $dev (keys %{$dp->{$re}}) { 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 $code = $dp->{$re}{$dev}; Log3 $dev, 4, "MQTT2_DEVICE_Parse: $dev $topic => $code"; - my $hash = $defs{$dev}; if($code =~ m/^{.*}$/s) { - $code = EvalSpecials($code, - ("%TOPIC"=>$topic, "%EVENT"=>$value, "%NAME"=>$hash->{NAME})); + $code = EvalSpecials($code, ("%TOPIC"=>$topic, "%EVENT"=>$value, + "%DEVICETOPIC"=>$hash->{DEVICETOPIC}, "%NAME"=>$hash->{NAME})); my $ret = AnalyzePerlCommand(undef, $code); if($ret && ref $ret eq "HASH") { readingsBeginUpdate($hash); @@ -101,32 +120,35 @@ MQTT2_DEVICE_Parse($$) checkForGet($hash, $code, $value); } - push @ret, $dev; + $fnd{$dev} = 1; } } - # 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 = json2nameValue($msg); -# if(keys %ret) { -# CommandAttr(undef, -# "$nn readingList $cid:$topic:.* { json2nameValue(\$EVENT) }"); -# } -# } -# $defs{$nn}{autocreated_on} = $msg; -# }, undef); -# return "UNDEFINED $nn MQTT2_DEVICE autocreated" -# } -# return ""; -# } + # autocreate and expand readingList + if($autocreate && !%fnd) { + return "" if($cid =~ m/mosqpub.*/); + my $cidHash = $modules{MQTT2_DEVICE}{defptr}{cid}{$cid}; + my $nn = $cidHash ? $cidHash->{NAME} : "MQTT2_$cid"; + PrioQueue_add(sub{ + return if(!$defs{$nn}); + my $add; + if($value =~ m/^{.*}$/) { + my $ret = json2nameValue($value); + $add = "{ json2nameValue(\$EVENT) }" if(keys %{$ret}); + } + if(!$add) { + $topic =~ m,[^/]*/(.*),; + $add = ($1 ? $1 : $topic); + } + my $rl = AttrVal($nn, "readingList", ""); + $rl .= "\n" if($rl); + CommandAttr(undef, "$nn readingList $rl$cid:$topic:.* $add"); + }, undef); + return "UNDEFINED $nn MQTT2_DEVICE $cid" if(!$cidHash); + return ""; + } - return @ret; + return keys %fnd; } sub @@ -170,6 +192,7 @@ MQTT2_DEVICE_Get($@) $cmd .= " ".join(" ",@a) if(@a); } + $cmd =~ s/\$DEVICETOPIC/$hash->{DEVICETOPIC}/g; IOWrite($hash, split(" ",$cmd,2)); return undef; } @@ -198,6 +221,8 @@ MQTT2_DEVICE_Set($@) shift @a; $cmd .= " ".join(" ",@a) if(@a); } + + $cmd =~ s/\$DEVICETOPIC/$hash->{DEVICETOPIC}/g; IOWrite($hash, split(" ",$cmd,2)); return undef; } @@ -207,6 +232,12 @@ sub MQTT2_DEVICE_Attr($$) { 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/) { my $atype = $1; @@ -217,6 +248,7 @@ MQTT2_DEVICE_Attr($$) } return "$dev attr $attrName: more parameters needed" if(!$param); #90145 + foreach my $el (split("\n", $param)) { my ($par1, $par2) = split(" ", $el, 2); next if(!$par1); @@ -226,8 +258,9 @@ MQTT2_DEVICE_Attr($$) if($atype eq "reading") { if($par2 =~ m/^{.*}$/) { - my $ret = perlSyntaxCheck($par2, - ("%TOPIC"=>1, "%EVENT"=>"0 1 2 3 4 5 6 7 8 9", "%NAME"=>$dev)); + my $ret = perlSyntaxCheck($par2, + ("%TOPIC"=>1, "%EVENT"=>"0 1 2 3 4 5 6 7 8 9", + "%NAME"=>$dev, "%DEVICETOPIC"=>$hash->{DEVICETOPIC})); return $ret if($ret); } else { return "unsupported character in readingname $par2" @@ -249,7 +282,7 @@ sub MQTT2_DEVICE_delReading($) { my ($name) = @_; - my $dp = $modules{MQTT2_DEVICE}{defptr}; + my $dp = $modules{MQTT2_DEVICE}{defptr}{re}; foreach my $re (keys %{$dp}) { if($dp->{$re}{$name}) { delete($dp->{$re}{$name}); @@ -264,7 +297,7 @@ MQTT2_DEVICE_addReading($$) my ($name, $param) = @_; foreach my $line (split("\n", $param)) { 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) = @_; MQTT2_DEVICE_delReading($arg); + delete $modules{MQTT2_DEVICE}{defptr}{cid}{$hash->{CID}} if($hash->{CID}); return undef; } @@ -331,6 +365,13 @@ MQTT2_DEVICE_Undef($$) Attributes