mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-01-31 18:59:33 +00:00
change: see change log inside
git-svn-id: https://svn.fhem.de/fhem/trunk@17363 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
8f52f8fd2c
commit
1db77264d1
@ -1,9 +1,11 @@
|
|||||||
###############################################################################
|
###############################################################################
|
||||||
#
|
#
|
||||||
# fhem bridge to mqtt (see http://mqtt.org)
|
# This module is an MQTT bridge, which simultaneously collects data
|
||||||
|
# from several FHEM devices and passes their readings via MQTT
|
||||||
|
# or set readings or attributes from the incoming MQTT messages or
|
||||||
|
# executes them as a 'set' command on the configured FHEM device.
|
||||||
#
|
#
|
||||||
# Copyright (C) 2017 Stephan Eisler
|
# Copyright (C) 2017 Alexander Schulz
|
||||||
# Copyright (C) 2014 - 2016 Norbert Truchsess
|
|
||||||
#
|
#
|
||||||
# This file is part of fhem.
|
# This file is part of fhem.
|
||||||
#
|
#
|
||||||
@ -29,19 +31,34 @@
|
|||||||
# CHANGE LOG
|
# CHANGE LOG
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# 15.09.2018 0.9.7 fix : Unterstuetzung Variablen
|
# 16.09.2018 0.9.7
|
||||||
# (base, name, message in expression)
|
# fix : defaults in globalPublish nicht verfuegbar
|
||||||
|
# fix : atopic in devinfo korrekt anzeigen
|
||||||
|
# improved : Anzeige devinfo
|
||||||
|
# change : Umstellung auf delFromDevAttrList (zum ENtfernen von UserArrt)
|
||||||
|
# change : Methoden-Doku
|
||||||
|
#
|
||||||
|
# 15.09.2018 0.9.7
|
||||||
|
# fix : Unterstuetzung Variablen (base, name, message in expression)
|
||||||
# feature : $base in device-mqttDefault kann auf $base aus
|
# feature : $base in device-mqttDefault kann auf $base aus
|
||||||
# globalDefault aus der Bridge zugreifen.
|
# globalDefault aus der Bridge zugreifen.
|
||||||
# improved : wenn fuer publish Expression definiert ist,
|
# improved : wenn fuer publish Expression definiert ist,
|
||||||
# muss nicht mehr zwingend topic definiert werden
|
# muss nicht mehr zwingend topic definiert werden
|
||||||
# (dafuer muss dann ggf. Expression sorgen)
|
# (dafuer muss dann ggf. Expression sorgen)
|
||||||
# improved : Formatierung Ausgabe devinfo
|
# improved : Formatierung Ausgabe devinfo
|
||||||
|
# feature : Unterstuetzung beliegiger (fast) variable
|
||||||
|
# in defaults / global defaults.
|
||||||
|
# Verwendung wie auch bei $base.
|
||||||
|
# z.B. hugo wird zum $hugo in Topics/Expression
|
||||||
# minor fixes
|
# minor fixes
|
||||||
# xx.08.2018 0.9.6 feature : Unterstuetzung 'atopic'/sub
|
#
|
||||||
# (Attribute empfangen und setzen)
|
# xx.08.2018 0.9.6
|
||||||
# xx.08.2018 0.9.5 feature : Unterstuetzung 'atopic'/pub
|
# feature : Unterstuetzung 'atopic'/sub (Attribute empfangen und setzen)
|
||||||
# (Attribute senden)
|
# feature : atopic kann jetzt auch als attr-topic und
|
||||||
|
# stopic als set-topic geschrieben werden.
|
||||||
|
#
|
||||||
|
# xx.08.2018 0.9.5
|
||||||
|
# feature : Unterstuetzung 'atopic'/pub (Attribute senden)
|
||||||
# ...
|
# ...
|
||||||
# ...
|
# ...
|
||||||
# ...
|
# ...
|
||||||
@ -63,7 +80,7 @@
|
|||||||
# - global subscribe
|
# - global subscribe
|
||||||
# - global excludes
|
# - global excludes
|
||||||
# - Support for MQTT2_SERVER
|
# - Support for MQTT2_SERVER
|
||||||
# - commanden per mqtt fuer die Bridge: Liste der Geraete, Infos dazu etc.
|
# - commands per mqtt fuer die Bridge: Liste der Geraete, Infos dazu etc.
|
||||||
#
|
#
|
||||||
# [I don't like it]
|
# [I don't like it]
|
||||||
# - templates (e.g. 'define template' in der Bridge, 'mqttUseTemplate' in Devices)
|
# - templates (e.g. 'define template' in der Bridge, 'mqttUseTemplate' in Devices)
|
||||||
@ -80,9 +97,10 @@
|
|||||||
# - Zeilenumbruch wird nicht als Trennen zw. topic-Definitionen erkannt, nur mit einem zusaetzlichen Leerzeichen
|
# - Zeilenumbruch wird nicht als Trennen 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
|
||||||
#
|
#
|
||||||
# [testing]
|
# [testing]
|
||||||
# - beim Aendern von mqttPublish (auch subscribe?) werden interne Tabellen erst nach dem debug Reinit aktualisiert
|
# - beim Aendern von mqttSubscribe und allen globalAttributes (auch publish?) werden interne Tabellen erst nach dem debug Reinit aktualisiert
|
||||||
#
|
#
|
||||||
# [works for me]
|
# [works for me]
|
||||||
# - von mehreren sich gleichzeitig aendernden Readings wird nur eine gepostet
|
# - von mehreren sich gleichzeitig aendernden Readings wird nur eine gepostet
|
||||||
@ -266,14 +284,25 @@ sub initUserAttr($);
|
|||||||
sub createRegexpForTopic($);
|
sub createRegexpForTopic($);
|
||||||
sub isDebug($);
|
sub isDebug($);
|
||||||
sub checkPublishDeviceReadingsUpdates($$);
|
sub checkPublishDeviceReadingsUpdates($$);
|
||||||
|
sub RefreshGlobalTableAll($);
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# prueft, ob debug Attribute auf 1 gesetzt ist (Debugmode)
|
||||||
sub isDebug($) {
|
sub isDebug($) {
|
||||||
my ($hash) = @_;
|
my ($hash) = @_;
|
||||||
return AttrVal($hash->{NAME},"debug",0);
|
return AttrVal($hash->{NAME},"debug",0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Entfernt Leerzeichen vom string vorne und hinten
|
||||||
sub trim { my $s = shift; $s =~ s/^\s+|\s+$//g; return $s }
|
sub trim { my $s = shift; $s =~ s/^\s+|\s+$//g; return $s }
|
||||||
|
|
||||||
|
# prueft, ob der erste gegebene String mit dem zweiten anfaengt
|
||||||
|
sub startsWith($$) {
|
||||||
|
my($str, $subStr) = @_;
|
||||||
|
return substr($str, 0, length($subStr)) eq $subStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
# Device define
|
# Device define
|
||||||
sub Define() {
|
sub Define() {
|
||||||
my ($hash, $def) = @_;
|
my ($hash, $def) = @_;
|
||||||
@ -333,6 +362,7 @@ sub Undefine() {
|
|||||||
removeOldUserAttr($hash);
|
removeOldUserAttr($hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# liefert TYPE des IODev, wenn definiert (MQTT; MQTT2,..)
|
||||||
sub retrieveIODev($) {
|
sub retrieveIODev($) {
|
||||||
my ($hash) = @_;
|
my ($hash) = @_;
|
||||||
my $iodn = AttrVal($hash->{NAME}, "IODev", undef);
|
my $iodn = AttrVal($hash->{NAME}, "IODev", undef);
|
||||||
@ -341,10 +371,10 @@ sub retrieveIODev($) {
|
|||||||
$iodt = $defs{$iodn}{TYPE};
|
$iodt = $defs{$iodn}{TYPE};
|
||||||
}
|
}
|
||||||
$hash->{+HELPER}->{+IO_DEV_TYPE} = $iodt;
|
$hash->{+HELPER}->{+IO_DEV_TYPE} = $iodt;
|
||||||
#Log3($hash->{NAME},1,"retrieveIODev: ".Dumper($hash->{+HELPER}->{+IO_DEV_TYPE}));
|
|
||||||
return $hash->{+HELPER}->{+IO_DEV_TYPE};
|
return $hash->{+HELPER}->{+IO_DEV_TYPE};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# prueft, ob IODev MQTT-Instanz ist
|
||||||
sub isIODevMQTT($) {
|
sub isIODevMQTT($) {
|
||||||
my ($hash) = @_;
|
my ($hash) = @_;
|
||||||
my $iodt = retrieveIODev($hash);
|
my $iodt = retrieveIODev($hash);
|
||||||
@ -353,6 +383,7 @@ sub isIODevMQTT($) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# prueft, ob IODev MQTT2-Instanz ist
|
||||||
sub isIODevMQTT2($) {
|
sub isIODevMQTT2($) {
|
||||||
my ($hash) = @_;
|
my ($hash) = @_;
|
||||||
my $iodt = retrieveIODev($hash);
|
my $iodt = retrieveIODev($hash);
|
||||||
@ -361,11 +392,11 @@ sub isIODevMQTT2($) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Fuegt notwendige UserAttr hinzu
|
||||||
sub initUserAttr($) {
|
sub initUserAttr($) {
|
||||||
my ($hash) = @_;
|
my ($hash) = @_;
|
||||||
# wenn bereits ein prefix bestand, die userAttr entfernen : HS_PROP_NAME_PREFIX_OLD != HS_PROP_NAME_PREFIX
|
# wenn bereits ein prefix bestand, die userAttr entfernen : HS_PROP_NAME_PREFIX_OLD != HS_PROP_NAME_PREFIX
|
||||||
my $prefix = $hash->{+HS_PROP_NAME_PREFIX};
|
my $prefix = $hash->{+HS_PROP_NAME_PREFIX};
|
||||||
#$hash->{+HS_PROP_NAME_DEVSPEC} = defined($devspec)?$devspec:".*";
|
|
||||||
my $devspec = $hash->{+HS_PROP_NAME_DEVSPEC};
|
my $devspec = $hash->{+HS_PROP_NAME_DEVSPEC};
|
||||||
$devspec = 'global' if ($devspec eq '.*'); # use global, if all devices observed
|
$devspec = 'global' if ($devspec eq '.*'); # use global, if all devices observed
|
||||||
my $prefix_old = $hash->{+HELPER}->{+HS_PROP_NAME_PREFIX_OLD};
|
my $prefix_old = $hash->{+HELPER}->{+HS_PROP_NAME_PREFIX_OLD};
|
||||||
@ -417,6 +448,7 @@ sub firstInit($) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Vom Timer periodisch aufzurufende Methode
|
||||||
sub timerProc($) {
|
sub timerProc($) {
|
||||||
my ($hash, $refresh_all) = @_;
|
my ($hash, $refresh_all) = @_;
|
||||||
my $name = $hash->{NAME};
|
my $name = $hash->{NAME};
|
||||||
@ -437,6 +469,7 @@ sub isConnected($) {
|
|||||||
return 1 if isIODevMQTT2($hash); #if $hash->{+HELPER}->{+IO_DEV_TYPE} eq 'MQTT2_SERVER';
|
return 1 if isIODevMQTT2($hash); #if $hash->{+HELPER}->{+IO_DEV_TYPE} eq 'MQTT2_SERVER';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Berechnet Anzahl der ueberwachten Geraete neu
|
||||||
sub updateDevCount($) {
|
sub updateDevCount($) {
|
||||||
my $hash = shift;
|
my $hash = shift;
|
||||||
# device count
|
# device count
|
||||||
@ -473,27 +506,27 @@ sub removeOldUserAttr($;$$) {
|
|||||||
# kann spaeter auch delFromDevAttrList Methode genutzt werden
|
# kann spaeter auch delFromDevAttrList Methode genutzt werden
|
||||||
my @devices = devspec2array($devspec);
|
my @devices = devspec2array($devspec);
|
||||||
foreach my $dev (@devices) {
|
foreach my $dev (@devices) {
|
||||||
# TODO: eingekommentieren, alte entfernen, testen
|
# neue Methode im fhem.pl
|
||||||
#delFromDevAttrList($dev,$prefix.CTRL_ATTR_NAME_DEFAULTS);
|
delFromDevAttrList($dev,$prefix.CTRL_ATTR_NAME_DEFAULTS);
|
||||||
#delFromDevAttrList($dev,$prefix.CTRL_ATTR_NAME_ALIAS);
|
delFromDevAttrList($dev,$prefix.CTRL_ATTR_NAME_ALIAS);
|
||||||
#delFromDevAttrList($dev,$prefix.CTRL_ATTR_NAME_PUBLISH);
|
delFromDevAttrList($dev,$prefix.CTRL_ATTR_NAME_PUBLISH);
|
||||||
#delFromDevAttrList($dev,$prefix.CTRL_ATTR_NAME_SUBSCRIBE);
|
delFromDevAttrList($dev,$prefix.CTRL_ATTR_NAME_SUBSCRIBE);
|
||||||
#delFromDevAttrList($dev,$prefix.CTRL_ATTR_NAME_IGNORE);
|
delFromDevAttrList($dev,$prefix.CTRL_ATTR_NAME_IGNORE);
|
||||||
my $ua = $main::attr{$dev}{userattr};
|
# my $ua = $main::attr{$dev}{userattr};
|
||||||
if (defined $ua) {
|
# if (defined $ua) {
|
||||||
my %h = map { ($_ => 1) } split(" ", "$ua");
|
# my %h = map { ($_ => 1) } split(" ", "$ua");
|
||||||
#delete $h{$prefix.CTRL_ATTR_NAME_DEFAULTS};
|
# #delete $h{$prefix.CTRL_ATTR_NAME_DEFAULTS};
|
||||||
delete $h{$prefix.CTRL_ATTR_NAME_DEFAULTS.":textField-long"};
|
# delete $h{$prefix.CTRL_ATTR_NAME_DEFAULTS.":textField-long"};
|
||||||
#delete $h{$prefix.CTRL_ATTR_NAME_ALIAS};
|
# #delete $h{$prefix.CTRL_ATTR_NAME_ALIAS};
|
||||||
delete $h{$prefix.CTRL_ATTR_NAME_ALIAS.":textField-long"};
|
# delete $h{$prefix.CTRL_ATTR_NAME_ALIAS.":textField-long"};
|
||||||
#delete $h{$prefix.CTRL_ATTR_NAME_PUBLISH};
|
# #delete $h{$prefix.CTRL_ATTR_NAME_PUBLISH};
|
||||||
delete $h{$prefix.CTRL_ATTR_NAME_PUBLISH.":textField-long"};
|
# delete $h{$prefix.CTRL_ATTR_NAME_PUBLISH.":textField-long"};
|
||||||
#delete $h{$prefix.CTRL_ATTR_NAME_SUBSCRIBE};
|
# #delete $h{$prefix.CTRL_ATTR_NAME_SUBSCRIBE};
|
||||||
delete $h{$prefix.CTRL_ATTR_NAME_SUBSCRIBE.":textField-long"};
|
# delete $h{$prefix.CTRL_ATTR_NAME_SUBSCRIBE.":textField-long"};
|
||||||
#delete $h{$prefix.CTRL_ATTR_NAME_IGNORE};
|
# #delete $h{$prefix.CTRL_ATTR_NAME_IGNORE};
|
||||||
delete $h{$prefix.CTRL_ATTR_NAME_IGNORE.":both,incoming,outgoing"};
|
# delete $h{$prefix.CTRL_ATTR_NAME_IGNORE.":both,incoming,outgoing"};
|
||||||
$main::attr{$dev}{userattr} = join(" ", sort keys %h);
|
# $main::attr{$dev}{userattr} = join(" ", sort keys %h);
|
||||||
}
|
# }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -539,6 +572,10 @@ sub _takeDefaults($$$$) {
|
|||||||
$map->{$dev}->{':defaults'}->{'sub:'.$key}=$valMap->{'sub:'.$key} if defined($valMap->{'sub:'.$key});
|
$map->{$dev}->{':defaults'}->{'sub:'.$key}=$valMap->{'sub:'.$key} if defined($valMap->{'sub:'.$key});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Erstellt Strukturen fuer 'Defaults' fuer ein bestimmtes Geraet.
|
||||||
|
# Params: Bridge-Hash, Dev-Name (im Map, ist auch = DevName),
|
||||||
|
# Internes Map mit allen Definitionen fuer alle Gerate,
|
||||||
|
# Attribute-Value zum Parsen
|
||||||
sub CreateSingleDeviceTableAttrDefaults($$$$) {
|
sub CreateSingleDeviceTableAttrDefaults($$$$) {
|
||||||
my($hash, $dev, $map, $attrVal) = @_;
|
my($hash, $dev, $map, $attrVal) = @_;
|
||||||
# collect defaults
|
# collect defaults
|
||||||
@ -546,16 +583,23 @@ sub CreateSingleDeviceTableAttrDefaults($$$$) {
|
|||||||
if(defined $attrVal) {
|
if(defined $attrVal) {
|
||||||
# format: [pub:|sub:]base=ha/wz/ [pub:|sub:]qos=0 [pub:|sub:]retain=0
|
# format: [pub:|sub:]base=ha/wz/ [pub:|sub:]qos=0 [pub:|sub:]retain=0
|
||||||
my($unnamed, $named) = MQTT::parseParams($attrVal,'\s',' ','='); #main::parseParams($attrVal);
|
my($unnamed, $named) = MQTT::parseParams($attrVal,'\s',' ','='); #main::parseParams($attrVal);
|
||||||
_takeDefaults($map, $dev, $named, 'base');
|
foreach my $param (keys %{$named}) {
|
||||||
_takeDefaults($map, $dev, $named, 'qos');
|
_takeDefaults($map, $dev, $named, $param);
|
||||||
_takeDefaults($map, $dev, $named, 'retain');
|
}
|
||||||
_takeDefaults($map, $dev, $named, 'expression');
|
# _takeDefaults($map, $dev, $named, 'base');
|
||||||
|
# _takeDefaults($map, $dev, $named, 'qos');
|
||||||
|
# _takeDefaults($map, $dev, $named, 'retain');
|
||||||
|
# _takeDefaults($map, $dev, $named, 'expression');
|
||||||
return defined($map->{$dev}->{':defaults'});
|
return defined($map->{$dev}->{':defaults'});
|
||||||
} else {
|
} else {
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Erstellt Strukturen fuer 'Alias' fuer ein bestimmtes Geraet.
|
||||||
|
# Params: Bridge-Hash, Dev-Name (im Map, ist auch = DevName),
|
||||||
|
# Internes Map mit allen Definitionen fuer alle Gerate,
|
||||||
|
# Attribute-Value zum Parsen
|
||||||
sub CreateSingleDeviceTableAttrAlias($$$$) {
|
sub CreateSingleDeviceTableAttrAlias($$$$) {
|
||||||
my($hash, $dev, $map, $attrVal) = @_;
|
my($hash, $dev, $map, $attrVal) = @_;
|
||||||
delete ($map->{$dev}->{':alias'});
|
delete ($map->{$dev}->{':alias'});
|
||||||
@ -584,6 +628,10 @@ sub CreateSingleDeviceTableAttrAlias($$$$) {
|
|||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Erstellt Strukturen fuer 'Publish' fuer ein bestimmtes Geraet.
|
||||||
|
# Params: Bridge-Hash, Dev-Name (im Map, ist auch = DevName),
|
||||||
|
# Internes Map mit allen Definitionen fuer alle Gerate,
|
||||||
|
# Attribute-Value zum Parsen
|
||||||
sub CreateSingleDeviceTableAttrPublish($$$$) {
|
sub CreateSingleDeviceTableAttrPublish($$$$) {
|
||||||
my($hash, $dev, $map, $attrVal) = @_;
|
my($hash, $dev, $map, $attrVal) = @_;
|
||||||
#Log3($hash->{NAME},1,"MQTT-GB:DEBUG:> CreateSingleDeviceTableAttrPublish: $dev, $attrVal, ".Dumper($map));
|
#Log3($hash->{NAME},1,"MQTT-GB:DEBUG:> CreateSingleDeviceTableAttrPublish: $dev, $attrVal, ".Dumper($map));
|
||||||
@ -614,17 +662,12 @@ sub CreateSingleDeviceTableAttrPublish($$$$) {
|
|||||||
my $namePart = shift(@nameParts);
|
my $namePart = shift(@nameParts);
|
||||||
next if($namePart eq "");
|
next if($namePart eq "");
|
||||||
$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'} = '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'}->{$name}->{$ident}=$val;
|
|
||||||
} #elsif ($ident eq 'qos') {
|
|
||||||
# # Sonderfaelle wie '*:' werden hier einfach abgespeichert und spaeter bei der Suche verwendet
|
|
||||||
# # Gueltige Werte 0, 1 oder 2 (wird nicht geprueft)
|
|
||||||
# $map->{$dev}->{':publish'}->{$name}->{$ident}=$val;
|
|
||||||
#} elsif ($ident eq 'retain') {
|
|
||||||
# # Sonderfaelle wie '*:' werden hier einfach abgespeichert und spaeter bei der Suche verwendet
|
|
||||||
# # Gueltige Werte 0 oder 1 (wird nicht geprueft)
|
|
||||||
# $map->{$dev}->{':publish'}->{$name}->{$ident}=$val;
|
|
||||||
#}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -632,7 +675,6 @@ sub CreateSingleDeviceTableAttrPublish($$$$) {
|
|||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
#########################################################################################
|
|
||||||
# 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)
|
||||||
# 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
|
||||||
@ -689,6 +731,12 @@ sub getDevicePublishRecIntern($$$$$) {
|
|||||||
$atopic = $globalReadingMap->{'atopic'} if (defined($globalReadingMap) and !defined($atopic));
|
$atopic = $globalReadingMap->{'atopic'} if (defined($globalReadingMap) and !defined($atopic));
|
||||||
$atopic = $globalDefaultReadingMap->{'atopic'} if (defined($globalDefaultReadingMap) and !defined($atopic));
|
$atopic = $globalDefaultReadingMap->{'atopic'} if (defined($globalDefaultReadingMap) and !defined($atopic));
|
||||||
|
|
||||||
|
# qos & retain & expression
|
||||||
|
my($qos, $retain, $expression) = retrieveQosRetainExpression($globalDefaultReadingMap, $globalReadingMap, $defaultReadingMap, $readingMap);
|
||||||
|
|
||||||
|
# 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));
|
||||||
|
|
||||||
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:'.$reading};
|
||||||
@ -698,28 +746,23 @@ sub getDevicePublishRecIntern($$$$$) {
|
|||||||
}
|
}
|
||||||
$name = $reading unless defined $name;
|
$name = $reading unless defined $name;
|
||||||
|
|
||||||
my $base = undef;
|
my $mode = $readingMap->{'mode'};
|
||||||
my $gbase = undef;
|
|
||||||
my $dbase = undef;
|
my $combined = computeDefaults($hash, 'pub:', $globalMap, $devMap, {'device'=>$dev,'reading'=>$reading,'name'=>$name,'mode'=>$mode});
|
||||||
if (defined($devMap) and defined($devMap->{':defaults'})) {
|
# $topic evaluieren (avialable vars: $device (device name), $reading (oringinal name), $name ($reading oder alias, if defined), defaults)
|
||||||
$dbase = $devMap->{':defaults'}->{'pub:base'};
|
|
||||||
}
|
|
||||||
if (defined($globalMap) and defined($globalMap->{':defaults'}) and !defined($base)) {
|
|
||||||
$gbase = $globalMap->{':defaults'}->{'pub:base'};
|
|
||||||
}
|
|
||||||
$dbase='' unless defined $dbase;
|
|
||||||
$gbase='' unless defined $gbase;
|
|
||||||
#Log3('xxx',1,"MQTT-GB:DEBUG:> getDevicePublishRec> vor eval: (base, dev, reading, name) $base, $dev, $reading, $name");
|
|
||||||
# eval
|
|
||||||
$base = _evalValue($hash->{NAME},$dbase,$gbase,$dev,$reading,$name);
|
|
||||||
$base='' unless defined $base;
|
|
||||||
# $topic evaluieren (avialable vars: $base, $dev (device name), $reading (oringinal name), $name ($reading oder alias, if defined))
|
|
||||||
if(defined($topic) and ($topic =~ m/^{.*}$/)) {
|
if(defined($topic) and ($topic =~ m/^{.*}$/)) {
|
||||||
$topic = _evalValue($hash->{NAME},$topic,$base,$dev,$reading,$name) if defined $topic;
|
$topic = _evalValue2($hash->{NAME},$topic,{'topic'=>$topic,'device'=>$dev,'reading'=>$reading,'name'=>$name,%$combined}) if defined $topic;
|
||||||
$atopic = _evalValue($hash->{NAME},$atopic,$base,$dev,$reading,$name) if defined $atopic;
|
}
|
||||||
|
if(defined($atopic) and ($atopic =~ m/^{.*}$/)) {
|
||||||
|
$atopic = _evalValue2($hash->{NAME},$atopic,{'topic'=>$atopic,'device'=>$dev,'reading'=>$reading,'name'=>$name,%$combined}) if defined $atopic;
|
||||||
}
|
}
|
||||||
|
|
||||||
# qos & retain & expression
|
return {'topic'=>$topic,'atopic'=>$atopic,'qos'=>$qos,'retain'=>$retain,'expression'=>$expression,'name'=>$name,'mode'=>$mode,'.defaultMap'=>$combined};
|
||||||
|
}
|
||||||
|
|
||||||
|
# sucht Qos, Retain, Expression Werte unter Beruecksichtigung von Defaults und Globals
|
||||||
|
sub retrieveQosRetainExpression($$$$) {
|
||||||
|
my($globalDefaultReadingMap, $globalReadingMap, $defaultReadingMap, $readingMap) = @_;
|
||||||
my $qos=undef;
|
my $qos=undef;
|
||||||
my $retain = undef;
|
my $retain = undef;
|
||||||
my $expression = undef;
|
my $expression = undef;
|
||||||
@ -771,10 +814,81 @@ sub getDevicePublishRecIntern($$$$$) {
|
|||||||
$qos = 0 unless defined $qos;
|
$qos = 0 unless defined $qos;
|
||||||
$retain = 0 unless defined $retain;
|
$retain = 0 unless defined $retain;
|
||||||
|
|
||||||
return {'topic'=>$topic,'atopic'=>$atopic,'qos'=>$qos,'retain'=>$retain,'expression'=>$expression,'base'=>$base, 'name'=>$name};
|
return ($qos, $retain, $expression);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub _evalValue($$$$$$) {
|
# Evaluiert Werte in Default, wenn diese Variable / Perl-Expressions enthalten
|
||||||
|
sub computeDefaults($$$$$) {
|
||||||
|
my($hash, $modifier, $globalMap, $devMap, $infoMap) = @_;
|
||||||
|
#Log3('xxx',1,"MQTT-GB:DEBUG:> computeDefaults> infoMap: ".Dumper($infoMap));
|
||||||
|
my $mdLng = length($modifier);
|
||||||
|
my $defaultCombined={};
|
||||||
|
$infoMap = {} unless defined $infoMap;
|
||||||
|
if (defined($globalMap) and defined($globalMap->{':defaults'})) {
|
||||||
|
foreach my $param (keys %{$globalMap->{':defaults'}} ) {
|
||||||
|
if(startsWith($param,$modifier)) {
|
||||||
|
my $key = substr($param,$mdLng);
|
||||||
|
my $val = $globalMap->{':defaults'}->{$param};
|
||||||
|
#Log3('xxx',1,"MQTT-GB:DEBUG:> computeDefaults> global eval: key: $key, val: $val");
|
||||||
|
$val = _evalValue2($hash->{NAME},$val,$infoMap);
|
||||||
|
#Log3('xxx',1,"MQTT-GB:DEBUG:> computeDefaults> global eval done: val: $val");
|
||||||
|
$defaultCombined->{$key}=$val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
my $devCombined={};
|
||||||
|
if (defined($devMap) and defined($devMap->{':defaults'})) {
|
||||||
|
foreach my $param (keys %{$devMap->{':defaults'}} ) {
|
||||||
|
if(startsWith($param,$modifier)) {
|
||||||
|
my $key = substr($param,$mdLng);
|
||||||
|
my $val = $devMap->{':defaults'}->{$param};
|
||||||
|
#$val = _evalValue2($hash->{NAME},$val,$defaultCombined);
|
||||||
|
$devCombined->{$key}=$val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach my $param (keys %{$devCombined} ) {
|
||||||
|
my $val = $devCombined->{$param};
|
||||||
|
$devCombined->{$param} = _evalValue2($hash->{NAME},$val,{%$defaultCombined, %$infoMap});
|
||||||
|
}
|
||||||
|
my $combined = {%$defaultCombined, %$devCombined};
|
||||||
|
return $combined;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Ersetzt im $str alle Variable $xxx durch entsprechende Werte aus dem Map {xxx=>wert, xxy=>wert2}
|
||||||
|
# Ersetzt wird jedoch nur dann, wenn $str mit '{' anfaengt und mit '}' endet.
|
||||||
|
# Nach dem Ersetzen wird (je $noEval-Wert) Perl-eval durchgefuehrt
|
||||||
|
sub _evalValue2($$;$$) {
|
||||||
|
my($mod, $str, $map, $noEval) = @_;
|
||||||
|
$noEval = 0 unless defined $noEval;
|
||||||
|
#Log3('xxx',1,"MQTT-GB:DEBUG:> eval2: str: $str; map: ".Dumper($map));
|
||||||
|
my$ret = $str;
|
||||||
|
if($str =~ m/^{.*}$/) {
|
||||||
|
no strict "refs";
|
||||||
|
local $@;
|
||||||
|
if(defined($map)) {
|
||||||
|
foreach my $param (keys %{$map}) {
|
||||||
|
my $val = $map->{$param};
|
||||||
|
my $pname = '$'.$param;
|
||||||
|
$val=$pname unless defined $val;
|
||||||
|
#Log3('xxx',1,"MQTT-GB:DEBUG:> replace2: $ret : $pname => $val");
|
||||||
|
$ret =~ s/\Q$pname\E/$val/g;
|
||||||
|
#Log3('xxx',1,"MQTT-GB:DEBUG:> replace2 done: $ret");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#Log3('xxx',1,"MQTT-GB:DEBUG:> eval2 !!!");
|
||||||
|
$ret = eval($ret) unless $noEval;
|
||||||
|
#Log3('xxx',1,"MQTT-GB:DEBUG:> eval2 done: $ret");
|
||||||
|
if ($@) {
|
||||||
|
Log3($mod,2,"user value ('".$str."'') eval error: ".$@);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Alte Methode, verwendet noch fixe Variable (base, dev, reading, name), kein Map
|
||||||
|
# soll durch _evalValue2 ersetzt werden
|
||||||
|
sub _evalValue($$;$$$$) {
|
||||||
my($mod, $str, $base, $device, $reading, $name) = @_;
|
my($mod, $str, $base, $device, $reading, $name) = @_;
|
||||||
#Log3('xxx',1,"MQTT-GB:DEBUG:> eval: (str, base, dev, reading, name) $str, $base, $device, $reading, $name");
|
#Log3('xxx',1,"MQTT-GB:DEBUG:> eval: (str, base, dev, reading, name) $str, $base, $device, $reading, $name");
|
||||||
my$ret = $str;
|
my$ret = $str;
|
||||||
@ -833,6 +947,8 @@ sub searchDeviceForTopic($$) {
|
|||||||
return $ret;
|
return $ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Erstellt RexExp-Definitionen zum Erkennen der ankommenden Topics
|
||||||
|
# Platzhaltern werden entsprechend verarbeitet
|
||||||
sub createRegexpForTopic($) {
|
sub createRegexpForTopic($) {
|
||||||
my $t = shift;
|
my $t = shift;
|
||||||
$t =~ s|#$|.\*|;
|
$t =~ s|#$|.\*|;
|
||||||
@ -847,9 +963,14 @@ sub createRegexpForTopic($) {
|
|||||||
return "^$t\$";
|
return "^$t\$";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Erstellt Strukturen fuer 'Subscribe' fuer ein bestimmtes Geraet.
|
||||||
|
# Params: Bridge-Hash, Dev-Name (im Map, ist auch = DevName),
|
||||||
|
# Internes Map mit allen Definitionen fuer alle Gerate,
|
||||||
|
# Attribute-Value zum Parsen
|
||||||
sub CreateSingleDeviceTableAttrSubscribe($$$$) {
|
sub CreateSingleDeviceTableAttrSubscribe($$$$) {
|
||||||
my($hash, $dev, $map, $attrVal) = @_;
|
my($hash, $dev, $map, $attrVal) = @_;
|
||||||
#Log3($hash->{NAME},1,"MQTT-GB:DEBUG:> CreateSingleDeviceTableAttrSubscribe: $dev, $attrVal, ".Dumper($map));
|
#Log3($hash->{NAME},1,"MQTT-GB:DEBUG:> CreateSingleDeviceTableAttrSubscribe: $dev, $attrVal, ".Dumper($map));
|
||||||
|
#Log3($hash->{NAME},1,"MQTT-GB:DEBUG:> CreateSingleDeviceTableAttrSubscribe: ".Dumper($map));
|
||||||
# collect subscribe topics
|
# collect subscribe topics
|
||||||
my $devMap = $map->{$dev};
|
my $devMap = $map->{$dev};
|
||||||
my $globalMap = $map->{':global'};
|
my $globalMap = $map->{':global'};
|
||||||
@ -870,6 +991,7 @@ sub CreateSingleDeviceTableAttrSubscribe($$$$) {
|
|||||||
#Log3($hash->{NAME},1,"MQTT-GB:DEBUG:> CreateSingleDeviceTableAttrSubscribe: parseParams: named ".Dumper($named));
|
#Log3($hash->{NAME},1,"MQTT-GB:DEBUG:> CreateSingleDeviceTableAttrSubscribe: parseParams: named ".Dumper($named));
|
||||||
#Log3($hash->{NAME},1,"MQTT-GB:DEBUG:> CreateSingleDeviceTableAttrSubscribe: parseParams: unnamed ".Dumper($unnamed));
|
#Log3($hash->{NAME},1,"MQTT-GB:DEBUG:> CreateSingleDeviceTableAttrSubscribe: parseParams: unnamed ".Dumper($unnamed));
|
||||||
if(defined($named)){
|
if(defined($named)){
|
||||||
|
#Log3($hash->{NAME},1,"MQTT-GB:DEBUG:> CreateSingleDeviceTableAttrSubscribe: ".Dumper($map));
|
||||||
my $dmap = {};
|
my $dmap = {};
|
||||||
foreach my $param (keys %{$named}) {
|
foreach my $param (keys %{$named}) {
|
||||||
my $val = $named->{$param};
|
my $val = $named->{$param};
|
||||||
@ -933,6 +1055,7 @@ sub CreateSingleDeviceTableAttrSubscribe($$$$) {
|
|||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Prueft, ob Geraete keine Definitionen mehr enthalten und entfernt diese ggf. aus der Tabelle
|
||||||
sub deleteEmptyDevices($$$) {
|
sub deleteEmptyDevices($$$) {
|
||||||
my ($hash, $map, $devMapName) = @_;
|
my ($hash, $map, $devMapName) = @_;
|
||||||
return unless defined $map;
|
return unless defined $map;
|
||||||
@ -945,6 +1068,10 @@ sub deleteEmptyDevices($$$) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Erstellt alle Strukturen fuer fuer ein bestimmtes Geraet (Default, Alias, Publish, Subscribe).
|
||||||
|
# Params: Bridge-Hash, Dev-Name , Dev-Map-Name (meist = DevName, kann aber auch ein Pseudegeraet wie ':global' sein),
|
||||||
|
# Attr-prefix (idR 'mqtt')
|
||||||
|
# Internes Map mit allen Definitionen fuer alle Gerate,
|
||||||
sub CreateSingleDeviceTable($$$$$) {
|
sub CreateSingleDeviceTable($$$$$) {
|
||||||
my ($hash, $dev, $devMapName, $prefix, $map) = @_;
|
my ($hash, $dev, $devMapName, $prefix, $map) = @_;
|
||||||
# Divece-Attribute fuer ein bestimmtes Device aus Device-Attributen auslesen
|
# Divece-Attribute fuer ein bestimmtes Device aus Device-Attributen auslesen
|
||||||
@ -955,6 +1082,7 @@ sub CreateSingleDeviceTable($$$$$) {
|
|||||||
deleteEmptyDevices($hash, $map, $devMapName);
|
deleteEmptyDevices($hash, $map, $devMapName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Geraet-Infos neu einlesen
|
||||||
sub _RefreshDeviceTable($$$$;$$) {
|
sub _RefreshDeviceTable($$$$;$$) {
|
||||||
my ($hash, $dev, $devMapName, $prefix, $attrName, $attrVal) = @_;
|
my ($hash, $dev, $devMapName, $prefix, $attrName, $attrVal) = @_;
|
||||||
#Log3($hash->{NAME},1,"MQTT-GB:DEBUG:> _RefreshDeviceTable: $dev, $devMapName, $prefix, $attrName, $attrVal");
|
#Log3($hash->{NAME},1,"MQTT-GB:DEBUG:> _RefreshDeviceTable: $dev, $devMapName, $prefix, $attrName, $attrVal");
|
||||||
@ -975,18 +1103,30 @@ sub _RefreshDeviceTable($$$$;$$) {
|
|||||||
UpdateSubscriptionsSingleDevice($hash, $dev);
|
UpdateSubscriptionsSingleDevice($hash, $dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Geraet-Infos neu einlesen
|
||||||
sub RefreshDeviceTable($$;$$) {
|
sub RefreshDeviceTable($$;$$) {
|
||||||
my ($hash, $dev, $attrName, $attrVal) = @_;
|
my ($hash, $dev, $attrName, $attrVal) = @_;
|
||||||
my $prefix = $hash->{+HS_PROP_NAME_PREFIX};
|
my $prefix = $hash->{+HS_PROP_NAME_PREFIX};
|
||||||
_RefreshDeviceTable($hash, $dev, $dev, $prefix, $attrName, $attrVal);
|
_RefreshDeviceTable($hash, $dev, $dev, $prefix, $attrName, $attrVal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub RefreshGlobalTableAll($) {
|
||||||
|
my ($hash) = @_;
|
||||||
|
my $name = $hash->{NAME};
|
||||||
|
RefreshGlobalTable($hash, CTRL_ATTR_NAME_GLOBAL_PREFIX.CTRL_ATTR_NAME_DEFAULTS, AttrVal($name,CTRL_ATTR_NAME_GLOBAL_PREFIX.CTRL_ATTR_NAME_DEFAULTS, undef));
|
||||||
|
RefreshGlobalTable($hash, CTRL_ATTR_NAME_GLOBAL_PREFIX.CTRL_ATTR_NAME_ALIAS, AttrVal($name,CTRL_ATTR_NAME_GLOBAL_PREFIX.CTRL_ATTR_NAME_ALIAS, undef));
|
||||||
|
RefreshGlobalTable($hash, CTRL_ATTR_NAME_GLOBAL_PREFIX.CTRL_ATTR_NAME_PUBLISH, AttrVal($name,CTRL_ATTR_NAME_GLOBAL_PREFIX.CTRL_ATTR_NAME_PUBLISH, undef));
|
||||||
|
#RefreshGlobalTable($hash, CTRL_ATTR_NAME_GLOBAL_PREFIX.CTRL_ATTR_NAME_SUBSCRIBE, AttrVal($name,CTRL_ATTR_NAME_GLOBAL_PREFIX.CTRL_ATTR_NAME_SUBSCRIBE, undef));
|
||||||
|
}
|
||||||
|
|
||||||
|
# GlobalTable-Infos neu einlesen fuer einen bestimmten Attribut
|
||||||
sub RefreshGlobalTable($;$$) {
|
sub RefreshGlobalTable($;$$) {
|
||||||
my ($hash, $attrName, $attrVal) = @_;
|
my ($hash, $attrName, $attrVal) = @_;
|
||||||
my $prefix = CTRL_ATTR_NAME_GLOBAL_PREFIX;
|
my $prefix = CTRL_ATTR_NAME_GLOBAL_PREFIX;
|
||||||
_RefreshDeviceTable($hash, $hash->{NAME}, ":global", $prefix, $attrName, $attrVal);
|
_RefreshDeviceTable($hash, $hash->{NAME}, ':global', $prefix, $attrName, $attrVal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Geraet umbenennen, wird aufgerufen, wenn ein Geraet in FHEM umbenannt wird
|
||||||
sub RenameDeviceInTable($$$) {
|
sub RenameDeviceInTable($$$) {
|
||||||
my($hash, $dev, $devNew) = @_;
|
my($hash, $dev, $devNew) = @_;
|
||||||
my $map = $hash->{+HS_TAB_NAME_DEVICES};
|
my $map = $hash->{+HS_TAB_NAME_DEVICES};
|
||||||
@ -999,6 +1139,7 @@ sub RenameDeviceInTable($$$) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Geraet loeschen (geloescht in FHEM)
|
||||||
sub DeleteDeviceInTable($$) {
|
sub DeleteDeviceInTable($$) {
|
||||||
my($hash, $dev) = @_;
|
my($hash, $dev) = @_;
|
||||||
my $map = $hash->{+HS_TAB_NAME_DEVICES};
|
my $map = $hash->{+HS_TAB_NAME_DEVICES};
|
||||||
@ -1008,10 +1149,14 @@ sub DeleteDeviceInTable($$) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# alle zu ueberwachende Geraete durchsuchen und relevanter Informationen einlesen
|
||||||
sub CreateDevicesTable($) {
|
sub CreateDevicesTable($) {
|
||||||
my ($hash) = @_;
|
my ($hash) = @_;
|
||||||
# alle zu ueberwachende Geraete durchgehen und Attribute erfassen
|
# alle zu ueberwachende Geraete durchgehen und Attribute erfassen
|
||||||
my $map={};
|
my $map={};
|
||||||
|
$hash->{+HS_TAB_NAME_DEVICES} = $map;
|
||||||
|
RefreshGlobalTableAll($hash);
|
||||||
|
$map = $hash->{+HS_TAB_NAME_DEVICES};
|
||||||
|
|
||||||
my @devices = devspec2array($hash->{+HS_PROP_NAME_DEVSPEC});
|
my @devices = devspec2array($hash->{+HS_PROP_NAME_DEVSPEC});
|
||||||
#Log3($hash->{NAME},1,"MQTT-GB:DEBUG:> CreateDevicesTable: ".Dumper(@devices));
|
#Log3($hash->{NAME},1,"MQTT-GB:DEBUG:> CreateDevicesTable: ".Dumper(@devices));
|
||||||
@ -1031,6 +1176,7 @@ sub CreateDevicesTable($) {
|
|||||||
$hash->{+HELPER}->{+HS_FLAG_INITIALIZED} = 1;
|
$hash->{+HELPER}->{+HS_FLAG_INITIALIZED} = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Ueberbleibsel eines Optimierungsversuchs
|
||||||
sub UpdateSubscriptionsSingleDevice($$) {
|
sub UpdateSubscriptionsSingleDevice($$) {
|
||||||
my ($hash, $dev) = @_;
|
my ($hash, $dev) = @_;
|
||||||
# Liste der Geraete mit der Liste der Subscriptions abgleichen
|
# Liste der Geraete mit der Liste der Subscriptions abgleichen
|
||||||
@ -1039,6 +1185,7 @@ sub UpdateSubscriptionsSingleDevice($$) {
|
|||||||
UpdateSubscriptions($hash);
|
UpdateSubscriptions($hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Alle MQTT-Subscriptions erneuern
|
||||||
sub UpdateSubscriptions($) {
|
sub UpdateSubscriptions($) {
|
||||||
my ($hash) = @_;
|
my ($hash) = @_;
|
||||||
|
|
||||||
@ -1095,6 +1242,7 @@ sub UpdateSubscriptions($) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Alle MQTT-Subscription erntfernen
|
||||||
sub RemoveAllSubscripton($) {
|
sub RemoveAllSubscripton($) {
|
||||||
my ($hash) = @_;
|
my ($hash) = @_;
|
||||||
|
|
||||||
@ -1120,6 +1268,7 @@ sub InitializeDevices($) {
|
|||||||
#UpdateSubscriptions($hash);
|
#UpdateSubscriptions($hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Falls noetig, Geraete initialisieren
|
||||||
sub CheckInitialization($) {
|
sub CheckInitialization($) {
|
||||||
my ($hash) = @_;
|
my ($hash) = @_;
|
||||||
# Pruefen, on interne Strukturen initialisiert sind
|
# Pruefen, on interne Strukturen initialisiert sind
|
||||||
@ -1127,12 +1276,14 @@ sub CheckInitialization($) {
|
|||||||
InitializeDevices($hash);
|
InitializeDevices($hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Zusaetzliche Attribute im Debug-Modus
|
||||||
my %getsDebug = (
|
my %getsDebug = (
|
||||||
"debugInfo" => "",
|
"debugInfo" => "",
|
||||||
"debugReinit" => "",
|
"debugReinit" => "",
|
||||||
"debugShowPubRec" => ""
|
"debugShowPubRec" => ""
|
||||||
);
|
);
|
||||||
|
|
||||||
|
# Routine fuer FHEM Get-Commando
|
||||||
sub Get($$$@) {
|
sub Get($$$@) {
|
||||||
my ($hash, $name, $command, $args) = @_;
|
my ($hash, $name, $command, $args) = @_;
|
||||||
return "Need at least one parameters" unless (defined $command);
|
return "Need at least one parameters" unless (defined $command);
|
||||||
@ -1156,8 +1307,6 @@ sub Get($$$@) {
|
|||||||
}
|
}
|
||||||
return $rstr;
|
return $rstr;
|
||||||
}
|
}
|
||||||
#return "Unknown argument $command, choose one of " . join(" ", sort keys %gets)
|
|
||||||
# unless (defined($gets{$command}));
|
|
||||||
|
|
||||||
COMMAND_HANDLER: {
|
COMMAND_HANDLER: {
|
||||||
$command eq "debugInfo" and isDebug($hash) and do {
|
$command eq "debugInfo" and isDebug($hash) and do {
|
||||||
@ -1214,13 +1363,26 @@ sub Get($$$@) {
|
|||||||
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);
|
||||||
if(defined($pubRec)) {
|
if(defined($pubRec)) {
|
||||||
my $topic = $pubRec->{'topic'};
|
my $expression = $pubRec->{'expression'};
|
||||||
$topic = '---' unless defined $topic;
|
my $mode = $pubRec->{'mode'};
|
||||||
|
$mode='E' if(defined($expression) and !defined($mode));
|
||||||
|
my $topic = undef;
|
||||||
|
if($mode eq 'R') {
|
||||||
|
$topic = $pubRec->{'topic'};
|
||||||
|
} elsif($mode eq 'A') {
|
||||||
|
$topic = $pubRec->{'atopic'};
|
||||||
|
} elsif($mode eq 'E') {
|
||||||
|
$topic = '[expression]';
|
||||||
|
} else {
|
||||||
|
$topic = '!unexpected mode!';
|
||||||
|
}
|
||||||
|
$topic = 'undefined' unless defined $topic;
|
||||||
my $qos = $pubRec->{'qos'};
|
my $qos = $pubRec->{'qos'};
|
||||||
my $retain = $pubRec->{'retain'};
|
my $retain = $pubRec->{'retain'};
|
||||||
my $expression = $pubRec->{'expression'};
|
|
||||||
$res.= sprintf(' %-16s => %s', $rname, $topic);
|
$res.= sprintf(' %-16s => %s', $rname, $topic);
|
||||||
$res.= " (qos: $qos";
|
$res.= " (";
|
||||||
|
$res.= "mode: $mode";
|
||||||
|
$res.= "; qos: $qos";
|
||||||
$res.= "; retain" if ($retain ne "0");
|
$res.= "; retain" if ($retain ne "0");
|
||||||
$res.= ")\n";
|
$res.= ")\n";
|
||||||
$res.= " exp: $expression\n" if defined ($expression);
|
$res.= " exp: $expression\n" if defined ($expression);
|
||||||
@ -1231,12 +1393,34 @@ sub Get($$$@) {
|
|||||||
my $qos = $subRec->{'qos'};
|
my $qos = $subRec->{'qos'};
|
||||||
my $mode = $subRec->{'mode'};
|
my $mode = $subRec->{'mode'};
|
||||||
my $expression = $subRec->{'expression'};
|
my $expression = $subRec->{'expression'};
|
||||||
$res.= sprintf(' %-16s <= %s', $subRec->{'reading'}, $subRec->{'topic'});
|
my $topic = $subRec->{'topic'};
|
||||||
|
$topic = '---' unless defined $topic;
|
||||||
|
$res.= sprintf(' %-16s <= %s', $subRec->{'reading'}, $topic);
|
||||||
$res.= " (mode: $mode";
|
$res.= " (mode: $mode";
|
||||||
$res.= "; qos: $qos" if defined ($qos);
|
$res.= "; qos: $qos" if defined ($qos);
|
||||||
$res.= ")\n";
|
$res.= ")\n";
|
||||||
$res.= " exp: $expression\n" if defined ($expression);
|
$res.= " exp: $expression\n" if defined ($expression);
|
||||||
# TODO
|
# TODO
|
||||||
|
# my $qos = $subRec->{'qos'};
|
||||||
|
# my $mode = $subRec->{'mode'};
|
||||||
|
# my $expression = $subRec->{'expression'};
|
||||||
|
# my $topic = "---";
|
||||||
|
# if($mode eq 'R') {
|
||||||
|
# $topic = $subRec->{'topic'};
|
||||||
|
# } elsif($mode eq 'S') {
|
||||||
|
# $topic = $subRec->{'stopic'};
|
||||||
|
# } elsif($mode eq 'A') {
|
||||||
|
# $topic = $subRec->{'atopic'};
|
||||||
|
# } else {
|
||||||
|
# $topic = '!unexpected mode!';
|
||||||
|
# }
|
||||||
|
# $topic = '---' unless defined $topic;
|
||||||
|
# $res.= sprintf(' %-16s <= %s', $subRec->{'reading'}, $topic);
|
||||||
|
# $res.= " (mode: $mode";
|
||||||
|
# $res.= "; qos: $qos" if defined ($qos);
|
||||||
|
# $res.= ")\n";
|
||||||
|
# $res.= " exp: $expression\n" if defined ($expression);
|
||||||
|
# # TODO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$res.= "\n";
|
$res.= "\n";
|
||||||
@ -1251,6 +1435,8 @@ sub Get($$$@) {
|
|||||||
# };
|
# };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Routine fuer FHEM Notify
|
||||||
sub Notify() {
|
sub Notify() {
|
||||||
my ($hash,$dev) = @_;
|
my ($hash,$dev) = @_;
|
||||||
if( $dev->{NAME} eq "global" ) {
|
if( $dev->{NAME} eq "global" ) {
|
||||||
@ -1450,6 +1636,8 @@ sub isTypeDevReadingExcluded($$$$) {
|
|||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# MQTT-Nachricht senden
|
||||||
|
# Params: Bridge-Hash, Topic, Nachricht, QOS- und Retain-Flags
|
||||||
sub doPublish($$$$$) {
|
sub doPublish($$$$$) {
|
||||||
my ($hash,$topic,$message,$qos,$retain) = @_;
|
my ($hash,$topic,$message,$qos,$retain) = @_;
|
||||||
|
|
||||||
@ -1475,6 +1663,10 @@ sub doPublish($$$$$) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# MQTT-Nachrichten entsprechend Geraete-Infos senden
|
||||||
|
# Params: Bridge-Hash, Device-Hash,
|
||||||
|
# Modus (Topics entsprechend Readings- oder Attributen-Tabelleneintraegen suchen),
|
||||||
|
# Name des Readings/Attributes, Wert
|
||||||
sub publishDeviceUpdate($$$$$) {
|
sub publishDeviceUpdate($$$$$) {
|
||||||
my ($hash, $devHash, $mode, $reading, $value) = @_;
|
my ($hash, $devHash, $mode, $reading, $value) = @_;
|
||||||
my $devn = $devHash->{NAME};
|
my $devn = $devHash->{NAME};
|
||||||
@ -1495,6 +1687,8 @@ sub publishDeviceUpdate($$$$$) {
|
|||||||
if(defined($pubRec)) {
|
if(defined($pubRec)) {
|
||||||
# my $msgid;
|
# my $msgid;
|
||||||
|
|
||||||
|
my $defMap = $pubRec->{'.defaultMap'};
|
||||||
|
|
||||||
my $topic = $pubRec->{'topic'}; # 'normale' Readings
|
my $topic = $pubRec->{'topic'}; # 'normale' Readings
|
||||||
$topic = $pubRec->{'atopic'} if $mode eq 'A'; # Attributaenderungen
|
$topic = $pubRec->{'atopic'} if $mode eq 'A'; # Attributaenderungen
|
||||||
my $qos = $pubRec->{'qos'};
|
my $qos = $pubRec->{'qos'};
|
||||||
@ -1515,9 +1709,8 @@ sub publishDeviceUpdate($$$$$) {
|
|||||||
# Bei einem Hash werden Paare als Topic-Message Paare verwendet und mehrere Nachrichten gesendet
|
# Bei einem Hash werden Paare als Topic-Message Paare verwendet und mehrere Nachrichten gesendet
|
||||||
no strict "refs";
|
no strict "refs";
|
||||||
local $@;
|
local $@;
|
||||||
#Log3($hash->{NAME},1,"MQTT-GB:DEBUG:> vars (base: $base, reading: $reading, msg: $message) !!!");
|
my $ret = _evalValue2($hash->{NAME},$expression,$defMap,1);
|
||||||
#Log3($hash->{NAME},1,"MQTT-GB:DEBUG:> eval ($expression) !!!");
|
$ret = eval($ret);
|
||||||
my $ret = eval($expression);
|
|
||||||
if(ref($ret) eq 'HASH') {
|
if(ref($ret) eq 'HASH') {
|
||||||
$redefMap = $ret;
|
$redefMap = $ret;
|
||||||
} elsif(ref($ret) eq 'ARRAY') {
|
} elsif(ref($ret) eq 'ARRAY') {
|
||||||
@ -1548,6 +1741,7 @@ sub publishDeviceUpdate($$$$$) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Routine fuer FHEM Attr
|
||||||
sub Attr($$$$) {
|
sub Attr($$$$) {
|
||||||
my ($command,$name,$attribute,$value) = @_;
|
my ($command,$name,$attribute,$value) = @_;
|
||||||
|
|
||||||
@ -1642,6 +1836,7 @@ sub Attr($$$$) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# CallBack-Handler fuer IODev beim Connect
|
||||||
sub ioDevConnect($) {
|
sub ioDevConnect($) {
|
||||||
my $hash = shift;
|
my $hash = shift;
|
||||||
return if isIODevMQTT2($hash); #if $hash->{+HELPER}->{+IO_DEV_TYPE} eq 'MQTT2_SERVER';
|
return if isIODevMQTT2($hash); #if $hash->{+HELPER}->{+IO_DEV_TYPE} eq 'MQTT2_SERVER';
|
||||||
@ -1654,6 +1849,7 @@ sub ioDevConnect($) {
|
|||||||
# TODO
|
# TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# CallBack-Handler fuer IODev beim Disconnect
|
||||||
sub ioDevDisconnect($) {
|
sub ioDevDisconnect($) {
|
||||||
my $hash = shift;
|
my $hash = shift;
|
||||||
return if isIODevMQTT2($hash); #if $hash->{+HELPER}->{+IO_DEV_TYPE} eq 'MQTT2_SERVER';
|
return if isIODevMQTT2($hash); #if $hash->{+HELPER}->{+IO_DEV_TYPE} eq 'MQTT2_SERVER';
|
||||||
@ -1663,7 +1859,8 @@ sub ioDevDisconnect($) {
|
|||||||
# TODO
|
# TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Per MQTT-Empfangenen Aktualisierungen an die entsprechende Geraete anwenden
|
||||||
|
# Params: Bridge-Hash, Modus (R=Readings, A=Attribute), Reading/Attribute-Name, Nachricht
|
||||||
sub doSetUpdate($$$$$) {
|
sub doSetUpdate($$$$$) {
|
||||||
my ($hash,$mode,$device,$reading,$message) = @_;
|
my ($hash,$mode,$device,$reading,$message) = @_;
|
||||||
if($mode eq 'S') {
|
if($mode eq 'S') {
|
||||||
@ -1692,10 +1889,11 @@ sub doSetUpdate($$$$$) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Routine MQTT-Message Callback
|
||||||
sub onmessage($$$) {
|
sub onmessage($$$) {
|
||||||
my ($hash,$topic,$message) = @_;
|
my ($hash,$topic,$message) = @_;
|
||||||
CheckInitialization($hash);
|
CheckInitialization($hash);
|
||||||
Log3($hash->{NAME},1,"MQTT_GENERIC_BRIDGE DEBUG: onmessage: $topic => $message");
|
#Log3($hash->{NAME},1,"MQTT_GENERIC_BRIDGE DEBUG: onmessage: $topic => $message");
|
||||||
|
|
||||||
$hash->{+HELPER}->{+HS_PROP_NAME_INCOMING_CNT}++;
|
$hash->{+HELPER}->{+HS_PROP_NAME_INCOMING_CNT}++;
|
||||||
readingsSingleUpdate($hash,"incoming-count",$hash->{+HELPER}->{+HS_PROP_NAME_INCOMING_CNT},1);
|
readingsSingleUpdate($hash,"incoming-count",$hash->{+HELPER}->{+HS_PROP_NAME_INCOMING_CNT},1);
|
||||||
@ -1767,7 +1965,7 @@ sub onmessage($$$) {
|
|||||||
<ul>
|
<ul>
|
||||||
<p>
|
<p>
|
||||||
This module is an MQTT bridge, which simultaneously collects data from several FHEM devices
|
This module is an MQTT bridge, which simultaneously collects data from several FHEM devices
|
||||||
and passes their readings via MQTT or set rreadings from the incoming MQTT messages or executes them
|
and passes their readings via MQTT or set readings from the incoming MQTT messages or executes them
|
||||||
as a 'set' command on the configured FHEM device.
|
as a 'set' command on the configured FHEM device.
|
||||||
<br/>An <a href="#MQTT">MQTT</a> device is needed as IODev.
|
<br/>An <a href="#MQTT">MQTT</a> device is needed as IODev.
|
||||||
</p>
|
</p>
|
||||||
@ -1954,7 +2152,9 @@ sub onmessage($$$) {
|
|||||||
Topics can also be defined as Perl expression with variables($reading, $device, $name, $base).<br/>
|
Topics can also be defined as Perl expression with variables($reading, $device, $name, $base).<br/>
|
||||||
Values for several readings can also be defined together, separated with '|'.<br/>
|
Values for several readings can also be defined together, separated with '|'.<br/>
|
||||||
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/>
|
||||||
It is also possible to define expressions (reading: expression = ...). <br/>
|
Topic can also be written as a 'readings-topic'.<br/>
|
||||||
|
Attributes can also be sent ("atopic" or "attr-topic").
|
||||||
|
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), 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/>
|
||||||
@ -1973,8 +2173,8 @@ sub onmessage($$$) {
|
|||||||
<p><a name="MQTT_GENERIC_BRIDGEmqttSubscribe">mqttSubscribe</a><br/>
|
<p><a name="MQTT_GENERIC_BRIDGEmqttSubscribe">mqttSubscribe</a><br/>
|
||||||
This attribute configured receiving the MQTT messages and the corresponding reactions.<br/>
|
This attribute configured receiving the MQTT messages and the corresponding reactions.<br/>
|
||||||
The configuration is similar to that for the 'mqttPublish' attribute.
|
The configuration is similar to that for the 'mqttPublish' attribute.
|
||||||
Topics can be defined for setting readings ('topic') and calls to the 'set' command on the device ('stopic').<br/>
|
Topics can be defined for setting readings ('topic' or 'readings-topic') and calls to the 'set' command on the device ('stopic' or 'set-topic').<br/>
|
||||||
Also attributes can be set ('atopic').</br>
|
Also attributes can be set ('atopic' or 'attr-topic').</br>
|
||||||
The result can be modified before setting the reading or executing of 'set' / 'attr' on the device with additional Perl expressions ('expression').<br/>
|
The result can be modified before setting the reading or executing of 'set' / 'attr' on the device with additional Perl expressions ('expression').<br/>
|
||||||
The following variables are available in the expression: $device, $reading, $message (initially equal to $value).
|
The following variables are available in the expression: $device, $reading, $message (initially equal to $value).
|
||||||
The expression can either change variable $value, or return a value != undef.
|
The expression can either change variable $value, or return a value != undef.
|
||||||
@ -2307,11 +2507,13 @@ sub onmessage($$$) {
|
|||||||
Hier werden konkrette Topics definiet und den Readings zugeordnet (Format: <reading>:topic=<topic>).
|
Hier werden konkrette Topics definiet und den Readings zugeordnet (Format: <reading>:topic=<topic>).
|
||||||
Weiterhin koennen diese einzeln mit 'qos'- und 'retain'-Flags versehen werden. <br/>
|
Weiterhin koennen diese einzeln mit 'qos'- und 'retain'-Flags versehen werden. <br/>
|
||||||
Topics koennen auch als Perl-Expression mit Variablen definiert werden ($reading, $device, $name, $base).<br/>
|
Topics koennen auch als Perl-Expression mit Variablen definiert werden ($reading, $device, $name, $base).<br/>
|
||||||
|
'topic' kann auch als 'readings-topic' geschrieben werden.<br/>
|
||||||
Werte fuer mehrere Readings koennen auch gemeinsam gleichzeitig definiert werden,
|
Werte fuer mehrere Readings koennen auch gemeinsam gleichzeitig definiert werden,
|
||||||
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/>
|
||||||
Es koennen auch Expressions (reading:expression=...) definiert werden. <br/>
|
Ebenso koennen auch Attributwerte gesendet werden ('atopic' oder 'attr-topic').
|
||||||
|
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) 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/>
|
||||||
@ -2330,9 +2532,9 @@ sub onmessage($$$) {
|
|||||||
<li>
|
<li>
|
||||||
<p><a name="MQTT_GENERIC_BRIDGEmqttSubscribe">mqttSubscribe</a><br/>
|
<p><a name="MQTT_GENERIC_BRIDGEmqttSubscribe">mqttSubscribe</a><br/>
|
||||||
Dieses Attribut konfiguriert das Empfangen der MQTT-Nachrichten und die entsprechenden Reaktionen darauf.<br/>
|
Dieses Attribut konfiguriert das Empfangen der MQTT-Nachrichten und die entsprechenden Reaktionen darauf.<br/>
|
||||||
Die Konfiguration ist aehnlich der fuer das 'mqttPublish'-Attribut. Es koennen Topics fuer das Setzen von Readings ('topic') und
|
Die Konfiguration ist aehnlich der fuer das 'mqttPublish'-Attribut. Es koennen Topics fuer das Setzen von Readings ('topic' oder auch 'readings-topic') und
|
||||||
Aufrufe von 'set'-Befehl an dem Geraet ('stopic') definiert werden. <br/>
|
Aufrufe von 'set'-Befehl an dem Geraet ('stopic' oder 'set-topic') definiert werden. <br/>
|
||||||
Ebenso koennen auch Attribute gesetzt werden ('atopic').</br>
|
Ebenso koennen auch Attribute gesetzt werden ('atopic' oder 'attr-topic').</br>
|
||||||
Mit Hilfe von zusaetzlichen auszufuehrenden Perl-Expressions ('expression') kann das Ergebnis vor dem Setzen/Ausfueren noch beeinflusst werden.<br/>
|
Mit Hilfe von zusaetzlichen auszufuehrenden Perl-Expressions ('expression') kann das Ergebnis vor dem Setzen/Ausfueren noch beeinflusst werden.<br/>
|
||||||
In der Expression sind folgende Variablen verfuegbar: $device, $reading, $message (initial gleich $value).
|
In der Expression sind folgende Variablen verfuegbar: $device, $reading, $message (initial gleich $value).
|
||||||
Die Expression kann dabei entweder Variable $value veraendern, oder einen Wert != undef zurueckgeben. Redefinition der Variable hat Vorrang.
|
Die Expression kann dabei entweder Variable $value veraendern, oder einen Wert != undef zurueckgeben. Redefinition der Variable hat Vorrang.
|
||||||
|
Loading…
Reference in New Issue
Block a user