2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-03-04 05:16:45 +00:00

bugfix: *:retain in mqttPublish without function (qos too)

improvement: $uid variable in expressions (mqttPublish)
bugfix: replace vars in _evalValue2
feature: supports multiple topics pro reading (mqttPublish)

git-svn-id: https://svn.fhem.de/fhem/trunk@19477 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
hexenmeister 2019-05-28 16:27:14 +00:00
parent 9726ddaf30
commit e7765ddb60

View File

@ -30,6 +30,30 @@
# #
# CHANGE LOG # CHANGE LOG
# #
# 27.05.2019 1.2.1
# bugfix : fixed *:retain in mqttPublish ohne Funktion (auch qos)
# jetzt werden *:xxx Angaben aus mqttPublish und auch
# mqttDefaultPublis (in der GenericBridge) ausgewertet
#
# 26.05.2019 1.2.0
# improvement: Unterstuetzung fuer Variable $uid in expression-Anweisungen
# (mqttPublish)
#
# 24.05.2019 1.1.9
# improvement: Aufnahme Methoden in Import: toJSON, TimeNow
# bugfix : Ersetzen von (Pseudo)Variablen in _evalValue2 ohne Auswirkung
# (falsche Variable verwendet)
#
# 23.05.2019 1.1.8
# feature : Unterstuetzung von mehrfachen Topics fuer eine und dieselbe
# reading (mqttPublish).
# Die Definitionen muessen einen durch '!' getrennten
# einmaligen Postfix erhalten: reading!postfix:topic=test/test
#
# 21.05.2019 1.1.7
# cleanup : Unnoetige 'stopic'-code bei 'publish'
# ('stopic' ist nur was fuer subscribe)
#
# 07.03.2019 1.1.7 # 07.03.2019 1.1.7
# fix : Anpassung fuer MQTT2* # fix : Anpassung fuer MQTT2*
# (https://forum.fhem.de/index.php?topic=98249.new#new) # (https://forum.fhem.de/index.php?topic=98249.new#new)
@ -272,6 +296,7 @@
# #
# [done] # [done]
# - global base (sollte in $base verwendet werden koennen) # - global base (sollte in $base verwendet werden koennen)
# - Support for MQTT2_SERVER
# #
# [i like it] # [i like it]
# - resend / interval, Warteschlange # - resend / interval, Warteschlange
@ -282,7 +307,6 @@
# - QOS for subscribe (fertig?), defaults(qos, fertig?), alias mapping # - QOS for subscribe (fertig?), defaults(qos, fertig?), alias mapping
# - global subscribe # - global subscribe
# - global excludes # - global excludes
# - Support for MQTT2_SERVER
# - commands per mqtt fuer die Bridge: Liste der Geraete, Infos dazu etc. # - commands per mqtt fuer die Bridge: Liste der Geraete, Infos dazu etc.
# - mqttOptions (zuschaltbare optionen im Form eines Perl-Routine) (json, ttl) # - mqttOptions (zuschaltbare optionen im Form eines Perl-Routine) (json, ttl)
# #
@ -299,7 +323,7 @@
# Bugs: # Bugs:
# #
# [done] # [done]
# - Zeilenumbruch wird nicht als Trennen zw. topic-Definitionen erkannt, nur mit einem zusaetzlichen Leerzeichen # - Zeilenumbruch wird nicht als Trenner zw. topic-Definitionen erkannt, nur mit einem zusaetzlichen Leerzeichen
# - Keys enthalten Zeilenumbrueche (topic, expression) => Namen der Readings etc. trimmen bzw. Parser anpassen # - Keys enthalten Zeilenumbrueche (topic, expression) => Namen der Readings etc. trimmen bzw. Parser anpassen
# - Variablen in Expression funktionieren nicht, wenn Topic kein perl-Expression ist # - Variablen in Expression funktionieren nicht, wenn Topic kein perl-Expression ist
# - atopic wird in devInfo nicht dargestellt # - atopic wird in devInfo nicht dargestellt
@ -319,7 +343,7 @@ use warnings;
#my $DEBUG = 1; #my $DEBUG = 1;
my $cvsid = '$Id$'; my $cvsid = '$Id$';
my $VERSION = "version 1.1.7 by hexenmeister\n$cvsid"; my $VERSION = "version 1.2.1 by hexenmeister\n$cvsid";
my %sets = ( my %sets = (
); );
@ -434,6 +458,8 @@ BEGIN {
InternalTimer InternalTimer
RemoveInternalTimer RemoveInternalTimer
json2nameValue json2nameValue
toJSON
TimeNow
IOWrite IOWrite
CTRL_ATTR_NAME_DEFAULTS CTRL_ATTR_NAME_DEFAULTS
CTRL_ATTR_NAME_ALIAS CTRL_ATTR_NAME_ALIAS
@ -489,7 +515,7 @@ sub defineGlobalTypeExclude($;$);
sub defineGlobalDevExclude($;$); sub defineGlobalDevExclude($;$);
sub defineDefaultGlobalExclude($); sub defineDefaultGlobalExclude($);
sub isTypeDevReadingExcluded($$$$$); sub isTypeDevReadingExcluded($$$$$);
sub getDevicePublishRecIntern($$$$$); sub getDevicePublishRecIntern($$$$$$$);
sub getDevicePublishRec($$$); sub getDevicePublishRec($$$);
sub isConnected($); sub isConnected($);
sub ioDevConnect($); sub ioDevConnect($);
@ -957,6 +983,8 @@ sub CreateSingleDeviceTableAttrAlias($$$$) {
# Params: Bridge-Hash, Dev-Name (im Map, ist auch = DevName), # Params: Bridge-Hash, Dev-Name (im Map, ist auch = DevName),
# Internes Map mit allen Definitionen fuer alle Gerate, # Internes Map mit allen Definitionen fuer alle Gerate,
# Attribute-Value zum Parsen # Attribute-Value zum Parsen
# NB: stopic gibt es beim 'publish' nicht
# ?: internal-topic? - keine Verwendung bis jetzt
sub CreateSingleDeviceTableAttrPublish($$$$) { sub CreateSingleDeviceTableAttrPublish($$$$) {
my($hash, $dev, $map, $attrVal) = @_; my($hash, $dev, $map, $attrVal) = @_;
#Log3($hash->{NAME},1,"MQTT_GENERIC_BRIDGE:DEBUG:> [$hash->{NAME}] CreateSingleDeviceTableAttrPublish: $dev, $attrVal, ".Dumper($map)); #Log3($hash->{NAME},1,"MQTT_GENERIC_BRIDGE:DEBUG:> [$hash->{NAME}] CreateSingleDeviceTableAttrPublish: $dev, $attrVal, ".Dumper($map));
@ -981,6 +1009,7 @@ sub CreateSingleDeviceTableAttrPublish($$$$) {
if(!defined($ident) or !defined($name)) { next; } if(!defined($ident) or !defined($name)) { next; }
if(($ident eq 'topic') or ($ident eq 'readings-topic') or if(($ident eq 'topic') or ($ident eq 'readings-topic') or
($ident eq 'atopic') or ($ident eq 'attr-topic') or ($ident eq 'atopic') or ($ident eq 'attr-topic') or
#($ident eq 'stopic') or ($ident eq 'set-topic') or # stopic nur bei subscribe
($ident eq 'qos') or ($ident eq 'retain') or ($ident eq 'qos') or ($ident eq 'retain') or
($ident eq 'expression') or ($ident eq 'expression') or
($ident eq 'resendOnConnect') or ($ident eq 'resendOnConnect') or
@ -992,7 +1021,7 @@ sub CreateSingleDeviceTableAttrPublish($$$$) {
$map->{$dev}->{':publish'}->{$namePart}->{$ident}=$val; $map->{$dev}->{':publish'}->{$namePart}->{$ident}=$val;
$map->{$dev}->{':publish'}->{$namePart}->{'mode'} = 'R' if (($ident eq 'topic') or ($ident eq 'readings-topic')); $map->{$dev}->{':publish'}->{$namePart}->{'mode'} = 'R' if (($ident eq 'topic') or ($ident eq 'readings-topic'));
$map->{$dev}->{':publish'}->{$namePart}->{'mode'} = 'S' if (($ident eq 'stopic') or ($ident eq 'set-topic')); #$map->{$dev}->{':publish'}->{$namePart}->{'mode'} = 'S' if (($ident eq 'stopic') or ($ident eq 'set-topic'));
$map->{$dev}->{':publish'}->{$namePart}->{'mode'} = 'A' if (($ident eq 'atopic') or ($ident eq 'attr-topic')); $map->{$dev}->{':publish'}->{$namePart}->{'mode'} = 'A' if (($ident eq 'atopic') or ($ident eq 'attr-topic'));
$autoResend->{$namePart} = $val if $ident eq 'autoResendInterval'; $autoResend->{$namePart} = $val if $ident eq 'autoResendInterval';
@ -1030,25 +1059,37 @@ sub updatePubTime($$$) {
} }
# sucht zu den gegebenen device und reading die publish-Eintraege (topic, qos, retain) # sucht zu den gegebenen device und reading die publish-Eintraege (topic, qos, retain)
# liefert Liste der passenden dev-hashes
# verwendet device-record und beruecksichtigt defaults und globals # verwendet device-record und beruecksichtigt defaults und globals
# parameter: $hash, device-name, reading-name # parameter: $hash, device-name, reading-name
sub getDevicePublishRec($$$) { sub getDevicePublishRec($$$) {
my($hash, $dev, $reading) = @_; my($hash, $dev, $reading) = @_;
my $ret = [];
my $map = $hash->{+HS_TAB_NAME_DEVICES}; my $map = $hash->{+HS_TAB_NAME_DEVICES};
return undef unless defined $map; return $ret unless defined $map;
my $devMap = $map->{$dev};
my $globalMap = $map->{':global'}; my $globalMap = $map->{':global'};
my $devMap = $map->{$dev};
return getDevicePublishRecIntern($hash, $devMap, $globalMap, $dev, $reading); foreach my $key (keys %{$devMap->{':publish'}} ) {
my($keyRName,$keyPostfix) = split("!",$key);
if($keyRName eq $reading) {
my $devRec = getDevicePublishRecIntern($hash, $devMap, $globalMap, $dev, $key, $reading, $keyPostfix);
#$devRec->{'postfix'}=defined($keyPostfix)?$keyPostfix:'';
push(@$ret, $devRec);
}
}
return $ret;
} }
# sucht zu den gegebenen device und reading die publish-Eintraege (topic, qos, retain) # sucht zu den gegebenen device und reading die publish-Eintraege (topic, qos, retain)
# in den uebergebenen Maps # in den uebergebenen Maps
# verwendet device-record und beruecksichtigt defaults und globals # verwendet device-record und beruecksichtigt defaults und globals
# parameter: $hash, map, globalMap, device-name, reading-name # parameter: $hash, map, globalMap, device-name, reading-name
sub getDevicePublishRecIntern($$$$$) { sub getDevicePublishRecIntern($$$$$$$) {
my($hash, $devMap, $globalMap, $dev, $reading) = @_; my($hash, $devMap, $globalMap, $dev, $readingKey, $reading, $postFix) = @_;
#Log3($hash->{NAME},1,"MQTT_GENERIC_BRIDGE:DEBUG:> [$hash->{NAME}] getDevicePublishRec> params> devmap: ".Dumper($devMap)); #Log3($hash->{NAME},1,"MQTT_GENERIC_BRIDGE:DEBUG:> [$hash->{NAME}] getDevicePublishRec> params> devmap: ".Dumper($devMap));
#Log3($hash->{NAME},1,"MQTT_GENERIC_BRIDGE:DEBUG:> [$hash->{NAME}] getDevicePublishRec> params> globalmap: ".Dumper($globalMap)); #Log3($hash->{NAME},1,"MQTT_GENERIC_BRIDGE:DEBUG:> [$hash->{NAME}] getDevicePublishRec> params> globalmap: ".Dumper($globalMap));
#Log3($hash->{NAME},1,"MQTT_GENERIC_BRIDGE:DEBUG:> [$hash->{NAME}] getDevicePublishRec> params> dev: ".Dumper($dev)); #Log3($hash->{NAME},1,"MQTT_GENERIC_BRIDGE:DEBUG:> [$hash->{NAME}] getDevicePublishRec> params> dev: ".Dumper($dev));
@ -1061,12 +1102,16 @@ sub getDevicePublishRecIntern($$$$$) {
my $globalPublishMap = $globalMap->{':publish'}; my $globalPublishMap = $globalMap->{':publish'};
# reading map # reading map
my $readingMap = $publishMap->{$reading} if defined $publishMap; my $readingMap = $publishMap->{$readingKey} if defined $publishMap;
my $wildcardReadingMap = $publishMap->{'*'} if defined $publishMap; my $wildcardReadingMap = $publishMap->{'*'} if defined $publishMap;
#my $defaultReadingMap = $devMap->{':defaults'} if defined $devMap; #my $defaultReadingMap = $devMap->{':defaults'} if defined $devMap;
# global reading map # global reading map
my $globalReadingMap = $globalPublishMap->{$reading} if defined $globalPublishMap; my $globalReadingMap = undef;
if (defined $globalPublishMap) {
$globalReadingMap = $globalPublishMap->{$readingKey};
$globalReadingMap = $globalPublishMap->{$reading} unless defined $globalReadingMap;
}
my $globalWildcardReadingsMap = $globalPublishMap->{'*'} if defined $globalPublishMap; my $globalWildcardReadingsMap = $globalPublishMap->{'*'} if defined $globalPublishMap;
#Log3($hash->{NAME},1,"MQTT_GENERIC_BRIDGE:DEBUG:> [$hash->{NAME}] getDevicePublishRec> readingMap ".Dumper($readingMap)); #Log3($hash->{NAME},1,"MQTT_GENERIC_BRIDGE:DEBUG:> [$hash->{NAME}] getDevicePublishRec> readingMap ".Dumper($readingMap));
@ -1093,7 +1138,7 @@ sub getDevicePublishRecIntern($$$$$) {
# qos & retain & expression # qos & retain & expression
#my($qos, $retain, $expression) = retrieveQosRetainExpression($globalWildcardReadingsMap, $globalReadingMap, $wildcardReadingMap, $readingMap); #my($qos, $retain, $expression) = retrieveQosRetainExpression($globalWildcardReadingsMap, $globalReadingMap, $wildcardReadingMap, $readingMap);
my($qos, $retain, $expression) = retrieveQosRetainExpression($globalMap->{':defaults'}, $globalReadingMap, $devMap->{':defaults'}, $readingMap); my($qos, $retain, $expression) = retrieveQosRetainExpression($globalMap->{':defaults'}, $globalReadingMap, $globalWildcardReadingsMap, $wildcardReadingMap, $devMap->{':defaults'}, $readingMap);
# wenn kein topic und keine expression definiert sind, kann auch nicht gesendet werden, es muss nichts mehr ausgewertet werden # wenn kein topic und keine expression definiert sind, kann auch nicht gesendet werden, es muss nichts mehr ausgewertet werden
return unless (defined($topic) or defined($atopic) or defined( $expression)); return unless (defined($topic) or defined($atopic) or defined( $expression));
@ -1109,10 +1154,12 @@ sub getDevicePublishRecIntern($$$$$) {
# map name # map name
my $name = undef; my $name = undef;
if (defined($devMap) and defined($devMap->{':alias'})) { if (defined($devMap) and defined($devMap->{':alias'})) {
$name = $devMap->{':alias'}->{'pub:'.$reading}; $name = $devMap->{':alias'}->{'pub:'.$readingKey};
$name = $devMap->{':alias'}->{'pub:'.$reading} unless defined $name;
} }
if (defined($globalMap) and defined($globalMap->{':alias'}) and !defined($name)) { if (defined($globalMap) and defined($globalMap->{':alias'}) and !defined($name)) {
$name = $globalMap->{':alias'}->{'pub:'.$reading}; $name = $globalMap->{':alias'}->{'pub:'.$readingKey};
$name = $globalMap->{':alias'}->{'pub:'.$reading} unless defined $name;
} }
$name = $reading unless defined $name; $name = $reading unless defined $name;
@ -1120,25 +1167,25 @@ sub getDevicePublishRecIntern($$$$$) {
my $mode = $readingMap->{'mode'}; my $mode = $readingMap->{'mode'};
# compute defaults # compute defaults
my $combined = computeDefaults($hash, 'pub:', $globalMap, $devMap, {'device'=>$dev,'reading'=>$reading,'name'=>$name,'mode'=>$mode}); my $combined = computeDefaults($hash, 'pub:', $globalMap, $devMap, {'device'=>$dev,'reading'=>$reading,'name'=>$name,'mode'=>$mode,'postfix'=>$postFix});
# $topic evaluieren (avialable vars: $device (device name), $reading (oringinal name), $name ($reading oder alias, if defined), defaults) # $topic evaluieren (avialable vars: $device (device name), $reading (oringinal name), $name ($reading oder alias, if defined), defaults)
$combined->{'base'} = '' unless defined $combined->{'base'}; # base leer anlegen wenn nicht definiert $combined->{'base'} = '' unless defined $combined->{'base'}; # base leer anlegen wenn nicht definiert
if(defined($topic) and ($topic =~ m/^{.*}$/)) { if(defined($topic) and ($topic =~ m/^{.*}$/)) {
$topic = _evalValue2($hash->{NAME},$topic,{'topic'=>$topic,'device'=>$dev,'reading'=>$reading,'name'=>$name,%$combined}) if defined $topic; $topic = _evalValue2($hash->{NAME},$topic,{'topic'=>$topic,'device'=>$dev,'reading'=>$reading,'name'=>$name,'postfix'=>$postFix,%$combined}) if defined $topic;
} }
if(defined($atopic) and ($atopic =~ m/^{.*}$/)) { if(defined($atopic) and ($atopic =~ m/^{.*}$/)) {
$atopic = _evalValue2($hash->{NAME},$atopic,{'topic'=>$atopic,'device'=>$dev,'reading'=>$reading,'name'=>$name,%$combined}) if defined $atopic; $atopic = _evalValue2($hash->{NAME},$atopic,{'topic'=>$atopic,'device'=>$dev,'reading'=>$reading,'name'=>$name,'postfix'=>$postFix,%$combined}) if defined $atopic;
} }
return {'topic'=>$topic,'atopic'=>$atopic,'qos'=>$qos,'retain'=>$retain, return {'topic'=>$topic,'atopic'=>$atopic,'qos'=>$qos,'retain'=>$retain,
'expression'=>$expression,'name'=>$name,'mode'=>$mode, 'expression'=>$expression,'name'=>$name,'mode'=>$mode, 'postfix'=>$postFix,
'resendOnConnect'=>$resendOnConnect,'.defaultMap'=>$combined}; 'resendOnConnect'=>$resendOnConnect,'.defaultMap'=>$combined};
} }
# sucht Qos, Retain, Expression Werte unter Beruecksichtigung von Defaults und Globals # sucht Qos, Retain, Expression Werte unter Beruecksichtigung von Defaults und Globals
sub retrieveQosRetainExpression($$$$) { sub retrieveQosRetainExpression($$$$$$) {
my($globalDefaultReadingMap, $globalReadingMap, $defaultReadingMap, $readingMap) = @_; my($globalDefaultReadingMap, $globalReadingMap, $wildcardDefaultReadingMap, $wildcardReadingMap, $defaultReadingMap, $readingMap) = @_;
my $qos=undef; my $qos=undef;
my $retain = undef; my $retain = undef;
my $expression = undef; my $expression = undef;
@ -1159,6 +1206,12 @@ sub retrieveQosRetainExpression($$$$) {
# } # }
} }
if(defined $wildcardReadingMap) {
$qos = $wildcardReadingMap->{'qos'} unless defined $qos;
$retain = $wildcardReadingMap->{'retain'} unless defined $retain;
$expression = $wildcardReadingMap->{'expression'} unless defined $expression;
}
if(defined $defaultReadingMap) { if(defined $defaultReadingMap) {
$qos = $defaultReadingMap->{'pub:qos'} unless defined $qos; $qos = $defaultReadingMap->{'pub:qos'} unless defined $qos;
$retain = $defaultReadingMap->{'pub:retain'} unless defined $retain; $retain = $defaultReadingMap->{'pub:retain'} unless defined $retain;
@ -1174,8 +1227,8 @@ sub retrieveQosRetainExpression($$$$) {
} }
if(defined $globalReadingMap) { if(defined $globalReadingMap) {
$qos = $globalReadingMap->{'qos'}; $qos = $globalReadingMap->{'qos'} unless defined $qos; # warum stand hier nicht unless defined?
$retain = $globalReadingMap->{'retain'}; $retain = $globalReadingMap->{'retain'} unless defined $retain; # s.o.?
$expression = $globalReadingMap->{'expression'} unless defined $expression; $expression = $globalReadingMap->{'expression'} unless defined $expression;
# if(defined($globalReadingMap->{':defaults'})) { # if(defined($globalReadingMap->{':defaults'})) {
# $qos = $globalReadingMap->{':defaults'}->{'pub:qos'} unless defined $qos; # $qos = $globalReadingMap->{':defaults'}->{'pub:qos'} unless defined $qos;
@ -1184,6 +1237,12 @@ sub retrieveQosRetainExpression($$$$) {
# } # }
} }
if(defined $wildcardDefaultReadingMap) {
$qos = $wildcardDefaultReadingMap->{'qos'} unless defined $qos;
$retain = $wildcardDefaultReadingMap->{'retain'} unless defined $retain;
$expression = $wildcardDefaultReadingMap->{'expression'} unless defined $expression;
}
if(defined $globalDefaultReadingMap) { if(defined $globalDefaultReadingMap) {
$qos = $globalDefaultReadingMap->{'pub:qos'} unless defined $qos; $qos = $globalDefaultReadingMap->{'pub:qos'} unless defined $qos;
$retain = $globalDefaultReadingMap->{'pub:retain'} unless defined $retain; $retain = $globalDefaultReadingMap->{'pub:retain'} unless defined $retain;
@ -1263,6 +1322,7 @@ sub _evalValue2($$;$$) {
my $device = ''; my $device = '';
my $reading = ''; my $reading = '';
my $name = ''; my $name = '';
#my $room = '';
if(defined($map)) { if(defined($map)) {
foreach my $param (keys %{$map}) { foreach my $param (keys %{$map}) {
my $val = $map->{$param}; my $val = $map->{$param};
@ -1277,17 +1337,20 @@ sub _evalValue2($$;$$) {
$device = $val; $device = $val;
} elsif ($pname eq '$name') { } elsif ($pname eq '$name') {
$name = $val; $name = $val;
# } elsif ($pname eq '$room') {
# $room = $val;
} else { } else {
#Log3('xxx',1,"MQTT_GENERIC_BRIDGE:DEBUG:> replace2: $ret : $pname => $val"); #Log3('xxx',1,"MQTT_GENERIC_BRIDGE:DEBUG:> replace2: $ret : $pname => $val");
$ret =~ s/\Q$pname\E/$val/g; #$ret =~ s/\Q$pname\E/$val/g;
#Log3('xxx',1,"MQTT_GENERIC_BRIDGE:DEBUG:> replace2 done: $ret"); $s2 =~ s/\Q$pname\E/$val/g;
#Log3('xxx',1,"MQTT_GENERIC_BRIDGE:DEBUG:> replace2 done: $s2");
} }
} }
} }
#Log3('xxx',1,"MQTT_GENERIC_BRIDGE:DEBUG:> eval2 expr: $ret"); #Log3('xxx',1,"MQTT_GENERIC_BRIDGE:DEBUG:> eval2 expr: $s2");
#$ret = eval($ret) unless $noEval; #$ret = eval($ret) unless $noEval;
$s2 = eval($s2) unless $noEval; $s2 = eval($s2) unless $noEval;
#Log3('xxx',1,"MQTT_GENERIC_BRIDGE:DEBUG:> eval2 done: $ret"); #Log3('xxx',1,"MQTT_GENERIC_BRIDGE:DEBUG:> eval2 done: $s2");
if ($@) { if ($@) {
Log3($mod,2,"MQTT_GENERIC_BRIDGE: evalValue: user value ('".$str."'') eval error: ".$@); Log3($mod,2,"MQTT_GENERIC_BRIDGE: evalValue: user value ('".$str."'') eval error: ".$@);
$ret=$s1.''.$s3; $ret=$s1.''.$s3;
@ -1845,7 +1908,10 @@ sub Get($$$@) {
$res.=$dname."\n"; $res.=$dname."\n";
$res.=" publish:\n"; $res.=" publish:\n";
foreach my $rname (sort keys %{$hash->{+HS_TAB_NAME_DEVICES}->{$dname}->{':publish'}}) { foreach my $rname (sort keys %{$hash->{+HS_TAB_NAME_DEVICES}->{$dname}->{':publish'}}) {
my $pubRec = getDevicePublishRec($hash, $dname, $rname); #my $pubRec = getDevicePublishRec($hash, $dname, $rname);
my $pubRecList = getDevicePublishRec($hash, $dname, $rname);
if(defined($pubRecList)) {
foreach my $pubRec (@$pubRecList) {
if(defined($pubRec)) { if(defined($pubRec)) {
my $expression = $pubRec->{'expression'}; my $expression = $pubRec->{'expression'};
my $mode = $pubRec->{'mode'}; my $mode = $pubRec->{'mode'};
@ -1863,7 +1929,10 @@ sub Get($$$@) {
$topic = 'undefined' unless defined $topic; $topic = 'undefined' unless defined $topic;
my $qos = $pubRec->{'qos'}; my $qos = $pubRec->{'qos'};
my $retain = $pubRec->{'retain'}; my $retain = $pubRec->{'retain'};
$res.= sprintf(' %-16s => %s', $rname, $topic); my $postFix = $pubRec->{'postfix'};
my $dispName = $rname;
if(defined($postFix) and ($postFix ne '')) {$dispName.='!'.$postFix;}
$res.= sprintf(' %-16s => %s', $dispName, $topic);
$res.= " ("; $res.= " (";
$res.= "mode: $mode"; $res.= "mode: $mode";
$res.= "; qos: $qos"; $res.= "; qos: $qos";
@ -1871,6 +1940,8 @@ sub Get($$$@) {
$res.= ")\n"; $res.= ")\n";
$res.= " exp: $expression\n" if defined ($expression); $res.= " exp: $expression\n" if defined ($expression);
} }
}
}
} }
$res.=" subscribe:\n"; $res.=" subscribe:\n";
my @resa; my @resa;
@ -2330,9 +2401,10 @@ sub publishDeviceUpdate($$$$$) {
return if(isTypeDevReadingExcluded($hash, 'pub', $type, $devn, $reading)); return if(isTypeDevReadingExcluded($hash, 'pub', $type, $devn, $reading));
#Log3($hash->{NAME},1,"MQTT_GENERIC_BRIDGE:DEBUG:> [$hash->{NAME}] publishDeviceUpdate for $devn, $reading, $value"); #Log3($hash->{NAME},1,"MQTT_GENERIC_BRIDGE:DEBUG:> [$hash->{NAME}] publishDeviceUpdate for $devn, $reading, $value");
my $pubRec = getDevicePublishRec($hash, $devn, $reading); my $pubRecList = getDevicePublishRec($hash, $devn, $reading);
#Log3($hash->{NAME},1,"MQTT_GENERIC_BRIDGE:DEBUG:> [$hash->{NAME}] publishDeviceUpdate pubRec: ".Dumper($pubRec)); #Log3($hash->{NAME},1,"MQTT_GENERIC_BRIDGE:DEBUG:> [$hash->{NAME}] publishDeviceUpdate pubRec: ".Dumper($pubRec));
if(defined($pubRecList)) {
foreach my $pubRec (@$pubRecList) {
if(defined($pubRec)) { if(defined($pubRec)) {
# my $msgid; # my $msgid;
@ -2368,10 +2440,18 @@ sub publishDeviceUpdate($$$$$) {
# $device, $reading, $name (und fuer alle Faelle $topic) in $defMap packen, so zur Verfügung stellen (für eval)reicht wegen _evalValue2 wohl nicht # $device, $reading, $name (und fuer alle Faelle $topic) in $defMap packen, so zur Verfügung stellen (für eval)reicht wegen _evalValue2 wohl nicht
my $name = $reading; # TODO: Name-Mapping my $name = $reading; # TODO: Name-Mapping
my $device = $devn; my $device = $devn;
#Log3($hash->{NAME},1,"MQTT_GENERIC_BRIDGE:DEBUG:> DEBUG: >>> expression: $expression"); #if(!defined($defMap->{'room'})) {
my $ret = _evalValue2($hash->{NAME},$expression,{'topic'=>$topic,'device'=>$devn,'reading'=>$reading,'name'=>$name,%$defMap},1); # $defMap->{'room'} = AttrVal($devn,'room','');
#}
if(!defined($defMap->{'uid'}) and defined($defs{$devn})) {
$defMap->{'uid'} = $defs{$devn}->{'FUUID'};
$defMap->{'uid'} = '' unless defined $defMap->{'uid'};
}
#Log3($hash->{NAME},1,"MQTT_GENERIC_BRIDGE:DEBUG:> DEBUG: >>> expression: $expression : ".Dumper($defMap));
my $ret = _evalValue2($hash->{NAME},$expression,{'topic'=>$topic,'device'=>$devn,'reading'=>$reading,'name'=>$name,'time'=>TimeNow(),%$defMap},1);
#Log3($hash->{NAME},1,"MQTT_GENERIC_BRIDGE:DEBUG:> DEBUG: <<< expression: ".Dumper($ret)); #Log3($hash->{NAME},1,"MQTT_GENERIC_BRIDGE:DEBUG:> DEBUG: <<< expression: ".Dumper($ret));
$ret = eval($ret); $ret = eval($ret);
#Log3($hash->{NAME},1,"MQTT_GENERIC_BRIDGE:DEBUG:> DEBUG: <<< eval expression: ".Dumper($ret));
if(ref($ret) eq 'HASH') { if(ref($ret) eq 'HASH') {
$redefMap = $ret; $redefMap = $ret;
} elsif(ref($ret) eq 'ARRAY') { } elsif(ref($ret) eq 'ARRAY') {
@ -2399,7 +2479,7 @@ sub publishDeviceUpdate($$$$$) {
$updated = 1 unless defined $r; $updated = 1 unless defined $r;
} }
} else { } else {
my $r = doPublish($hash,$devn,$reading,$topic,$message,$qos,$retain,$resendOnConnect) if defined $topic; my $r = doPublish($hash,$devn,$reading,$topic,$message,$qos,$retain,$resendOnConnect) if defined $topic and defined $message;
$updated = 1 unless defined $r; $updated = 1 unless defined $r;
} }
if($updated) { if($updated) {
@ -2408,6 +2488,8 @@ sub publishDeviceUpdate($$$$$) {
} }
} }
}
}
# Routine fuer FHEM Attr # Routine fuer FHEM Attr
sub Attr($$$$) { sub Attr($$$$) {
@ -2944,8 +3026,10 @@ sub onmessage($$$) {
If a '*' is used instead of a read name, this definition applies to all readings for which no explicit information was provided.<br/> If a '*' is used instead of a read name, this definition applies to all readings for which no explicit information was provided.<br/>
Topic can also be written as a 'readings-topic'.<br/> Topic can also be written as a 'readings-topic'.<br/>
Attributes can also be sent ("atopic" or "attr-topic"). Attributes can also be sent ("atopic" or "attr-topic").
If you wont to send several messages (meaningfully to different topics) for an event, the respective definitions must be defined by appending
unique suffixes (separated from the reading name by a !-sign): reading!1:topic=... reading!2:topic=.... <br/>
It is possible to define expressions (reading: expression = ...). <br/> It is possible to define expressions (reading: expression = ...). <br/>
The expressions could usefully change variables ($value, $topic, $qos, $retain, $message), or return a value of != undef.<br/> The expressions could usefully change variables ($value, $topic, $qos, $retain, $message, $uid), or return a value of != undef.<br/>
The return value is used as a new message value, the changed variables have priority.<br/> The return value is used as a new message value, the changed variables have priority.<br/>
If the return value is undef, setting / execution is suppressed. <br/> If the return value is undef, setting / execution is suppressed. <br/>
If the return is a hash (topic only), its key values are used as the topic, and the contents of the messages are the values from the hash.</p> If the return is a hash (topic only), its key values are used as the topic, and the contents of the messages are the values from the hash.</p>
@ -2967,8 +3051,9 @@ sub onmessage($$$) {
attr &lt;dev&gt; mqttPublish *:topic={"$base/$name"} reading:expression={"message: $value"}<br/> attr &lt;dev&gt; mqttPublish *:topic={"$base/$name"} reading:expression={"message: $value"}<br/>
attr &lt;dev&gt; mqttPublish *:topic={"$base/$name"} reading:expression={$value="message: $value"}<br/> attr &lt;dev&gt; mqttPublish *:topic={"$base/$name"} reading:expression={$value="message: $value"}<br/>
attr &lt;dev&gt; mqttPublish *:topic={"$base/$name"} reading:expression={"/TEST/Topic1"=>"$message", "/TEST/Topic2"=>"message: $message"}<br/> attr &lt;dev&gt; mqttPublish *:topic={"$base/$name"} reading:expression={"/TEST/Topic1"=>"$message", "/TEST/Topic2"=>"message: $message"}<br/>
attr &lt;dev&gt; mqttPublish *:resendOnConnect=last attr &lt;dev&gt; mqttPublish *:resendOnConnect=last<br/>
attr &lt;dev&gt; mqttPublish temperature:topic={"$base/temperature/01/value"} temperature!json:topic={"$base/temperature/01/json"}
temperature!json:expression={toJSON({value=>$value,type=>"temperature",unit=>"°C",format=>"00.0"})}<br/>
</code></p> </code></p>
</p> </p>
</li> </li>
@ -3340,9 +3425,11 @@ sub onmessage($$$) {
indem sie, mittels '|' getrennt, zusammen angegeben werden.<br/> indem sie, mittels '|' getrennt, zusammen angegeben werden.<br/>
Wird anstatt eines Readingsnamen ein '*' verwendet, gilt diese Definition fuer alle Readings, Wird anstatt eines Readingsnamen ein '*' verwendet, gilt diese Definition fuer alle Readings,
fuer die keine explizite Angaben gemacht wurden.<br/> fuer die keine explizite Angaben gemacht wurden.<br/>
Ebenso koennen auch Attributwerte gesendet werden ('atopic' oder 'attr-topic'). Ebenso koennen auch Attributwerte gesendet werden ('atopic' oder 'attr-topic').<br/>
Sollten fuer ein Event mehrere Nachrichten (sinnvollerweise an verschiedene Topics) versendet werden, muessen jeweilige Definitionen durch Anhaengen von
einmaligen Suffixen (getrennt von dem Readingnamen durch ein !-Zeichen) unterschieden werden: reading!1:topic=... reading!2:topic=....<br/>
Weiterhin koennen auch Expressions (reading:expression=...) definiert werden. <br/> Weiterhin koennen auch Expressions (reading:expression=...) definiert werden. <br/>
Die Expressions koenne sinnvollerweise entweder Variablen ($value, $topic, $qos, $retain, $message) veraendern, oder einen Wert != undef zurrueckgeben.<br/> Die Expressions koenne sinnvollerweise entweder Variablen ($value, $topic, $qos, $retain, $message, $uid) veraendern, oder einen Wert != undef zurrueckgeben.<br/>
Der Rueckhgabe wert wird als neue Nachricht-Value verwendet, die Aenderung der Variablen hat dabei jedoch Vorrang.<br/> Der Rueckhgabe wert wird als neue Nachricht-Value verwendet, die Aenderung der Variablen hat dabei jedoch Vorrang.<br/>
Ist der Rueckgabewert undef, dann wird das Setzen/Ausfuehren unterbunden. <br/> Ist der Rueckgabewert undef, dann wird das Setzen/Ausfuehren unterbunden. <br/>
Ist die Rueckgabe ein Hash (nur 'topic'), werden seine Schluesselwerte als Topic verwendet, Ist die Rueckgabe ein Hash (nur 'topic'), werden seine Schluesselwerte als Topic verwendet,
@ -3366,7 +3453,9 @@ sub onmessage($$$) {
attr &lt;dev&gt; mqttPublish *:topic={"$base/$name"} reading:expression={"message: $value"}<br/> attr &lt;dev&gt; mqttPublish *:topic={"$base/$name"} reading:expression={"message: $value"}<br/>
attr &lt;dev&gt; mqttPublish *:topic={"$base/$name"} reading:expression={$value="message: $value"}<br/> attr &lt;dev&gt; mqttPublish *:topic={"$base/$name"} reading:expression={$value="message: $value"}<br/>
attr &lt;dev&gt; mqttPublish *:topic={"$base/$name"} reading:expression={"/TEST/Topic1"=>"$message", "/TEST/Topic2"=>"message: $message"}</br> attr &lt;dev&gt; mqttPublish *:topic={"$base/$name"} reading:expression={"/TEST/Topic1"=>"$message", "/TEST/Topic2"=>"message: $message"}</br>
attr &lt;dev&gt; mqttPublish [...] *:resendOnConnect=last attr &lt;dev&gt; mqttPublish [...] *:resendOnConnect=last<br/>
attr &lt;dev&gt; mqttPublish temperature:topic={"$base/temperature/01/value"} temperature!json:topic={"$base/temperature/01/json"}
temperature!json:expression={toJSON({value=>$value,type=>"temperature",unit=>"°C",format=>"00.0"})}<br/>
</code></p> </code></p>
</p> </p>
</li> </li>