| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492 |
- # $Id: 75_MSG.pm 13281 2017-01-30 01:37:46Z loredo $
- ##############################################################################
- #
- # 75_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/>.
- #
- ##############################################################################
- package main;
- use strict;
- use warnings;
- use Time::HiRes qw(time);
- use Data::Dumper;
- no if $] >= 5.017011, warnings => 'experimental';
- sub CommandMsg($$;$$);
- ########################################
- sub MSG_Initialize($$) {
- my %hash = (
- Fn => "CommandMsg",
- Hlp =>
- "[<type>] [<\@device>|<e-mail address>] [<priority>] [|<title>|] <message-text>",
- );
- $cmds{msg} = \%hash;
- require "$attr{global}{modpath}/FHEM/msgSchema.pm";
- }
- ########################################
- sub CommandMsg($$;$$) {
- my ( $cl, $msg, $testMode ) = @_;
- my $return = "";
- if ( $featurelevel >= 5.7 ) {
- my %dummy;
- my ( $err, @a ) = ReplaceSetMagic( \%dummy, 0, ($msg) );
- $msg = join( " ", @a )
- unless ($err);
- }
- # find existing msgConfig device or create a new instance
- my $globalDevName = "globalMsg";
- if ( defined( $modules{msgConfig}{defptr} ) ) {
- $globalDevName = $modules{msgConfig}{defptr}{NAME};
- }
- else {
- fhem "define $globalDevName msgConfig";
- $return .=
- "Global configuration device $globalDevName was created.\n\n";
- }
- if ( $msg eq "" || $msg =~ /^\?[\s\t]*$/ || $msg eq "help" ) {
- return $return
- . "Usage: msg [<type>] [<\@device>|<e-mail address>] [<priority>] [|<title>|] <message>";
- }
- # default settings
- my $cmdSchema = msgSchema::get();
- my $settings = {
- 'audio' => {
- 'typeEscalation' => {
- 'gwUnavailable' => 'text',
- 'emergency' => 'text',
- 'residentGone' => 'text',
- 'residentAbsent' => 'text',
- },
- },
- 'light' => {
- 'typeEscalation' => {
- 'gwUnavailable' => 'audio',
- 'emergency' => 'audio',
- 'residentGone' => 'audio',
- 'residentAbsent' => 'audio',
- },
- },
- 'push' => {
- 'typeEscalation' => {
- 'gwUnavailable' => 'mail',
- 'emergency' => 'mail',
- },
- },
- 'screen' => {
- 'typeEscalation' => {
- 'gwUnavailable' => 'light',
- 'emergency' => 'light',
- 'residentGone' => 'light',
- 'residentAbsent' => 'light',
- },
- },
- };
- ################################################################
- ### extract message details
- ###
- my $types = "";
- my $recipients = "";
- my $priority = "";
- my $title = "-";
- my $advanced = "";
- my $priorityCat = "";
- # check for message types
- if ( $msg =~
- s/^[\s\t]*([a-z,]*!?(screen|light|audio|text|push|mail)[a-z,!|]*)[\s\t]+//
- )
- {
- Log3 $globalDevName, 5, "msg: found types=$1";
- $types = $1;
- }
- # programatic exception:
- # e.g. recipients were given automatically from empty readings
- if ( $msg =~ s/^[\s\t]*([!]?(([A-Za-z0-9%+._-])*@([,\-:|]+)))[\s\t]+// ) {
- Log3 $globalDevName, 4,
- "msg: message won't be sent - recipient '$1' contains special"
- . " characters like ',-:|' or behind the @ character is simply"
- . " emptiness. This might be okay, e.g. if you are using something"
- . " like a reading from RESIDENTS/ROOMMATE/GUEST to address present"
- . " or absent residents and this list is simply empty at this time."
- . " ($msg)";
- return;
- }
- # check for given recipients
- if ( $msg =~
- s/^[\s\t]*([!]?(([A-Za-z0-9%+._-])*@([%+a-z0-9A-Z.-]+))[\w,@.!|:]*)[\s\t]+//
- )
- {
- Log3 $globalDevName, 5, "msg: found recipient=$1";
- $recipients = $1;
- }
- # check for given priority
- if ( $msg =~ s/^[\s\t]*([-+]{0,1}\d+[.\d]*)[\s\t]*// ) {
- Log3 $globalDevName, 5, "msg: found priority=$1";
- $priority = $1;
- }
- # check for given message title
- if ( $msg =~ s/^[\s\t]*\|(.*?)\|[\s\t]*// ) {
- Log3 $globalDevName, 5, "msg: found title=$1";
- $title = $1;
- }
- # check for advanced options
- if ( $msg =~ s/[\s\t]*O(\[\{.*\}\])[\s\t]*$// ) {
- Log3 $globalDevName, 5, "msg: found options=$1";
- # Use JSON module if possible
- eval {
- require JSON;
- import JSON qw( decode_json );
- };
- if ( !$@ ) {
- eval '$advanced = decode_json( Encode::encode_utf8($1) ); 1';
- if ( !$@ ) {
- Log3 $globalDevName, 5,
- "msg: Decoded advanced options\n" . Dumper($advanced);
- }
- else {
- Log3 $globalDevName, 5,
- "msg: Error decoding JSON for advanced options";
- $advanced = "";
- }
- }
- else {
- Log3 $globalDevName, 3,
- "msg: To use advanced options, please install Perl JSON.";
- }
- }
- ################################################################
- ### command queue
- ###
- $types = AttrVal( "msgType", $globalDevName, "text" )
- if ( $types eq "" );
- my $msgSent = 0;
- my $forwarded = "";
- my %sentTypesPerDevice;
- my $sentCounter = 0;
- my $msgID = time();
- my $isTypeOr = 1;
- my $isRecipientOr = 1;
- my $hasTypeOr = 0;
- my $hasRecipientOr = 0;
- $recipients = "\@" . $globalDevName if ( $recipients eq "" );
- my @typesOr = split( /\|/, $types );
- $hasTypeOr = 1 if ( scalar( grep { defined $_ } @typesOr ) > 1 );
- Log3 $globalDevName, 5,
- "msg: typeOr total is " . scalar( grep { defined $_ } @typesOr )
- if ( $testMode ne "1" );
- for (
- my $iTypesOr = 0 ;
- $iTypesOr < scalar( grep { defined $_ } @typesOr ) ;
- $iTypesOr++
- )
- {
- Log3 $globalDevName, 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 $globalDevName, 5, "msg: running loop for type $type[$i]"
- if ( $testMode ne "1" );
- last if ( !defined( $type[$i] ) );
- my $forceType = 0;
- if ( $type[$i] =~ s/(.*)![\s\t]*$// ) {
- $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 $globalDevName, 5,
- "msg: recipientOr total is "
- . scalar( grep { defined $_ } @recipientsOr )
- if ( $testMode ne "1" );
- for (
- my $iRecipOr = 0 ;
- $iRecipOr < scalar( grep { defined $_ } @recipientsOr ) ;
- $iRecipOr++
- )
- {
- Log3 $globalDevName, 5,
- "msg: start recipientsOr loop for recipient(s) $recipientsOr[$iRecipOr]"
- if ( $testMode ne "1" );
- my @recipient = split( /,/, $recipientsOr[$iRecipOr] );
- foreach my $device (@recipient) {
- Log3 $globalDevName, 5,
- "msg: running loop for device $device"
- if ( $testMode ne "1" );
- my $msgSentDev = 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/^@?(.*)![\s\t]*$// ) {
- $device = $1;
- $forceDevice = 1;
- }
- elsif ( $device =~ s/^@(.*)// ) {
- $device = $1;
- }
- # sub-recipient
- my $subRecipient = "";
- my $termRecipient = "";
- if ( $device =~
- m/^@?([A-Za-z0-9._]+):([A-Za-z0-9._\-\/@+]*):?([A-Za-z0-9._\-\/@+]*)$/
- )
- {
- $device = $1;
- $subRecipient = $2;
- $termRecipient = $3;
- }
- # FATAL ERROR: device does not exist
- if ( !defined( $defs{$device} )
- && $deviceType eq "device" )
- {
- $return .= "Device $device does not exist\n";
- Log3 $globalDevName, 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//gi;
- $recipients =~ s/$regex2$/|/gi;
- $recipients =~ s/$regex3/,/gi;
- $recipients =~ s/$regex4/|/gi;
- next;
- }
- # next type loop if device is an email address and this is not the mail type loop run
- if ( $deviceType eq "email"
- && $type[$i] ne "mail"
- && $type[$i] ne "text" )
- {
- Log3 $globalDevName, 5,
- "msg $device: Skipping loop for device type 'email' with unmatched message type '"
- . $type[$i] . "'";
- next;
- }
- my $typeUc = ucfirst( $type[$i] );
- my $catchall = 0;
- my $useLocation = 0;
- my $logDevice;
- $logDevice = $globalDevName;
- $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(
- $globalDevName, "msgLocationDevs",
- #look for global indirect
- AttrVal(
- AttrVal(
- $globalDevName,
- "msgRecipient$typeUc",
- ""
- ),
- "msgLocationDevs",
- # look for global indirect general
- AttrVal(
- AttrVal(
- $globalDevName,
- "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 "" && $deviceType eq "device" )
- {
- # lookup matching location
- foreach (@locationDevs) {
- if ( $featurelevel >= 5.7 ) {
- my %dummy;
- my ( $err, @a ) =
- ReplaceSetMagic( \%dummy, 0, ($_) );
- $_ = join( " ", @a )
- unless ($err);
- }
- my $lName =
- AttrVal( $_, "msgLocationName", "" );
- if ( $lName ne "" && $lName eq $deviceLocation )
- {
- $locationDev = $_;
- last;
- }
- }
- if ( $featurelevel >= 5.7 ) {
- my %dummy;
- my ( $err, @a ) =
- ReplaceSetMagic( \%dummy, 0, ($locationDev) );
- $locationDev = join( " ", @a )
- unless ($err);
- }
- # 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 "" ) {
- if ( $featurelevel >= 5.7 ) {
- my %dummy;
- my ( $err, @a ) =
- ReplaceSetMagic( \%dummy, 0,
- ($gatewayDevs) );
- $gatewayDevs = join( " ", @a )
- unless ($err);
- }
- foreach
- my $gatewayDevOr ( split /\|/, $gatewayDevs )
- {
- foreach my $gatewayDev ( split /,/,
- $gatewayDevOr )
- {
- my $tmpSubRecipient;
- if ( $gatewayDev =~ s/:(.*)// ) {
- $tmpSubRecipient = $1;
- }
- if ( $type[$i] ne "mail"
- && !defined( $defs{$gatewayDev} )
- && $deviceType eq "device" )
- {
- $useLocation = 2
- if ( $useLocation == 0 );
- }
- elsif ( $type[$i] ne "mail"
- && IsDisabled($gatewayDev) )
- {
- $useLocation = 2
- if ( $useLocation == 0 );
- }
- elsif (
- $type[$i] ne "mail"
- && (
- ReadingsVal(
- $gatewayDev, "presence",
- "present"
- ) =~
- m/^(0|false|absent|disappeared|unauthorized|disconnected|unreachable)$/i
- || ReadingsVal(
- $gatewayDev, "state",
- "present"
- ) =~
- m/^(absent|disappeared|unauthorized|disconnected|unreachable)$/i
- || ( $defs{$gatewayDev}{STATE}
- && $defs{$gatewayDev}{STATE}
- =~ m/^(absent|disappeared|unauthorized|disconnected|unreachable)$/i
- )
- || ReadingsVal(
- $gatewayDev, "available",
- "yes"
- ) =~ m/^(0|no|false)$/i
- || ReadingsVal(
- $gatewayDev, "reachable",
- "yes"
- ) =~ m/^(0|no|false)$/i
- )
- )
- {
- $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
- ###
- my $deviceType2 =
- defined( $defs{$device} ) ? $defs{$device}{TYPE} : "";
- if (
- $gatewayDevs eq ""
- && $deviceType eq "device"
- && $deviceType2 ne ""
- && (
- (
- $type[$i] eq "audio" && defined(
- $cmdSchema->{ $type[$i] }{$deviceType2}
- )
- )
- || (
- $type[$i] eq "light"
- && defined(
- $cmdSchema->{ $type[$i] }{$deviceType2}
- )
- )
- || (
- $type[$i] eq "push"
- && defined(
- $cmdSchema->{ $type[$i] }{$deviceType2}
- )
- )
- || (
- $type[$i] eq "screen"
- && defined(
- $cmdSchema->{ $type[$i] }{$deviceType2}
- )
- )
- )
- )
- {
- Log3 $logDevice, 4,
- "msg $device: Recipient type $deviceType2 is a gateway device itself for message type "
- . $type[$i]
- . ". Still checking for any delegates ..."
- if ( $testMode ne "1" );
- $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 $globalDevName );
- Log3 $logDevice, 5,
- "msg $device: (No $typeUc contact defined, trying global instead)"
- if ( $catchall == 1 );
- $gatewayDevs =
- # look for direct
- AttrVal(
- $globalDevName, "msgContact$typeUc",
- #look for indirect
- AttrVal(
- AttrVal(
- $globalDevName, "msgRecipient$typeUc",
- ""
- ),
- "msgContact$typeUc",
- #look for indirect general
- AttrVal(
- AttrVal(
- $globalDevName, "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(
- $globalDevName, "msgPriority$typeUc",
- #look for global indirect
- AttrVal(
- AttrVal(
- $globalDevName,
- "msgRecipient$typeUc",
- ""
- ),
- "msgPriority$typeUc",
- #look for global indirect general
- AttrVal(
- AttrVal(
- $globalDevName,
- "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;
- }
- $routes{mail} = 1
- if ( $deviceType eq "email" );
- 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" ) {
- # 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(
- $globalDevName,
- "msgThPrioTextEmergency",
- #look for global indirect type
- AttrVal(
- AttrVal(
- $globalDevName,
- "msgRecipient$typeUc",
- ""
- ),
- "msgThPrioTextEmergency",
- #look for global indirect general
- AttrVal(
- AttrVal(
- $globalDevName,
- "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(
- $globalDevName, "msgThPrioTextNormal",
- #look for global indirect type
- AttrVal(
- AttrVal(
- $globalDevName,
- "msgRecipient$typeUc",
- ""
- ),
- "msgThPrioTextNormal",
- #look for global indirect general
- AttrVal(
- AttrVal(
- $globalDevName,
- "msgRecipient",
- ""
- ),
- "msgThPrioTextNormal",
- # default
- "-2"
- )
- )
- )
- )
- )
- );
- # Decide push and/or e-mail destination based on priorities
- if ( $loopPriority >= $prioThresTextEmg
- && $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 >= $prioThresTextEmg
- && $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 >= $prioThresTextEmg
- && $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 >= $prioThresTextNormal
- && $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 >= $prioThresTextNormal
- && $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 $globalDevName )
- {
- $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: Could not find any general $typeUc contact. Please specify a destination device or set attributes in general msg configuration device $globalDevName : 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 audio-visual 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(
- $globalDevName, "msgSwitcherDev",
- #look for global indirect type
- AttrVal(
- AttrVal(
- $globalDevName,
- "msgRecipient$typeUc",
- ""
- ),
- "msgSwitcherDev",
- #look for global indirect general
- AttrVal(
- AttrVal(
- $globalDevName,
- "msgRecipient",
- ""
- ),
- "msgSwitcherDev",
- # default
- ""
- )
- )
- )
- )
- )
- ),
- "state",
- "long"
- );
- # 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(
- $globalDevName, "msgThPrioAudioEmergency",
- #look for global indirect type
- AttrVal(
- AttrVal(
- $globalDevName,
- "msgRecipient$typeUc",
- ""
- ),
- "msgThPrioAudioEmergency",
- #look for global indirect general
- AttrVal(
- AttrVal(
- $globalDevName,
- "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(
- $globalDevName, "msgThPrioAudioHigh",
- #look for global indirect type
- AttrVal(
- AttrVal(
- $globalDevName,
- "msgRecipient$typeUc",
- ""
- ),
- "msgThPrioAudioHigh",
- #look for global indirect general
- AttrVal(
- AttrVal(
- $globalDevName,
- "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(
- $globalDevName, "msgThPrioHigh",
- #look for global indirect type
- AttrVal(
- AttrVal(
- $globalDevName,
- "msgRecipient$typeUc",
- ""
- ),
- "msgThPrioHigh",
- #look for global indirect general
- AttrVal(
- AttrVal(
- $globalDevName,
- "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(
- $globalDevName, "msgThPrioNormal",
- #look for global indirect type
- AttrVal(
- AttrVal(
- $globalDevName,
- "msgRecipient$typeUc",
- ""
- ),
- "msgThPrioNormal",
- #look for global indirect general
- AttrVal(
- AttrVal(
- $globalDevName,
- "msgRecipient",
- ""
- ),
- "msgThPrioNormal",
- # default
- "0"
- )
- )
- )
- )
- )
- );
- if ( $type[$i] eq "audio" ) {
- if ( $annState eq "long"
- || $forceType == 1
- || $forceDevice == 1
- || $loopPriority >= $prioThresAudioEmg )
- {
- $priorityCat = "";
- }
- elsif ( $loopPriority >= $prioThresAudioHigh ) {
- $priorityCat = "ShortPrio";
- }
- else {
- $priorityCat = "Short";
- }
- }
- else {
- if ( $loopPriority >= $prioThresHigh ) {
- $priorityCat = "High";
- }
- elsif ( $loopPriority >= $prioThresNormal ) {
- $priorityCat = "";
- }
- else {
- $priorityCat = "Low";
- }
- }
- # 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(
- $globalDevName, "msgRecipient$typeUc", ""
- ),
- "presence",
- "-"
- ) ne "-"
- )
- {
- $residentDevState = ReadingsVal(
- AttrVal(
- $globalDevName, "msgRecipient$typeUc", ""
- ),
- "state", ""
- ) if ( $residentDevState eq "" );
- $residentDevPresence = ReadingsVal(
- AttrVal(
- $globalDevName, "msgRecipient$typeUc", ""
- ),
- "presence",
- ""
- ) if ( $residentDevPresence eq "" );
- }
- # global indirect general
- if (
- (
- $residentDevState eq ""
- || $residentDevPresence eq ""
- )
- && ReadingsVal(
- AttrVal( $globalDevName, "msgRecipient", "" ),
- "presence", "-" ) ne "-"
- )
- {
- $residentDevState =
- ReadingsVal(
- AttrVal( $globalDevName, "msgRecipient", "" ),
- "state", "" )
- if ( $residentDevState eq "" );
- $residentDevPresence =
- ReadingsVal(
- AttrVal( $globalDevName, "msgRecipient", "" ),
- "presence", "" )
- if ( $residentDevPresence eq "" );
- }
- # global explicit
- if (
- (
- $residentDevState eq ""
- || $residentDevPresence eq ""
- )
- && ReadingsVal(
- AttrVal( $globalDevName, "msgResidentsDev", "" ),
- "presence", "-" ) ne "-"
- )
- {
- $residentDevState =
- ReadingsVal(
- AttrVal( $globalDevName, "msgResidentsDev", "" ),
- "state", "" )
- if ( $residentDevState eq "" );
- $residentDevPresence =
- ReadingsVal(
- AttrVal( $globalDevName, "msgResidentsDev", "" ),
- "presence", "" )
- if ( $residentDevPresence eq "" );
- }
- ################################################################
- ### Send message
- ###
- # 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(
- $globalDevName, "msgThPrioGwEmergency",
- #look for global indirect type
- AttrVal(
- AttrVal(
- $globalDevName,
- "msgRecipient$typeUc",
- ""
- ),
- "msgThPrioGwEmergency",
- #look for global indirect general
- AttrVal(
- AttrVal(
- $globalDevName,
- "msgRecipient",
- ""
- ),
- "msgThPrioGwEmergency",
- # default
- "2"
- )
- )
- )
- )
- )
- );
- if ( $featurelevel >= 5.7 ) {
- my %dummy;
- my ( $err, @a ) =
- ReplaceSetMagic( \%dummy, 0, ($gatewayDevs) );
- $gatewayDevs = join( " ", @a )
- unless ($err);
- }
- my %gatewaysStatus;
- foreach my $gatewayDevOr ( split /\|/, $gatewayDevs ) {
- foreach my $gatewayDev ( split /,/, $gatewayDevOr ) {
- if ( $gatewayDev =~
- m/^@?([A-Za-z0-9._]+):([A-Za-z0-9._\-\/@+]*):?([A-Za-z0-9._\-\/@+]*)$/
- )
- {
- $gatewayDev = $1;
- $subRecipient = $2 if ( $subRecipient eq "" );
- $termRecipient = $3 if ( $termRecipient eq "" );
- }
- my $logMsg =
- "msg $device: Trying to send message via gateway $gatewayDev";
- $logMsg .= " to recipient $subRecipient"
- if ( $subRecipient ne "" );
- $logMsg .= ", terminal device $termRecipient"
- if ( $termRecipient ne "" );
- Log3 $logDevice, 5, $logMsg;
- ##############
- # 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"
- && IsDisabled($gatewayDev) )
- {
- $routeStatus = "DISABLED";
- }
- elsif (
- $type[$i] ne "mail"
- && (
- ReadingsVal( $gatewayDev, "presence",
- "present" ) =~
- m/^(0|false|absent|disappeared|unauthorized|disconnected|unreachable)$/i
- || ReadingsVal( $gatewayDev, "state",
- "present" ) =~
- m/^(absent|disappeared|unauthorized|disconnected|unreachable)$/i
- || ( $defs{$gatewayDev}{STATE}
- && $defs{$gatewayDev}{STATE} =~
- m/^(absent|disappeared|unauthorized|disconnected|unreachable)$/i
- )
- || ReadingsVal( $gatewayDev, "available",
- "yes" ) =~ m/^(0|no|off|false)$/i
- || ReadingsVal( $gatewayDev, "reachable",
- "yes" ) =~ m/^(0|no|off|false)$/i
- )
- )
- {
- $routeStatus = "UNAVAILABLE";
- }
- elsif ( $type[$i] eq "screen"
- && ReadingsVal( $gatewayDev, "power", "on" ) =~
- m/^(0|off)$/i )
- {
- $routeStatus = "OFF";
- }
- 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";
- }
- elsif ($type[$i] ne "push"
- && $type[$i] ne "mail"
- && $residentDevState eq "asleep" )
- {
- $routeStatus = "USER_ASLEEP";
- }
- # enforce by user request
- if (
- (
- $routeStatus eq "USER_DISABLED"
- || $routeStatus eq "USER_ABSENT"
- || $routeStatus eq "USER_ASLEEP"
- )
- && ( $forceType == 1 || $forceDevice == 1 )
- )
- {
- $routeStatus = "OK_ENFORCED";
- }
- # enforce by priority
- if (
- (
- $routeStatus eq "USER_DISABLED"
- || $routeStatus eq "USER_ABSENT"
- || $routeStatus eq "USER_ASLEEP"
- )
- && $loopPriority >= $prioThresGwEmg
- )
- {
- $routeStatus = "OK_EMERGENCY";
- }
- # add location status
- if ( $useLocation == 2 ) {
- $routeStatus .= "+LOCATION-UNAVAILABLE";
- }
- elsif ( $useLocation == 1 ) {
- $routeStatus .= "+LOCATION";
- }
- my $gatewayType = (
- $type[$i] eq "mail" ? "fhemMsgMail"
- : (
- $defs{$gatewayDev}{TYPE}
- ? $defs{$gatewayDev}{TYPE}
- : "UNDEFINED"
- )
- );
- my $defTitle = "";
- $defTitle =
- $cmdSchema->{ $type[$i] }{$gatewayType}
- {defaultValues}{$priorityCat}{TITLE}
- if (
- defined(
- $cmdSchema->{ $type[$i] }{$gatewayType}
- {defaultValues}{$priorityCat}{TITLE}
- )
- && $priorityCat ne ""
- );
- $defTitle =
- $cmdSchema->{ $type[$i] }{$gatewayType}
- {defaultValues}{Normal}{TITLE}
- if (
- defined(
- $cmdSchema->{ $type[$i] }{$gatewayType}
- {defaultValues}{Normal}{TITLE}
- )
- && $priorityCat eq ""
- );
- Log3 $logDevice, 5,
- "msg $device: Determined default title: $defTitle";
- # use title from device, global or internal default
- my $loopTitle = $title;
- $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(
- $globalDevName,
- "msgTitle$typeUc$priorityCat",
- # look for global indirect high
- AttrVal(
- AttrVal(
- $globalDevName,
- "msgRecipient$typeUc",
- ""
- ),
- "msgTitle$typeUc$priorityCat",
- #look for global indirect general high
- AttrVal(
- AttrVal(
- $globalDevName,
- "msgRecipient",
- ""
- ),
- "msgTitle$typeUc$priorityCat",
- # default
- $defTitle
- )
- )
- )
- )
- )
- ) if ( $title eq "-" );
- $loopTitle = ""
- if ( $loopTitle eq "none"
- || $loopTitle eq "-" );
- my $loopMsg = $msg;
- if ( $catchall == 1 ) {
- $loopTitle = "Fw: $loopTitle"
- if ( $loopTitle
- && $type[$i] !~ /^(audio|screen)$/ );
- $loopMsg = "Forwarded Message: $loopMsg"
- if ( !$loopTitle );
- if ( $type[$i] eq "mail" ) {
- $loopMsg .=
- "\n\n-- \nMail catched from device $device";
- }
- elsif ( $type[$i] !~ /^(audio|screen)$/ ) {
- $loopMsg .=
- " ### (Catched from device $device)";
- }
- }
- # correct message format
- #
- # Remove Sonos Speak commands
- $loopMsg =~ s/(\s*\|\w+\|\s*)/\\n\\n/gi
- if ( $type[$i] ne "audio" );
- # Replace new line with HTML break
- # for e-mails
- $loopMsg =~ s/\n/<br \/>\n/gi
- if ( $type[$i] eq "mail" );
- # use command from device, global or internal default
- my $defCmd = "";
- $defCmd =
- $cmdSchema->{ $type[$i] }{$gatewayType}
- {$priorityCat}
- if (
- defined(
- $cmdSchema->{ $type[$i] }{$gatewayType}
- {$priorityCat}
- )
- && $priorityCat ne ""
- );
- $defCmd =
- $cmdSchema->{ $type[$i] }{$gatewayType}{Normal}
- if (
- defined(
- $cmdSchema->{ $type[$i] }{$gatewayType}
- {Normal}
- )
- && $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(
- $globalDevName,
- "msgCmd$typeUc$priorityCat",
- # look for global indirect
- AttrVal(
- AttrVal(
- $globalDevName,
- "msgRecipient$typeUc",
- ""
- ),
- "msgCmd$typeUc$priorityCat",
- #look for global indirect general
- AttrVal(
- AttrVal(
- $globalDevName,
- "msgRecipient",
- ""
- ),
- "msgCmd$typeUc$priorityCat",
- # internal
- $defCmd
- )
- )
- )
- )
- )
- )
- );
- if ( $cmd eq "" ) {
- Log3 $logDevice, 4,
- "$gatewayDev: Unknown command schema for gateway device type $gatewayType. Use manual definition by userattr msgCmd*";
- $return .=
- "$gatewayDev: Unknown command schema for gateway device type $gatewayType. Use manual definition by userattr msgCmd*\n";
- next;
- }
- # ReplaceSetMagic
- #
- my $replaceError;
- if ( $featurelevel >= 5.7 ) {
- my %dummy;
- my ( $err, @a );
- # TITLE
- ( $err, @a ) =
- ReplaceSetMagic( \%dummy, 0, ($loopTitle) );
- $replaceError .=
- "ReplaceSetMagic failed for TITLE: $err\n"
- if ($err);
- $loopTitle = join( " ", @a )
- unless ($err);
- # RECIPIENT
- if ( $subRecipient ne "" ) {
- ( $err, @a ) =
- ReplaceSetMagic( \%dummy, 0,
- ($subRecipient) );
- $replaceError .=
- "ReplaceSetMagic failed "
- . "for RECIPIENT: $err\n"
- if ($err);
- $subRecipient = join( " ", @a )
- unless ($err);
- }
- # TERMINAL
- if ( $termRecipient ne "" ) {
- ( $err, @a ) =
- ReplaceSetMagic( \%dummy, 0,
- ($termRecipient) );
- $replaceError .=
- "ReplaceSetMagic failed "
- . "for TERMINAL: $err\n"
- if ($err);
- $termRecipient = join( " ", @a )
- unless ($err);
- }
- }
- $cmd =~ s/%DEVICE%/$gatewayDev/gi;
- $cmd =~ s/%PRIORITY%/$loopPriority/gi;
- $cmd =~ s/%TITLE%/$loopTitle/gi;
- $cmd =~ s/%MSG%/$loopMsg/gi;
- $cmd =~ s/%RECIPIENT%/$subRecipient/gi
- if ( $subRecipient ne "" );
- $cmd =~ s/%TERMINAL%/$termRecipient/gi
- if ( $termRecipient ne "" );
- # advanced options from message
- if ( ref($advanced) eq "ARRAY" ) {
- for my $item (@$advanced) {
- for my $key ( keys(%$item) ) {
- my $val = $item->{$key};
- $cmd =~ s/%$key%/$val/gi;
- }
- }
- }
- # advanced options from command schema hash
- if (
- $priorityCat ne ""
- && defined(
- $cmdSchema->{ $type[$i] }{$gatewayType}
- {defaultValues}{$priorityCat}
- )
- )
- {
- for my $item (
- $cmdSchema->{ $type[$i] }{$gatewayType}
- {defaultValues}{$priorityCat} )
- {
- for my $key ( keys(%$item) ) {
- my $val = $item->{$key};
- $cmd =~ s/%$key%/$val/gi;
- }
- }
- }
- elsif (
- $priorityCat eq ""
- && defined(
- $cmdSchema->{ $type[$i] }{$gatewayType}
- {defaultValues}{Normal}
- )
- )
- {
- for my $item (
- $cmdSchema->{ $type[$i] }{$gatewayType}
- {defaultValues}{Normal} )
- {
- for my $key ( keys(%$item) ) {
- my $val = $item->{$key};
- $cmd =~ s/%$key%/$val/gi;
- }
- }
- }
- $sentCounter++;
- if ( $routeStatus =~ /^OK\w*/ ) {
- my $error = 0;
- # ReplaceSetMagic
- #
- if ( $featurelevel >= 5.7 && !$replaceError ) {
- my %dummy;
- my ( $err, @a ) =
- ReplaceSetMagic( \%dummy, 0, ($cmd) );
- $replaceError .=
- "ReplaceSetMagic failed for CMD: $err\n"
- if ($err);
- $cmd = join( " ", @a )
- unless ($err);
- }
- # run command
- if ($replaceError) {
- $error = 2;
- $return .= $replaceError;
- }
- elsif ( $cmd =~ /^\s*\{.*\}\s*$/ ) {
- Log3 $logDevice, 5,
- "msg $device: $type[$i] route command (Perl): $cmd";
- eval $cmd;
- if ($@) {
- $error = 1;
- $return .= "$gatewayDev: $@\n";
- }
- }
- else {
- Log3 $logDevice, 5,
- "msg $device: $type[$i] route command (fhem): $cmd";
- fhem $cmd, 1;
- if ($@) {
- $error = 1;
- $return .= "$gatewayDev: $@\n";
- }
- }
- $routeStatus = "ERROR"
- if ( $error == 1 );
- $routeStatus = "ERROR_EVAL"
- if ( $error == 2 );
- Log3 $logDevice, 3,
- "msg $device: ID=$msgID.$sentCounter TYPE=$type[$i] ROUTE=$gatewayDev RECIPIENT=$subRecipient STATUS=$routeStatus PRIORITY=$loopPriority($priorityCat) TITLE='$loopTitle' MSG='$loopMsg'"
- if ( $priorityCat ne ""
- && $subRecipient ne "" );
- Log3 $logDevice, 3,
- "msg $device: ID=$msgID.$sentCounter TYPE=$type[$i] ROUTE=$gatewayDev RECIPIENT=$subRecipient STATUS=$routeStatus PRIORITY=$loopPriority TITLE='$loopTitle' MSG='$loopMsg'"
- if ( $priorityCat eq ""
- && $subRecipient ne "" );
- Log3 $logDevice, 3,
- "msg $device: ID=$msgID.$sentCounter TYPE=$type[$i] ROUTE=$gatewayDev STATUS=$routeStatus PRIORITY=$loopPriority($priorityCat) TITLE='$loopTitle' MSG='$loopMsg'"
- if ( $priorityCat ne ""
- && $subRecipient eq "" );
- Log3 $logDevice, 3,
- "msg $device: ID=$msgID.$sentCounter TYPE=$type[$i] ROUTE=$gatewayDev STATUS=$routeStatus PRIORITY=$loopPriority TITLE='$loopTitle' MSG='$loopMsg'"
- if ( $priorityCat eq ""
- && $subRecipient eq "" );
- $msgSent = 1 if ( $error == 0 );
- $msgSentDev = 1 if ( $error == 0 );
- if ( $subRecipient ne "" ) {
- $gatewaysStatus{"$gatewayDev:$subRecipient"}
- = $routeStatus
- if ( $globalDevName ne $gatewayDev );
- $gatewaysStatus{"$device:$subRecipient"} =
- $routeStatus
- if ( $globalDevName eq $gatewayDev );
- }
- else {
- $gatewaysStatus{$gatewayDev} = $routeStatus
- if ( $globalDevName ne $gatewayDev );
- $gatewaysStatus{$device} = $routeStatus
- if ( $globalDevName eq $gatewayDev );
- }
- }
- elsif ($routeStatus eq "UNAVAILABLE"
- || $routeStatus eq "UNDEFINED" )
- {
- Log3 $logDevice, 3,
- "msg $device: ID=$msgID.$sentCounter TYPE=$type[$i] ROUTE=$gatewayDev RECIPIENT=$subRecipient STATUS=$routeStatus PRIORITY=$loopPriority TITLE='$loopTitle' '$loopMsg'"
- if ( $subRecipient ne "" );
- Log3 $logDevice, 3,
- "msg $device: ID=$msgID.$sentCounter TYPE=$type[$i] ROUTE=$gatewayDev STATUS=$routeStatus PRIORITY=$loopPriority TITLE='$loopTitle' '$loopMsg'"
- if ( $subRecipient eq "" );
- $gatewaysStatus{$gatewayDev} = $routeStatus
- if ( $globalDevName ne $gatewayDev );
- $gatewaysStatus{$device} = $routeStatus
- if ( $globalDevName eq $gatewayDev );
- }
- else {
- Log3 $logDevice, 3,
- "msg $device: ID=$msgID.$sentCounter TYPE=$type[$i] ROUTE=$gatewayDev RECIPIENT=$subRecipient STATUS=$routeStatus PRIORITY=$loopPriority TITLE='$loopTitle' '$loopMsg'"
- if ( $subRecipient ne "" );
- Log3 $logDevice, 3,
- "msg $device: ID=$msgID.$sentCounter TYPE=$type[$i] ROUTE=$gatewayDev STATUS=$routeStatus PRIORITY=$loopPriority TITLE='$loopTitle' '$loopMsg'"
- if ( $subRecipient eq "" );
- $msgSent = 2 if ( $msgSent != 1 );
- $msgSentDev = 2 if ( $msgSentDev != 1 );
- $gatewaysStatus{$gatewayDev} = $routeStatus
- if ( $globalDevName ne $gatewayDev );
- $gatewaysStatus{$device} = $routeStatus
- if ( $globalDevName eq $gatewayDev );
- }
- }
- last if ( $msgSentDev == 1 );
- }
- if ( $catchall == 0 ) {
- if ( !defined( $sentTypesPerDevice{$device} ) ) {
- $sentTypesPerDevice{$device} = "";
- }
- else {
- $sentTypesPerDevice{$device} .= " ";
- }
- $sentTypesPerDevice{$device} .=
- $type[$i] . ":" . $msgSentDev;
- }
- else {
- if ( !defined( $sentTypesPerDevice{$device} ) ) {
- $sentTypesPerDevice{$globalDevName} = "";
- }
- else {
- $sentTypesPerDevice{$globalDevName} .= " ";
- }
- $sentTypesPerDevice{$globalDevName} .=
- $type[$i] . ":" . $msgSentDev;
- }
- # update device readings
- my $readingsDev = $defs{$device};
- $readingsDev = $defs{$globalDevName}
- if ( $catchall == 1 || $deviceType eq "email" );
- readingsBeginUpdate($readingsDev);
- readingsBulkUpdate( $readingsDev, "fhemMsg" . $typeUc,
- $msg );
- readingsBulkUpdate( $readingsDev,
- "fhemMsg" . $typeUc . "Title", $title );
- readingsBulkUpdate( $readingsDev,
- "fhemMsg" . $typeUc . "Prio",
- $loopPriority );
- my $gwStates = "-";
- while ( ( my $gwName, my $gwState ) = each %gatewaysStatus )
- {
- $gwStates = "" if $gwStates eq "-";
- $gwStates .= " " if $gwStates ne "-";
- $gwStates .= "$gwName:$gwState";
- }
- readingsBulkUpdate( $readingsDev,
- "fhemMsg" . $typeUc . "Gw", $gwStates );
- readingsBulkUpdate( $readingsDev,
- "fhemMsg" . $typeUc . "State", $msgSentDev );
- ################################################################
- ### 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 ( $msgSentDev != 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$/|/gi;
- $recipients =~ s/$regex3/,/gi;
- $recipients =~ s/$regex4/|/gi;
- }
- next;
- }
- # Skip if recipientOr is defined
- # and this is not the last device entry
- # TODO: bei mehreren gleichzeitigen Empfängern
- # (and-Definition)?
- if ( $msgSentDev != 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 =
- defined(
- $settings->{ $type[$i] }{typeEscalation}{gwUnavailable}
- )
- ? $settings->{ $type[$i] }{typeEscalation}{gwUnavailable}
- : "";
- my $fw_emergency =
- defined(
- $settings->{ $type[$i] }{typeEscalation}{emergency} )
- ? $settings->{ $type[$i] }{typeEscalation}{emergency}
- : "";
- my $fw_residentAbsent =
- defined(
- $settings->{ $type[$i] }{typeEscalation}{residentAbsent}
- )
- ? $settings->{ $type[$i] }{typeEscalation}{residentAbsent}
- : "";
- my $fw_residentGone =
- defined(
- $settings->{ $type[$i] }{typeEscalation}{residentGone} )
- ? $settings->{ $type[$i] }{typeEscalation}{residentGone}
- : "";
- # Forward message
- # if no gateway device for this type was available
- if ( $msgSentDev == 0
- && $fw_gwUnavailable ne ""
- && !( $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
- && $fw_emergency ne ""
- && !( $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
- && $fw_residentGone ne ""
- && !( $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
- && $fw_residentAbsent ne ""
- && !( $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 ( $msgSent == 1 );
- $isRecipientOr++;
- }
- }
- last if ( $msgSent == 1 );
- $isTypeOr++;
- }
- # finalize device readings
- while ( ( my $device, my $types ) = each %sentTypesPerDevice ) {
- $device = $globalDevName
- if ( $device =~ /^(([A-Za-z0-9%+._-])+[@]+([%+a-z0-9A-Z.-]*))$/ );
- readingsBulkUpdate( $defs{$device}, "fhemMsgStateTypes", $types )
- if ( $forwarded eq "" );
- readingsBulkUpdate( $defs{$device}, "fhemMsgStateTypes",
- $types . " forwards:" . $forwarded )
- if ( $forwarded ne "" );
- readingsBulkUpdate( $defs{$device}, "fhemMsgState", $msgSent );
- readingsEndUpdate( $defs{$device}, 1 );
- }
- if ( $msgSent == 1 && $return ne "" ) {
- $return .= "However, message was still sent to some recipients!";
- }
- if ( $msgSent == 2 ) {
- $return .=
- "FATAL ERROR: Message NOT sent. No gateway device was available.";
- }
- return $return;
- }
- 1;
- =pod
- =item command
- =item summary dynamic routing of messages to FHEM devices and modules
- =item summary_DE dynamisches Routing für Nachrichten an FHEM Geräte und Module
- =begin html
- <a name="MSG"></a>
- <h3>msg</h3>
- <ul>
- <code>msg [<type>] [<@device>|<e-mail address>] [<priority>] [|<title>|] <message></code>
- <br>
- <br>
- No documentation here 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="MSG"></a>
- <h3>msg</h3>
- <ul>
- <code>msg [<type>] [<@device>|<e-mail address>] [<priority>] [|<title>|] <message></code>
- <br>
- <br>
- Bisher keine Dokumentation hier, sorry.<br>
- <a href="http://forum.fhem.de/index.php/topic,39983.0.html">FHEM Forum</a>
- </ul>
- =end html_DE
- =cut
|