2015-09-24 12:19:46 +00:00
# $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 <http://www.gnu.org/licenses/>.
#
#
# 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 ) ;
2015-10-05 08:04:23 +00:00
no if $] >= 5.017011 , warnings = > 'experimental::smartmatch' ;
2015-09-24 12:19:46 +00:00
sub CommandMsg ( $ $ ; $$ ) ;
########################################
sub msg_Initialize ($$) {
my % hash = (
Fn = > "CommandMsg" ,
Hlp = >
"[<type>] [<\@device>|<e-mail address>] [<priority>] [|<title>|] <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
2015-10-06 09:10:01 +00:00
msgThPrioHigh: - 2 , - 1 , 0 , 1 , 2
msgThPrioNormal: - 2 , - 1 , 0 , 1 , 2
msgThPrioAudioEmergency: - 2 , - 1 , 0 , 1 , 2
msgThPrioAudioHigh: - 2 , - 1 , 0 , 1 , 2
msgThPrioTextEmergency: - 2 , - 1 , 0 , 1 , 2
msgThPrioTextNormal: - 2 , - 1 , 0 , 1 , 2
msgThPrioGwEmergency: - 2 , - 1 , 0 , 1 , 2
2015-09-24 12:19:46 +00:00
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" ) {
2015-10-06 09:10:01 +00:00
# user selected emergency priority text threshold
my $ prioThresTextEmg =
# look for direct
AttrVal (
$ device , "msgThPrioTextEmergency" ,
#look for indirect audio
AttrVal (
AttrVal ( $ device , "msgRecipient$typeUc" , "" ) ,
"msgThPrioTextEmergency" ,
#look for indirect general
AttrVal (
AttrVal ( $ device , "msgRecipient" , "" ) ,
"msgThPrioTextEmergency" ,
# look for global direct
AttrVal (
"global" , "msgThPrioTextEmergency" ,
#look for global indirect type
AttrVal (
AttrVal (
"global" ,
"msgRecipient$typeUc" , ""
) ,
"msgThPrioTextEmergency" ,
#look for global indirect general
AttrVal (
AttrVal (
"global" , "msgRecipient" ,
""
) ,
"msgThPrioTextEmergency" ,
# default
"2"
)
)
)
)
)
)
;
# user selected low priority text threshold
my $ prioThresTextNormal =
# look for direct
AttrVal (
$ device , "msgThPrioTextNormal" ,
#look for indirect audio
AttrVal (
AttrVal ( $ device , "msgRecipient$typeUc" , "" ) ,
"msgThPrioTextNormal" ,
#look for indirect general
AttrVal (
AttrVal ( $ device , "msgRecipient" , "" ) ,
"msgThPrioTextNormal" ,
# look for global direct
AttrVal (
"global" , "msgThPrioTextNormal" ,
#look for global indirect type
AttrVal (
AttrVal (
"global" ,
"msgRecipient$typeUc" , ""
) ,
"msgThPrioTextNormal" ,
#look for global indirect general
AttrVal (
AttrVal (
"global" , "msgRecipient" ,
""
) ,
"msgThPrioTextNormal" ,
# default
"-2"
)
)
)
)
)
)
;
2015-09-24 12:19:46 +00:00
# Decide push and/or e-mail destination based on priorities
2015-10-06 09:10:01 +00:00
if ( $ loopPriority >= $ prioThresTextEmg
2015-09-24 12:19:46 +00:00
&& $ 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 ) ;
}
2015-10-06 09:10:01 +00:00
elsif ( $ loopPriority >= $ prioThresTextEmg
2015-09-24 12:19:46 +00:00
&& $ 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 ) ;
}
2015-10-06 09:10:01 +00:00
elsif ( $ loopPriority >= $ prioThresTextEmg
2015-09-24 12:19:46 +00:00
&& $ 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 ) ;
}
2015-10-06 09:10:01 +00:00
elsif ( $ loopPriority >= $ prioThresTextNormal && $ routes { push } == 1 ) {
2015-09-24 12:19:46 +00:00
Log3 $ logDevice , 4 ,
"msg $device: Text routing decision: push(4)" ;
$ forwarded . = ","
if ( $ forwarded ne "" ) ;
$ forwarded . = "text>push" ;
push @ type , "push" if ! ( "push" ~ ~ @ type ) ;
}
2015-10-06 09:10:01 +00:00
elsif ( $ loopPriority >= $ prioThresTextNormal && $ routes { mail } == 1 ) {
2015-09-24 12:19:46 +00:00
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 "" ) ;
}
2015-10-06 09:10:01 +00:00
# user selected audio-visual announcement state
2015-09-24 12:19:46 +00:00
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" ,
2015-10-06 09:10:01 +00:00
#look for global indirect type
2015-09-24 12:19:46 +00:00
AttrVal (
AttrVal (
"global" ,
"msgRecipient$typeUc" , ""
) ,
"msgSwitcherDev" ,
#look for global indirect general
AttrVal (
AttrVal (
"global" , "msgRecipient" ,
""
) ,
"msgSwitcherDev" ,
# default
""
)
)
)
)
)
) ,
"state" ,
"long"
) ;
2015-10-06 09:10:01 +00:00
# user selected emergency priority audio threshold
my $ prioThresAudioEmg =
# look for direct
AttrVal (
$ device , "msgThPrioAudioEmergency" ,
#look for indirect audio
AttrVal (
AttrVal ( $ device , "msgRecipient$typeUc" , "" ) ,
"msgThPrioAudioEmergency" ,
#look for indirect general
AttrVal (
AttrVal ( $ device , "msgRecipient" , "" ) ,
"msgThPrioAudioEmergency" ,
# look for global direct
AttrVal (
"global" , "msgThPrioAudioEmergency" ,
#look for global indirect type
AttrVal (
AttrVal (
"global" ,
"msgRecipient$typeUc" , ""
) ,
"msgThPrioAudioEmergency" ,
#look for global indirect general
AttrVal (
AttrVal (
"global" , "msgRecipient" ,
""
) ,
"msgThPrioAudioEmergency" ,
# default
"2"
)
)
)
)
)
)
;
# user selected high priority audio threshold
my $ prioThresAudioHigh =
# look for direct
AttrVal (
$ device , "msgThPrioAudioHigh" ,
#look for indirect audio
AttrVal (
AttrVal ( $ device , "msgRecipient$typeUc" , "" ) ,
"msgThPrioAudioHigh" ,
#look for indirect general
AttrVal (
AttrVal ( $ device , "msgRecipient" , "" ) ,
"msgThPrioAudioHigh" ,
# look for global direct
AttrVal (
"global" , "msgThPrioAudioHigh" ,
#look for global indirect type
AttrVal (
AttrVal (
"global" ,
"msgRecipient$typeUc" , ""
) ,
"msgThPrioAudioHigh" ,
#look for global indirect general
AttrVal (
AttrVal (
"global" , "msgRecipient" ,
""
) ,
"msgThPrioAudioHigh" ,
# default
"1"
)
)
)
)
)
)
;
# user selected high priority threshold
my $ prioThresHigh =
# look for direct
AttrVal (
$ device , "msgThPrioHigh" ,
#look for indirect audio
AttrVal (
AttrVal ( $ device , "msgRecipient$typeUc" , "" ) ,
"msgThPrioHigh" ,
#look for indirect general
AttrVal (
AttrVal ( $ device , "msgRecipient" , "" ) ,
"msgThPrioHigh" ,
# look for global direct
AttrVal (
"global" , "msgThPrioHigh" ,
#look for global indirect type
AttrVal (
AttrVal (
"global" ,
"msgRecipient$typeUc" , ""
) ,
"msgThPrioHigh" ,
#look for global indirect general
AttrVal (
AttrVal (
"global" , "msgRecipient" ,
""
) ,
"msgThPrioHigh" ,
# default
"2"
)
)
)
)
)
)
;
# user selected normal priority threshold
my $ prioThresNormal =
# look for direct
AttrVal (
$ device , "msgThPrioNormal" ,
#look for indirect audio
AttrVal (
AttrVal ( $ device , "msgRecipient$typeUc" , "" ) ,
"msgThPrioNormal" ,
#look for indirect general
AttrVal (
AttrVal ( $ device , "msgRecipient" , "" ) ,
"msgThPrioNormal" ,
# look for global direct
AttrVal (
"global" , "msgThPrioNormal" ,
#look for global indirect type
AttrVal (
AttrVal (
"global" ,
"msgRecipient$typeUc" , ""
) ,
"msgThPrioNormal" ,
#look for global indirect general
AttrVal (
AttrVal (
"global" , "msgRecipient" ,
""
) ,
"msgThPrioNormal" ,
# default
"0"
)
)
)
)
)
)
;
2015-09-24 12:19:46 +00:00
if ( $ type [ $ i ] eq "audio" ) {
if ( $ annState eq "long"
|| $ forceType == 1
|| $ forceDevice == 1
2015-10-06 09:10:01 +00:00
|| $ loopPriority >= $ prioThresAudioEmg )
2015-09-24 12:19:46 +00:00
{
$ priorityCat = "" ;
}
2015-10-06 09:10:01 +00:00
elsif ( $ loopPriority >= $ prioThresAudioHigh ) {
2015-09-24 12:19:46 +00:00
$ priorityCat = "ShortPrio" ;
}
else {
$ priorityCat = "Short" ;
}
}
else {
2015-10-06 09:10:01 +00:00
if ( $ loopPriority >= $ prioThresHigh ) {
2015-09-24 12:19:46 +00:00
$ priorityCat = "High" ;
}
2015-10-06 09:10:01 +00:00
elsif ( $ loopPriority >= $ prioThresNormal ) {
2015-09-24 12:19:46 +00:00
$ 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
###
2015-10-06 09:10:01 +00:00
# user selected emergency priority text threshold
my $ prioThresGwEmg =
# look for direct
AttrVal (
$ device , "msgThPrioGwEmergency" ,
#look for indirect type
AttrVal (
AttrVal ( $ device , "msgRecipient$typeUc" , "" ) ,
"msgThPrioGwEmergency" ,
#look for indirect general
AttrVal (
AttrVal ( $ device , "msgRecipient" , "" ) ,
"msgThPrioGwEmergency" ,
# look for global direct
AttrVal (
"global" , "msgThPrioGwEmergency" ,
#look for global indirect type
AttrVal (
AttrVal (
"global" ,
"msgRecipient$typeUc" , ""
) ,
"msgThPrioGwEmergency" ,
#look for global indirect general
AttrVal (
AttrVal (
"global" , "msgRecipient" ,
""
) ,
"msgThPrioGwEmergency" ,
# default
"2"
)
)
)
)
)
)
;
2015-09-24 12:19:46 +00:00
my % gatewaysStatus ;
foreach my $ gatewayDevOr ( split /\|/ , $ gatewayDevs ) {
foreach my $ gatewayDev ( split /,/ , $ gatewayDevOr ) {
Log3 $ logDevice , 5 ,
"msg $device: Trying to send message via gateway $gatewayDev" ;
##############
# 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"
)
2015-10-06 09:10:01 +00:00
&& $ loopPriority >= $ prioThresGwEmg
2015-09-24 12:19:46 +00:00
)
{
$ 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 [ & lt ; type & gt ; ] [ & lt ; @ device & gt ; | & lt ; e - mail address & gt ; ] [ & lt ; priority & gt ; ] [ | & lt ; title & gt ; | ] & lt ; message & gt ; </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 [ & lt ; type & gt ; ] [ & lt ; @ device & gt ; | & lt ; e - mail address & gt ; ] [ & lt ; priority & gt ; ] [ | & lt ; title & gt ; | ] & lt ; message & gt ; </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