diff --git a/fhem/CHANGED b/fhem/CHANGED index 030c73944..718d9fef0 100644 --- a/fhem/CHANGED +++ b/fhem/CHANGED @@ -1,5 +1,7 @@ # Add changes at the top of the list. Keep it in ASCII, and 80-char wide. # Do not insert empty lines here, update check depends on it. + - new: 76_msgDialog: introducing new module to define dialogs for instant + messaging via TelegramBot, Jabber and yowsub - feature: 93_DbLog: V2.22.11, Attribute DbLogType expanded by SampleFill - feature: 93_DbRep: V5.7.1, commands tableCurrentPurge, tableCurrentFillup - feature: 98_DOIFtools: add getter modelColorGradient, returns a table of diff --git a/fhem/FHEM/76_msgDialog.pm b/fhem/FHEM/76_msgDialog.pm new file mode 100644 index 000000000..308106210 --- /dev/null +++ b/fhem/FHEM/76_msgDialog.pm @@ -0,0 +1,1293 @@ +# Id ########################################################################## +# $Id$ + +# copyright ################################################################### +# +# 76_msgDialog.pm +# +# Copyright by igami +# +# 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 . + +# packages #################################################################### +package main; + use strict; + use warnings; + +# variables ################################################################### +my $msgDialog_devspec = "TYPE=(ROOMMATE|GUEST):FILTER=msgContactPush=.+"; + +# forward declarations ######################################################## +sub msgDialog_Initialize($); + +sub msgDialog_Define($$); +sub msgDialog_Set($@); +sub msgDialog_Get($@); +sub msgDialog_Notify($$); + +sub msgDialog_progress($$$;$); +sub msgDialog_reset($); +sub msgDialog_evalSpecials($$); +sub msgDialog_updateAllowed; + +# initialize ################################################################## +sub msgDialog_Initialize($) { + my ($hash) = @_; + my $TYPE = "msgDialog"; + + $hash->{DefFn} = "$TYPE\_Define"; + $hash->{SetFn} = "$TYPE\_Set"; + $hash->{GetFn} = "$TYPE\_Get"; + $hash->{AttrFn} = "$TYPE\_Attr"; + $hash->{NotifyFn} = "$TYPE\_Notify"; + + $hash->{AttrList} = + "allowed:multiple-strict,everyone ". + "disable:0,1 ". + "disabledForIntervals ". + "evalSpecials:textField-long ". + "msgCommand ". + $readingFnAttributes + ; + + if($modules{msgConfig}{defptr}){ + my $msgConfig = $modules{msgConfig}{defptr}{NAME}; + + addToDevAttrList($msgConfig, "$TYPE\_evalSpecials:textField-long "); + addToDevAttrList($msgConfig, "$TYPE\_msgCommand:textField "); + } +} + +# regular Fn ################################################################## +sub msgDialog_Define($$) { + my ($hash, $def) = @_; + my ($SELF, $TYPE, $DEF) = split(/[\s]+/, $def, 3); + my $rc = eval{ + require JSON; + JSON->import(); + 1; + }; + + return( + "Error loading JSON. Maybe this module is not installed? ". + "\nUnder debian (based) system it can be installed using ". + "\"apt-get install libjson-perl\"" + ) unless($rc); + return( + "No global configuration device defined: ". + "Please define a msgConfig device first" + ) unless($modules{msgConfig}{defptr}); + + $DEF = msgDialog_evalSpecials($hash, $DEF); + $DEF = eval{JSON->new->decode($DEF)}; + + if($@){ + Log3($SELF, 2, "$TYPE ($SELF) - DEF is not a valid JSON: $@"); + return("Usage: define $TYPE {JSON}\n\n$@"); + } + + my @TRIGGER; + + foreach (keys(%{$DEF})){ + next if(defined($DEF->{$_}{setOnly})); + + push(@TRIGGER, $_); + } + + $hash->{TRIGGER} = join(",", @TRIGGER); + $hash->{NOTIFYDEV} = "TYPE=(ROOMMATE|GUEST)"; + + msgDialog_update_msgCommand($hash); + msgDialog_reset($hash); + msgDialog_updateAllowed(); + + return; +} + +sub msgDialog_Set($@) { + my ($hash, @a) = @_; + my $TYPE = $hash->{TYPE}; + + return "\"set $TYPE\" needs at least one argument" if(@a < 2); + + my $SELF = shift @a; + my $argument = shift @a; + my $value = join(" ", @a) if (@a); + my %sets = ( + "reset" => "reset:noArg", + "say" => "say:textField", + "updateAllowed" => "updateAllowed:noArg" + ); + + Log3($SELF, 5, "$TYPE ($SELF) - entering msgDialog_Set"); + + return( + "Unknown argument $argument, choose one of ".join(" ", values %sets) + ) unless(exists($sets{$argument})); + + if($argument eq "reset"){ + msgDialog_reset($hash); + } + elsif($argument eq "updateAllowed"){ + msgDialog_updateAllowed(); + } + + return if(IsDisabled($SELF)); + + if($argument eq "say" && $value){ + my $recipients = join(",", ($value =~ m/@(\S+)\s+/g)); + $recipients = AttrVal($SELF, "allowed", "") unless($recipients); + my (undef, $say) = ($value =~ m/(^|\s)([^@].+)/g); + + return unless($recipients || $say); + + msgDialog_progress($hash, $recipients, $say, 1); + } + + return; +} + +sub msgDialog_Get($@) { + my ($hash, @a) = @_; + my $TYPE = $hash->{TYPE}; + + return "\"get $TYPE\" needs at least one argument" if(@a < 2); + + my $SELF = shift @a; + my $argument = shift @a; + my $value = join(" ", @a) if (@a); + my %gets = ( + "trigger" => "trigger:noArg" + ); + + Log3($SELF, 5, "$TYPE ($SELF) - entering msgDialog_Get"); + + return( + "Unknown argument $argument, choose one of ".join(" ", values %gets) + ) unless(exists($gets{$argument})); + + return if(IsDisabled($SELF)); + + if($argument eq "trigger"){ + return(join("\n", split(",", InternalVal($SELF, "TRIGGER", undef)))); + } + + return; +} + +sub msgDialog_Attr(@) { + my ($cmd, $SELF, $attribute, $value) = @_; + my ($hash) = $defs{$SELF}; + my $TYPE = $hash->{TYPE}; + + Log3($SELF, 5, "$TYPE ($SELF) - entering msgDialog_Attr"); + + if($attribute eq "disable"){ + if($cmd eq "set" and $value == 1){ + readingsSingleUpdate($hash, "state", "Initialized", 1); + } + else{ + readingsSingleUpdate($hash, "state", "disabled", 1); + } + } + elsif($attribute eq "msgCommand"){ + if($cmd eq "set"){ + $attr{$SELF}{$attribute} = $value; + } + else{ + delete($attr{$SELF}{$attribute}); + } + + msgDialog_update_msgCommand($hash); + } + + return; +} + +sub msgDialog_Notify($$) { + my ($hash, $dev_hash) = @_; + my $SELF = $hash->{NAME}; + my $TYPE = $hash->{TYPE}; + my $device = $dev_hash->{NAME}; + + Log3($SELF, 5, "$TYPE ($SELF) - entering msgDialog_Notify"); + + return if(IsDisabled($SELF)); + + my @events = @{deviceEvents($dev_hash, 1)}; + + return unless( + @events && AttrVal($SELF, "allowed", "") =~ m/(^|,)($device|everyone)(,|$)/ + ); + + foreach my $event (@events){ + next unless($event =~ m/(fhemMsgPushReceived|fhemMsgRcvPush): (.+)/); + + Log3($SELF, 4 , "$TYPE ($SELF) triggered by \"$device $event\""); + + msgDialog_progress($hash, $device, $2); + } + + return; +} + +# module Fn ################################################################### +sub msgDialog_evalSpecials($$) { + my ($hash, $string) = @_; + my $SELF = $hash->{NAME}; + my $TYPE = $hash->{TYPE}; + + Log3($SELF, 5, "$TYPE ($SELF) - entering msgDialog_evalSpecials"); + + my $msgConfig = $modules{msgConfig}{defptr}{NAME} + if($modules{msgConfig}{defptr}); + $string =~ s/\$SELF/$SELF/g; + my $evalSpecials = + AttrVal($msgConfig, "$TYPE\_evalSpecials", ""). + " ". + AttrVal($SELF, "evalSpecials", "") + ; + + return($string) if($evalSpecials eq " "); + + (undef, $evalSpecials) = parseParams($evalSpecials, "\\s", " "); + + return($string) unless($evalSpecials); + + foreach(keys(%{$evalSpecials})){ + $evalSpecials->{$_} = eval $evalSpecials->{$_} + if($evalSpecials->{$_} =~ m/^{.*}$/); + } + + my $specials = join("|", keys(%{$evalSpecials})); + $string =~ s/%($specials)%/$evalSpecials->{$1}/g; + + return($string); +} + +sub msgDialog_progress($$$;$) { + my ($hash, $recipients, $message, $force) = @_; + my $SELF = $hash->{NAME}; + my $TYPE = $hash->{TYPE}; + $recipients = join(",", devspec2array($msgDialog_devspec)) + if($recipients eq "everyone"); + + return unless($recipients); + + Log3( + $SELF, 5 , "$TYPE ($SELF)" + . "\n entering msgDialog_progress" + . "\n recipients: $recipients" + . "\n message: $message" + . "\n force: ".($force ? $force : 0) + ); + + my @oldHistory; + @oldHistory = split("\\|", ReadingsVal($SELF, "$recipients\_history", "")) + unless($force); + push(@oldHistory, split("\\|", $message)); + my (@history); + my $dialog =$hash->{DEF}; + $dialog = msgDialog_evalSpecials($hash, $dialog); + $dialog =~ s/\$recipient/$recipients/g; + $dialog = eval{JSON->new->decode($dialog)}; + + foreach (@oldHistory){ + $message = $_; + + if(defined($dialog->{$message})){ + $dialog = $dialog->{$message}; + push(@history, $message); + } + else{ + foreach (keys(%{$dialog})){ + next unless( + $dialog->{$_} =~ m/HASH/ && + defined($dialog->{$_}{match}) && + $message =~ m/^$dialog->{$_}{match}$/ + ); + + $dialog = $dialog->{$_}; + push(@history, $_); + + last; + } + } + } + + return if(@history != @oldHistory || !$force && $dialog->{setOnly}); + + $dialog = eval{JSON->new->encode($dialog)}; + $dialog =~ s/\$message/$message/g; + $dialog = eval{JSON->new->decode($dialog)}; + my $history = ""; + + foreach (keys(%{$dialog})){ + if($_ !~ m/(setOnly|match|commands|message)/){ + $history = join("|", @history); + + last; + } + } + + readingsBeginUpdate($hash); + readingsBulkUpdate($hash, $_."_history", $history) + foreach (split(",", $recipients)); + readingsBulkUpdate($hash, "state", "$recipients: $message"); + readingsEndUpdate($hash, 1); + + if($dialog->{commands}){ + my @commands = + $dialog->{commands} =~ m/ARRAY/ ? + @{$dialog->{commands}} + : $dialog->{commands} + ; + + foreach (@commands){ + $_ =~ s/;/;;/g; + my $ret = AnalyzeCommandChain(undef, $_); + + Log3($SELF, 4, "$TYPE ($SELF) - return from command \"$_\": $ret") + if($ret); + } + } + + if($dialog->{message}){ + my @message = + $dialog->{message} =~ m/ARRAY/ ? + @{$dialog->{message}} + : $dialog->{message} + ; + + foreach (@message){ + if($_ =~ m/^{.*}$/s){ + $_ =~ s/;/;;/g; + $_ = AnalyzePerlCommand(undef, $_); + } + } + + my $message = join("\n", @message); + my $msgCommand = '"'.InternalVal($SELF, "MSGCOMMAND", "").'"'; + $msgCommand = eval($msgCommand); + + fhem($msgCommand); + } + + return; +} + +sub msgDialog_reset($) { + my ($hash) = @_; + my $SELF = $hash->{NAME}; + my $TYPE = $hash->{TYPE}; + + Log3($SELF, 5, "$TYPE ($SELF) - entering msgDialog_reset"); + + delete($hash->{READINGS}); + + readingsSingleUpdate($hash, "state", "Initialized", 1) + unless(IsDisabled($SELF)); + + return; +} + +sub msgDialog_updateAllowed { + Log(5, "msgDialog - entering msgDialog_updateAllowed"); + + my $allowed = join(",", sort(devspec2array($msgDialog_devspec))); + + $modules{msgDialog}{AttrList} =~ + s/allowed:multiple-strict,\S*/allowed:multiple-strict,everyone,$allowed/; +} + +sub msgDialog_update_msgCommand($) { + my ($hash) = @_; + my $SELF = $hash->{NAME}; + my $TYPE = $hash->{TYPE}; + my $msgConfig = $modules{msgConfig}{defptr}{NAME}; + + Log3($SELF, 5, "$TYPE ($SELF) - entering msgDialog_update_msgCommand"); + + $hash->{MSGCOMMAND} = + AttrVal($SELF, "msgCommand", + AttrVal($msgConfig, "$TYPE\_msgCommand", + 'msg push \@$recipients $message' + ) + ) + ; + + return; +} + +1; + +# commandref ################################################################## +=pod +=item helper +=item summary dialogs for instant messaging +=item summary_DE Dialoge für Sofortnachrichten + +=begin html + + +

msgDialog

+ + +=end html + +=begin html_DE + + +

msgDialog

+ + +=end html_DE +=cut diff --git a/fhem/MAINTAINER.txt b/fhem/MAINTAINER.txt index 5641b0633..57a12dcd3 100644 --- a/fhem/MAINTAINER.txt +++ b/fhem/MAINTAINER.txt @@ -328,6 +328,7 @@ FHEM/74_THINKINGCLEANER.pm loredo Unterstuetzende Dienste FHEM/74_Unifi.pm rapster Automatisierung FHEM/75_MSG.pm loredo Automatisierung FHEM/75_msgConfig.pm loredo Automatisierung +FHEM/76_msgDialog.pm igami Frontends/Sprachsteuerung FHEM/76_MSGFile.pm gandy Automatisierung FHEM/76_MSGMail.pm gandy Automatisierung FHEM/76_SMAInverter.pm DS_Starter Sonstige Systeme