From dbf557f2cc2df5fe27c3d63db5b1ca91f915dcfc Mon Sep 17 00:00:00 2001
From: hexenmeister
Date: Sun, 14 Oct 2018 19:25:32 +0000
Subject: [PATCH] feature: 'mqttForward' implemented
git-svn-id: https://svn.fhem.de/fhem/trunk@17533 2b470e98-0d58-463d-a4d8-8e2adae1ed80
---
fhem/FHEM/10_MQTT_GENERIC_BRIDGE.pm | 110 ++++++++++++++++++++++++----
1 file changed, 96 insertions(+), 14 deletions(-)
diff --git a/fhem/FHEM/10_MQTT_GENERIC_BRIDGE.pm b/fhem/FHEM/10_MQTT_GENERIC_BRIDGE.pm
index ac8cb4499..c3be90f06 100644
--- a/fhem/FHEM/10_MQTT_GENERIC_BRIDGE.pm
+++ b/fhem/FHEM/10_MQTT_GENERIC_BRIDGE.pm
@@ -30,6 +30,25 @@
#
# CHANGE LOG
#
+# 14.10.2018 0.9.9
+# change : 'mqttForward' dokumentiert
+# improved : Laden von MQTT-Modul in BEGIN-Block verlagert.
+# Es gab Meldungen ueber Probleme (undefined subroutine) wenn
+# MQTT-Modul in fhem.cfg nach dem Bridge-Modul stand.
+#
+# 11.10.2018 0.9.9
+# change : 'self-trigger-topic' wieder ausgebaut.
+# feature : 'mqttForward' Attribute fuer ueberwachte Geraete implmentiert.
+# Moegliche Werte derzeit: 'all' und 'none'.
+# Bei 'none' werden per MQTT angekommene Nachrichten nicht
+# aus dem selben Device per MQTT weiter gepublisht.
+# 'all' bewirkt das Gegenteil.
+# Fehlt der Attribut, dann wird standartmaeßig für alle
+# Geraetetypen außer 'Dummy' 'all' angenommen und entsprechend
+# 'none' für Dummies.
+# Deise Einstellung ist notwendig, damit Aktoren ihren zustand
+# zurueckmelden koennen, jedoch Dummies beim Einsatz als
+# FHEM-UI-Schalter keine Endlosschleifen verursachen.
#
# 30.09.2018 0.9.9
# feature finished: globalTypeExclude und globalDeviceExclude incl. Commandref
@@ -203,6 +222,7 @@ use constant {
CTRL_ATTR_NAME_PUBLISH => "Publish",
CTRL_ATTR_NAME_SUBSCRIBE => "Subscribe",
CTRL_ATTR_NAME_IGNORE => "Disable",
+ CTRL_ATTR_NAME_FORWARD => "Forward",
CTRL_ATTR_NAME_GLOBAL_TYPE_EXCLUDE => "globalTypeExclude",
CTRL_ATTR_NAME_GLOBAL_DEV_EXCLUDE => "globalDeviceExclude",
CTRL_ATTR_NAME_GLOBAL_PREFIX => "global"
@@ -236,7 +256,7 @@ sub MQTT_GENERIC_BRIDGE_Initialize($) {
"debug:0,1 ".
$main::readingFnAttributes;
- main::LoadModule("MQTT");
+ #main::LoadModule("MQTT");
# Beim ModulReload Deviceliste loeschen (eig. nur fuer bei der Entwicklung nuetzich)
#if($DEBUG) {
@@ -268,6 +288,7 @@ use Net::MQTT::Constants;
#}
BEGIN {
+ main::LoadModule("MQTT");
MQTT->import(qw(:all));
GP_Import(qw(
@@ -297,6 +318,7 @@ BEGIN {
CTRL_ATTR_NAME_PUBLISH
CTRL_ATTR_NAME_SUBSCRIBE
CTRL_ATTR_NAME_IGNORE
+ CTRL_ATTR_NAME_FORWARD
CTRL_ATTR_NAME_GLOBAL_TYPE_EXCLUDE
CTRL_ATTR_NAME_GLOBAL_DEV_EXCLUDE
CTRL_ATTR_NAME_GLOBAL_PREFIX
@@ -504,6 +526,7 @@ sub initUserAttr($) {
addToDevAttrList($dev, $prefix.CTRL_ATTR_NAME_PUBLISH.":textField-long");
addToDevAttrList($dev, $prefix.CTRL_ATTR_NAME_SUBSCRIBE.":textField-long");
addToDevAttrList($dev, $prefix.CTRL_ATTR_NAME_IGNORE.":both,incoming,outgoing");
+ addToDevAttrList($dev, $prefix.CTRL_ATTR_NAME_FORWARD.":all,none");
}
return \@devices;
}
@@ -628,6 +651,7 @@ sub removeOldUserAttr($;$$$) {
# delFromDevAttrList($dev,$prefix.CTRL_ATTR_NAME_PUBLISH.":textField-long");
# delFromDevAttrList($dev,$prefix.CTRL_ATTR_NAME_SUBSCRIBE.":textField-long");
# delFromDevAttrList($dev,$prefix.CTRL_ATTR_NAME_IGNORE.":both,incoming,outgoing");
+ # delFromDevAttrList($dev,$prefix.CTRL_ATTR_NAME_FORWARD.":all,none");
# => stattdessen selbst loeschen (nur die 'userattr')
my $ua = $main::attr{$dev}{userattr};
if (defined $ua) {
@@ -642,6 +666,8 @@ sub removeOldUserAttr($;$$$) {
delete $h{$prefix.CTRL_ATTR_NAME_SUBSCRIBE.":textField-long"};
#delete $h{$prefix.CTRL_ATTR_NAME_IGNORE};
delete $h{$prefix.CTRL_ATTR_NAME_IGNORE.":both,incoming,outgoing"};
+ #delete $h{$prefix.CTRL_ATTR_NAME_FORWARD};
+ delete $h{$prefix.CTRL_ATTR_NAME_FORWARD.":all,none"};
if(!keys %h && defined($main::attr{$dev}{userattr})) {
# ganz loeschen, wenn nichts mehr drin
delete $main::attr{$dev}{userattr};
@@ -672,6 +698,9 @@ sub IsObservedAttribute($$) {
if($aname eq $prefix.CTRL_ATTR_NAME_IGNORE) {
return 1;
}
+ if($aname eq $prefix.CTRL_ATTR_NAME_FORWARD) {
+ return 1;
+ }
return undef;
}
@@ -1163,11 +1192,12 @@ sub CreateSingleDeviceTableAttrSubscribe($$$$) {
if(!defined($ident) or !defined($name)) { next; }
$ident = 'topic' if $ident eq 'readings-topic';
- $ident = 'sttopic' if $ident eq 'self-trigger-topic';
+ #$ident = 'sttopic' if $ident eq 'self-trigger-topic';
$ident = 'stopic' if $ident eq 'set-topic';
$ident = 'atopic' if $ident eq 'attr-topic';
- if(($ident eq 'topic') or ($ident eq 'sttopic') or
+ if(($ident eq 'topic') or
+ #($ident eq 'sttopic') or
($ident eq 'stopic') or ($ident eq 'atopic') or
($ident eq 'qos') or ($ident eq 'retain') or
($ident eq 'expression')) {
@@ -1182,11 +1212,12 @@ sub CreateSingleDeviceTableAttrSubscribe($$$$) {
#$rmap->{'evalTarget'} = $namePart =~ /^{.+}.*$/;
$rmap->{'dev'}=$dev;
$rmap->{$ident}=$val;
- if(($ident eq 'topic') or ($ident eq 'sttopic') or
+ if(($ident eq 'topic') or
+ #($ident eq 'sttopic') or
($ident eq 'stopic') or ($ident eq 'atopic')) { # -> topic
$rmap->{'mode'} = 'R';
- $rmap->{'mode'} = 'T' if $ident eq 'sttopic';
+ #$rmap->{'mode'} = 'T' if $ident eq 'sttopic';
$rmap->{'mode'} = 'S' if $ident eq 'stopic';
$rmap->{'mode'} = 'A' if $ident eq 'atopic';
@@ -1886,6 +1917,25 @@ sub isTypeDevReadingExcluded($$$$$) {
return undef;
}
+# Prueft, ob per MQTT ankommende Nachrichten ggf. per MQTT weiter geleitet werden duerfen.
+# Parameter:
+# $hash: HASH
+# $devName: Geraetename
+# $reading: Reading (ggf. for future use)
+sub isDoForward($$$) {
+ my ($hash, $devName, $reading) = @_;
+ my $doForward = $main::attr{$devName}{$hash->{+HS_PROP_NAME_PREFIX}.CTRL_ATTR_NAME_FORWARD};
+
+ $doForward = 'none' if (!defined($doForward) and ($defs{$devName}->{TYPE} eq 'dummy')); # Hack fuer Dummy-Devices
+
+ $doForward = 'all' unless defined $doForward;
+
+ #Log3($hash->{NAME},1,"MQTT_GENERIC_BRIDGE:DEBUG:> isDoForward $devName => $doForward");
+
+ return 1 if $doForward eq 'all';
+ return 0;
+}
+
# MQTT-Nachricht senden
# Params: Bridge-Hash, Topic, Nachricht, QOS- und Retain-Flags
sub doPublish($$$$$$$$) {
@@ -2113,8 +2163,10 @@ sub Attr($$$$) {
(($attribute eq $prefix.CTRL_ATTR_NAME_DEFAULTS) or
($attribute eq $prefix.CTRL_ATTR_NAME_ALIAS) or
($attribute eq $prefix.CTRL_ATTR_NAME_PUBLISH) or
- ($attribute eq $prefix.CTRL_ATTR_NAME_SUBSCRIBE)or
- ($attribute eq $prefix.CTRL_ATTR_NAME_IGNORE))and do {
+ ($attribute eq $prefix.CTRL_ATTR_NAME_SUBSCRIBE) or
+ ($attribute eq $prefix.CTRL_ATTR_NAME_IGNORE) or
+ ($attribute eq $prefix.CTRL_ATTR_NAME_FORWARD)
+ ) and do {
if ($command eq "set") {
return "this attribute is not allowed here";
}
@@ -2198,16 +2250,18 @@ sub doSetUpdate($$$$$) {
my $dhash = $defs{$device};
return unless defined $dhash;
+ my $doForward = isDoForward($hash, $device,$reading);
+
if($mode eq 'S') {
my $err;
my @args = split ("[ \t]+",$message);
#Log3($hash->{NAME},1,"MQTT_GENERIC_BRIDGE:DEBUG:> mqttGenericBridge_triggeredReading=".Dumper($dhash->{'.mqttGenericBridge_triggeredReading'}));
if(($reading eq '') or ($reading eq 'state')) {
- $dhash->{'.mqttGenericBridge_triggeredReading'}="state" if $dhash->{TYPE} eq 'dummy'; # Schneller Hack
+ $dhash->{'.mqttGenericBridge_triggeredReading'}="state" unless $doForward;
#$err = DoSet($device,$message);
$err = DoSet($device,@args);
} else {
- $dhash->{'.mqttGenericBridge_triggeredReading'}=$reading if $dhash->{TYPE} eq 'dummy'; # Schneller Hack
+ $dhash->{'.mqttGenericBridge_triggeredReading'}=$reading unless $doForward;
#$err = DoSet($device,$reading,$message);
$err = DoSet($device,$reading,@args);
}
@@ -2218,13 +2272,13 @@ sub doSetUpdate($$$$$) {
}
Log3($hash->{NAME},1,"MQTT_GENERIC_BRIDGE: setUpdate: error in set command: ".$err);
return "error in set command: $err";
- } elsif($mode eq 'R' or $mode eq 'T') {
+ } elsif($mode eq 'R') { # or $mode eq 'T') {
# R - Normale Topic (beim Empfang nicht weiter publishen)
# T - Selt-Trigger-Topic (Sonderfall, auch wenn gerade empfangen, kann weiter getriggert/gepublisht werden. Vorsicht! Gefahr von 'Loops'!)
readingsBeginUpdate($dhash);
- if ($mode eq 'R') {
- $dhash->{'.mqttGenericBridge_triggeredReading'}=$reading if $dhash->{TYPE} eq 'dummy'; # Schneller Hack
- }
+ #if ($mode eq 'R') {
+ $dhash->{'.mqttGenericBridge_triggeredReading'}=$reading unless $doForward;
+ #}
readingsBulkUpdate($dhash,$reading,$message);
readingsEndUpdate($dhash,1);
# wird in 'notify' entfernt # delete $dhash->{'.mqttGenericBridge_triggeredReading'};
@@ -2585,6 +2639,20 @@ sub onmessage($$$) {
+
+ mqttForward
+ This attribute defines what happens when one and the same reading is both subscribed and posted.
+ Possible values: 'all' and 'none'.
+ If 'none' is selected, than messages received via MQTT will not be published from the same device.
+ The setting 'all' does the opposite, so that the forwarding is possible.
+ If this attribute is missing, the default setting for all device types except 'dummy' is 'all'
+ (so that actuators can receive commands and send their changes in the same time) and for dummies 'none' is used.
+ This was chosen because dummies are often used as a kind of GUI switch element.
+ In this case, 'all' might cause an endless loop of messages.
+
+
+
+
mqttDisable
If this attribute is set in a device, this device is excluded from sending or receiving the readings.
@@ -2960,12 +3028,26 @@ sub onmessage($$$) {
+
+ mqttForward
+ Dieses Attribut definiert was passiert, wenn eine und dieselbe Reading sowohl aboniert als auch gepublisht wird.
+ Moegliche Werte: 'all' und 'none'.
+ Bei 'none' werden per MQTT angekommene Nachrichten nicht aus dem selben Gerät per MQTT weiter gesendet.
+ Die Einstellung 'all' bewirkt das Gegenteil, also damit wird das Weiterleiten ermoeglicht.
+ Fehlt dieser Attribut, dann wird standartmaeßig für alle Geraetetypen außer 'Dummy' die Einstellung 'all' angenommen
+ (damit koennen Aktoren Befehle empfangen und ihre Änderungen im gleichem Zug weiter senden)
+ und fuer Dummies wird 'none' verwendet. Das wurde so gewaehlt, weil Dummies oft als eine Art GUI-Schalterelement verwendet werden.
+ In diesem Fall wuerde 'all' unter Umstaenden eine Endlosschleife der Nachrichten verursachen.
+
+
+
+
mqttDisable
Wird dieses Attribut in einem Geraet gesetzt, wird dieses Geraet vom Versand bzw. Empfang der Readingswerten ausgeschlossen.
-
+
Beispiele