From adb1743e250a8b3ff4b9a8520a162426791e557b Mon Sep 17 00:00:00 2001 From: igami Date: Sun, 15 Oct 2017 07:50:11 +0000 Subject: [PATCH] 76_msgDialog: initial commit git-svn-id: https://svn.fhem.de/fhem/trunk@15262 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/CHANGED | 2 + fhem/FHEM/76_msgDialog.pm | 1293 +++++++++++++++++++++++++++++++++++++ fhem/MAINTAINER.txt | 1 + 3 files changed, 1296 insertions(+) create mode 100644 fhem/FHEM/76_msgDialog.pm 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

+
    + With msgDialog you can define dialogs for instant messages via TelegramBot, Jabber and yowsup (WhatsApp).
    + The communication uses the msg command. Therefore, a device of type msgConfig must be defined first.
    + For each dialog you can define which person is authorized to do so. Devices of the type ROOMMATE or GUEST with a defined msgContactPush attribute are required for this. Make sure that the reading fhemMsgRcvPush generates an event.
    +
    + Prerequisites: +
      + The Perl module "JSON" is required.
      + Under Debian (based) system, this can be installed using + "apt-get install libjson-perl". +
    +
    + + + Define +
      + define <name> msgDialog <JSON>
      + Because of the complexity, it is easiest to define an empty dialog first. + define <name> msgDialog {} + Then edit the DEF in the detail view. +
      +{
      +  "<TRIGGER>": {
      +    "match": "<regex>",
      +    "setOnly": (true|false),
      +    "commands": "(fhem command|{perl code})",
      +    "message": [
      +      "{perl code}",
      +      "text"
      +    ],
      +    "<NEXT TRIGGER 1>": {
      +      ...
      +    },
      +    "<NEXT TRIGGER 2>": {
      +      ...
      +    }
      +  }
      +}
      +    
      +
    • + TRIGGER
      + Can be any text. The device checks whether the incoming message equals it. If so, the dialogue will be continued at this point. +
    • +
      +
    • + match
      + If you do not want to allow only one message, you can specify a regex. The regex must apply to the whole incoming message. +
    • +
      +
    • + setOnly
      + Can be optionally set to true or false. In both cases, the TRIGGER will + not be returned at "get <name> trigger".
      + If setOnly is set to true, the dialog at this point cannot be triggered + by incoming messages, but only by using "get <name> say + TRIGGER".
      + This can be used to initiate a dialog from FHEM. +
    • +
      +
    • + commands
      + Can contain a single or multiple commands: +
      +"commands": "single command"
      +
      +"commands": [
      +"command 1",
      +"command 2",
      +"{perl command}"
      +]
      +      
      +
    • +
    • + message
      + Can contain a single or multiple text that is connected by a line break: +
      +"message": "text"
      +
      +"message": [
      +"text 1",
      +"text 2",
      +"{return from perl command}"
      +]
      +      
      +
    • + For multi-level dialogs, this structure is specified nested.
      +
      + Variables and placeholders defined under the attribute evalSpecials are + evaluated.
      + Variables: +
    • + $SELF
      + name of the msgDialog +
    • +
      +
    • + $message
      + received message +
    • +
      +
    • + $recipient
      + Name of the dialog partner +
    • +
    +
    + + + Set +
      +
    • + reset
      + Resets the dialog for all users. +
    • +
      +
    • + + say [@<recipient1>[,<recipient2>,...]] + <TRIGGER>[|<NEXT TRIGGER>|...] +
      + The dialog is continued for all specified recipients at the specified + position.
      + If no recipients are specified, the dialog is continued for all + recipients specified under the allowed attribute. +
    • +
      +
    • + updateAllowed
      + Updates the selection for the allowed attribute. +
    • +
    +
    + + + Get +
      +
    • + trigger
      + Lists all TRIGGERs of the first level where setOnly is not specified. +
    • +
    +
    + + + Attribute +
      +
    • + allowed
      + List with all RESIDENTS and ROOMMATE that are authorized for this dialog. +
    • +
      +
    • + disable 1
      + Dialog is deactivated. +
    • +
      +
    • + + disabledForIntervals HH:MM-HH:MM HH:MM-HH-MM ... + +
    • +
      +
    • + evalSpecials key1=value1 key2=value2 ...
      + Space Separate list of name=value pairs.
      + Value may contain spaces if included in "" or {}.
      + Value is evaluated as a perl expression if it is included in {}.
      + In the DEF, %Name% strings are replaced by the corresponding value.
      + This attribute is available as "msgDialog_evalSpecials" in the msgConfig + device.
      + If the same name was defined in the msgConfig and msgDialog, the value + from msgDialog is used. +
    • +
      +
    • + msgCommand <command>
      + Command used to send a message.
      + The default is + "msg push \@$recipients $message".
      + This attribute is available as "msgDialog_msgCommand" in the msgConfig device. +
    • +
    +
    + + + Reading +
      +
    • + $recipient_history
      + | separated list of TRIGGERS to save the current state of the dialog.
      + A readings is created for each dialog partner. When the dialog is + finished, the reading will be cleared. +
    • +
    +
    + + + Notes for use with TelegramBot: +
      + It may be necessary to set the attribute "utf8specials" to 1 in the + TelegramBot, for messages with special characters to be sent.
      +
      + The msg command supports the TelegramBot_MTYPE. The default is message. The + queryInline value can be used to create an inline keyboard. +
    +
    + + + Notes for use with Jabber: +
      + The msg command supports the TelegramBot_MTYPE. The default is empty. The + value otr can be used to send an OTR message. +
    +
    + + + Notes for use with yowsub (WhatsApp): +
      + No experiences so far. +
    +
    + + + Examples: +
      + + + The following example codes can be imported by "Raw defnition". + + +
      +
      + All examples are designed for communication via the TelegramBot. When using + Jabber or yowsup, they may need to be adjusted.
      + It is assumed that the msgConfig device contains the evalSpecials "me" with + a name which is used to call the bot.
      +
      + Meta dialog for listing all authorized dialogs: +
        +
        +defmod meta_Dialog msgDialog {\
        +  "%me%": {\
        +    "match": "\/?(start|%me%)",\
        +    "commands": "deletereading TYPE=msgDialog $recipient_history",\
        +    "message": [\
        +      "{return('(' . join(') (', sort(split('\n', fhem('get TYPE=msgDialog:FILTER=NAME!=$SELF:FILTER=allowed=.*($recipient|everyone).* trigger'))), 'abbrechen') . ') ')}",\
        +      "Ich kann folgendes für dich tun:"\
        +    ]\
        +  },\
        +  "zurück": {\
        +    "commands": "set $recipient_history=.+|.+ say @$recipient {(ReadingsVal($DEV, '$recipient_history', '') =~ m/(.+)\\|.+$/;; return $2 ? $2 : $1;;)}"\
        +  },\
        +  "abbrechen": {\
        +    "match": "\/?abbrechen",\
        +    "commands": "deletereading TYPE=msgDialog $recipient_history",\
        +    "message": [\
        +      "TelegramBot_MTYPE=queryInline (%me%) ",\
        +      "Dialog abgebrochen."\
        +    ]\
        +  },\
        +  "beenden": {\
        +    "match": "\/?beenden",\
        +    "commands": "deletereading TYPE=msgDialog $recipient_history",\
        +    "message": [\
        +      "TelegramBot_MTYPE=queryInline (%me%) ",\
        +      "Dialog beendet."\
        +    ]\
        +  }\
        +}
        +attr meta_Dialog allowed everyone
        +
        +
      + Request of current fuel prices +
        +
        +defmod Tankstelle_Dialog msgDialog {\
        +  "Tankstelle": {\
        +    "message": [\
        +      "TelegramBot_MTYPE=queryInline (%me%) ",\
        +      "Die Krafstoffpreise der betragen aktuell folgende Werte:",\
        +      "",\
        +      "AIVA",\
        +      "",\
        +      "[%AIVA%:Diesel] €/l Diesel",\
        +      "[%AIVA%:Super] €/l Super",\
        +      "[%AIVA%:E10] €/l E10",\
        +      "[%AIVA%:Autogas] €/l Autogas"\
        +    ]\
        +  }\
        +}
        +attr Tankstelle_Dialog evalSpecials AIVA=AIVA_petrolStation
        +
        +
      + Programming of the washing machine +
        +
        +defmod Waschmaschine_Dialog msgDialog { "Waschmaschine": {\
        +    "message": [\
        +      "{return('(Zeitprogramm stoppen) ') if(ReadingsVal('%controlUnit%', 'controlMode', '') eq 'auto')}",\
        +      "{return('(programmieren) ') if(ReadingsVal('%actor%', 'state', '') ne 'on')}",\
        +      "{return('(einschalten) ') if(ReadingsVal('%actor%', 'state', '') ne 'on')}",\
        +      "(Verlaufsdiagramm) ",\
        +      "(abbrechen) ",\
        +      "{return('Waschmaschine: ' . (ReadingsVal('%actor%', 'state', '') eq 'on' ? 'eingeschaltet' : 'ausgeschaltet'))}",\
        +      "{return('Modus: ' . (ReadingsVal('%controlUnit%', 'controlMode', '') eq 'auto' ? 'Automatik' : 'Manuell (' . ReadingsVal('%controlUnit%', 'time', '') . ')'))}"\
        +    ],\
        +    "Zeitprogramm stoppen": {\
        +      "commands": "set %controlUnit% controlMode manual",\
        +      "message": [\
        +        "TelegramBot_MTYPE=queryInline (%me%) ",\
        +        "Das Zeitprogramm wurde gestoppt."\
        +      ]\
        +    },\
        +    "programmieren": {\
        +      "message": [\
        +        "(bestätigen|zurück|abbrechen) ",\
        +        "( 00:00 | 00:15 | 00:30 | 00:45 ) ",\
        +        "( 01:00 | 01:15 | 01:30 | 01:45 ) ",\
        +        "( 02:00 | 02:15 | 02:30 | 02:45 ) ",\
        +        "( 03:00 | 03:15 | 03:30 | 03:45 ) ",\
        +        "( 04:00 | 04:15 | 04:30 | 04:45 ) ",\
        +        "( 05:00 | 05:15 | 05:30 | 05:45 ) ",\
        +        "( 06:00 | 06:15 | 06:30 | 06:45 ) ",\
        +        "( 07:00 | 07:15 | 07:30 | 07:45 ) ",\
        +        "( 08:00 | 08:15 | 08:30 | 08:45 ) ",\
        +        "( 09:00 | 09:15 | 09:30 | 09:45 ) ",\
        +        "( 10:00 | 10:15 | 10:30 | 10:45 ) ",\
        +        "( 11:00 | 11:15 | 11:30 | 11:45 ) ",\
        +        "( 12:00 | 12:15 | 12:30 | 12:45 ) ",\
        +        "( 13:00 | 13:15 | 13:30 | 13:45 ) ",\
        +        "( 14:00 | 14:15 | 14:30 | 14:45 ) ",\
        +        "( 15:00 | 15:15 | 15:30 | 15:45 ) ",\
        +        "( 16:00 | 16:15 | 16:30 | 16:45 ) ",\
        +        "( 17:00 | 17:15 | 17:30 | 17:45 ) ",\
        +        "( 18:00 | 18:15 | 18:30 | 18:45 ) ",\
        +        "( 19:00 | 19:15 | 19:30 | 19:45 ) ",\
        +        "( 20:00 | 20:15 | 20:30 | 20:45 ) ",\
        +        "( 21:00 | 21:15 | 21:30 | 21:45 ) ",\
        +        "( 22:00 | 22:15 | 22:30 | 22:45 ) ",\
        +        "( 23:00 | 23:15 | 23:30 | 23:45 ) ",\
        +        "Wann soll die Wäsche fertig sein?",\
        +        "Bitte Uhrzeit in HH:MM angeben.",\
        +        "Aktuell ist [%controlUnit%:time] Uhr eingestellt."\
        +      ],\
        +      "Uhrzeit": {\
        +        "match": " ?([0-1][0-9]|2[0-3]):[0-5][0-9] ?",\
        +        "commands": [\
        +          "set %controlUnit% time $message",\
        +          "set $SELF say @$recipient Waschmaschine|programmieren|bestätigen"\
        +        ]\
        +      },\
        +      "bestätigen": {\
        +        "commands": "set %controlUnit% controlMode auto",\
        +        "message": [\
        +          "TelegramBot_MTYPE=queryInline (%me%) ",\
        +          "Das Zeitprogramm wurde eingestellt.",\
        +          "Die Wäsche wird voraussichtlich um [%controlUnit%:time] Uhr fertig sein.",\
        +          "Bitte die Waschmaschine vorbereiten."\
        +        ]\
        +      }\
        +    },\
        +    "einschalten": {\
        +      "commands": [\
        +        "set %controlUnit% controlMode manual",\
        +        "set %actor% on"\
        +      ]\
        +    },\
        +    "Verlaufsdiagramm": {\
        +      "commands": "set %TelegramBot% cmdSend {plotAsPng('%plot%')}",\
        +      "message": "TelegramBot_MTYPE=queryInline (%me%) $message"\
        +    }\
        +  },\
        +  "auto": {\
        +    "setOnly": true,\
        +    "commands": [\
        +      "set %actor% on",\
        +      "set %controlUnit% controlMode manual"\
        +    ],\
        +    "message": [\
        +      "TelegramBot_MTYPE=queryInline (%me%) ",\
        +      "Die Wachmaschine wurde automatisch eingeschaltet."\
        +    ]\
        +  },\
        +  "manual": {\
        +    "setOnly": true,\
        +    "message": [\
        +      "TelegramBot_MTYPE=queryInline (%me%) ",\
        +      "Die Wachmaschine wurde manuell eingeschaltet."\
        +    ]\
        +  },\
        +  "done": {\
        +    "setOnly": true,\
        +    "commands": "set %actor% off",\
        +    "message": [\
        +      "TelegramBot_MTYPE=queryInline (%me%) ",\
        +      "Die Wachmaschine ist fertig."\
        +    ]\
        +  }\
        +}
        +attr Waschmaschine_Dialog evalSpecials actor=HM_2C10D8_Sw\
        +controlUnit=Waschkeller_washer_controlUnit\
        +plot=Waschkeller_washer_SVG
        +
        +
      +
    +
