From ec4ff1de57a4074abada9219056e53ff99b788b0 Mon Sep 17 00:00:00 2001 From: loredo Date: Thu, 24 Sep 2015 12:19:46 +0000 Subject: [PATCH] =?UTF-8?q?99=5Fmsg:=20initial=20commit=20in=20contrib=20f?= =?UTF-8?q?or=20new=20command=20=E2=80=98msg=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git-svn-id: https://svn.fhem.de/fhem/trunk@9293 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/contrib/99_msg.pm | 1843 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1843 insertions(+) create mode 100644 fhem/contrib/99_msg.pm diff --git a/fhem/contrib/99_msg.pm b/fhem/contrib/99_msg.pm new file mode 100644 index 000000000..cf91c57b6 --- /dev/null +++ b/fhem/contrib/99_msg.pm @@ -0,0 +1,1843 @@ +# $Id$ +############################################################################## +# +# 99_msg.pm +# Dynamic message and notification routing for FHEM +# +# Copyright by Julian Pawlowski +# e-mail: julian.pawlowski at gmail.com +# +# This file is part of fhem. +# +# Fhem is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# Fhem is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with fhem. If not, see . +# +# +# Version: 1.0.0 +# +# Major Version History: +# +# - 1.0.0 - 2015-09-23 +# -- First release +# +############################################################################## + +package main; +use strict; +use warnings; +use Time::HiRes qw(time); + +sub CommandMsg($$;$$); + +######################################## +sub msg_Initialize($$) { + my %hash = ( + Fn => "CommandMsg", + Hlp => +"[] [<\@device>|] [] [||] <message>", + ); + $cmds{msg} = \%hash; + + # add attributes for configuration + no warnings 'qw'; + my @attrList = qw( + msgCmdAudio + msgCmdAudioShort + msgCmdAudioShortPrio + msgCmdLight + msgCmdLightHigh + msgCmdLightLow + msgCmdMail + msgCmdMailHigh + msgCmdMailLow + msgCmdPush + msgCmdPushHigh + msgCmdPushLow + msgCmdScreen + msgCmdScreenHigh + msgCmdScreenLow + msgFwPrioAbsentAudio + msgFwPrioAbsentLight + msgFwPrioAbsentScreen + msgFwPrioEmergencyAudio + msgFwPrioEmergencyLight + msgFwPrioEmergencyPush + msgFwPrioEmergencyScreen + msgFwPrioGoneAudio + msgFwPrioGoneLight + msgFwPrioGoneScreen + msgLocationDevs + msgPriorityAudio:-2,-1,0,1,2 + msgPriorityLight:-2,-1,0,1,2 + msgPriorityMail:-2,-1,0,1,2 + msgPriorityPush:-2,-1,0,1,2 + msgPriorityScreen:-2,-1,0,1,2 + msgPriorityText:-2,-1,0,1,2 + msgResidentsDev + msgSwitcherDev + msgTitleAudio + msgTitleAudioShort + msgTitleAudioShortPrio + msgTitleLight + msgTitleLightHigh + msgTitleLightLow + msgTitleMail + msgTitleMailHigh + msgTitleMailLow + msgTitlePush + msgTitlePushHigh + msgTitlePushLow + msgTitleScreen + msgTitleScreenHigh + msgTitleScreenLow + msgTitleText + msgTitleTextHigh + msgTitleTextLow + + ); + use warnings 'qw'; + $modules{Global}{AttrList} .= " " . join( " ", @attrList ); + + # add global attributes + foreach ( + "msgContactAudio", "msgContactMail", "msgContactPush", + "msgContactScreen", "msgContactLight", "msgRecipient", + "msgRecipientAudio", "msgRecipientMail", "msgRecipientPush", + "msgRecipientScreen", "msgRecipientText", "msgRecipientLight", + ) + { + addToAttrList($_); + } +} + +######################################## +sub CommandMsg($$;$$) { + my ( $cl, $msg, $testMode ) = @_; + my $return = ""; + + if ( $msg eq "" || $msg =~ /^\?[\s\t]*$/ || $msg eq "help" ) { + return +"Usage: msg [<type>] [<\@device>|<e-mail address>] [<priority>] [|<title>|] <message>"; + } + + # default commands + my %defaults; + + $defaults{audio}{Normal} = "set \$DEVICE Speak 40 de |\$TITLE| \$MSG"; + $defaults{audio}{ShortPrio} = "set \$DEVICE Speak 30 de |\$TITLE| Achtung!"; + $defaults{audio}{Short} = "set \$DEVICE Speak 30 de |\$TITLE|"; + + $defaults{light}{Normal} = +"{my \$state=ReadingsVal(\"\$DEVICE\",\"state\",\"off\"); fhem \"set \$DEVICE blink 2 1\"; fhem \"sleep 4;set \$DEVICE:FILTER=state!=\$state \$state\"}"; + $defaults{light}{High} = +"{my \$state=ReadingsVal(\"\$DEVICE\",\"state\",\"off\"); fhem \"set \$DEVICE blink 10 1\"; fhem \"sleep 20;set \$DEVICE:FILTER=state!=\$state \$state\"}"; + $defaults{light}{Low} = "set \$DEVICE alert select"; + + $defaults{mail}{Normal} = +"{system(\"echo '\$MSG' | /usr/bin/mail -s '\$TITLE' -t '\$RECIPIENT'\")}"; + $defaults{mail}{High} = +"{system(\"/bin/echo '\$MSG' | /usr/bin/mail -s '\$TITLE' -t '\$RECIPIENT' -a 'MIME-Version: 1.0' -a 'Content-Type: text/html; charset=UTF-8' -a 'X-Priority: 1 (Highest)' -a 'X-MSMail-Priority: High' -a 'Importance: high'\")}"; + $defaults{mail}{Low} = +"{system(\"/bin/echo '\$MSG' | /usr/bin/mail -s '\$TITLE' -t '\$RECIPIENT' -a 'MIME-Version: 1.0' -a 'Content-Type: text/html; charset=UTF-8' -a 'X-Priority: 5 (Lowest)' -a 'X-MSMail-Priority: Low' -a 'Importance: low'\")}"; + + $defaults{push}{Normal} = + "set \$DEVICE msg '\$TITLE' '\$MSG' '' \$PRIORITY ''"; + $defaults{push}{High} = + "set \$DEVICE msg '\$TITLE' '\$MSG' '' \$PRIORITY '' 120 600"; + $defaults{push}{Low} = + "set \$DEVICE msg '\$TITLE' '\$MSG' '' \$PRIORITY ''"; + + $defaults{screen}{Normal} = "set \$DEVICE msg info 8 \$MSG"; + $defaults{screen}{High} = "set \$DEVICE msg attention 12 \$MSG"; + $defaults{screen}{Low} = "set \$DEVICE msg message 8 \$MSG"; + + # default forwards + my %forwards; + $forwards{screen}{gwUnavailable} = "light"; + $forwards{light}{gwUnavailable} = "audio"; + $forwards{audio}{gwUnavailable} = "text"; + $forwards{push}{gwUnavailable} = "mail"; + + $forwards{screen}{emergency} = "light"; + $forwards{light}{emergency} = "audio"; + $forwards{audio}{emergency} = "text"; + $forwards{push}{emergency} = "mail"; + + $forwards{screen}{highPrio}{residentGone} = "light"; + $forwards{light}{highPrio}{residentGone} = "audio"; + $forwards{audio}{highPrio}{residentGone} = "text"; + + $forwards{screen}{highPrio}{residentAbsent} = "light"; + $forwards{light}{highPrio}{residentAbsent} = "audio"; + $forwards{audio}{highPrio}{residentAbsent} = "text"; + + ################################################################ + ### extract message details + ### + + my $types = ""; + my $recipients = ""; + my $priority = ""; + my $title = ""; + + my $priorityCat = ""; + + # check for message types + if ( $msg =~ +s/^[\s\t]*([a-z,]*!?(screen|light|audio|text|push|mail)[a-z,!|]*)[\s\t]+// + ) + { + $types = $1; + } + + # check for given recipients + if ( $msg =~ +s/^[\s\t]*([!]?(([A-Za-z0-9%+._-])*@([%+a-z0-9A-Z.-]+))[\w,@.!|]*)[\s\t]+// + ) + { + $recipients = $1; + } + + # check for given priority + if ( $msg =~ s/^[\s\t]*([-+]{0,1}\d+[.\d]*)[\s\t]*// ) { + $priority = $1; + } + + # check for given message title + if ( $msg =~ +s/^[\s\t]*\|([\w\süöäß^°!"§$%&\/\\()<>=?´`"+\[\]#*@€]+)\|[\s\t]+// + ) + { + $title = $1; + } + + ################################################################ + ### command queue + ### + + $types = "text" + if ( $types eq "" ); + my $messageSent = 0; + my $forwarded = ""; + my %sentTypesPerDevice; + my $sentCounter = 0; + my $messageID = time(); + my $isTypeOr = 1; + my $isRecipientOr = 1; + my $hasTypeOr = 0; + my $hasRecipientOr = 0; + $recipients = "\@global" if ( $recipients eq "" ); + + my @typesOr = split( /\|/, $types ); + $hasTypeOr = 1 if ( scalar( grep { defined $_ } @typesOr ) > 1 ); + Log3 "global", 5, + "msg: typeOr total is " . scalar( grep { defined $_ } @typesOr ) + if ( $testMode ne "1" ); + + for ( + my $iTypesOr = 0 ; + $iTypesOr < scalar( grep { defined $_ } @typesOr ) ; + $iTypesOr++ + ) + { + Log3 "global", 5, + "msg: start typeOr loop for type(s) $typesOr[$iTypesOr]" + if ( $testMode ne "1" ); + + my @type = split( /,/, $typesOr[$iTypesOr] ); + for ( my $i = 0 ; $i < scalar( grep { defined $_ } @type ) ; $i++ ) { + Log3 "global", 5, "msg: running loop for type $type[$i]" + if ( $testMode ne "1" ); + last if ( !defined( $type[$i] ) ); + + my $forceType = 0; + if ( $type[$i] =~ s/^!(.*)// ) { + $type[$i] = $1; + $forceType = 1; + } + + # check for correct type + my @msgCmds = + ( "screen", "light", "audio", "text", "push", "mail" ); + if ( !( $type[$i] ~~ @msgCmds ) ) { + $return .= "Unknown message type $type[$i]\n"; + next; + } + + ################################################################ + ### recipient loop + ### + + my @recipientsOr = split( /\|/, $recipients ); + $hasRecipientOr = 1 + if ( scalar( grep { defined $_ } @recipientsOr ) > 1 ); + Log3 "global", 5, + "msg: recipientOr total is " + . scalar( grep { defined $_ } @recipientsOr ) + if ( $testMode ne "1" ); + + for ( + my $iRecipOr = 0 ; + $iRecipOr < scalar( grep { defined $_ } @recipientsOr ) ; + $iRecipOr++ + ) + { + Log3 "global", 5, +"msg: start recipientsOr loop for recipient(s) $recipientsOr[$iRecipOr]" + if ( $testMode ne "1" ); + + my @recipient = split( /,/, $recipientsOr[$iRecipOr] ); + foreach my $device (@recipient) { + + Log3 "global", 5, "msg: running loop for device $device" + if ( $testMode ne "1" ); + + my $messageSentDev = 0; + my $gatewayDevs = ""; + my $forceDevice = 0; + + # for device type + my $deviceType = "device"; + if ( $device =~ + /^(([A-Za-z0-9%+._-])+[@]+([%+a-z0-9A-Z.-]*))$/ ) + { + $gatewayDevs = $1; + $deviceType = "email"; + } + elsif ( $device =~ s/^!@?(.*)// ) { + $device = $1; + $forceDevice = 1; + } + elsif ( $device =~ s/^@(.*)// ) { + $device = $1; + } + + # FATAL ERROR: device does not exist + if ( !defined( $defs{$device} ) + && $deviceType eq "device" ) + { + $return .= "Device $device does not exist\n"; + Log3 "global", 5, "msg $device: Device does not exist" + if ( $testMode ne "1" ); + + my $regex1 = + "\s*!?@?" . $device . "[,|]"; # at the beginning + my $regex2 = "[,|]!?@?" . $device . "\s*"; # at the end + my $regex3 = + ",!?@?" . $device . ","; # in the middle with comma + my $regex4 = + "[\|,]!?@?" + . $device + . "[\|,]"; # in the middle with pipe and/or comma + + $recipients =~ s/^$regex1//; + $recipients =~ s/$regex2$/|/g; + $recipients =~ s/$regex3/,/g; + $recipients =~ s/$regex4/|/g; + + next; + } + + my $typeUc = ucfirst( $type[$i] ); + my $catchall = 0; + my $useLocation = 0; + + my $logDevice; + $logDevice = "global"; + $logDevice = $device + if ( + # look for direct + AttrVal( + $device, "verbose", + + #look for indirect + AttrVal( + AttrVal( $device, "msgRecipient$typeUc", "" ), + "verbose", + + #look for indirect general + AttrVal( + AttrVal( $device, "msgRecipient", "" ), + "verbose", + + # no verbose found + "" + ) + ) + ) ne "" + ); + + ################################################################ + ### get target information from device location + ### + + # search for location references + my @locationDevs; + @locationDevs = split( + /,/, + + # look for direct + AttrVal( + $device, "msgLocationDevs", + + #look for indirect + AttrVal( + AttrVal( $device, "msgRecipient$typeUc", "" ), + "msgLocationDevs", + + # look for indirect general + AttrVal( + AttrVal( $device, "msgRecipient", "" ), + "msgLocationDevs", + + # look for global direct + AttrVal( + "global", "msgLocationDevs", + + #look for global indirect + AttrVal( + AttrVal( + "global", + "msgRecipient$typeUc", "" + ), + "msgLocationDevs", + + # look for global indirect general + AttrVal( + AttrVal( + "global", "msgRecipient", + "" + ), + "msgLocationDevs", + + # no locations defined + "" + ) + ) + ) + ) + ) + ) + ); + + if ( $deviceType eq "device" ) { + + # get device location + my $deviceLocation = + + # look for direct + ReadingsVal( + $device, "location", + + # look for indirect + ReadingsVal( + AttrVal( $device, "msgRecipient$typeUc", "" ), + "location", + + # look for indirect general + ReadingsVal( + AttrVal( $device, "msgRecipient", "" ), + "location", + + # no location found + "" + ) + ) + ); + + my $locationDev = ""; + if ( $deviceLocation ne "" ) { + + # lookup matching location + foreach (@locationDevs) { + my $lName = + AttrVal( $_, "msgLocationName", "" ); + if ( $lName ne "" && $lName eq $deviceLocation ) + { + $locationDev = $_; + last; + } + } + + # look for gateway device + $gatewayDevs = + + # look for direct + AttrVal( + $locationDev, "msgContact$typeUc", + + # look for indirect + AttrVal( + AttrVal( + $locationDev, "msgRecipient$typeUc", + "" + ), + "msgContact$typeUc", + + # look for indirect general + AttrVal( + AttrVal( + $locationDev, "msgRecipient", + "" + ), + "msgContact$typeUc", + + # no contact found + "" + ) + ) + ); + + # at least one of the location gateways needs to + # be available. Otherwise we fall back to + # non-location contacts + if ( $gatewayDevs ne "" ) { + + foreach + my $gatewayDevOr ( split /\|/, $gatewayDevs ) + { + + foreach my $gatewayDev ( split /,/, + $gatewayDevOr ) + { + + if ( $type[$i] ne "mail" + && !defined( $defs{$gatewayDev} ) + && $deviceType eq "device" ) + { + $useLocation = 2 + if ( $useLocation == 0 ); + } + elsif ( + $type[$i] ne "mail" + && AttrVal( $gatewayDev, "disable", + "0" ) eq "1" + ) + { + $useLocation = 2 + if ( $useLocation == 0 ); + } + elsif ( + $type[$i] ne "mail" + && ( + AttrVal( + $gatewayDev, "disable", + "0" + ) eq "1" + || ReadingsVal( + $gatewayDev, "power", + "on" + ) eq "off" + || ReadingsVal( + $gatewayDev, "presence", + "present" + ) eq "absent" + || ReadingsVal( + $gatewayDev, "presence", + "appeared" + ) eq "disappeared" + || ReadingsVal( + $gatewayDev, "state", + "present" + ) eq "absent" + || ReadingsVal( + $gatewayDev, "state", + "connected" + ) eq "unauthorized" + || ReadingsVal( + $gatewayDev, "state", + "connected" + ) eq "disconnected" + || ReadingsVal( + $gatewayDev, "state", + "reachable" + ) eq "unreachable" + || ReadingsVal( + $gatewayDev, "available", + "1" + ) eq "0" + || ReadingsVal( + $gatewayDev, "available", + "yes" + ) eq "no" + || ReadingsVal( + $gatewayDev, "reachable", + "1" + ) eq "0" + || ReadingsVal( + $gatewayDev, "reachable", + "yes" + ) eq "no" + ) + ) + { + $useLocation = 2 + if ( $useLocation == 0 ); + } + else { + $useLocation = 1; + } + + } + + } + + # use gatewayDevs from location only + # if it has been confirmed to be available + if ( $useLocation == 1 ) { + Log3 $logDevice, 4, +"msg $device: Matching location definition found."; + } + else { + $gatewayDevs = ""; + } + } + } + } + + ################################################################ + ### given device name is already a gateway device itself + ### + + if ( + $gatewayDevs eq "" + && defined( $defs{$device} ) + && ( + $type[$i] eq "screen" + && ( $defs{$device}{TYPE} eq "ENIGMA2" + || $defs{$device}{TYPE} eq "STV" ) + + || ( $type[$i] eq "light" + && $defs{$device}{TYPE} eq "HUEDevice" ) + + || ( + $type[$i] eq "audio" + && ( + $defs{$device}{TYPE} eq "SONOSPLAYER" + || $defs{$device}{TYPE} eq "SB_PLAYER" + || $defs{$device}{TYPE} eq "Text2Speech" + || ( $defs{$device}{TYPE} eq "CUL_HM" + && AttrVal( $device, "model", "" ) eq + "HM-OU-CFM-PI" ) + ) + ) + + || ( + $type[$i] eq "push" + && ( $defs{$device}{TYPE} eq "PushNotifier" + || $defs{$device}{TYPE} eq "Pushalot" + || $defs{$device}{TYPE} eq "Pushbullet" + || $defs{$device}{TYPE} eq "Pushover" + || $defs{$device}{TYPE} eq "yowsup" + || $defs{$device}{TYPE} eq "Jabber" ) + ) + ) + ) + { + Log3 $logDevice, 4, +"msg $device: This recipient seems to be a gateway device itself. Still checking for any delegates ..."; + + $gatewayDevs = + + # look for direct + AttrVal( + $device, + "msgContact$typeUc", + + # look for indirect + AttrVal( + AttrVal( $device, "msgRecipient$typeUc", "" ), + "msgContact$typeUc", + + # look for indirect general + AttrVal( + AttrVal( $device, "msgRecipient", "" ), + "msgContact$typeUc", + + # self + $device + ) + ) + ); + + } + + ################################################################ + ### get target information from device + ### + + elsif ( $deviceType eq "device" && $gatewayDevs eq "" ) { + + # look for gateway device + $gatewayDevs = + + # look for direct + AttrVal( + $device, "msgContact$typeUc", + + #look for indirect + AttrVal( + AttrVal( $device, "msgRecipient$typeUc", "" ), + "msgContact$typeUc", + + #look for indirect general + AttrVal( + AttrVal( $device, "msgRecipient", "" ), + "msgContact$typeUc", + + # no contact found + "" + ) + ) + ); + + # fallback/catchall + if ( $gatewayDevs eq "" ) { + $catchall = 1 + if ( $device ne "global" ); + + Log3 $logDevice, 5, +"msg $device: (No $typeUc contact defined, trying global instead)" + if ( $catchall == 1 ); + + $gatewayDevs = + + # look for direct + AttrVal( + "global", "msgContact$typeUc", + + #look for indirect + AttrVal( + AttrVal( + "global", "msgRecipient$typeUc", "" + ), + "msgContact$typeUc", + + #look for indirect general + AttrVal( + AttrVal( "global", "msgRecipient", "" ), + "msgContact$typeUc", + + # no contact found + "" + ) + ) + ); + } + } + + # Find priority if none was explicitly specified + my $loopPriority = $priority; + $loopPriority = + + # look for direct + AttrVal( + $device, "msgPriority$typeUc", + + #look for indirect + AttrVal( + AttrVal( $device, "msgRecipient$typeUc", "" ), + "msgPriority$typeUc", + + #look for indirect general + AttrVal( + AttrVal( $device, "msgRecipient", "" ), + "msgPriority$typeUc", + + # look for global direct + AttrVal( + "global", "msgPriority$typeUc", + + #look for global indirect + AttrVal( + AttrVal( + "global", "msgRecipient$typeUc", + "" + ), + "msgPriority$typeUc", + + #look for global indirect general + AttrVal( + AttrVal( + "global", "msgRecipient", + "" + ), + "msgPriority$typeUc", + + # default + 0 + ) + ) + ) + ) + ) + ) if ( !$priority ); + + # check for available routes + # + my %routes; + $routes{screen} = 0; + $routes{light} = 0; + $routes{audio} = 0; + $routes{text} = 0; + $routes{push} = 0; + $routes{mail} = 0; + + if ( + !defined($testMode) + || ( $testMode ne "1" + && $testMode ne "2" ) + ) + { + Log3 $logDevice, 5, +"msg $device: Checking for available routes (triggered by type $type[$i])"; + + $routes{screen} = 1 + if ( + $deviceType eq "device" + && CommandMsg( "screen", + "screen \@$device $priority Routing Test", 1 ) + eq "ROUTE_AVAILABLE" + ); + + $routes{light} = 1 + if ( + $deviceType eq "device" + && CommandMsg( "light", + "light \@$device $priority Routing Test", 1 ) + eq "ROUTE_AVAILABLE" + ); + + $routes{audio} = 1 + if ( + $deviceType eq "device" + && CommandMsg( "audio", + "audio \@$device $priority Routing Test", 1 ) + eq "ROUTE_AVAILABLE" + ); + + if ( + $deviceType eq "device" + && CommandMsg( "push", + "push \@$device $priority Routing Test", 1 ) eq + "ROUTE_AVAILABLE" + ) + { + $routes{push} = 1; + $routes{text} = 1; + } + + if ( + CommandMsg( "mail", + "mail \@$device $priority Routing Test", 1 ) eq + "ROUTE_AVAILABLE" + ) + { + $routes{mail} = 1; + $routes{text} = 1; + } + + Log3 $logDevice, 4, + "msg $device: Available routes: screen=" + . $routes{screen} + . " light=" + . $routes{light} + . " audio=" + . $routes{audio} + . " text=" + . $routes{text} + . " push=" + . $routes{push} + . " mail=" + . $routes{mail}; + } + + ################################################## + ### dynamic routing for text (->push, ->mail) + ### + if ( $type[$i] eq "text" ) { + + # Decide push and/or e-mail destination based on priorities + if ( $loopPriority >= 2 + && $routes{push} == 1 + && $routes{mail} == 1 ) + { + Log3 $logDevice, 4, +"msg $device: Text routing decision: push+mail(1)"; + $forwarded .= "," + if ( $forwarded ne "" ); + $forwarded .= "text>push+mail"; + push @type, "push" if !( "push" ~~ @type ); + push @type, "mail" if !( "mail" ~~ @type ); + } + elsif ($loopPriority >= 2 + && $routes{push} == 1 + && $routes{mail} == 0 ) + { + Log3 $logDevice, 4, + "msg $device: Text routing decision: push(2)"; + $forwarded .= "," + if ( $forwarded ne "" ); + $forwarded .= "text>push"; + push @type, "push" if !( "push" ~~ @type ); + } + elsif ($loopPriority >= 2 + && $routes{push} == 0 + && $routes{mail} == 1 ) + { + Log3 $logDevice, 4, + "msg $device: Text routing decision: mail(3)"; + $forwarded .= "," + if ( $forwarded ne "" ); + $forwarded .= "text>mail"; + push @type, "mail" if !( "mail" ~~ @type ); + } + elsif ( $loopPriority >= -2 && $routes{push} == 1 ) { + Log3 $logDevice, 4, + "msg $device: Text routing decision: push(4)"; + $forwarded .= "," + if ( $forwarded ne "" ); + $forwarded .= "text>push"; + push @type, "push" if !( "push" ~~ @type ); + } + elsif ( $loopPriority >= -2 && $routes{mail} == 1 ) { + Log3 $logDevice, 4, + "msg $device: Text routing decision: mail(5)"; + $forwarded .= "," + if ( $forwarded ne "" ); + $forwarded .= "text>mail"; + push @type, "mail" if !( "mail" ~~ @type ); + } + elsif ( $routes{mail} == 1 ) { + Log3 $logDevice, 4, + "msg $device: Text routing decision: mail(6)"; + $forwarded .= "," + if ( $forwarded ne "" ); + $forwarded .= "text>mail"; + push @type, "mail" if !( "mail" ~~ @type ); + } + elsif ( $routes{push} == 1 ) { + Log3 $logDevice, 4, + "msg $device: Text routing decision: push(7)"; + $forwarded .= "," + if ( $forwarded ne "" ); + $forwarded .= "text>push"; + push @type, "push" if !( "push" ~~ @type ); + } + + # FATAL ERROR: routing decision failed + else { + Log3 $logDevice, 4, +"msg $device: Text routing FAILED - priority=$loopPriority push=" + . $routes{push} + . " mail=" + . $routes{mail}; + + $return .= +"ERROR: Could not find any Push or Mail contact for device $device - set attributes: msgContactPush | msgContactMail | msgContactText | msgRecipientPush | msgRecipientMail | msgRecipientText | msgRecipient\n"; + } + + next; + } + + # FATAL ERROR: we could not find any targets for + # user specified device... + if ( $gatewayDevs eq "" + && $device ne "global" ) + { + $return .= +"ERROR: Could not find any $typeUc contact for device $device - set attributes: msgContact$typeUc | msgRecipient$typeUc | msgRecipient\n"; + } + + # FATAL ERROR: we could not find any targets at all + elsif ( $gatewayDevs eq "" ) { + $return .= +"ERROR: No global $typeUc contact defined. Please specify a destination device or set global attributes: msgContact$typeUc | msgRecipient$typeUc | msgRecipient\n"; + } + + ##################### + # return if we are in routing target test mode + # + if ( defined($testMode) && $testMode eq "1" ) { + Log3 $logDevice, 5, +"msg $device: $type[$i] route check result: ROUTE_AVAILABLE" + if ( $return eq "" ); + Log3 $logDevice, 5, +"msg $device: $type[$i] route check result: ROUTE_UNAVAILABLE" + if ( $return ne "" ); + return "ROUTE_AVAILABLE" if ( $return eq "" ); + return "ROUTE_UNAVAILABLE" if ( $return ne "" ); + } + + # user selected announcement state + my $annState = ReadingsVal( + + # look for direct + AttrVal( + $device, "msgSwitcherDev", + + #look for indirect audio + AttrVal( + AttrVal( $device, "msgRecipient$typeUc", "" ), + "msgSwitcherDev", + + #look for indirect general + AttrVal( + AttrVal( $device, "msgRecipient", "" ), + "msgSwitcherDev", + + # look for global direct + AttrVal( + "global", "msgSwitcherDev", + + #look for global indirect audio + AttrVal( + AttrVal( + "global", + "msgRecipient$typeUc", "" + ), + "msgSwitcherDev", + + #look for global indirect general + AttrVal( + AttrVal( + "global", "msgRecipient", + "" + ), + "msgSwitcherDev", + + # default + "" + ) + ) + ) + ) + ) + ), + "state", + "long" + ); + + if ( $type[$i] eq "audio" ) { + if ( $annState eq "long" + || $forceType == 1 + || $forceDevice == 1 + || $loopPriority >= 2 ) + { + $priorityCat = ""; + } + elsif ( $loopPriority >= 1 ) { + $priorityCat = "ShortPrio"; + } + else { + $priorityCat = "Short"; + } + } + else { + if ( $loopPriority >= 2 ) { + $priorityCat = "High"; + } + elsif ( $loopPriority >= 0 ) { + $priorityCat = ""; + } + else { + $priorityCat = "Low"; + } + } + + my $defTitle = "System Message"; + $defTitle = "Announcement" if ( $type[$i] eq "audio" ); + $defTitle = "Announcement" if ( $type[$i] eq "light" ); + $defTitle = "Info" if ( $type[$i] eq "screen" ); + + # use title from device, global or internal default + my $loopTitle; + $loopTitle = $title if ( $title ne "" ); + $loopTitle = + + # look for direct high + AttrVal( + $device, "msgTitle$typeUc$priorityCat", + + # look for indirect high + AttrVal( + AttrVal( $device, "msgRecipient$typeUc", "" ), + "msgTitle$typeUc$priorityCat", + + #look for indirect general high + AttrVal( + AttrVal( $device, "msgRecipient", "" ), + "msgTitle$typeUc$priorityCat", + + # look for global direct high + AttrVal( + "global", "msgTitle$typeUc$priorityCat", + + # look for global indirect high + AttrVal( + AttrVal( + "global", "msgRecipient$typeUc", + "" + ), + "msgTitle$typeUc$priorityCat", + + #look for global indirect general high + AttrVal( + AttrVal( + "global", "msgRecipient", + "" + ), + "msgTitle$typeUc$priorityCat", + + # default + $defTitle + ) + ) + ) + ) + ) + ) if ( $title eq "" ); + + my $loopMsg = $msg; + if ( $catchall == 1 ) { + $loopTitle = "Fw: $loopTitle"; + if ( $type[$i] eq "mail" ) { + $loopMsg .= +"\n\n-- \nMail forwarded from device $device due to catchall"; + } + else { + $loopMsg .= " ### (from device $device)"; + } + } + + # correct message format + # + $loopMsg =~ s/((|(\d+)| )\|\w+\|( |))/\n\n/g + if ( $type[$i] ne "audio" ); # Remove Sonos Speak commands + + if ( $type[$i] eq "mail" && $priorityCat ne "" ) { + $loopTitle = "[$priorityCat] $loopTitle"; + $loopMsg =~ s/\n/<br \/>/g; + } + + # get resident presence information + # + my $residentDevState = ""; + my $residentDevPresence = ""; + + # device + if ( ReadingsVal( $device, "presence", "-" ) ne "-" ) { + $residentDevState = ReadingsVal( $device, "state", "" ); + $residentDevPresence = + ReadingsVal( $device, "presence", "" ); + } + + # device indirect + if ( + ( + $residentDevState eq "" + || $residentDevPresence eq "" + ) + && ReadingsVal( + AttrVal( $device, "msgRecipient$typeUc", "" ), + "presence", "-" ) ne "-" + ) + { + $residentDevState = + ReadingsVal( + AttrVal( $device, "msgRecipient$typeUc", "" ), + "state", "" ) + if ( $residentDevState eq "" ); + $residentDevPresence = + ReadingsVal( + AttrVal( $device, "msgRecipient$typeUc", "" ), + "presence", "" ) + if ( $residentDevPresence eq "" ); + } + + # device indirect general + if ( + ( + $residentDevState eq "" + || $residentDevPresence eq "" + ) + && ReadingsVal( AttrVal( $device, "msgRecipient", "" ), + "presence", "-" ) ne "-" + ) + { + $residentDevState = + ReadingsVal( AttrVal( $device, "msgRecipient", "" ), + "state", "" ) + if ( $residentDevState eq "" ); + $residentDevPresence = + ReadingsVal( AttrVal( $device, "msgRecipient", "" ), + "presence", "" ) + if ( $residentDevPresence eq "" ); + } + + # device explicit + if ( + ( + $residentDevState eq "" + || $residentDevPresence eq "" + ) + && ReadingsVal( + AttrVal( $device, "msgResidentsDev", "" ), + "presence", "-" ) ne "-" + ) + { + $residentDevState = + ReadingsVal( + AttrVal( $device, "msgResidentsDev", "" ), + "state", "" ) + if ( $residentDevState eq "" ); + $residentDevPresence = + ReadingsVal( + AttrVal( $device, "msgResidentsDev", "" ), + "presence", "" ) + if ( $residentDevPresence eq "" ); + } + + # global indirect + if ( + ( + $residentDevState eq "" + || $residentDevPresence eq "" + ) + && ReadingsVal( + AttrVal( "global", "msgRecipient$typeUc", "" ), + "presence", "-" ) ne "-" + ) + { + $residentDevState = + ReadingsVal( + AttrVal( "global", "msgRecipient$typeUc", "" ), + "state", "" ) + if ( $residentDevState eq "" ); + $residentDevPresence = + ReadingsVal( + AttrVal( "global", "msgRecipient$typeUc", "" ), + "presence", "" ) + if ( $residentDevPresence eq "" ); + } + + # global indirect general + if ( + ( + $residentDevState eq "" + || $residentDevPresence eq "" + ) + && ReadingsVal( AttrVal( "global", "msgRecipient", "" ), + "presence", "-" ) ne "-" + ) + { + $residentDevState = + ReadingsVal( AttrVal( "global", "msgRecipient", "" ), + "state", "" ) + if ( $residentDevState eq "" ); + $residentDevPresence = + ReadingsVal( AttrVal( "global", "msgRecipient", "" ), + "presence", "" ) + if ( $residentDevPresence eq "" ); + } + + # global explicit + if ( + ( + $residentDevState eq "" + || $residentDevPresence eq "" + ) + && ReadingsVal( + AttrVal( "global", "msgResidentsDev", "" ), + "presence", "-" ) ne "-" + ) + { + $residentDevState = + ReadingsVal( + AttrVal( "global", "msgResidentsDev", "" ), + "state", "" ) + if ( $residentDevState eq "" ); + $residentDevPresence = + ReadingsVal( + AttrVal( "global", "msgResidentsDev", "" ), + "presence", "" ) + if ( $residentDevPresence eq "" ); + } + + ################################################################ + ### Send message + ### + + my %gatewaysStatus; + + foreach my $gatewayDevOr ( split /\|/, $gatewayDevs ) { + foreach my $gatewayDev ( split /,/, $gatewayDevOr ) { + Log3 $logDevice, 5, +"msg $device: Trying to send message via gateway $gatewayDev"; + + # restricted priority scope for Pushover + if ( $type[$i] eq "push" + && $defs{$gatewayDev}{TYPE} eq "Pushover" ) + { + $loopPriority = 2 + if ( $loopPriority > 2 ); + $loopPriority = -2 + if ( $loopPriority < -2 ); + } + + ############## + # check for gateway availability and set route status + # + + my $routeStatus = "OK"; + if ( $type[$i] ne "mail" + && !defined( $defs{$gatewayDev} ) + && $deviceType eq "device" ) + { + $routeStatus = "UNDEFINED"; + } + elsif ( $type[$i] ne "mail" + && AttrVal( $gatewayDev, "disable", "0" ) eq + "1" ) + { + $routeStatus = "DISABLED"; + } + elsif ( + $type[$i] ne "mail" + && ( + AttrVal( $gatewayDev, "disable", "0" ) eq + "1" + || ReadingsVal( $gatewayDev, "power", "on" ) + eq "off" + || ReadingsVal( $gatewayDev, "presence", + "present" ) eq "absent" + || ReadingsVal( $gatewayDev, "presence", + "appeared" ) eq "disappeared" + || ReadingsVal( $gatewayDev, "state", + "present" ) eq "absent" + || ReadingsVal( $gatewayDev, "state", + "reachable" ) eq "unreachable" + || ReadingsVal( + $gatewayDev, "reachable", "1" + ) eq "0" + || ReadingsVal( $gatewayDev, "reachable", + "yes" ) eq "no" + ) + ) + { + $routeStatus = "UNAVAILABLE"; + } + elsif ($type[$i] eq "audio" + && $annState ne "long" + && $annState ne "short" ) + { + $routeStatus = "USER_DISABLED"; + } + elsif ( $type[$i] eq "light" && $annState eq "off" ) + { + $routeStatus = "USER_DISABLED"; + } + elsif ($type[$i] ne "push" + && $type[$i] ne "mail" + && $residentDevPresence eq "absent" ) + { + $routeStatus = "USER_ABSENT"; + } + + # enforce by user request + if ( + ( + $routeStatus eq "USER_DISABLED" + || $routeStatus eq "USER_ABSENT" + ) + && ( $forceType == 1 || $forceDevice == 1 ) + ) + { + $routeStatus = "OK_ENFORCED"; + } + + # enforce by priority + if ( + ( + $routeStatus eq "USER_DISABLED" + || $routeStatus eq "USER_ABSENT" + ) + && $loopPriority >= 2 + ) + { + $routeStatus = "OK_EMERGENCY"; + } + + # add location status + if ( $useLocation == 2 ) { + $routeStatus .= "+LOCATION-UNAVAILABLE"; + } + elsif ( $useLocation == 1 ) { + $routeStatus .= "+LOCATION"; + } + + # use command from device, global or internal default + my $defCmd; + $defCmd = $defaults{ $type[$i] }{$priorityCat} + if ( $priorityCat ne "" ); + $defCmd = $defaults{ $type[$i] }{Normal} + if ( $priorityCat eq "" ); + my $cmd = + + # gateway device + AttrVal( + $gatewayDev, "msgCmd$typeUc$priorityCat", + + # look for direct + AttrVal( + $device, "msgCmd$typeUc$priorityCat", + + # look for indirect + AttrVal( + AttrVal( + $device, "msgRecipient$typeUc", + "" + ), + "msgCmd$typeUc$priorityCat", + + #look for indirect general + AttrVal( + AttrVal( + $device, "msgRecipient", "" + ), + "msgCmd$typeUc$priorityCat", + + # look for global direct + AttrVal( + "global", + "msgCmd$typeUc$priorityCat", + + # look for global indirect + AttrVal( + AttrVal( + "global", + "msgRecipient$typeUc", + "" + ), + "msgCmd$typeUc$priorityCat", + + #look for global indirect general + AttrVal( + AttrVal( + "global", + "msgRecipient", + "" + ), +"msgCmd$typeUc$priorityCat", + + # internal + $defCmd + ) + ) + ) + ) + ) + ) + ); + + $cmd =~ s/\$RECIPIENT/$gatewayDev/g; + $cmd =~ s/\$DEVICE/$gatewayDev/g; + $cmd =~ s/\$PRIORITY/$loopPriority/g; + $cmd =~ s/\$TITLE/$loopTitle/g; + $cmd =~ s/\$MSG/$loopMsg/g; + + $sentCounter++; + + if ( $routeStatus =~ /^OK\w*/ ) { + + Log3 $logDevice, 3, +"msg $device: ID=$messageID.$sentCounter TYPE=$type[$i] ROUTE=$gatewayDev STATUS=$routeStatus PRIORITY=$loopPriority($priorityCat) TITLE='$loopTitle' MSG='$msg'" + if ( $priorityCat ne "" ); + Log3 $logDevice, 3, +"msg $device: ID=$messageID.$sentCounter TYPE=$type[$i] ROUTE=$gatewayDev STATUS=$routeStatus PRIORITY=$loopPriority TITLE='$loopTitle' MSG='$msg'" + if ( $priorityCat eq "" ); + + # run command + if ( $cmd =~ s/^[ \t]*\{|\}[ \t]*$//g ) { + $cmd =~ s/@\w+/\\$&/g; + Log3 $logDevice, 5, +"msg $device: $type[$i] route command (Perl): $cmd"; + eval $cmd; + } + else { + Log3 $logDevice, 5, +"msg $device: $type[$i] route command (fhem): $cmd"; + fhem $cmd; + } + + $messageSent = 1; + $messageSentDev = 1; + $gatewaysStatus{$gatewayDev} = $routeStatus; + } + elsif ($routeStatus eq "UNAVAILABLE" + || $routeStatus eq "UNDEFINED" ) + { + Log3 $logDevice, 3, +"msg $device: ID=$messageID.$sentCounter TYPE=$type[$i] ROUTE=$gatewayDev STATUS=$routeStatus PRIORITY=$loopPriority TITLE='$loopTitle' '$msg'"; + $gatewaysStatus{$gatewayDev} = $routeStatus; + } + else { + Log3 $logDevice, 3, +"msg $device: ID=$messageID.$sentCounter TYPE=$type[$i] ROUTE=$gatewayDev STATUS=$routeStatus PRIORITY=$loopPriority TITLE='$loopTitle' '$msg'"; + $messageSent = 2 if ( $messageSent != 1 ); + $messageSentDev = 2 if ( $messageSentDev != 1 ); + $gatewaysStatus{$gatewayDev} = $routeStatus; + } + + } + + last if ( $messageSentDev == 1 ); + } + + if ( $catchall == 0 ) { + if ( !defined( $sentTypesPerDevice{$device} ) ) { + $sentTypesPerDevice{$device} = ""; + } + else { + $sentTypesPerDevice{$device} .= " "; + } + + $sentTypesPerDevice{$device} .= + $type[$i] . ":" . $messageSentDev; + } + else { + if ( !defined( $sentTypesPerDevice{$device} ) ) { + $sentTypesPerDevice{"global"} = ""; + } + else { + $sentTypesPerDevice{"global"} .= " "; + } + + $sentTypesPerDevice{"global"} .= + $type[$i] . ":" . $messageSentDev; + } + + # update device readings + my $readingsDev = $defs{$device}; + $readingsDev = $defs{"global"} if ( $catchall == 1 ); + readingsBeginUpdate($readingsDev); + + readingsBulkUpdate( $readingsDev, "fhemMsg" . $typeUc, + $loopMsg ); + readingsBulkUpdate( $readingsDev, + "fhemMsg" . $typeUc . "Title", $loopTitle ); + readingsBulkUpdate( $readingsDev, + "fhemMsg" . $typeUc . "Prio", + $loopPriority ); + + my $gwStates = ""; + + while ( ( my $gwName, my $gwState ) = each %gatewaysStatus ) + { + $gwStates .= " " if $gwStates ne ""; + $gwStates .= "$gwName:$gwState"; + } + readingsBulkUpdate( $readingsDev, + "fhemMsg" . $typeUc . "Gw", $gwStates ); + readingsBulkUpdate( $readingsDev, + "fhemMsg" . $typeUc . "State", + $messageSentDev ); + + ################################################################ + ### Implicit forwards based on priority or presence + ### + + # no implicit escalations for type mail + next if ( $type[$i] eq "mail" ); + + # Skip if typeOr is defined + # and this is not the last type entry + # TODO: bei mehreren gleichzeitigen Typen (and-Definition)? + if ( $messageSentDev != 1 + && $hasTypeOr == 1 + && $isTypeOr < scalar( grep { defined $_ } @typesOr ) ) + { + Log3 $logDevice, 4, +"msg $device: Skipping implicit forward due to typesOr definition"; + + # remove recipient from list to avoid + # other interaction when using recipientOr in parallel + if ( $hasRecipientOr == 1 + && $isRecipientOr < + scalar( grep { defined $_ } @recipientsOr ) ) + { + my $regex1 = + "\s*!?@?" . $device . "[,|]"; # at the beginning + my $regex2 = + "[,|]!?@?" . $device . "\s*"; # at the end + my $regex3 = + ",!?@?" + . $device + . ","; # in the middle with comma + my $regex4 = + "[\|,]!?@?" + . $device + . "[\|,]"; # in the middle with pipe and/or comma + + $recipients =~ s/^$regex1//; + $recipients =~ s/$regex2$/|/g; + $recipients =~ s/$regex3/,/g; + $recipients =~ s/$regex4/|/g; + } + + next; + } + + # Skip if recipientOr is defined + # and this is not the last device entry + # TODO: bei mehreren gleichzeitigen Empfängern + # (and-Definition)? + if ( $messageSentDev != 1 + && $hasRecipientOr == 1 + && $isRecipientOr < + scalar( grep { defined $_ } @recipientsOr ) ) + { + Log3 $logDevice, 4, +"msg $device: Skipping implicit forward due to recipientOr definition"; + + next; + } + + # priority forward thresholds + # + + ### emergency + my $msgFwPrioEmergency = + + # look for direct + AttrVal( + $device, "msgFwPrioEmergency$typeUc", + + #look for indirect + AttrVal( + AttrVal( $device, "msgRecipient$typeUc", "" ), + "msgFwPrioEmergency$typeUc", + + #look for indirect general + AttrVal( + AttrVal( $device, "msgRecipient", "" ), + "msgFwPrioEmergency$typeUc", + + # default + 2 + ) + ) + ); + + ### absent + my $msgFwPrioAbsent = + + # look for direct + AttrVal( + $device, "msgFwPrioAbsent$typeUc", + + #look for indirect + AttrVal( + AttrVal( $device, "msgRecipient$typeUc", "" ), + "msgFwPrioAbsent$typeUc", + + #look for indirect general + AttrVal( + AttrVal( $device, "msgRecipient", "" ), + "msgFwPrioAbsent$typeUc", + + # default + 0 + ) + ) + ); + + ### gone + my $msgFwPrioGone = + + # look for direct + AttrVal( + $device, "msgFwPrioGone$typeUc", + + #look for indirect + AttrVal( + AttrVal( $device, "msgRecipient$typeUc", "" ), + "msgFwPrioGone$typeUc", + + #look for indirect general + AttrVal( + AttrVal( $device, "msgRecipient", "" ), + "msgFwPrioGone$typeUc", + + # default + 1 + ) + ) + ); + + Log3 $logDevice, 5, +"msg $device: Implicit forwards: recipient presence=$residentDevPresence state=$residentDevState" + if ( $residentDevPresence ne "" + || $residentDevState ne "" ); + + my $fw_gwUnavailable = + $forwards{ $type[$i] }{gwUnavailable}; + my $fw_emergency = $forwards{ $type[$i] }{emergency}; + my $fw_residentAbsent = + $forwards{ $type[$i] }{highPrio}{residentAbsent}; + my $fw_residentGone = + $forwards{ $type[$i] }{highPrio}{residentGone}; + + # Forward message + # if no gateway device for this type was available + if ( $messageSentDev == 0 + && defined($fw_gwUnavailable) + && !( $fw_gwUnavailable ~~ @type ) + && $routes{$fw_gwUnavailable} == 1 ) + { + Log3 $logDevice, 4, +"msg $device: Implicit forwards: No $type[$i] gateway device available for recipient $device ($gatewayDevs). Trying alternative message type " + . $fw_gwUnavailable; + + push @type, $fw_gwUnavailable; + $forwarded .= "," . $type[$i] . ">" . $fw_gwUnavailable + if ( $forwarded ne "" ); + $forwarded .= $type[$i] . ">" . $fw_gwUnavailable + if ( $forwarded eq "" ); + } + + # Forward message + # if emergency priority + if ( $loopPriority >= $msgFwPrioEmergency + && defined($fw_emergency) + && !( $fw_emergency ~~ @type ) + && $routes{$fw_emergency} == 1 ) + { + Log3 $logDevice, 4, +"msg $device: Implicit forwards: Escalating high priority $type[$i] message via " + . $fw_emergency; + + push @type, $fw_emergency; + $forwarded .= "," . $type[$i] . ">" . $fw_emergency + if ( $forwarded ne "" ); + $forwarded .= $type[$i] . ">" . $fw_emergency + if ( $forwarded eq "" ); + } + + # Forward message + # if high priority and residents are constantly not at home + if ( $residentDevPresence eq "absent" + && $loopPriority >= $msgFwPrioGone + && defined($fw_residentGone) + && !( $fw_residentGone ~~ @type ) + && $routes{$fw_residentGone} == 1 ) + { + Log3 $logDevice, 4, +"msg $device: Implicit forwards: Escalating high priority $type[$i] message via " + . $fw_residentGone; + + push @type, $fw_residentGone; + $forwarded .= "," . $type[$i] . ">" . $fw_residentGone + if ( $forwarded ne "" ); + $forwarded .= $type[$i] . ">" . $fw_residentGone + if ( $forwarded eq "" ); + } + + # Forward message + # if priority is normal or higher and residents + # are not at home but nearby + if ( $residentDevState eq "absent" + && $loopPriority >= $msgFwPrioAbsent + && defined($fw_residentAbsent) + && !( $fw_residentAbsent ~~ @type ) + && $routes{$fw_residentAbsent} == 1 ) + { + Log3 $logDevice, 4, +"msg $device: Implicit forwards: Escalating $type[$i] message via " + . $fw_residentAbsent + . " due to absence"; + + push @type, $fw_residentAbsent; + $forwarded .= "," . $type[$i] . ">" . $fw_residentAbsent + if ( $forwarded ne "" ); + $forwarded .= $type[$i] . ">" . $fw_residentAbsent + if ( $forwarded eq "" ); + } + + } + + last if ( $messageSent == 1 ); + + $isRecipientOr++; + } + } + + last if ( $messageSent == 1 ); + + $isTypeOr++; + } + + # finalize device readings + while ( ( my $device, my $types ) = each %sentTypesPerDevice ) { + readingsBulkUpdate( $defs{$device}, "fhemMsgStateTypes", $types ) + if ( $forwarded eq "" ); + readingsBulkUpdate( $defs{$device}, "fhemMsgStateTypes", + $types . " forwards:" . $forwarded ) + if ( $forwarded ne "" ); + readingsBulkUpdate( $defs{$device}, "fhemMsgState", $messageSent ); + readingsEndUpdate( $defs{$device}, 1 ); + } + + if ( $messageSent == 1 && $return ne "" ) { + $return .= "However, message was still sent to some recipients!"; + } + + if ( $messageSent == 2 ) { + $return .= + "FATAL ERROR: Message NOT sent. No gateway device was available."; + } + + return $return; +} + +1; + +=pod +=begin html + +<a name="msg"></a> +<h3>mail</h3> +<ul> + <code>msg [<type>] [<@device>|<e-mail address>] [<priority>] [|<title>|] <message></code> + <br> + <br> + No documentation yet, sorry.<br> + <a href="http://forum.fhem.de/index.php/topic,39983.0.html">FHEM Forum</a> +</ul> + +=end html +=begin html_DE + +<a name="mail"></a> +<h3>mail</h3> +<ul> + <code>msg [<type>] [<@device>|<e-mail address>] [<priority>] [|<title>|] <message></code> + <br> + <br> + Bisher keine Dokumentation, sorry.<br> + <a href="http://forum.fhem.de/index.php/topic,39983.0.html">FHEM Forum</a> +</ul> + + +=end html_DE +=cut