2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-01-31 06:39:11 +00:00

RHASSPY: extend msgDialog features, continous dialogues (experimental)

git-svn-id: https://svn.fhem.de/fhem/trunk@25354 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
Beta-User 2021-12-18 06:50:10 +00:00
parent 9e5a769e37
commit 16086ddc07
2 changed files with 201 additions and 171 deletions

View File

@ -3,12 +3,12 @@
# #
# FHEM RHASSPY module (https://github.com/rhasspy) # FHEM RHASSPY module (https://github.com/rhasspy)
# #
# Originally written 2018 by Tobias Wiedenmann (Thyraz) # Originally initiated 2018 by Tobias Wiedenmann (Thyraz)
# as FHEM Snips.ai module (thanks to Matthias Kleine) # as FHEM Snips.ai module (thanks to Matthias Kleine)
# #
# Adapted for RHASSPY 2020/2021 by Beta-User and drhirn # Adapted for RHASSPY 2020/2021 by Beta-User and drhirn
# #
# Thanks to Beta-User, rudolfkoenig, JensS, cb2sela and all the others # Thanks to rudolfkoenig, JensS, cb2sela and all the others
# who did a great job getting this to work! # who did a great job getting this to work!
# #
# This file is part of fhem. # This file is part of fhem.
@ -39,6 +39,7 @@ use HttpUtils;
use utf8; use utf8;
use List::Util 1.45 qw(max min uniq); use List::Util 1.45 qw(max min uniq);
use Scalar::Util qw(looks_like_number); use Scalar::Util qw(looks_like_number);
use Time::HiRes qw(gettimeofday);
use POSIX qw(strftime); use POSIX qw(strftime);
#use Data::Dumper; #use Data::Dumper;
use FHEM::Core::Timer::Register qw(:ALL); use FHEM::Core::Timer::Register qw(:ALL);
@ -137,6 +138,11 @@ my $languagevars = {
'waterLevel' => 'water level in $location is $value percent', 'waterLevel' => 'water level in $location is $value percent',
'knownType' => '$mappingType in $location is $value percent', 'knownType' => '$mappingType in $location is $value percent',
'unknownType' => 'value in $location is $value percent' 'unknownType' => 'value in $location is $value percent'
},
'getStateResponses' => {
'STATE' => '$deviceName value is [$device:STATE]',
'price' => 'current prize of $reading in $deviceName is [$device:$reading:d]',
'update' => 'initiated update for $deviceName'
} }
}, },
'stateResponses' => { 'stateResponses' => {
@ -239,7 +245,6 @@ BEGIN {
ReadingsVal ReadingsVal
ReadingsNum ReadingsNum
devspec2array devspec2array
gettimeofday
toJSON toJSON
setVolume setVolume
AnalyzeCommandChain AnalyzeCommandChain
@ -307,7 +312,7 @@ sub Define {
my @unknown; my @unknown;
for (keys %{$h}) { for (keys %{$h}) {
push @unknown, $_ if $_ !~ m{\A(?:baseUrl|defaultRoom|language|devspec|fhemId|prefix|siteId|encoding|useGenericAttrs|keepOpenDelay|handleHotword|experimental|Babble)\z}xm; push @unknown, $_ if $_ !~ m{\A(?:baseUrl|defaultRoom|language|devspec|fhemId|prefix|siteId|encoding|useGenericAttrs|sessionTimeout|handleHotword|experimental|Babble)\z}xm;
} }
my $err = join q{, }, @unknown; my $err = join q{, }, @unknown;
return "unknown key(s) in DEF: $err" if @unknown && $init_done; return "unknown key(s) in DEF: $err" if @unknown && $init_done;
@ -315,7 +320,7 @@ sub Define {
$hash->{defaultRoom} = $defaultRoom; $hash->{defaultRoom} = $defaultRoom;
my $language = $h->{language} // shift @{$anon} // lc AttrVal('global','language','en'); my $language = $h->{language} // shift @{$anon} // lc AttrVal('global','language','en');
$hash->{MODULE_VERSION} = '0.5.08a'; $hash->{MODULE_VERSION} = '0.5.10';
$hash->{baseUrl} = $Rhasspy; $hash->{baseUrl} = $Rhasspy;
initialize_Language($hash, $language) if !defined $hash->{LANGUAGE} || $hash->{LANGUAGE} ne $language; initialize_Language($hash, $language) if !defined $hash->{LANGUAGE} || $hash->{LANGUAGE} ne $language;
$hash->{LANGUAGE} = $language; $hash->{LANGUAGE} = $language;
@ -328,7 +333,7 @@ sub Define {
$hash->{encoding} = $h->{encoding} // q{utf8}; $hash->{encoding} = $h->{encoding} // q{utf8};
$hash->{useGenericAttrs} = $h->{useGenericAttrs} // 1; $hash->{useGenericAttrs} = $h->{useGenericAttrs} // 1;
for my $key (qw( experimental handleHotword keepOpenDelay Babble)) { for my $key (qw( experimental handleHotword sessionTimeout Babble)) {
delete $hash->{$key}; delete $hash->{$key};
$hash->{$key} = $h->{$key} if defined $h->{$key}; $hash->{$key} = $h->{$key} if defined $h->{$key};
} }
@ -780,6 +785,7 @@ sub configure_DialogManager {
my $toDisable = shift // [qw(ConfirmAction CancelAction ChoiceRoom ChoiceDevice)]; my $toDisable = shift // [qw(ConfirmAction CancelAction ChoiceRoom ChoiceDevice)];
my $enable = shift // q{false}; my $enable = shift // q{false};
my $timer = shift; my $timer = shift;
my $retArr = shift;
#option to delay execution to make reconfiguration last action after everything else has been done and published. #option to delay execution to make reconfiguration last action after everything else has been done and published.
if ( defined $timer ) { if ( defined $timer ) {
@ -832,6 +838,9 @@ hermes/dialogueManager/configure (JSON)
my $disable = {intentId => "$id", enable => "$enable"}; my $disable = {intentId => "$id", enable => "$enable"};
push @disabled, $disable; push @disabled, $disable;
} }
return \@disabled if $retArr;
my $sendData = { my $sendData = {
siteId => $siteId, siteId => $siteId,
intents => [@disabled] intents => [@disabled]
@ -962,13 +971,11 @@ sub _analyze_rhassypAttr {
my $mappingsString = AttrVal($device, "${prefix}Mapping", q{}); my $mappingsString = AttrVal($device, "${prefix}Mapping", q{});
for (split m{\n}x, $mappingsString) { for (split m{\n}x, $mappingsString) {
my ($key, $val) = split m{:}x, $_, 2; my ($key, $val) = split m{:}x, $_, 2;
#$key = lc($key); next if !$val;
#$val = lc($val); my $currentMapping = splitMappingString($val) // next;
my %currentMapping = splitMappingString($val);
next if !%currentMapping;
# Übersetzen, falls möglich: # Übersetzen, falls möglich:
$currentMapping{type} = $currentMapping{type} // $key; $currentMapping->{type} //= $key;
$hash->{helper}{devicemap}{devices}{$device}{intents}{$key}->{$currentMapping{type}} = \%currentMapping; $hash->{helper}{devicemap}{devices}{$device}{intents}{$key}->{$currentMapping->{type}} = $currentMapping;
} }
#Specials #Specials
@ -1213,6 +1220,13 @@ sub _analyze_genDevType {
$hash->{helper}{devicemap}{devices}{$device}{intents} = $currentMapping; $hash->{helper}{devicemap}{devices}{$device}{intents} = $currentMapping;
return; return;
} }
if ( $gdt eq 'info' ) {
my $r = $defs{$device}{READINGS};
$currentMapping->{GetState}->{$gdt} = {currentVal => 'STATE', type => 'STATE' };
$currentMapping = _analyze_genDevType_setter( $hash, $device, $allset, $currentMapping );
$hash->{helper}{devicemap}{devices}{$device}{intents} = $currentMapping;
}
return; return;
} }
@ -1255,13 +1269,15 @@ sub _analyze_genDevType_setter {
my $allValMappings = { my $allValMappings = {
MediaControls => { MediaControls => {
cmdPlay => 'play', cmdPause => 'pause' ,cmdStop => 'stop', cmdBack => 'previous', cmdFwd => 'next', chanUp => 'channelUp', chanDown => 'channelDown' } cmdPlay => 'play', cmdPause => 'pause' ,cmdStop => 'stop', cmdBack => 'previous', cmdFwd => 'next', chanUp => 'channelUp', chanDown => 'channelDown' },
GetState => {
update => 'reread|update|reload' },
}; };
for my $okey ( keys %{$allValMappings} ) { for my $okey ( keys %{$allValMappings} ) {
my $ikey = $allValMappings->{$okey}; my $ikey = $allValMappings->{$okey};
for ( keys %{$ikey} ) { for ( keys %{$ikey} ) {
my $val = $ikey->{$_}; my $val = $ikey->{$_};
$mapping->{$okey}->{$okey}->{$_} = $val if $setter =~ m{\b$val([\b:\s]|\Z)}xms; $mapping->{$okey}->{$okey}->{$_} = $1 if $setter =~ m{\b($val)(?:[\b:\s]|\Z)}xmsi;
} }
} }
my $allKeyMappings = { my $allKeyMappings = {
@ -1348,7 +1364,7 @@ sub initialize_msgDialog {
for my $line (split m{\n}x, $attrVal) { for my $line (split m{\n}x, $attrVal) {
next if !length $line; next if !length $line;
my ($keywd, $values) = split m{=}x, $line, 2; my ($keywd, $values) = split m{=}x, $line, 2;
if ( $keywd =~ m{\Aallowed|msgCommand|hello|goodbye|querymark|keepOpenDelay\z}xms ) { if ( $keywd =~ m{\Aallowed|msgCommand|hello|goodbye|querymark|sessionTimeout\z}xms ) {
$hash->{helper}->{msgDialog}->{config}->{$keywd} = trim($values); $hash->{helper}->{msgDialog}->{config}->{$keywd} = trim($values);
next; next;
} }
@ -1365,10 +1381,10 @@ sub initialize_msgDialog {
} }
$hash->{helper}->{msgDialog}->{config}->{open} //= q{hi.rhasspy}; $hash->{helper}->{msgDialog}->{config}->{open} //= q{hi.rhasspy};
$hash->{helper}->{msgDialog}->{config}->{close} //= q{close}; $hash->{helper}->{msgDialog}->{config}->{close} //= q{close};
$hash->{helper}->{msgDialog}->{config}->{hello} //= q{Hi! What can I do for you?}; $hash->{helper}->{msgDialog}->{config}->{hello} //= q{Hi $you! What can I do for you?|at your service|There you go again!};
$hash->{helper}->{msgDialog}->{config}->{goodbye} //= q{Till next time.}; $hash->{helper}->{msgDialog}->{config}->{goodbye} //= q{Till next time.|Bye|CU|Cheers!|so long};
$hash->{helper}->{msgDialog}->{config}->{querymark} //= q{this is a feminine request}; $hash->{helper}->{msgDialog}->{config}->{querymark} //= q{this is a feminine request};
$hash->{helper}->{msgDialog}->{config}->{keepOpenDelay} //= $hash->{keepOpenDelay}; $hash->{helper}->{msgDialog}->{config}->{sessionTimeout} //= $hash->{sessionTimeout} // _getDialogueTimeout($hash);
my $msgConfig = $modules{msgConfig}{defptr}{NAME}; my $msgConfig = $modules{msgConfig}{defptr}{NAME};
#addToDevAttrList($msgConfig, "$hash->{prefix}EvalSpecials:textField-long ",'RHASSPY'); #addToDevAttrList($msgConfig, "$hash->{prefix}EvalSpecials:textField-long ",'RHASSPY');
@ -1763,7 +1779,7 @@ sub getDevicesByIntentAndType {
return if !defined $hash->{helper}{devicemap}; return if !defined $hash->{helper}{devicemap};
for my $devs (keys %{$hash->{helper}{devicemap}{devices}}) { for my $devs (keys %{$hash->{helper}{devicemap}{devices}}) {
my $mapping = getMapping($hash, $devs, $intent, { type => $type, subType => $subType }, 1, 1) // next; my $mapping = getMapping($hash, $devs, $intent, { type => $type, subType => $subType }, 1) // next;
my $mappingType = $mapping->{type}; my $mappingType = $mapping->{type};
my $rooms = $hash->{helper}{devicemap}{devices}{$devs}->{rooms}; my $rooms = $hash->{helper}{devicemap}{devices}{$devs}->{rooms};
@ -1897,7 +1913,7 @@ sub getActiveDeviceForIntentAndType {
my $match; my $match;
for (@{$devices}) { for (@{$devices}) {
my $mapping = getMapping($subhash, $_, 'GetOnOff', undef, defined $hash->{helper}{devicemap}, 1); my $mapping = getMapping($subhash, $_, 'GetOnOff', undef, 1);
if (defined $mapping ) { if (defined $mapping ) {
# Gerät ein- oder ausgeschaltet? # Gerät ein- oder ausgeschaltet?
my $value = _getOnOffState($subhash, $_, $mapping); my $value = _getOnOffState($subhash, $_, $mapping);
@ -2064,7 +2080,7 @@ sub splitMappingString {
# Tokens in Keys/Values trennen # Tokens in Keys/Values trennen
%parsedMapping = map {split m{=}x, $_, 2} @tokens; #Beta-User: Odd number of elements in hash assignment %parsedMapping = map {split m{=}x, $_, 2} @tokens; #Beta-User: Odd number of elements in hash assignment
return %parsedMapping; return \%parsedMapping;
} }
@ -2074,7 +2090,6 @@ sub getMapping {
my $device = shift // return; my $device = shift // return;
my $intent = shift // return; my $intent = shift // return;
my $type = shift // $intent; #Beta-User: seems first three parameters are obligatory...? my $type = shift // $intent; #Beta-User: seems first three parameters are obligatory...?
my $fromHash = shift // 0;
my $disableLog = shift // 0; my $disableLog = shift // 0;
my $subType = $type; my $subType = $type;
@ -2085,34 +2100,14 @@ sub getMapping {
my $matchedMapping; my $matchedMapping;
if ( $fromHash ) { $matchedMapping = $hash->{helper}{devicemap}{devices}{$device}{intents}{$intent}{$subType} if defined $subType && defined $hash->{helper}{devicemap}{devices}{$device}{intents}{$intent}{$subType};
$matchedMapping = $hash->{helper}{devicemap}{devices}{$device}{intents}{$intent}{$subType} if defined $subType && defined $hash->{helper}{devicemap}{devices}{$device}{intents}{$intent}{$subType}; return $matchedMapping if $matchedMapping;
return $matchedMapping if $matchedMapping;
for (sort keys %{$hash->{helper}{devicemap}{devices}{$device}{intents}{$intent}}) {
for (sort keys %{$hash->{helper}{devicemap}{devices}{$device}{intents}{$intent}}) { #simply pick first item in alphabetical order...
#simply pick first item in alphabetical order... return $hash->{helper}{devicemap}{devices}{$device}{intents}{$intent}{$_};
return $hash->{helper}{devicemap}{devices}{$device}{intents}{$intent}{$_};
}
} }
my $prefix = $hash->{prefix};
my $mappingsString = AttrVal($device, "${prefix}Mapping", undef) // return;
for (split m{\n}x, $mappingsString) {
# Nur Mappings vom gesuchten Typ verwenden
next if $_ !~ qr/^$intent/x;
$_ =~ s/$intent://x;
my %currentMapping = splitMappingString($_);
# Erstes Mapping vom passenden Intent wählen (unabhängig vom Type), dann ggf. weitersuchen ob noch ein besserer Treffer mit passendem Type kommt
if ( !defined $matchedMapping
|| lc($matchedMapping->{type}) ne lc($type) && lc($currentMapping{type}) eq lc($type) ) {
$matchedMapping = \%currentMapping;
#Beta-User: könnte man ergänzen durch den match "vorne" bei Reading, kann aber sein, dass es effektiver geht, wenn wir das künftig sowieso anders machen...
Log3($hash->{NAME}, 5, "${prefix}Mapping selected: $_") if !$disableLog;
}
}
return $matchedMapping; return $matchedMapping;
} }
@ -2233,6 +2228,7 @@ sub _getValue {
my @replace = split m{:}x, $getString; my @replace = split m{:}x, $getString;
$device = $replace[0]; $device = $replace[0];
$getString = $replace[1] // $getString; $getString = $replace[1] // $getString;
return InternalVal($device,'STATE',0) if $getString eq 'STATE';
return ReadingsVal($device, $getString, 0); return ReadingsVal($device, $getString, 0);
} }
@ -2431,7 +2427,7 @@ sub setMsgDialogTimeout {
sub msgDialog_close { sub msgDialog_close {
my $hash = shift // return; my $hash = shift // return;
my $device = shift // return; my $device = shift // return;
my $response = shift // $hash->{helper}->{msgDialog}->{config}->{goodbye}; my $response = shift // _shuffle_answer($hash->{helper}->{msgDialog}->{config}->{goodbye});
Log3($hash, 5, "msgDialog_close called with $device"); Log3($hash, 5, "msgDialog_close called with $device");
deleteSingleRegIntTimer($device, $hash); deleteSingleRegIntTimer($device, $hash);
@ -2461,9 +2457,12 @@ sub msgDialog_open {
customData => $device customData => $device
}; };
setMsgDialogTimeout($hash, $sendData, $hash->{helper}->{msgDialog}->{config}->{keepOpenDelay}); setMsgDialogTimeout($hash, $sendData, $hash->{helper}->{msgDialog}->{config}->{sessionTimeout});
return msgDialog_progress($hash, $device, $msgtext, $sendData) if $msgtext; return msgDialog_progress($hash, $device, $msgtext, $sendData) if $msgtext;
return msgDialog_respond($hash, $device, $hash->{helper}->{msgDialog}->{config}->{hello}, 0); my $you = AttrVal($device,'alias',$device);
my $response = _shuffle_answer($hash->{helper}->{msgDialog}->{config}->{hello});
$response =~ s{(\$\w+)}{$1}eegx;
return msgDialog_respond($hash, $device, $response, 0);
} }
#handle messages from FHEM/messenger side #handle messages from FHEM/messenger side
@ -2509,7 +2508,7 @@ sub msgDialog_respond {
$msgCommand =~ s{\\[\@]}{@}x; $msgCommand =~ s{\\[\@]}{@}x;
$msgCommand =~ s{(\$\w+)}{$1}eegx; $msgCommand =~ s{(\$\w+)}{$1}eegx;
AnalyzeCommand($hash, $msgCommand); AnalyzeCommand($hash, $msgCommand);
resetRegIntTimer( $recipients, time + $hash->{helper}->{msgDialog}->{config}->{keepOpenDelay}, \&RHASSPY_msgDialogTimeout, $hash, 0) if $keepopen; resetRegIntTimer( $recipients, time + $hash->{helper}->{msgDialog}->{config}->{sessionTimeout}, \&RHASSPY_msgDialogTimeout, $hash, 0) if $keepopen;
return $recipients; return $recipients;
} }
@ -2708,7 +2707,7 @@ sub analyzeMQTTmessage {
if ($mute) { if ($mute) {
$data->{requestType} = $message =~ m{${fhemId}.textCommand}x ? 'text' : 'voice'; $data->{requestType} = $message =~ m{${fhemId}.textCommand}x ? 'text' : 'voice';
respond( $hash, $data, q{ } ); respond( $hash, $data, q{ }, 'endSession', 0 );
#Beta-User: Da fehlt mir der Soll-Ablauf für das "room-listening"-Reading; das wird ja über einen anderen Topic abgewickelt #Beta-User: Da fehlt mir der Soll-Ablauf für das "room-listening"-Reading; das wird ja über einen anderen Topic abgewickelt
return \@updatedList; return \@updatedList;
} }
@ -2753,7 +2752,7 @@ sub respond {
my $data = shift // return; my $data = shift // return;
my $response = shift // return; my $response = shift // return;
my $topic = shift // q{endSession}; my $topic = shift // q{endSession};
my $delay = shift // $hash->{keepOpenDelay}; my $delay = shift // ReadingsNum($hash->{NAME}, "sessionTimeout_$data->{siteId}", $hash->{sessionTimeout});
my $type = $data->{requestType} // return; my $type = $data->{requestType} // return;
@ -2769,10 +2768,14 @@ sub respond {
for my $key (keys %{$response}) { for my $key (keys %{$response}) {
$sendData->{$key} = $response->{$key}; $sendData->{$key} = $response->{$key};
} }
} elsif ( $topic eq 'continueSession' || $delay ) { } elsif ( $topic eq 'continueSession' ) {
$sendData->{text} = $response; $sendData->{text} = $response;
configure_DialogManager($hash,$data->{siteId}) if $topic ne 'continueSession';
$sendData->{intentFilter} = 'null'; $sendData->{intentFilter} = 'null';
} elsif ( $delay ) {
$sendData->{text} = $response;
$topic = 'continueSession';
my @ca_strings = configure_DialogManager($hash,$data->{siteId}, [qw(ConfirmAction ChoiceRoom ChoiceDevice)], 'false', undef, 1 );
$sendData->{intentFilter} = [@ca_strings];
} else { } else {
$sendData->{text} = $response; $sendData->{text} = $response;
$sendData->{intentFilter} = 'null'; $sendData->{intentFilter} = 'null';
@ -2798,6 +2801,7 @@ sub respond {
} }
IOWrite($hash, 'publish', qq{hermes/dialogueManager/$topic $json}); IOWrite($hash, 'publish', qq{hermes/dialogueManager/$topic $json});
Log3($hash, 5, "published " . qq{hermes/dialogueManager/$topic $json});
#setDialogTimeout( $hash, $data, $delay, getResponse( $hash, 'SilentCancelConfirmation' ) ) if $delay; #setDialogTimeout( $hash, $data, $delay, getResponse( $hash, 'SilentCancelConfirmation' ) ) if $delay;
#no audio output in msgDialog session #no audio output in msgDialog session
@ -2822,8 +2826,7 @@ sub getResponse {
? $hash->{helper}{lng}->{responses}->{$identifier}->{$subtype} ? $hash->{helper}{lng}->{responses}->{$identifier}->{$subtype}
: getKeyValFromAttr($hash, $hash->{NAME}, 'response', $identifier) // $hash->{helper}{lng}->{responses}->{$identifier}; : getKeyValFromAttr($hash, $hash->{NAME}, 'response', $identifier) // $hash->{helper}{lng}->{responses}->{$identifier};
return $responses if ref $responses eq 'HASH'; return $responses if ref $responses eq 'HASH';
my @arr = split m{\|}, $responses; return _shuffle_answer($responses);
return $arr[ rand @arr ];
} }
@ -3292,7 +3295,7 @@ sub handleIntentShortcuts {
$response = $hash->{helper}{shortcuts}{$data->{input}}{conf_req}; $response = $hash->{helper}{shortcuts}{$data->{input}}{conf_req};
return setDialogTimeout($hash, $data, $timeout, $response); return setDialogTimeout($hash, $data, $timeout, $response);
} }
$response = $shortcut->{response} // getResponse($hash, 'DefaultConfirmation'); $response = _shuffle_answer($shortcut->{response}) // getResponse($hash, 'DefaultConfirmation');
my $ret; my $ret;
my $device = $shortcut->{NAME}; my $device = $shortcut->{NAME};
my $cmd = $shortcut->{perl}; my $cmd = $shortcut->{perl};
@ -3344,7 +3347,7 @@ sub handleIntentSetOnOff {
$room = getRoomName($hash, $data); $room = getRoomName($hash, $data);
$value = $data->{Value}; $value = $data->{Value};
$device = getDeviceByName($hash, $room, $data->{Device}); $device = getDeviceByName($hash, $room, $data->{Device});
$mapping = getMapping($hash, $device, 'SetOnOff', undef, defined $hash->{helper}{devicemap}); $mapping = getMapping($hash, $device, 'SetOnOff');
# Mapping found? # Mapping found?
if ( defined $device && defined $mapping ) { if ( defined $device && defined $mapping ) {
@ -3361,14 +3364,14 @@ sub handleIntentSetOnOff {
# Define response # Define response
if ( defined $mapping->{response} ) { if ( defined $mapping->{response} ) {
$numericValue = $value eq 'on' ? 1 : 0; $numericValue = $value eq 'on' ? 1 : 0;
$response = _getValue($hash, $device, $mapping->{response}, $numericValue, $room); $response = _getValue($hash, $device, _shuffle_answer($mapping->{response}), $numericValue, $room);
Log3($hash->{NAME}, 5, "Response is $response" ); Log3($hash->{NAME}, 5, "Response is $response" );
} }
else { $response = getResponse($hash, 'DefaultConfirmation'); } else { $response = getResponse($hash, 'DefaultConfirmation'); }
} }
} }
# Send response # Send response
$response = $response // getResponse($hash, 'DefaultError'); $response //= getResponse($hash, 'DefaultError');
respond( $hash, $data, $response ); respond( $hash, $data, $response );
return $device; return $device;
} }
@ -3406,7 +3409,7 @@ sub handleIntentSetOnOffGroup {
my $needs_sorting = (@{$hash->{".asyncQueue"}}); my $needs_sorting = (@{$hash->{".asyncQueue"}});
for my $device (@devlist) { for my $device (@devlist) {
my $mapping = getMapping($hash, $device, 'SetOnOff', undef, defined $hash->{helper}{devicemap}); my $mapping = getMapping($hash, $device, 'SetOnOff');
# Mapping found? # Mapping found?
next if !defined $mapping; next if !defined $mapping;
@ -3448,57 +3451,57 @@ sub handleIntentSetTimedOnOff {
if !defined $data->{Hourabs} && !defined $data->{Hour} && !defined $data->{Min} && !defined $data->{Sec}; if !defined $data->{Hourabs} && !defined $data->{Hour} && !defined $data->{Min} && !defined $data->{Sec};
# Device AND Value must exist # Device AND Value must exist
if ( exists $data->{Device} && exists $data->{Value} ) { return if !exists $data->{Device} || !exists $data->{Value};
$room = getRoomName($hash, $data);
$value = $data->{Value};
$device = getDeviceByName($hash, $room, $data->{Device});
$mapping = getMapping($hash, $device, 'SetOnOff', undef, defined $hash->{helper}{devicemap});
# Mapping found? $room = getRoomName($hash, $data);
if ( defined $device && defined $mapping ) { $value = $data->{Value};
return $hash->{NAME} if !$data->{Confirmation} && getNeedsConfirmation( $hash, $data, 'SetTimedOnOff', $device ); $device = getDeviceByName($hash, $room, $data->{Device});
my $cmdOn = $mapping->{cmdOn} // 'on'; $mapping = getMapping($hash, $device, 'SetOnOff');
my $cmdOff = $mapping->{cmdOff} // 'off';
my $cmd = $value eq 'on' ? $cmdOn : $cmdOff;
$cmd .= "-for-timer";
my $allset = getAllSets($device); # Mapping found?
return respond( $hash, $data, getResponse($hash, 'NoTimedOnDeviceFound') ) if $allset !~ m{\b$cmd(?:[\b:\s]|\Z)}xms; if ( defined $device && defined $mapping ) {
return $hash->{NAME} if !$data->{Confirmation} && getNeedsConfirmation( $hash, $data, 'SetTimedOnOff', $device );
my $cmdOn = $mapping->{cmdOn} // 'on';
my $cmdOff = $mapping->{cmdOff} // 'off';
my $cmd = $value eq 'on' ? $cmdOn : $cmdOff;
$cmd .= "-for-timer";
my $hour = 0; my $allset = getAllSets($device);
my $now1 = time; return respond( $hash, $data, getResponse($hash, 'NoTimedOnDeviceFound') ) if $allset !~ m{\b$cmd(?:[\b:\s]|\Z)}xms;
my $now = $now1;
my @time = localtime($now);
if ( defined $data->{Hourabs} ) {
$hour = $data->{Hourabs};
$now1 = $now1 - ($time[2] * HOURSECONDS) - ($time[1] * MINUTESECONDS) - $time[0]; #last midnight
}
elsif ($data->{Hour}) {
$hour = $data->{Hour};
}
$now1 += HOURSECONDS * $hour;
$now1 += MINUTESECONDS * $data->{Min} if $data->{Min};
$now1 += $data->{Sec} if $data->{Sec};
$now1 += +DAYSECONDS if $now1 < $now; my $hour = 0;
$now1 = $now1 - $now; my $now1 = time;
my $now = $now1;
$cmd .= " $now1"; my @time = localtime($now);
# execute Cmd if ( defined $data->{Hourabs} ) {
analyzeAndRunCmd($hash, $device, $cmd); $hour = $data->{Hourabs};
Log3($hash->{NAME}, 5, "Running command [$cmd] on device [$device]" ); $now1 = $now1 - ($time[2] * HOURSECONDS) - ($time[1] * MINUTESECONDS) - $time[0]; #last midnight
# Define response
if ( defined $mapping->{response} ) {
$numericValue = $value eq 'on' ? 1 : 0;
$response = _getValue($hash, $device, $mapping->{response}, $numericValue, $room);
Log3($hash->{NAME}, 5, "Response is $response" );
}
else { $response = getResponse($hash, 'DefaultConfirmation'); }
} }
elsif ($data->{Hour}) {
$hour = $data->{Hour};
}
$now1 += HOURSECONDS * $hour;
$now1 += MINUTESECONDS * $data->{Min} if $data->{Min};
$now1 += $data->{Sec} if $data->{Sec};
$now1 += +DAYSECONDS if $now1 < $now;
$now1 = $now1 - $now;
$cmd .= " $now1";
# execute Cmd
analyzeAndRunCmd($hash, $device, $cmd);
Log3($hash->{NAME}, 5, "Running command [$cmd] on device [$device]" );
# Define response
if ( defined $mapping->{response} ) {
$numericValue = $value eq 'on' ? 1 : 0;
$response = _getValue($hash, $device, _shuffle_answer($mapping->{response}), $numericValue, $room);
Log3($hash->{NAME}, 5, "Response is $response" );
}
else { $response = getResponse($hash, 'DefaultConfirmation'); }
} }
# Send response # Send response
$response = $response // getResponse($hash, 'DefaultError'); $response //= getResponse($hash, 'DefaultError');
respond( $hash, $data, $response ); respond( $hash, $data, $response );
return $device; return $device;
} }
@ -3557,7 +3560,7 @@ sub handleIntentSetTimedOnOffGroup {
my $needs_sorting = (@{$hash->{".asyncQueue"}}); my $needs_sorting = (@{$hash->{".asyncQueue"}});
for my $device (@devlist) { for my $device (@devlist) {
my $mapping = getMapping($hash, $device, 'SetOnOff', undef, defined $hash->{helper}{devicemap}); my $mapping = getMapping($hash, $device, 'SetOnOff');
# Mapping found? # Mapping found?
next if !defined $mapping; next if !defined $mapping;
@ -3610,7 +3613,7 @@ sub handleIntentGetOnOff {
$device = getDeviceByName($hash, $room, $data->{Device}); $device = getDeviceByName($hash, $room, $data->{Device});
my $deviceName = $data->{Device}; my $deviceName = $data->{Device};
my $mapping; my $mapping;
$mapping = getMapping($hash, $device, 'GetOnOff', undef, defined $hash->{helper}{devicemap}, 0) if defined $device; $mapping = getMapping($hash, $device, 'GetOnOff') if defined $device;
my $status = $data->{State}; my $status = $data->{State};
# Mapping found? # Mapping found?
@ -3620,17 +3623,18 @@ sub handleIntentGetOnOff {
# Define reponse # Define reponse
if ( defined $mapping->{response} ) { if ( defined $mapping->{response} ) {
$response = _getValue($hash, $device, $mapping->{response}, $value, $room); $response = _getValue($hash, $device, _shuffle_answer($mapping->{response}), $value, $room);
$response //= _shuffle_answer($response);
} }
else { else {
my $stateResponseType = $internal_mappings->{stateResponseType}->{$status}; my $stateResponseType = $internal_mappings->{stateResponseType}->{$status};
$response = $hash->{helper}{lng}->{stateResponses}{$stateResponseType}->{$value}; $response = _shuffle_answer($hash->{helper}{lng}->{stateResponses}{$stateResponseType}->{$value});
$response =~ s{(\$\w+)}{$1}eegx; $response =~ s{(\$\w+)}{$1}eegx;
} }
} }
} }
# Send response # Send response
$response = getResponse($hash, 'DefaultError') if !defined $response; $response //= getResponse($hash, 'DefaultError');
respond( $hash, $data, $response ); respond( $hash, $data, $response );
return $device; return $device;
} }
@ -3737,7 +3741,7 @@ sub handleIntentSetNumeric {
# Gerät über Name suchen, oder falls über Lautstärke ohne Device getriggert wurde das ActiveMediaDevice suchen # Gerät über Name suchen, oder falls über Lautstärke ohne Device getriggert wurde das ActiveMediaDevice suchen
if ( !defined $device && exists $data->{Device} ) { if ( !defined $device && exists $data->{Device} ) {
$device = getDeviceByName($hash, $room, $data->{Device}); $device = getDeviceByName($hash, $room, $data->{Device});
} elsif ( defined $type && ( $type eq 'volume' || $type eq 'Lautstärke' ) ) { } elsif ( defined $type && $type eq 'volume' ) {
$device = $device =
getActiveDeviceForIntentAndType($hash, $room, 'SetNumeric', $type) getActiveDeviceForIntentAndType($hash, $room, 'SetNumeric', $type)
// return respond( $hash, $data, getResponse( $hash, 'NoActiveMediaDevice') ); // return respond( $hash, $data, getResponse( $hash, 'NoActiveMediaDevice') );
@ -3745,7 +3749,7 @@ sub handleIntentSetNumeric {
return respond( $hash, $data, getResponse( $hash, 'NoDeviceFound' ) ) if !defined $device; return respond( $hash, $data, getResponse( $hash, 'NoDeviceFound' ) ) if !defined $device;
my $mapping = getMapping($hash, $device, 'SetNumeric', $type, defined $hash->{helper}{devicemap}, 0); my $mapping = getMapping($hash, $device, 'SetNumeric', $type);
if ( !defined $mapping ) { if ( !defined $mapping ) {
if ( defined $data->{'.inBulk'} ) { if ( defined $data->{'.inBulk'} ) {
@ -3855,14 +3859,16 @@ sub handleIntentSetNumeric {
} }
} }
return $device if defined $data->{'.inBulk'};
# get response # get response
defined $mapping->{response} defined $mapping->{response}
? $response = _getValue($hash, $device, $mapping->{response}, $newVal, $room) ? $response = _getValue($hash, $device, _shuffle_answer($mapping->{response}), $newVal, $room)
: $response = getResponse($hash, 'DefaultConfirmation'); : $response = getResponse($hash, 'DefaultConfirmation');
# send response # send response
$response = getResponse($hash, 'DefaultError') if !defined $response; $response //= getResponse($hash, 'DefaultError');
respond( $hash, $data, $response ) if !defined $data->{'.inBulk'}; respond( $hash, $data, $response );
return $device; return $device;
} }
@ -3902,7 +3908,7 @@ sub handleIntentGetNumeric {
return setDialogTimeout($hash, $data, _getDialogueTimeout($hash), $response, $toActivate); return setDialogTimeout($hash, $data, _getDialogueTimeout($hash), $response, $toActivate);
} }
my $mapping = getMapping($hash, $device, 'GetNumeric', { type => $type, subType => $subType }, defined $hash->{helper}{devicemap}, 0) my $mapping = getMapping($hash, $device, 'GetNumeric', { type => $type, subType => $subType })
// return respond( $hash, $data, getResponse( $hash, 'NoMappingFound' ) ); // return respond( $hash, $data, getResponse( $hash, 'NoMappingFound' ) );
# Mapping found # Mapping found
@ -3957,6 +3963,7 @@ sub handleIntentGetNumeric {
} }
# Variablen ersetzen? # Variablen ersetzen?
$response =_shuffle_answer($response);
$response =~ s{(\$\w+)}{$1}eegx; $response =~ s{(\$\w+)}{$1}eegx;
# Antwort senden # Antwort senden
return respond( $hash, $data, $response ); return respond( $hash, $data, $response );
@ -3967,24 +3974,38 @@ sub handleIntentGetNumeric {
sub handleIntentGetState { sub handleIntentGetState {
my $hash = shift // return; my $hash = shift // return;
my $data = shift // return; my $data = shift // return;
my $device = $data->{Device} // return; my $device = $data->{Device} // return respond( $hash, $data, getResponse($hash, 'NoValidData'));
my $response;
my $response;
Log3($hash->{NAME}, 5, 'handleIntentGetState called'); Log3($hash->{NAME}, 5, 'handleIntentGetState called');
# Mindestens Device muss existieren my $room = getRoomName($hash, $data);
if (exists $data->{Device}) { my $deviceName = $device;
my $room = getRoomName($hash, $data); $device = getDeviceByName($hash, $room, $device);
$device = getDeviceByName($hash, $room, $device); my $mapping = getMapping($hash, $device, 'GetState') // return respond( $hash, $data, getResponse($hash, 'NoMappingFound') );
my $mapping = getMapping($hash, $device, 'GetState', undef, defined $hash->{helper}{devicemap}, 0);
if ( defined $mapping->{response} ) { if ( defined $data->{Update} ) {
$response = _getValue($hash, $device, $mapping->{response}, undef, $room); my $cmd = $mapping->{update} // return respond( $hash, $data, getResponse($hash, 'DefaultError') );
$response = _ReplaceReadingsVal($hash, $mapping->{response}) if !$response; #Beta-User: case: plain Text with [device:reading] # execute Cmd
} analyzeAndRunCmd($hash, $device, $cmd);
$response = getResponse( $hash, 'getStateResponses', 'update');
$response =~ s{(\$\w+)}{$1}eegx;
} elsif ( defined $mapping->{response} ) {
$response = _getValue($hash, $device, _shuffle_answer($mapping->{response}), undef, $room);
$response = _ReplaceReadingsVal($hash, _shuffle_answer($mapping->{response})) if !$response; #Beta-User: case: plain Text with [device:reading]
} elsif ( defined $data->{Type} ) {
my $reading = $data->{Reading} // 'STATE';
$response = getResponse( $hash, 'getStateResponses', $data->{Type} );
$response =~ s{(\$\w+)}{$1}eegx;
$response = _ReplaceReadingsVal($hash, $response );
} else {
$response = getResponse( $hash, 'getStateResponses', 'STATE' );
$response =~ s{(\$\w+)}{$1}eegx;
$response = _ReplaceReadingsVal($hash, $response );
} }
# Antwort senden # Antwort senden
$response = getResponse($hash, 'DefaultError') if !defined $response; $response //= getResponse($hash, 'DefaultError');
return respond( $hash, $data, $response ); return respond( $hash, $data, $response );
} }
@ -4013,7 +4034,7 @@ sub handleIntentMediaControls {
$response = getResponse($hash, 'NoActiveMediaDevice') if !defined $device; $response = getResponse($hash, 'NoActiveMediaDevice') if !defined $device;
} }
$mapping = getMapping($hash, $device, 'MediaControls', undef, defined $hash->{helper}{devicemap}, 0); $mapping = getMapping($hash, $device, 'MediaControls');
if ( defined $device && defined $mapping && defined $mapping->{$command} ) { if ( defined $device && defined $mapping && defined $mapping->{$command} ) {
#check if confirmation is required #check if confirmation is required
@ -4023,10 +4044,10 @@ sub handleIntentMediaControls {
analyzeAndRunCmd($hash, $device, $cmd); analyzeAndRunCmd($hash, $device, $cmd);
# Define voice response # Define voice response
$response = defined $mapping->{response} ? $response = defined $mapping->{response} ?
_getValue($hash, $device, $mapping->{response}, $command, $room) _getValue($hash, $device, _shuffle_answer($mapping->{response}), $command, $room)
: getResponse($hash, 'DefaultConfirmation'); : getResponse($hash, 'DefaultConfirmation');
} }
$response = getResponse($hash, 'DefaultError') if !defined $response; $response //= getResponse($hash, 'DefaultError');
# Send voice response # Send voice response
respond( $hash, $data, $response ); respond( $hash, $data, $response );
return $device; return $device;
@ -4048,7 +4069,7 @@ sub handleIntentSetScene{
$room = getRoomName($hash, $data); $room = getRoomName($hash, $data);
$scene = $data->{Scene}; $scene = $data->{Scene};
$device = getDeviceByName($hash, $room, $data->{Device}); $device = getDeviceByName($hash, $room, $data->{Device});
$mapping = getMapping($hash, $device, 'SetScene', undef, defined $hash->{helper}{devicemap}); $mapping = getMapping($hash, $device, 'SetScene');
# restore HUE scenes # restore HUE scenes
$scene = qq([$scene]) if $scene =~ m{id=.+}xms; $scene = qq([$scene]) if $scene =~ m{id=.+}xms;
@ -4065,7 +4086,7 @@ sub handleIntentSetScene{
Log3($hash->{NAME}, 5, "Running command [$cmd] on device [$device]" ); Log3($hash->{NAME}, 5, "Running command [$cmd] on device [$device]" );
# Define response # Define response
$response = $mapping->{response} // getResponse( $hash, 'DefaultConfirmation' ); $response = _shuffle_answer($mapping->{response}) // getResponse( $hash, 'DefaultConfirmation' );
respond( $hash, $data, $response ); respond( $hash, $data, $response );
return $device; return $device;
@ -4114,44 +4135,30 @@ sub handleIntentGetDate {
sub handleIntentMediaChannels { sub handleIntentMediaChannels {
my $hash = shift // return; my $hash = shift // return;
my $data = shift // return; my $data = shift // return;
my $channel; my $device; my $room;
my $cmd;
my $response; # = getResponse($hash, 'DefaultError');
Log3($hash->{NAME}, 5, "handleIntentMediaChannels called"); Log3($hash->{NAME}, 5, "handleIntentMediaChannels called");
# Mindestens Channel muss übergeben worden sein # Mindestens Channel muss übergeben worden sein
if ( exists $data->{Channel} ) { return respond( $hash, $data, getResponse($hash, 'NoMediaChannelFound') ) if !exists $data->{Channel};
$room = getRoomName($hash, $data);
$channel = $data->{Channel};
# Passendes Gerät suchen my $room = getRoomName($hash, $data);
if ( exists $data->{Device} ) { my $channel = $data->{Channel};
$device = getDeviceByName($hash, $room, $data->{Device});
} else {
$device = getDeviceByMediaChannel($hash, $room, $channel);
}
if (defined $hash->{helper}{devicemap}) {
$cmd = $hash->{helper}{devicemap}{devices}{$device}{Channels}{$channel};
}
else {
$cmd = getKeyValFromAttr($hash, $device, 'rhasspyChannels', $channel, undef);
}
#$cmd = (split m{=}x, $cmd, 2)[1];
if ( defined $device && defined $cmd ) { # Passendes Gerät suchen
#check if confirmation is required my $device = exists $data->{Device}
return $hash->{NAME} if !$data->{Confirmation} && getNeedsConfirmation( $hash, $data, 'MediaChannels' ); ? getDeviceByName($hash, $room, $data->{Device})
$response = getResponse($hash, 'DefaultConfirmation'); : getDeviceByMediaChannel($hash, $room, $channel);
# Cmd ausführen return respond( $hash, $data, getResponse($hash, 'NoMediaChannelFound') ) if !defined $device;
analyzeAndRunCmd($hash, $device, $cmd);
} my $cmd = $hash->{helper}{devicemap}{devices}{$device}{Channels}{$channel} // return respond( $hash, $data, getResponse($hash, 'NoMediaChannelFound') );
}
#check if confirmation is required
return $hash->{NAME} if !$data->{Confirmation} && getNeedsConfirmation( $hash, $data, 'MediaChannels' );
# Cmd ausführen
analyzeAndRunCmd($hash, $device, $cmd);
# Antwort senden # Antwort senden
$response = getResponse($hash, 'NoMediaChannelFound') if !defined $response; respond( $hash, $data, getResponse($hash, 'DefaultConfirmation') );
respond( $hash, $data, $response );
return $device; return $device;
} }
@ -4520,7 +4527,7 @@ sub handleIntentSetTimer {
$response =~ s{(\$\w+)}{$1}eegx; $response =~ s{(\$\w+)}{$1}eegx;
} }
$response = getResponse($hash, 'DefaultError') if !defined $response; $response //= getResponse($hash, 'DefaultError');
respond( $hash, $data, $response ); respond( $hash, $data, $response );
return $name; return $name;
@ -4563,7 +4570,7 @@ sub handleIntentCancelAction {
deleteSingleRegIntTimer($identiy, $hash); deleteSingleRegIntTimer($identiy, $hash);
delete $hash->{helper}{'.delayed'}->{$identiy}; delete $hash->{helper}{'.delayed'}->{$identiy};
respond( $hash, $data, getResponse( $hash, 'DefaultCancelConfirmation' ) ); respond( $hash, $data, getResponse( $hash, 'DefaultCancelConfirmation' ), undef, 0 );
return $hash->{NAME}; return $hash->{NAME};
} }
@ -4883,6 +4890,12 @@ sub _toregex {
return $toclean; return $toclean;
} }
sub _shuffle_answer {
my $txts = shift // return;
my @arr = split m{\|}, $txts;
return $arr[ rand @arr ];
}
1; 1;
__END__ __END__
@ -5300,7 +5313,7 @@ i="i am hungry" f="set Stove on" d="Stove" c="would you like roast pork"</code><
<ul> <ul>
<li><i>allowed</i> The <a href="#ROOMMATE">ROOMMATE</a> or <a href="#GUEST">GUEST</a> devices allowed to interact with RHASSPY (comma-separated device names). This ist the only <b>mandatory</b> key to be set.</li> <li><i>allowed</i> The <a href="#ROOMMATE">ROOMMATE</a> or <a href="#GUEST">GUEST</a> devices allowed to interact with RHASSPY (comma-separated device names). This ist the only <b>mandatory</b> key to be set.</li>
<li><i>open</i> A keyword or expression used to initiate a dialogue (will be converted to a regex compatible notation)</li> <li><i>open</i> A keyword or expression used to initiate a dialogue (will be converted to a regex compatible notation)</li>
<li><i>keepOpenDelay</i> timout limit in seconds (<b>recommended</b>). All sessions will be closed automatically when timeout has passed. Timer will be reset with each incoming message .</li> <li><i>sessionTimeout</i> timout limit in seconds (<b>recommended</b>). All sessions will be closed automatically when timeout has passed. Timer will be reset with each incoming message .</li>
<li><i>close</i> keyword used to exit a dialogue (similar to open) before timeout has reached</li> <li><i>close</i> keyword used to exit a dialogue (similar to open) before timeout has reached</li>
<li><i>hello</i> and <i>goodbye</i> are texts to be sent when opening or exiting a dialogue</li> <li><i>hello</i> and <i>goodbye</i> are texts to be sent when opening or exiting a dialogue</li>
<li><i>msgCommand</i> the fhem-command to be used to send messages to the messenger service.</li> <li><i>msgCommand</i> the fhem-command to be used to send messages to the messenger service.</li>
@ -5512,8 +5525,9 @@ yellow=rgb FFFF00</code></p>
You may overwrite that behaviour by setting values to siteId2room readings: <code>setreading siteId2room_mobile_phone1 kitchen</code> will force RHASSPY to link your satellite <i>phone1 kitchen</i> to kitchen as room. You may overwrite that behaviour by setting values to siteId2room readings: <code>setreading siteId2room_mobile_phone1 kitchen</code> will force RHASSPY to link your satellite <i>phone1 kitchen</i> to kitchen as room.
<li>siteId2doubleSpeak_&lt;siteId&gt;</li> <li>siteId2doubleSpeak_&lt;siteId&gt;</li>
RHASSPY will always respond via the satellite where the dialogue was initiated from. In some cases, you may want additional output to other satellites - e.g. if they don't have (always on) sound output options. Setting this type of reading will lead to (additional!) responses to the given second satellite; naming scheme is the same as for site2room. RHASSPY will always respond via the satellite where the dialogue was initiated from. In some cases, you may want additional output to other satellites - e.g. if they don't have (always on) sound output options. Setting this type of reading will lead to (additional!) responses to the given second satellite; naming scheme is the same as for site2room.
<li>sessionTimeout_&lt;siteId&gt;</li>
RHASSPY will by default automatically close every dialogue after an executable commandset is detected. By setting this type of reading, you may keep open the dialoge to wait for the next command to be spoken on a "by siteId" base; naming scheme is similar as for site2room. Intent <i>CancelAction</i> will close any session immedately.
</ul> </ul>
=end html =end html
=cut =cut

View File

@ -21,7 +21,8 @@
}, },
"words": { "words": {
"on": "an", "on": "an",
"off": "aus" "off": "aus",
"and": "und"
}, },
"units": { "units": {
"unitHours" : { "unitHours" : {
@ -93,6 +94,11 @@
"setTarget": "$device ist auf $value gesetzt", "setTarget": "$device ist auf $value gesetzt",
"knownType": "$mappingType von $location beträgt $value Prozent", "knownType": "$mappingType von $location beträgt $value Prozent",
"unknownType": "Der Wert von $location beträgt $value Prozent" "unknownType": "Der Wert von $location beträgt $value Prozent"
},
"getStateResponses": {
"STATE": "$deviceName hat den Status [$device:STATE]",
"price": "Der aktuelle Preis für $reading in $deviceName beträgt [$device:$reading:d] Euro",
"update": "update für $deviceName ist angestoßen"
} }
}, },
"stateResponses": { "stateResponses": {
@ -124,8 +130,18 @@
} }
}, },
"responses": { "responses": {
"DefaultConfirmation": "Gerne!", "DefaultConfirmation": "Gerne!|wird erledigt|ok|jawohl|zu diensten",
"DefaultError": "Da paßt irgend was nicht" "DefaultError": "Da paßt irgend was nicht"
},
"stateResponses": {
"onOff": {
"0": "$deviceName ist ausgeschaltet|$deviceName ist aus",
"1": "$deviceName ist eingeschaltet|$deviceName ist an"
},
"openClose": {
"0": "$deviceName ist geöffnet|$deviceName ist offen",
"1": "$deviceName ist geschlossen|$deviceName ist zu"
}
} }
}, },
#Der Bereich "slots" enthält Daten, die an Rhasspy direkt übermittelt werden, um FHEM-spezifische slots zu erstellen. Er kann entsprechend der nachfolgenden Muster im Prinzip beliebig erweitert werden. #Der Bereich "slots" enthält Daten, die an Rhasspy direkt übermittelt werden, um FHEM-spezifische slots zu erstellen. Er kann entsprechend der nachfolgenden Muster im Prinzip beliebig erweitert werden.