+ +=end html + +=begin html_DE + + +

msgDialog

+
    + Mit msgDialog können Dialoge für Sofortnachrichten über + TelegramBot, Jabber und yowsup (WhatsApp) definiert werden.
    + Die Kommunikation erfolgt über den msg Befehl. Daher muss ein Gerät + vom Typ msgConfig zuerst definiert werden.
    + Für jeden Dialog kann festgelegt werden welche Person dazu berechtigt + ist. Dazu sind Geräte vom Typ ROOMMATE oder GUEST mit definiertem + msgContactPush Attribut erforderlich. Es ist darauf zu achten, dass das + Reading fhemMsgRcvPush ein Event erzeugt.
    +
    + Vorraussetzungen: +
      + Das Perl-Modul "JSON" wird benötigt.
      + Unter Debian (basierten) System, kann dies mittels + "apt-get install libjson-perl" installiert werden. +
    +
    + + + Define +
      + define <name> msgDialog <JSON>
      + Aufgrunder komplexität ist es am einfachsten erst einen leeren Dialog + zu definieren. + define <name> msgDialog {} + Anschließend die DEF dann in der Detail-Ansicht bearbeiten. +
      +{
      +  "<TRIGGER>": {
      +    "match": "<regex>",
      +    "setOnly": (true|false),
      +    "commands": "(fhem command|{perl code})",
      +    "message": [
      +      "{perl code}",
      +      "text"
      +    ],
      +    "<NEXT TRIGGER 1>": {
      +      ...
      +    },
      +    "<NEXT TRIGGER 2>": {
      +      ...
      +    }
      +  }
      +}
      +    
      +
    • + TRIGGER
      + Kann ein beliebiger Text sein. Es wird geprüft ob die eingehende + Nachricht damit übereinstimmt. Falls ja, wird der Dialog an dieser + Stelle fortgesetzt. +
    • +
      +
    • + match
      + Wenn nicht nur genau eine Nachricht zugelassen werden soll, kann noch + eine regex angegeben werden. Die regex muss auf die gesamte eingehnde + Nachricht zutreffen. +
    • +
      +
    • + setOnly
      + Kann optional auf true oder false gestellt werden. In beiden + fällen wird der TRIGGER dann nicht bei "get <name> trigger" + zurück gegeben.
      + Wenn setOnly auf true gestellt wird kann der Dialog an dieser Stelle + nicht durch eingehnde Nachrichten ausgelöst werden, sondern nur + über "get <name> say TRIGGER".
      + Dies kann dazu genutzt werden um einen Dialog von FHEM zu aus zu + initieren. +
    • +
      +
    • + commands
      + Kann einen einzelnen oder mehrere Befehle enthalten: +
      +"commands": "single command"
      +
      +"commands": [
      +"command 1",
      +"command 2",
      +"{perl command}"
      +]
      +      
      +
    • +
    • + message
      + Kann einen einzelnen oder mehrere Textte enthalten die mit einen + Zeilenumbruch verbunden werden: +
      +"message": "text"
      +
      +"message": [
      +"text 1",
      +"text 2",
      +"{return from perl command}"
      +]
      +      
      +
    • + Bei mehrstufigen Dialogen wird diese Struktur ineinander verschachtelt + angegeben.
      +
      + Es werden Variablen und unter dem Attribut evalSpecials definierte + Platzhalter ausgewertet.
      + Variablen: +
    • + $SELF
      + Eigenname des msgDialog +
    • +
      +
    • + $message
      + eingegangene Nachricht +
    • +
      +
    • + $recipient
      + Name des Dialogpartners +
    • +
    +
    + + + Set +
      +
    • + reset
      + Setzt den Dialog für alle Benutzer zurück. +
    • +
      +
    • + + say [@<recipient1>[,<recipient2>,...]] + <TRIGGER>[|<NEXT TRIGGER>|...] +
      + Der Dialog wird für alle angegeben Empänger an der angegeben + Stelle fortgeführt.
      + Sind keine Empfänger angegeben wird der Dialog für alle unter + dem Attribut allowed angegebenen Empfänger fortgeführt. +
    • +
      +
    • + updateAllowed
      + Aktualisiert die Auswahl für das Attribut allowed. +
    • +
    +
    + + + Get +
      +
    • + trigger
      + Listet alle TRIGGER der ersten Ebene auf bei denen nicht setOnly + angegeben ist. +
    • +
    +
    + + + Attribute +
      +
    • + allowed
      + Liste mit allen RESIDENTS und ROOMMATE die für diesen Dialog + berechtigt sind. +
    • +
      +
    • + disable 1
      + Dialog ist deaktiviert. +
    • +
      +
    • + + disabledForIntervals HH:MM-HH:MM HH:MM-HH-MM ... + +
    • +
      +
    • + evalSpecials key1=value1 key2=value2 ...
      + Leerzeichen getrennte Liste von Name=Wert Paaren.
      + Wert kann Leerzeichen enthalten, falls es in "" oder {} eingeschlossen + ist.
      + Wert wird als + perl-Ausdruck ausgewertet, falls es in {} eingeschlossen ist.
      + In der DEF werden %Name% Zeichenketten durch den zugehörigen Wert + ersetzt.
      + Dieses Attribut ist als "msgDialog_evalSpecials" im msgConfig Gerät + vorhanden.
      + Wenn der selbe Name im msgConfig und msgDialog definiert wurde, wird der + Wert aus msgDialog verwendet. +
    • +
      +
    • + msgCommand <command>
      + Befehl der zum Versenden einer Nachricht verwendet wird.
      + Die Vorgabe ist + "msg push \@$recipients $message"
      + Dieses Attribut ist als "msgDialog_msgCommand" im msgConfig Gerät + vorhanden. +
    • +
    +
    + + + Reading +
      +
    • + $recipient_history
      + Durch | getrennte Liste von TRIGGERN um den aktuellen Zustand des Dialogs + zu sichern.
      + Für jeden Dialogpartner wird ein Readings angelegt. Wenn der Dialog + beendet ist wird das Reading zurückgesetzt. +
    • +
    +
    + + + Hinweise zur Benutzung mit Telegram: +
      + Es kann notwendig sein, dass im TelegramBot das Attribut "utf8specials" auf + 1 gesetzt wird, damit Nachrichten mit Umlauten gesendert werden.
      +
      + Bei dem msg Befehl kann der TelegramBot_MTYPE angegeben werden. Die Vorgabe + ist message. Durch den Wert queryInline lässt sich ein inline Keyboard + erzeugen. +
    +
    + + + Hinweise zur Benutzung mit Jabber: +
      + Bei dem msg Befehl kann der Jabber_MTYPE angegeben werden. Die Vorgabe ist + leer. Durch den Wert otr lässt sich eine OTR Nachricht versenden. +
    +
    + + + Hinweise zur Benutzung mit yowsub (WhatsApp): +
      + Bisher noch keine Erfahungen. +
    +
    + + + Beispiele: +
      + + + Die folgenden beispiel Codes können nur per "Raw defnition" + importiert werden. + + +
      +
      + Alle Beispiele sind für die Kommunikation über den TelegramBot + ausgelegt. Bei der Verwendung von Jabber oder yowsup müssen diese + gegebenenfalls angepasst werden.
      + Es wird davon ausgegangen, dass im msgConfig Gerät das evalSpecials + "me" mit einem Namen gepflegt ist, über welchen der Bot angesprochen + wird.
      +
      + Meta Dialog zur auflistung aller Berechtigten Dialoge: +
        +
        +defmod meta_Dialog msgDialog {\
        +  "%me%": {\
        +    "match": "\/?(start|%me%)",\
        +    "commands": "deletereading TYPE=msgDialog $recipient_history",\
        +    "message": [\
        +      "{return('(' . join(') (', sort(split('\n', fhem('get TYPE=msgDialog:FILTER=NAME!=$SELF:FILTER=allowed=.*($recipient|everyone).* trigger'))), 'abbrechen') . ') ')}",\
        +      "Ich kann folgendes für dich tun:"\
        +    ]\
        +  },\
        +  "zurück": {\
        +    "commands": "set $recipient_history=.+|.+ say @$recipient {(ReadingsVal($DEV, '$recipient_history', '') =~ m/(.+)\\|.+$/;; return $2 ? $2 : $1;;)}"\
        +  },\
        +  "abbrechen": {\
        +    "match": "\/?abbrechen",\
        +    "commands": "deletereading TYPE=msgDialog $recipient_history",\
        +    "message": [\
        +      "TelegramBot_MTYPE=queryInline (%me%) ",\
        +      "Dialog abgebrochen."\
        +    ]\
        +  },\
        +  "beenden": {\
        +    "match": "\/?beenden",\
        +    "commands": "deletereading TYPE=msgDialog $recipient_history",\
        +    "message": [\
        +      "TelegramBot_MTYPE=queryInline (%me%) ",\
        +      "Dialog beendet."\
        +    ]\
        +  }\
        +}
        +attr meta_Dialog allowed everyone
        +
        +
      + Abfrage der aktuellen Krafstoffpreise +
        +
        +defmod Tankstelle_Dialog msgDialog {\
        +  "Tankstelle": {\
        +    "message": [\
        +      "TelegramBot_MTYPE=queryInline (%me%) ",\
        +      "Die Krafstoffpreise der betragen aktuell folgende Werte:",\
        +      "",\
        +      "AIVA",\
        +      "",\
        +      "[%AIVA%:Diesel] €/l Diesel",\
        +      "[%AIVA%:Super] €/l Super",\
        +      "[%AIVA%:E10] €/l E10",\
        +      "[%AIVA%:Autogas] €/l Autogas"\
        +    ]\
        +  }\
        +}
        +attr Tankstelle_Dialog evalSpecials AIVA=AIVA_petrolStation
        +
        +
      + Programmierung der Waschmaschine +
        +
        +defmod Waschmaschine_Dialog msgDialog { "Waschmaschine": {\
        +    "message": [\
        +      "{return('(Zeitprogramm stoppen) ') if(ReadingsVal('%controlUnit%', 'controlMode', '') eq 'auto')}",\
        +      "{return('(programmieren) ') if(ReadingsVal('%actor%', 'state', '') ne 'on')}",\
        +      "{return('(einschalten) ') if(ReadingsVal('%actor%', 'state', '') ne 'on')}",\
        +      "(Verlaufsdiagramm) ",\
        +      "(abbrechen) ",\
        +      "{return('Waschmaschine: ' . (ReadingsVal('%actor%', 'state', '') eq 'on' ? 'eingeschaltet' : 'ausgeschaltet'))}",\
        +      "{return('Modus: ' . (ReadingsVal('%controlUnit%', 'controlMode', '') eq 'auto' ? 'Automatik' : 'Manuell (' . ReadingsVal('%controlUnit%', 'time', '') . ')'))}"\
        +    ],\
        +    "Zeitprogramm stoppen": {\
        +      "commands": "set %controlUnit% controlMode manual",\
        +      "message": [\
        +        "TelegramBot_MTYPE=queryInline (%me%) ",\
        +        "Das Zeitprogramm wurde gestoppt."\
        +      ]\
        +    },\
        +    "programmieren": {\
        +      "message": [\
        +        "(bestätigen|zurück|abbrechen) ",\
        +        "( 00:00 | 00:15 | 00:30 | 00:45 ) ",\
        +        "( 01:00 | 01:15 | 01:30 | 01:45 ) ",\
        +        "( 02:00 | 02:15 | 02:30 | 02:45 ) ",\
        +        "( 03:00 | 03:15 | 03:30 | 03:45 ) ",\
        +        "( 04:00 | 04:15 | 04:30 | 04:45 ) ",\
        +        "( 05:00 | 05:15 | 05:30 | 05:45 ) ",\
        +        "( 06:00 | 06:15 | 06:30 | 06:45 ) ",\
        +        "( 07:00 | 07:15 | 07:30 | 07:45 ) ",\
        +        "( 08:00 | 08:15 | 08:30 | 08:45 ) ",\
        +        "( 09:00 | 09:15 | 09:30 | 09:45 ) ",\
        +        "( 10:00 | 10:15 | 10:30 | 10:45 ) ",\
        +        "( 11:00 | 11:15 | 11:30 | 11:45 ) ",\
        +        "( 12:00 | 12:15 | 12:30 | 12:45 ) ",\
        +        "( 13:00 | 13:15 | 13:30 | 13:45 ) ",\
        +        "( 14:00 | 14:15 | 14:30 | 14:45 ) ",\
        +        "( 15:00 | 15:15 | 15:30 | 15:45 ) ",\
        +        "( 16:00 | 16:15 | 16:30 | 16:45 ) ",\
        +        "( 17:00 | 17:15 | 17:30 | 17:45 ) ",\
        +        "( 18:00 | 18:15 | 18:30 | 18:45 ) ",\
        +        "( 19:00 | 19:15 | 19:30 | 19:45 ) ",\
        +        "( 20:00 | 20:15 | 20:30 | 20:45 ) ",\
        +        "( 21:00 | 21:15 | 21:30 | 21:45 ) ",\
        +        "( 22:00 | 22:15 | 22:30 | 22:45 ) ",\
        +        "( 23:00 | 23:15 | 23:30 | 23:45 ) ",\
        +        "Wann soll die Wäsche fertig sein?",\
        +        "Bitte Uhrzeit in HH:MM angeben.",\
        +        "Aktuell ist [%controlUnit%:time] Uhr eingestellt."\
        +      ],\
        +      "Uhrzeit": {\
        +        "match": " ?([0-1][0-9]|2[0-3]):[0-5][0-9] ?",\
        +        "commands": [\
        +          "set %controlUnit% time $message",\
        +          "set $SELF say @$recipient Waschmaschine|programmieren|bestätigen"\
        +        ]\
        +      },\
        +      "bestätigen": {\
        +        "commands": "set %controlUnit% controlMode auto",\
        +        "message": [\
        +          "TelegramBot_MTYPE=queryInline (%me%) ",\
        +          "Das Zeitprogramm wurde eingestellt.",\
        +          "Die Wäsche wird voraussichtlich um [%controlUnit%:time] Uhr fertig sein.",\
        +          "Bitte die Waschmaschine vorbereiten."\
        +        ]\
        +      }\
        +    },\
        +    "einschalten": {\
        +      "commands": [\
        +        "set %controlUnit% controlMode manual",\
        +        "set %actor% on"\
        +      ]\
        +    },\
        +    "Verlaufsdiagramm": {\
        +      "commands": "set %TelegramBot% cmdSend {plotAsPng('%plot%')}",\
        +      "message": "TelegramBot_MTYPE=queryInline (%me%) $message"\
        +    }\
        +  },\
        +  "auto": {\
        +    "setOnly": true,\
        +    "commands": [\
        +      "set %actor% on",\
        +      "set %controlUnit% controlMode manual"\
        +    ],\
        +    "message": [\
        +      "TelegramBot_MTYPE=queryInline (%me%) ",\
        +      "Die Wachmaschine wurde automatisch eingeschaltet."\
        +    ]\
        +  },\
        +  "manual": {\
        +    "setOnly": true,\
        +    "message": [\
        +      "TelegramBot_MTYPE=queryInline (%me%) ",\
        +      "Die Wachmaschine wurde manuell eingeschaltet."\
        +    ]\
        +  },\
        +  "done": {\
        +    "setOnly": true,\
        +    "commands": "set %actor% off",\
        +    "message": [\
        +      "TelegramBot_MTYPE=queryInline (%me%) ",\
        +      "Die Wachmaschine ist fertig."\
        +    ]\
        +  }\
        +}
        +attr Waschmaschine_Dialog evalSpecials actor=HM_2C10D8_Sw\
        +controlUnit=Waschkeller_washer_controlUnit\
        +plot=Waschkeller_washer_SVG
        +
        +
      +
    +
+ +=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