2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-03-03 16:56:54 +00:00

10_RHASSPY.pm: Fixed scenes; updated GetWeekday

git-svn-id: https://svn.fhem.de/fhem/trunk@24553 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
drhirn 2021-06-01 06:38:47 +00:00
parent 5e5dd8602c
commit b685cb73f6
2 changed files with 351 additions and 238 deletions

View File

@ -39,7 +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 Data::Dumper; use Data::Dumper;
use Scalar::Util qw(weaken); use FHEM::Core::Timer::Register qw(:ALL);
sub ::RHASSPY_Initialize { goto &Initialize } sub ::RHASSPY_Initialize { goto &Initialize }
@ -80,6 +80,7 @@ my $languagevars = {
'DefaultError' => "Sorry but something seems not to work as expected", 'DefaultError' => "Sorry but something seems not to work as expected",
'NoValidData' => "Sorry but the received data is not sufficient to derive any action", 'NoValidData' => "Sorry but the received data is not sufficient to derive any action",
'NoDeviceFound' => "Sorry but I could not find a matching device", 'NoDeviceFound' => "Sorry but I could not find a matching device",
'NoTimedOnDeviceFound' => "Sorry but device does not support requested timed on or off command",
'NoMappingFound' => "Sorry but I could not find a suitable mapping", 'NoMappingFound' => "Sorry but I could not find a suitable mapping",
'NoNewValDerived' => "Sorry but I could not calculate a new value to set", 'NoNewValDerived' => "Sorry but I could not calculate a new value to set",
'NoActiveMediaDevice' => "Sorry no active playback device", 'NoActiveMediaDevice' => "Sorry no active playback device",
@ -108,7 +109,7 @@ my $languagevars = {
}, },
'timerCancellation' => '$label for $room deleted', 'timerCancellation' => '$label for $room deleted',
'timeRequest' => 'it is $hour o clock $min minutes', 'timeRequest' => 'it is $hour o clock $min minutes',
'weekdayRequest' => 'today it is $weekDay', 'weekdayRequest' => 'today is $weekDay, $month the $day., $year',
'duration_not_understood' => "Sorry I could not understand the desired duration", 'duration_not_understood' => "Sorry I could not understand the desired duration",
'reSpeak_failed' => 'i am sorry i can not remember', 'reSpeak_failed' => 'i am sorry i can not remember',
'Change' => { 'Change' => {
@ -311,7 +312,7 @@ sub Initialize {
$hash->{DefFn} = \&Define; $hash->{DefFn} = \&Define;
$hash->{UndefFn} = \&Undefine; $hash->{UndefFn} = \&Undefine;
$hash->{DeleteFn} = \&Delete; $hash->{DeleteFn} = \&Delete;
$hash->{RenameFn} = \&Rename; #$hash->{RenameFn} = \&Rename;
$hash->{SetFn} = \&Set; $hash->{SetFn} = \&Set;
$hash->{AttrFn} = \&Attr; $hash->{AttrFn} = \&Attr;
$hash->{AttrList} = "IODev rhasspyIntents:textField-long rhasspyShortcuts:textField-long rhasspyTweaks:textField-long response:textField-long forceNEXT:0,1 disable:0,1 disabledForIntervals languageFile " . $readingFnAttributes; $hash->{AttrList} = "IODev rhasspyIntents:textField-long rhasspyShortcuts:textField-long rhasspyTweaks:textField-long response:textField-long forceNEXT:0,1 disable:0,1 disabledForIntervals languageFile " . $readingFnAttributes;
@ -336,7 +337,7 @@ sub Define {
my @unknown; my @unknown;
for (keys %{$h}) { for (keys %{$h}) {
push @unknown, $_ if $_ !~ m{\A(baseUrl|defaultRoom|language|devspec|fhemId|prefix|encoding|useGenericAttrs)\z}xm; push @unknown, $_ if $_ !~ m{\A(?:baseUrl|defaultRoom|language|devspec|fhemId|prefix|encoding|useGenericAttrs)\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;
@ -344,14 +345,12 @@ 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.4.15'; $hash->{MODULE_VERSION} = '0.4.19';
$hash->{baseUrl} = $Rhasspy; $hash->{baseUrl} = $Rhasspy;
#$hash->{helper}{defaultRoom} = $defaultRoom;
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;
$hash->{devspec} = $h->{devspec} // q{room=Rhasspy}; $hash->{devspec} = $h->{devspec} // q{room=Rhasspy};
$hash->{fhemId} = $h->{fhemId} // q{fhem}; $hash->{fhemId} = $h->{fhemId} // q{fhem};
#$hash->{baseId} = $h->{baseId} // q{default};
initialize_prefix($hash, $h->{prefix}) if !defined $hash->{prefix} || defined $h->{prefix} && $hash->{prefix} ne $h->{prefix}; initialize_prefix($hash, $h->{prefix}) if !defined $hash->{prefix} || defined $h->{prefix} && $hash->{prefix} ne $h->{prefix};
$hash->{prefix} = $h->{prefix} // q{rhasspy}; $hash->{prefix} = $h->{prefix} // q{rhasspy};
$hash->{encoding} = $h->{encoding} // q{utf8}; $hash->{encoding} = $h->{encoding} // q{utf8};
@ -378,6 +377,7 @@ sub firstInit {
return if !$init_done || !defined $IODev; return if !$init_done || !defined $IODev;
RemoveInternalTimer($hash); RemoveInternalTimer($hash);
deleteAllRegIntTimer($hash);
IOWrite($hash, 'subscriptions', join q{ }, @topics) if InternalVal($IODev,'TYPE',undef) eq 'MQTT2_CLIENT'; IOWrite($hash, 'subscriptions', join q{ }, @topics) if InternalVal($IODev,'TYPE',undef) eq 'MQTT2_CLIENT';
@ -460,10 +460,9 @@ sub initialize_prefix {
sub Undefine { sub Undefine {
my $hash = shift // return; my $hash = shift // return;
deleteAllRegisteredInternalTimer($hash); deleteAllRegIntTimer($hash);
RemoveInternalTimer($hash); RemoveInternalTimer($hash);
return; return;
} }
@ -471,7 +470,7 @@ sub Delete {
my $hash = shift // return; my $hash = shift // return;
#my $prefix = $hash->{prefix} // return; #my $prefix = $hash->{prefix} // return;
deleteAllRegisteredInternalTimer($hash); deleteAllRegIntTimer($hash);
RemoveInternalTimer($hash); RemoveInternalTimer($hash);
# DELETE POD AFTER TESTS ARE COMPLETED # DELETE POD AFTER TESTS ARE COMPLETED
@ -505,13 +504,6 @@ sub Delete {
return; return;
} }
sub Rename {
my $new_name = shift // return;
my $old_name = shift // return;
my $hash = $defs{$new_name} // return;
return renameAllRegisteredInternalTimer($hash, $new_name, $old_name);
}
# Set Befehl aufgerufen # Set Befehl aufgerufen
sub Set { sub Set {
@ -948,7 +940,7 @@ sub _analyze_rhassypAttr {
my($unnamed, $named) = parseParams($val); my($unnamed, $named) = parseParams($val);
my $combined = _combineHashes( $hash->{helper}{devicemap}{devices}{$device}{intents}{SetScene}->{SetScene}, $named); my $combined = _combineHashes( $hash->{helper}{devicemap}{devices}{$device}{intents}{SetScene}->{SetScene}, $named);
for (keys %{$combined}) { for (keys %{$combined}) {
delete $combined->{$_} if $combined->{$_} eq 'none'; delete $combined->{$_} if $combined->{$_} eq 'none' || $named->{all} eq 'none';
} }
keys %{$combined} ? keys %{$combined} ?
$hash->{helper}{devicemap}{devices}{$device}{intents}{SetScene}->{SetScene} = $combined $hash->{helper}{devicemap}{devices}{$device}{intents}{SetScene}->{SetScene} = $combined
@ -1039,7 +1031,7 @@ sub _analyze_genDevType {
$currentMapping->{SetNumeric} = { $currentMapping->{SetNumeric} = {
brightness => { cmd => 'brightness', currentVal => 'brightness', maxVal => '255', minVal => '0', step => '10', map => 'percent', type => 'brightness'}}; brightness => { cmd => 'brightness', currentVal => 'brightness', maxVal => '255', minVal => '0', step => '10', map => 'percent', type => 'brightness'}};
} }
$currentMapping = _analyze_genDevType_setter( $allset, $currentMapping, $device ); $currentMapping = _analyze_genDevType_setter( $hash, $device, $allset, $currentMapping );
$hash->{helper}{devicemap}{devices}{$device}{intents} = $currentMapping; $hash->{helper}{devicemap}{devices}{$device}{intents} = $currentMapping;
} }
elsif ( $gdt eq 'thermostat' ) { elsif ( $gdt eq 'thermostat' ) {
@ -1138,7 +1130,6 @@ sub _analyze_genDevType_setter {
my $ikey = $allKeyMappings->{$okey}; my $ikey = $allKeyMappings->{$okey};
for ( keys %{$ikey} ) { for ( keys %{$ikey} ) {
$mapping->{$okey}->{$ikey->{$_}->{type}} = $ikey->{$_} if $setter =~ m{\b$_([\b:\s]|\Z)}xms; $mapping->{$okey}->{$ikey->{$_}->{type}} = $ikey->{$_} if $setter =~ m{\b$_([\b:\s]|\Z)}xms;
#for my $col (qw(ct hue color sat)) {
if ( $okey eq 'SetColorParms') { #=~ m{\A(ct|hue|color|sat)\z}xms ) { if ( $okey eq 'SetColorParms') { #=~ m{\A(ct|hue|color|sat)\z}xms ) {
my $col = $_; my $col = $_;
if ($setter =~ m{\b${col}:[^\s\d]+,(?<min>[0-9.]+),(?<step>[0-9.]+),(?<max>[0-9.]+)\b}xms) { if ($setter =~ m{\b${col}:[^\s\d]+,(?<min>[0-9.]+),(?<step>[0-9.]+),(?<max>[0-9.]+)\b}xms) {
@ -1152,7 +1143,14 @@ sub _analyze_genDevType_setter {
if ($setter =~ m{\bscene:(?<scnames>[\S]+)}xm) { if ($setter =~ m{\bscene:(?<scnames>[\S]+)}xm) {
for my $scname (split m{,}xms, $+{scnames}) { for my $scname (split m{,}xms, $+{scnames}) {
$mapping->{SetScene}->{SetScene}->{$scname} = $scname; my $clscene = $scname;
# cleanup HUE scenes
if ($clscene =~ m{[#]\[id}xms) {
$clscene = (split m{[#]\[id}xms, $clscene)[0] if $clscene =~ m{[#]\[id}xms;
$clscene =~ s{[#]}{ }gxm;
$scname =~ s{.*[#]\[(id=.+)]}{$1}xms;
}
$mapping->{SetScene}->{SetScene}->{$scname} = $clscene;
} }
} }
return $mapping; return $mapping;
@ -1189,7 +1187,7 @@ sub RHASSPY_DialogTimeout {
my $data = shift // $hash->{helper}{'.delayed'}->{$identiy}; my $data = shift // $hash->{helper}{'.delayed'}->{$identiy};
delete $hash->{helper}{'.delayed'}{$identiy}; delete $hash->{helper}{'.delayed'}{$identiy};
deleteSingleRegisteredInternalTimer($identiy, $hash); deleteSingleRegIntTimer($identiy, $hash, 1);
my $siteId = $data->{siteId}; my $siteId = $data->{siteId};
my $toDisable = defined $data->{'.ENABLED'} ? $data->{'.ENABLED'} : [qw(ConfirmAction CancelAction)]; my $toDisable = defined $data->{'.ENABLED'} ? $data->{'.ENABLED'} : [qw(ConfirmAction CancelAction)];
@ -1203,7 +1201,7 @@ sub RHASSPY_DialogTimeout {
sub setDialogTimeout { sub setDialogTimeout {
my $hash = shift // return; my $hash = shift // return;
my $data = shift // return; # $hash->{helper}{'.delayed'}; my $data = shift // return;
my $timeout = shift; my $timeout = shift;
my $response = shift; my $response = shift;
my $toEnable = shift // [qw(ConfirmAction CancelAction)]; my $toEnable = shift // [qw(ConfirmAction CancelAction)];
@ -1215,8 +1213,7 @@ sub setDialogTimeout {
$response = $hash->{helper}{lng}->{responses}->{DefaultConfirmationReceived} if $response eq 'default'; $response = $hash->{helper}{lng}->{responses}->{DefaultConfirmationReceived} if $response eq 'default';
$hash->{helper}{'.delayed'}{$identiy} = $data; $hash->{helper}{'.delayed'}{$identiy} = $data;
resetRegisteredInternalTimer( $identiy, time + $timeout, \&RHASSPY_DialogTimeout, $hash, 0); resetRegIntTimer( $identiy, time + $timeout, \&RHASSPY_DialogTimeout, $hash, 0);
#InternalTimer(time + $timeout, \&RHASSPY_DialogTimeout, $hash, 0);
#interactive dialogue as described in https://rhasspy.readthedocs.io/en/latest/reference/#dialoguemanager_continuesession and https://docs.snips.ai/articles/platform/dialog/multi-turn-dialog #interactive dialogue as described in https://rhasspy.readthedocs.io/en/latest/reference/#dialoguemanager_continuesession and https://docs.snips.ai/articles/platform/dialog/multi-turn-dialog
my @ca_strings; my @ca_strings;
@ -1467,9 +1464,12 @@ sub getRoomName {
$siteId =~ s{\A([^.]+).*}{$1}xms; $siteId =~ s{\A([^.]+).*}{$1}xms;
utf8::downgrade($siteId, 1); utf8::downgrade($siteId, 1);
$room = ReadingsVal($hash->{NAME}, $rreading, lc $siteId); $room = ReadingsVal($hash->{NAME}, $rreading, lc $siteId);
#$room = ReadingsVal($hash->{NAME}, $rreading, $siteId); if ($room eq 'default' || !(length $room)) {
$room = $hash->{defaultRoom} if $room eq 'default' || !(length $room); $room = $hash->{defaultRoom};
Log3($hash->{NAME}, 5, "room is identified using siteId as $room"); Log3($hash->{NAME}, 5, "default room used");
} else {
Log3($hash->{NAME}, 5, "room is identified using siteId as $room");
}
return $room; return $room;
} }
@ -1488,7 +1488,7 @@ sub getDeviceByName {
return if !defined $hash->{helper}{devicemap}; return if !defined $hash->{helper}{devicemap};
$device = $hash->{helper}{devicemap}{rhasspyRooms}{$room}{$name}; $device = $hash->{helper}{devicemap}{rhasspyRooms}{$room}{$name};
#return $device if $device;
if ($device) { if ($device) {
Log3($hash->{NAME}, 5, "Device selected (by hash, with room and name): $device"); Log3($hash->{NAME}, 5, "Device selected (by hash, with room and name): $device");
return $device ; return $device ;
@ -1617,8 +1617,8 @@ sub getDeviceByIntentAndType {
} else { } else {
push @{$device}, join q{,}, @rooms; push @{$device}, join q{,}, @rooms;
$last_item = pop @rooms; $last_item = pop @rooms;
my $first_items = join q{ }, @rooms; $first_items = join q{ }, @rooms;
my $response = getResponse ($hash, 'RequestChoiceRoom'); $response = getResponse ($hash, 'RequestChoiceRoom');
$response =~ s{(\$\w+)}{$1}eegx; $response =~ s{(\$\w+)}{$1}eegx;
unshift @{$device}, $response; unshift @{$device}, $response;
unshift @{$device}, $matchesOutsideRoom->[0]; unshift @{$device}, $matchesOutsideRoom->[0];
@ -1709,16 +1709,17 @@ sub getDevicesByGroup {
my $data = shift // return; my $data = shift // return;
my $group = $data->{Group} // return; my $group = $data->{Group} // return;
my $room = $data->{Room} // return; #my $room = $data->{Room} // return;
my $room = getRoomName($hash, $data);
my $devices = {}; my $devices = {};
for my $dev (keys %{$hash->{helper}{devicemap}{devices}}) { for my $dev (keys %{$hash->{helper}{devicemap}{devices}}) {
my $allrooms = $hash->{helper}{devicemap}{devices}{$dev}->{rooms}; my $allrooms = $hash->{helper}{devicemap}{devices}{$dev}->{rooms};
next if $room ne 'global' && $allrooms !~ m{\b$room\b}x; next if $room ne 'global' && $allrooms !~ m{\b$room(?:[\b:\s]|\Z)}x;
my $allgroups = $hash->{helper}{devicemap}{devices}{$dev}->{groups} // next; my $allgroups = $hash->{helper}{devicemap}{devices}{$dev}->{groups} // next;
next if $allgroups !~ m{\b$group\b}x; next if $allgroups !~ m{\b$group(?:[\b:\s]|\Z)}x;
my $specials = $hash->{helper}{devicemap}{devices}{$dev}{group_specials}; my $specials = $hash->{helper}{devicemap}{devices}{$dev}{group_specials};
my $label = $specials->{partOf} // $dev; my $label = $specials->{partOf} // $dev;
@ -1989,11 +1990,6 @@ sub parseJSONPayload {
for my $key (qw(sessionId siteId input rawInput customData)) { for my $key (qw(sessionId siteId input rawInput customData)) {
$data->{$key} = $decoded->{$key} if exists $decoded->{$key}; $data->{$key} = $decoded->{$key} if exists $decoded->{$key};
} }
#$data->{sessionId} = $decoded->{sessionId} if exists $decoded->{sessionId};
#$data->{siteId} = $decoded->{siteId} if exists $decoded->{siteId};
#$data->{input} = $decoded->{input} if exists $decoded->{input};
#$data->{rawInput} = $decoded->{rawInput} if exists $decoded->{rawInput};
#$data->{customData} = $decoded->{custom_data} if exists $decoded->{custom_data};
# Überprüfen ob Slot Array existiert # Überprüfen ob Slot Array existiert
if (exists $decoded->{slots}) { if (exists $decoded->{slots}) {
@ -2074,27 +2070,29 @@ sub updateLastIntentReadings {
#Make globally available to allow later use by other functions, esp. handleIntentConfirmAction #Make globally available to allow later use by other functions, esp. handleIntentConfirmAction
my $dispatchFns = { my $dispatchFns = {
Shortcuts => \&handleIntentShortcuts, Shortcuts => \&handleIntentShortcuts,
SetOnOff => \&handleIntentSetOnOff, SetOnOff => \&handleIntentSetOnOff,
SetOnOffGroup => \&handleIntentSetOnOffGroup, SetOnOffGroup => \&handleIntentSetOnOffGroup,
GetOnOff => \&handleIntentGetOnOff, SetTimedOnOff => \&handleIntentSetTimedOnOff,
SetNumeric => \&handleIntentSetNumeric, SetTimedOnOffGroup => \&handleIntentSetTimedOnOffGroup,
SetNumericGroup => \&handleIntentSetNumericGroup, GetOnOff => \&handleIntentGetOnOff,
GetNumeric => \&handleIntentGetNumeric, SetNumeric => \&handleIntentSetNumeric,
GetState => \&handleIntentGetState, SetNumericGroup => \&handleIntentSetNumericGroup,
MediaControls => \&handleIntentMediaControls, GetNumeric => \&handleIntentGetNumeric,
MediaChannels => \&handleIntentMediaChannels, GetState => \&handleIntentGetState,
SetColor => \&handleIntentSetColor, MediaControls => \&handleIntentMediaControls,
SetColorGroup => \&handleIntentSetColorGroup, MediaChannels => \&handleIntentMediaChannels,
SetScene => \&handleIntentSetScene, SetColor => \&handleIntentSetColor,
GetTime => \&handleIntentGetTime, SetColorGroup => \&handleIntentSetColorGroup,
GetWeekday => \&handleIntentGetWeekday, SetScene => \&handleIntentSetScene,
SetTimer => \&handleIntentSetTimer, GetTime => \&handleIntentGetTime,
ConfirmAction => \&handleIntentConfirmAction, GetWeekday => \&handleIntentGetWeekday,
CancelAction => \&handleIntentCancelAction, SetTimer => \&handleIntentSetTimer,
ChoiceRoom => \&handleIntentChoiceRoom, ConfirmAction => \&handleIntentConfirmAction,
ChoiceDevice => \&handleIntentChoiceDevice, CancelAction => \&handleIntentCancelAction,
ReSpeak => \&handleIntentReSpeak ChoiceRoom => \&handleIntentChoiceRoom,
ChoiceDevice => \&handleIntentChoiceDevice,
ReSpeak => \&handleIntentReSpeak
}; };
@ -2777,6 +2775,163 @@ sub handleIntentSetOnOffGroup {
return $updatedList; return $updatedList;
} }
# Handle incoming "SetTimedOnOff" intents
sub handleIntentSetTimedOnOff {
my $hash = shift // return;
my $data = shift // return;
my ($value, $numericValue, $device, $room, $siteId, $mapping, $response);
Log3($hash->{NAME}, 5, "handleIntentSetTimedOnOff called");
return respond ($hash, $data->{requestType}, $data->{sessionId}, $data->{siteId}, $hash->{helper}{lng}->{responses}->{duration_not_understood})
if !defined $data->{Hourabs} && !defined $data->{Hour} && !defined $data->{Min} && !defined $data->{Sec};
# Device AND Value must exist
if ( exists $data->{Device} && exists $data->{Value} ) {
$room = getRoomName($hash, $data);
$value = $data->{Value};
$value = $value eq $de_mappings->{on} ? 'on' : $value; #Beta-User: compability
$device = getDeviceByName($hash, $room, $data->{Device});
$mapping = getMapping($hash, $device, 'SetOnOff', undef, defined $hash->{helper}{devicemap});
# Mapping found?
if ( defined $device && defined $mapping ) {
my $cmdOn = $mapping->{cmdOn} // 'on';
my $cmdOff = $mapping->{cmdOff} // 'off';
my $cmd = $value eq 'on' ? $cmdOn : $cmdOff;
$cmd .= "-for-timer";
my $allset = getAllSets($device);
return respond ($hash, $data->{requestType}, $data->{sessionId}, $data->{siteId}, getResponse($hash, 'NoTimedOnDeviceFound')) if $allset !~ m{\b$cmd(?:[\b:\s]|\Z)}xms;
my $hour = 0;
my $now1 = time;
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;
$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, $mapping->{response}, $numericValue, $room);
Log3($hash->{NAME}, 5, "Response is $response" );
}
else { $response = getResponse($hash, 'DefaultConfirmation'); }
}
}
# Send response
$response = $response // getResponse($hash, 'DefaultError');
respond ($hash, $data->{requestType}, $data->{sessionId}, $data->{siteId}, $response);
return $device;
}
sub handleIntentSetTimedOnOffGroup {
my $hash = shift // return;
my $data = shift // return;
Log3($hash->{NAME}, 5, "handleIntentSetTimedOnOffGroup called");
return respond ($hash, $data->{requestType}, $data->{sessionId}, $data->{siteId}, getResponse($hash, 'NoValidData')) if !defined $data->{Value};
return respond ($hash, $data->{requestType}, $data->{sessionId}, $data->{siteId}, $hash->{helper}{lng}->{responses}->{duration_not_understood})
if !defined $data->{Hourabs} && !defined $data->{Hour} && !defined $data->{Min} && !defined $data->{Sec};
my $devices = getDevicesByGroup($hash, $data);
#see https://perlmaven.com/how-to-sort-a-hash-of-hashes-by-value for reference
my @devlist = sort {
$devices->{$a}{prio} <=> $devices->{$b}{prio}
or
$devices->{$a}{delay} <=> $devices->{$b}{delay}
} keys %{$devices};
Log3($hash, 5, 'sorted devices list is: ' . join q{ }, @devlist);
return respond ($hash, $data->{requestType}, $data->{sessionId}, $data->{siteId}, getResponse($hash, 'NoDeviceFound')) if !keys %{$devices};
#calculate duration for on/off-timer
my $hour = 0;
my $now1 = time;
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;
$now1 = $now1 - $now;
my $delaysum = 0;
my $value = $data->{Value};
$value = $value eq $de_mappings->{on} ? 'on' : $value;
my $updatedList;
my $init_delay = 0;
my $needs_sorting = (@{$hash->{".asyncQueue"}});
for my $device (@devlist) {
my $mapping = getMapping($hash, $device, 'SetOnOff', undef, defined $hash->{helper}{devicemap});
# Mapping found?
next if !defined $mapping;
my $cmdOn = $mapping->{cmdOn} // 'on';
my $cmdOff = $mapping->{cmdOff} // 'off';
my $cmd = $value eq 'on' ? $cmdOn : $cmdOff;
$cmd .= "-for-timer";
my $allset = getAllSets($device);
if ($allset !~ m{\b$cmd(?:[\b:\s]|\Z)}xms) {
Log3($hash->{NAME}, 3, "Running command [$cmd] on device [$device] is not possible!");
next;
}
$cmd .= " $now1";
# execute Cmd
if ( !$delaysum ) {
analyzeAndRunCmd($hash, $device, $cmd);
Log3($hash->{NAME}, 5, "Running command [$cmd] on device [$device]" );
$delaysum += $devices->{$device}->{delay};
$updatedList = $updatedList ? "$updatedList,$device" : $device;
} else {
my $hlabel = $devices->{$device}->{delay};
push @{$hash->{".asyncQueue"}}, {device => $device, cmd => $cmd, prio => $devices->{$device}->{prio}, delay => $hlabel};
InternalTimer(time+$delaysum,\&RHASSPY_asyncQueue,$hash,0) if !$init_delay;
$init_delay = 1;
}
}
_sortAsyncQueue($hash) if $init_delay && $needs_sorting;
# Send response
respond ($hash, $data->{requestType}, $data->{sessionId}, $data->{siteId}, getResponse($hash, 'DefaultConfirmation'));
return $updatedList;
}
# Handle incomint GetOnOff intents # Handle incomint GetOnOff intents
sub handleIntentGetOnOff { sub handleIntentGetOnOff {
my $hash = shift // return; my $hash = shift // return;
@ -2833,7 +2988,6 @@ sub isValidData {
&& (defined $internal_mappings->{Change}->{$data->{Change}} ||defined $de_mappings->{ToEn}->{$data->{Change}}) && (defined $internal_mappings->{Change}->{$data->{Change}} ||defined $de_mappings->{ToEn}->{$data->{Change}})
#$data->{Change}= =~ m/^(lauter|leiser)$/i); #$data->{Change}= =~ m/^(lauter|leiser)$/i);
# Nur Type = Lautstärke und Value angegeben -> Valid (z.B. Lautstärke auf 10) # Nur Type = Lautstärke und Value angegeben -> Valid (z.B. Lautstärke auf 10)
#||!exists $data->{Device} && defined $data->{Type} && exists $data->{Value} && $data->{Type} =~ #||!exists $data->{Device} && defined $data->{Type} && exists $data->{Value} && $data->{Type} =~
#m{\A$hash->{helper}{lng}->{Change}->{regex}->{volume}\z}xim; #m{\A$hash->{helper}{lng}->{Change}->{regex}->{volume}\z}xim;
@ -2903,7 +3057,6 @@ sub handleIntentSetNumeric {
my $hash = shift // return; my $hash = shift // return;
my $data = shift // return; my $data = shift // return;
my $device = $data->{'.DevName'}; my $device = $data->{'.DevName'};
#my $mapping;
my $response; my $response;
Log3($hash->{NAME}, 5, "handleIntentSetNumeric called"); Log3($hash->{NAME}, 5, "handleIntentSetNumeric called");
@ -3242,6 +3395,18 @@ sub handleIntentSetScene{
$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', undef, defined $hash->{helper}{devicemap});
# restore HUE scenes
if ($scene =~ m{id=.+}xms) {
my $allset = getAllSets($device);
if ($allset =~ m{\bscene:(?<scnames>[\S]+)}xm) {
for my $scname (split m{,}xms, $+{scnames}) {
if ($scname =~ m{$scene}xms) {
$scene = $scname;
last $scname;
}
}
}
}
# Mapping found? # Mapping found?
return respond ($hash, $data->{requestType}, $data->{sessionId}, $data->{siteId}, getResponse($hash, 'NoValidData')) if !$device || !defined $mapping; return respond ($hash, $data->{requestType}, $data->{sessionId}, $data->{siteId}, getResponse($hash, 'NoValidData')) if !$device || !defined $mapping;
@ -3285,6 +3450,11 @@ sub handleIntentGetWeekday {
Log3($hash->{NAME}, 5, "handleIntentGetWeekday called"); Log3($hash->{NAME}, 5, "handleIntentGetWeekday called");
my $weekDay = strftime( '%A', localtime ); my $weekDay = strftime( '%A', localtime );
$weekDay = $hash->{helper}{lng}{words}->{$weekDay} if defined $hash->{helper}{lng}{words}->{$weekDay};
my $month = strftime( '%B', localtime );
$month = $hash->{helper}{lng}{words}->{$month} if defined $hash->{helper}{lng}{words}->{$month};
my $year = strftime( '%G', localtime );
my $day = strftime( '%e', localtime );
my $response = $hash->{helper}{lng}->{responses}->{weekdayRequest}; my $response = $hash->{helper}{lng}->{responses}->{weekdayRequest};
$response =~ s{(\$\w+)}{$1}eegx; $response =~ s{(\$\w+)}{$1}eegx;
@ -3353,7 +3523,7 @@ sub handleIntentSetColor {
# At least Device AND Color have to be received # At least Device AND Color have to be received
if ( !exists $data->{Color} && !exists $data->{Rgb} &&!exists $data->{Saturation} && !exists $data->{Colortemp} && !exists $data->{Hue} || !exists $data->{Device} && !defined $device) { if ( !exists $data->{Color} && !exists $data->{Rgb} &&!exists $data->{Saturation} && !exists $data->{Colortemp} && !exists $data->{Hue} || !exists $data->{Device} && !defined $device) {
return if $inBulk; return if $inBulk;
return respond ($hash, $data->{requestType}, $data->{sessionId}, $data->{siteId}, getResponse($hash, 'NoValidData')) ; return respond ($hash, $data->{requestType}, $data->{sessionId}, $data->{siteId}, getResponse($hash, 'NoValidData'));
} }
#if (exists $data->{Color} && exists $data->{Device}) { #if (exists $data->{Color} && exists $data->{Device}) {
@ -3649,12 +3819,9 @@ sub handleIntentSetTimer {
$responseEnd =~ s{(\$\w+)}{$1}eegx; $responseEnd =~ s{(\$\w+)}{$1}eegx;
my $soundoption = $hash->{helper}{tweaks}{timerSounds}->{$label} // $hash->{helper}{tweaks}{timerSounds}->{default}; my $soundoption = $hash->{helper}{tweaks}{timerSounds}->{$label} // $hash->{helper}{tweaks}{timerSounds}->{default};
#my $timerTrigger = $hash->{helper}{tweaks}->{timerTrigger};
#my $addtrigger = defined $timerTrigger && ( $timerTrigger eq 'default' || $timerTrigger =~ m{\b$label\b}x ) ?
#my $addtrigger = defined $timerTrigger && $label ne '' && $timerTrigger =~ m{\bdefault|$label\b}x ?
my $addtrigger = qq{; trigger $name timerEnd $siteId $room}; my $addtrigger = qq{; trigger $name timerEnd $siteId $room};
$addtrigger .= " $label" if defined $label; $addtrigger .= " $label" if defined $label;
# : q{};
if ( !defined $soundoption ) { if ( !defined $soundoption ) {
CommandDefMod($hash, "-temporary $roomReading at +$attime set $name speak siteId=\"$timerRoom\" text=\"$responseEnd\";deletereading $name ${roomReading}$addtrigger"); CommandDefMod($hash, "-temporary $roomReading at +$attime set $name speak siteId=\"$timerRoom\" text=\"$responseEnd\";deletereading $name ${roomReading}$addtrigger");
@ -3719,10 +3886,8 @@ sub handleIntentCancelAction {
return respond ($hash, $data->{requestType}, $data->{sessionId}, $data->{siteId}, $response) if !defined $data->{customData}; return respond ($hash, $data->{requestType}, $data->{sessionId}, $data->{siteId}, $response) if !defined $data->{customData};
#might lead to problems, if there's more than one timeout running...
#RemoveInternalTimer( $hash, \&RHASSPY_DialogTimeout );
my $identiy = qq($data->{sessionId}); my $identiy = qq($data->{sessionId});
deleteSingleRegisteredInternalTimer($identiy, $hash); deleteSingleRegIntTimer($identiy, $hash);
$response = $hash->{helper}{lng}->{responses}->{ 'DefaultCancelConfirmation' }; $response = $hash->{helper}{lng}->{responses}->{ 'DefaultCancelConfirmation' };
configure_DialogManager($hash, $data->{siteId}, $toDisable, 'false'); configure_DialogManager($hash, $data->{siteId}, $toDisable, 'false');
@ -3737,25 +3902,20 @@ sub handleIntentConfirmAction {
Log3($hash->{NAME}, 5, 'handleIntentConfirmAction called'); Log3($hash->{NAME}, 5, 'handleIntentConfirmAction called');
#cancellation case #cancellation case
#return RHASSPY_DialogTimeout($hash, 1, $data) if $data->{Mode} ne 'OK';
return handleIntentCancelAction($hash, $data) if $data->{Mode} ne 'OK'; return handleIntentCancelAction($hash, $data) if $data->{Mode} ne 'OK';
#confirmed case #confirmed case
my $identiy = qq($data->{sessionId}); my $identiy = qq($data->{sessionId});
my $data_saved = $hash->{helper}{'.delayed'}->{$identiy}; my $data_saved = $hash->{helper}{'.delayed'}->{$identiy};
delete $hash->{helper}{'.delayed'}{$identiy}; delete $hash->{helper}{'.delayed'}{$identiy};
deleteSingleRegisteredInternalTimer($identiy, $hash); deleteSingleRegIntTimer($identiy, $hash);
#my $data_old = $hash->{helper}{'.delayed'};
#my $data_old = $data->{customData} // $data_saved;
my $data_old = $data_saved; my $data_old = $data_saved;
#my $toDisable = defined $data->{customData} && defined $data->{customData}->{'.ENABLED'} ? $data->{customData}->{'.ENABLED'} : [qw(ConfirmAction CancelAction)];
my $toDisable = defined $data_old && defined $data_old->{'.ENABLED'} ? $data_old->{'.ENABLED'} : [qw(ConfirmAction CancelAction)]; my $toDisable = defined $data_old && defined $data_old->{'.ENABLED'} ? $data_old->{'.ENABLED'} : [qw(ConfirmAction CancelAction)];
configure_DialogManager($hash, $data->{siteId}, $toDisable, 'false'); configure_DialogManager($hash, $data->{siteId}, $toDisable, 'false');
return respond( $hash, $data->{requestType}, $data->{sessionId}, $data->{siteId}, getResponse( $hash, 'DefaultConfirmationNoOutstanding' ) ) if ! defined $data_old; return respond( $hash, $data->{requestType}, $data->{sessionId}, $data->{siteId}, getResponse( $hash, 'DefaultConfirmationNoOutstanding' ) ) if ! defined $data_old;
#delete $hash->{helper}{'.delayed'};
$data_old->{siteId} = $data->{siteId}; $data_old->{siteId} = $data->{siteId};
$data_old->{sessionId} = $data->{sessionId}; $data_old->{sessionId} = $data->{sessionId};
@ -3779,11 +3939,10 @@ sub handleIntentChoiceRoom {
Log3($hash->{NAME}, 5, 'handleIntentChoiceRoom called'); Log3($hash->{NAME}, 5, 'handleIntentChoiceRoom called');
#my $data_old = $data->{customData};
my $identiy = qq($data->{sessionId}); my $identiy = qq($data->{sessionId});
my $data_old = $hash->{helper}{'.delayed'}->{$identiy}; my $data_old = $hash->{helper}{'.delayed'}->{$identiy};
delete $hash->{helper}{'.delayed'}{$identiy}; delete $hash->{helper}{'.delayed'}{$identiy};
deleteSingleRegisteredInternalTimer($identiy, $hash); deleteSingleRegIntTimer($identiy, $hash);
return respond( $hash, $data->{requestType}, $data->{sessionId}, $data->{siteId}, getResponse( $hash, 'DefaultChoiceNoOutstanding' ) ) if !defined $data_old; return respond( $hash, $data->{requestType}, $data->{sessionId}, $data->{siteId}, getResponse( $hash, 'DefaultChoiceNoOutstanding' ) ) if !defined $data_old;
@ -3813,7 +3972,7 @@ sub handleIntentChoiceDevice {
my $identiy = qq($data->{sessionId}); my $identiy = qq($data->{sessionId});
my $data_old = $hash->{helper}{'.delayed'}->{$identiy}; my $data_old = $hash->{helper}{'.delayed'}->{$identiy};
delete $hash->{helper}{'.delayed'}{$identiy}; delete $hash->{helper}{'.delayed'}{$identiy};
deleteSingleRegisteredInternalTimer($identiy, $hash); deleteSingleRegIntTimer($identiy, $hash);
return respond( $hash, $data->{requestType}, $data->{sessionId}, $data->{siteId}, getResponse( $hash, 'DefaultChoiceNoOutstanding' ) ) if ! defined $data_old; return respond( $hash, $data->{requestType}, $data->{sessionId}, $data->{siteId}, getResponse( $hash, 'DefaultChoiceNoOutstanding' ) ) if ! defined $data_old;
@ -3875,8 +4034,8 @@ sub setPlayWav {
return if !$repeats; return if !$repeats;
my $name = $hash->{NAME}; my $name = $hash->{NAME};
my $wait = $cmd->{wait} // 15; my $wait = $cmd->{wait} // 15;
my $id = $cmd->{id}; my $id = $cmd->{id};
$repeats--; $repeats--;
my $attime = strftime( '%H:%M:%S', gmtime $wait ); my $attime = strftime( '%H:%M:%S', gmtime $wait );
@ -3994,67 +4153,6 @@ sub _getDialogueTimeout {
} }
# borrowed from Twilight
################################################################################
################################################################################
sub resetRegisteredInternalTimer {
my ( $modifier, $tim, $callback, $hash, $waitIfInitNotDone ) = @_;
deleteSingleRegisteredInternalTimer( $modifier, $hash, $callback );
return setRegisteredInternalTimer ( $modifier, $tim, $callback, $hash, $waitIfInitNotDone );
}
################################################################################
sub setRegisteredInternalTimer {
my ( $modifier, $tim, $callback, $hash, $waitIfInitNotDone ) = @_;
my $timerName = "$hash->{NAME}_$modifier";
my $fnHash = {
HASH => $hash,
NAME => $timerName,
MODIFIER => $modifier
};
weaken($fnHash->{HASH});
if ( defined( $hash->{TIMER}{$timerName} ) ) {
Log3( $hash, 1, "[$hash->{NAME}] possible overwriting of timer $timerName - please delete it first" );
stacktrace();
}
else {
$hash->{TIMER}{$timerName} = $fnHash;
}
Log3( $hash, 5, "[$hash->{NAME}] setting Timer: $timerName " . FmtDateTime($tim) );
InternalTimer( $tim, $callback, $fnHash, $waitIfInitNotDone );
return $fnHash;
}
################################################################################
sub deleteSingleRegisteredInternalTimer {
my $modifier = shift;
my $hash = shift // return;
my $timerName = "$hash->{NAME}_$modifier";
my $fnHash = $hash->{TIMER}{$timerName};
if ( defined($fnHash) ) {
Log3( $hash, 5, "[$hash->{NAME}] removing Timer: $timerName" );
RemoveInternalTimer($fnHash);
delete $hash->{TIMER}{$timerName};
}
return;
}
################################################################################
sub deleteAllRegisteredInternalTimer {
my $hash = shift // return;
for my $key ( keys %{ $hash->{TIMER} } ) {
deleteSingleRegisteredInternalTimer( $hash->{TIMER}{$key}{MODIFIER}, $hash );
}
return;
}
1; 1;
__END__ __END__
@ -4076,7 +4174,7 @@ __END__
# "rhasspySpecials" bzw. rhasspyTweaks als weitere Attribute # "rhasspySpecials" bzw. rhasspyTweaks als weitere Attribute
Denkbare Verwendung: Denkbare Verwendung:
- siteId2room für mobile Geräte (Denkbare Anwendungsfälle: Auswertung BT-RSSI per Perl, aktives Setzen über ein Reading? Oder einen intent? (tweak) - siteId2room für mobile Geräte (Denkbare Anwendungsfälle: Auswertung BT-RSSI per Perl, aktives Setzen über ein Reading? Oder einen intent? (tweak)
- Bestätigungs-Mapping (special) - Bestätigungs-Mapping (special) (ist noch offen)
# Sonstiges, siehe insbes. https://forum.fhem.de/index.php/topic,119447.msg1148832.html#msg1148832 # Sonstiges, siehe insbes. https://forum.fhem.de/index.php/topic,119447.msg1148832.html#msg1148832
- kein "match in room" bei GetNumeric - kein "match in room" bei GetNumeric
@ -4089,7 +4187,9 @@ Denkbare Verwendung:
# Parameter-Check für define? Anregung DrBasch aus https://forum.fhem.de/index.php/topic,119447.msg1157700.html#msg1157700 # Parameter-Check für define? Anregung DrBasch aus https://forum.fhem.de/index.php/topic,119447.msg1157700.html#msg1157700
# Keine shortcuts-Intents, wenn Attribut nicht gesetzt: Anregung DrBasch aus https://forum.fhem.de/index.php/topic,119447.msg1157700.html#msg1157700 # Keine shortcuts-Intents, wenn Attribut nicht gesetzt: Anregung DrBasch aus https://forum.fhem.de/index.php/topic,119447.msg1157700.html#msg1157700 (erl.)
# Doku zu den "üblichen Formaten" (z.B. JSON-Keywords beginnen mit Großbuchstaben)?
=end ToDo =end ToDo
@ -4375,37 +4475,14 @@ i="i am hungry" f="set Stove on" d="Stove" c="would you like roast pork"</code><
<p>&nbsp;</p> <p>&nbsp;</p>
<a id="RHASSPY-attr-subdevice"></a> <a id="RHASSPY-attr-subdevice"></a>
<p><b>For the subordinated devices</b>, a list of the possible attributes is automatically extended by several further entries.</p> <p><b>For the subordinated devices</b>, a list of the possible attributes is automatically extended by several further entries</p>
<p>There are two ways to tell RHASSPY which devices it should control:</p> <p>The names of these attributes all start with the <i>prefix</i> previously defined in RHASSPY - except for <a href="#RHASSPY-genericDeviceType">genericDeviceType</a> (gDT).<br>
<ul>
<li><a href="#RHASSPY-genericDeviceType">genericDeviceType</a> (gDT)</li>
<li><a href="#RHASSPY-rhasspySpecificAttributes">RHASSPY specific attributes</a></li>
</ul>
<p>It's also possible to mix these two options if one of it isn't enough.</p>
<a id="RHASSPY-genericDeviceType"></a><p><b>genericDeviceType</b></p>
<p>If this attribute is set, RHASSPY will try to determine mapping (and other) information from the attributes already present (if devices match devspec). Currently the following subset of genericDeviceType is supported:</p>
<ul>
<li>switch</li>
<li>light</li>
<li>thermostat</li>
<li>thermometer</li>
<li>blind</li>
<li>media</li>
</ul>
<p>When using genericDeviceType, collected information about the device are for example:</p>
<ul>
<li>the name (NAME or alias)</li>
<li>the ROOM or GROUP the device is in</li>
<li>how to GET information from the device</li>
<li>how to SET state/values</li>
</ul>
<p>This is the easiest way to get devices to work with RHASSPY. In some cases it may happen that gDT delivers to less or not suitable information for this particular device. Then it's possible to overwrite this with the following RHASSPY specific device attributes.</p>
<a href="#RHASSPY-rhasspySpecificAttributes"></a><p><b>RHASSPY specific attributes</b></p>
<p>The names of these attributes all start with the <i>prefix</i> previously defined in RHASSPY<br>
These attributes are used to configure the actual mapping to the intents and the content sent by Rhasspy.</p> These attributes are used to configure the actual mapping to the intents and the content sent by Rhasspy.</p>
<p>Note: As the analyses of the gDT is intented to lead to fast configuration progress, it's highly recommended to use this as a starting point. All other RHASSPY-specific attributes will then be considered as a user command to <b>overwrite</b> the results provided by the automatics initiated by gDT usage.</p>
<p>By default, the following attribute names are used: rhasspyName, rhasspyRoom, rhasspyGroup, rhasspyChannels, rhasspyColors, rhasspySpecials.<br> <p>By default, the following attribute names are used: rhasspyName, rhasspyRoom, rhasspyGroup, rhasspyChannels, rhasspyColors, rhasspySpecials.<br>
Each of the keywords found in these attributes will be sent by <a href="#RHASSPY-set-update">update</a> to Rhasspy to create the corresponding slot.</p> Each of the keywords found in these attributes will be sent by <a href="#RHASSPY-set-update">update</a> to Rhasspy to create the corresponding slot.</p>
<ul> <ul>
<li> <li>
<a id="RHASSPY-attr-rhasspyName"></a><b>rhasspyName</b> <a id="RHASSPY-attr-rhasspyName"></a><b>rhasspyName</b>
@ -4486,7 +4563,7 @@ yellow=rgb FFFF00</code></p>
<p>If set, the slat target position will be set to the same level than the main device.</p> <p>If set, the slat target position will be set to the same level than the main device.</p>
</li> </li>
<li><b>colorCommandMap</b> <li><b>colorCommandMap</b>
<p>Allows mapping of values from the <i>Color</i> key to individual commands.</p> <p>Allows mapping of values from the <i>Color></i> key to individual commands.</p>
<p>Example:</p> <p>Example:</p>
<p><code>attr lamp1 rhasspySpecials colorCommandMap:0='rgb FF0000' 120='rgb 00FF00' 240='rgb 0000FF'</code></p> <p><code>attr lamp1 rhasspySpecials colorCommandMap:0='rgb FF0000' 120='rgb 00FF00' 240='rgb 0000FF'</code></p>
</li> </li>
@ -4500,6 +4577,12 @@ yellow=rgb FFFF00</code></p>
<p>Example:</p> <p>Example:</p>
<p><code>attr sensor_outside_main rhasspySpecials priority:inRoom=temperature outsideRoom=temperature,humidity,pressure</code></p> <p><code>attr sensor_outside_main rhasspySpecials priority:inRoom=temperature outsideRoom=temperature,humidity,pressure</code></p>
</li> </li>
<li><b>scenes</b>
<p><code>attr lamp1 rhasspySpecials scenes:scene2="Kino zu zweit" scene3=Musik scene1=none scene4=none</code></p>
<p>Explanation:
<p>If set, the label provided will be sent to Rhasspy instead of the <i>tech names</i> (derived from available setters). Keyword <i>none</i> will delete the scene from the internal list, setting the combination <i>all=none</i> will exclude the entire device from beeing recognized for SetScene.</p>
</li>
</ul> </ul>
</li> </li>
</ul> </ul>
@ -4512,6 +4595,8 @@ yellow=rgb FFFF00</code></p>
<li>Shortcuts</li> <li>Shortcuts</li>
<li>SetOnOff</li> <li>SetOnOff</li>
<li>SetOnOffGroup</li> <li>SetOnOffGroup</li>
<li>SetTimedOnOff</li>
<li>SetTimedOnOffGroup</li>
<li>GetOnOff</li> <li>GetOnOff</li>
<li>SetNumeric</li> <li>SetNumeric</li>
<li>SetNumericGroup</li> <li>SetNumericGroup</li>

View File

@ -6,7 +6,7 @@
# "commaconversion", "units" und "mutated_vowels" sind optional, wenn nicht # "commaconversion", "units" und "mutated_vowels" sind optional, wenn nicht
# angegeben, werden die englischen Werte/Gepflogenheiten verwendet bzw. keine # angegeben, werden die englischen Werte/Gepflogenheiten verwendet bzw. keine
# Ersetzungen vorgenommen. # Ersetzungen vorgenommen.
# Der Bereich "default" enthält übersetzte Standardantworten. Falls diese nicht gefallen, sollten individuelle Änderungen im Bereich "user" erfolgen, siehe nähere Erläuternungen dort.
{"default": {"default":
{ {
"commaconversion": "1", "commaconversion": "1",
@ -43,14 +43,16 @@
"DefaultConfirmationNoOutstanding": "warte grade nicht auf eine bestätigung", "DefaultConfirmationNoOutstanding": "warte grade nicht auf eine bestätigung",
"DefaultCancelConfirmation": "Habe abgebrochen", "DefaultCancelConfirmation": "Habe abgebrochen",
"DefaultConfirmationReceived": "Ok, werde ich machen", "DefaultConfirmationReceived": "Ok, werde ich machen",
"DefaultConfirmationRequest": "please confirm switching $device $wanted", "DefaultConfirmationRequest": "Bitte bestätigen, dass $device auf $wanted gestellt werden soll",
"DefaultChoiceNoOutstanding": "warte grade nicht auf eine Auswahl",
"RequestChoiceDevice": "Es kommen mehrere Geräte in Frage, bitte wähle zwischen $first_items oder $last_item", "RequestChoiceDevice": "Es kommen mehrere Geräte in Frage, bitte wähle zwischen $first_items oder $last_item",
"RequestChoiceRoom": "Es kommen mehrere Geräte in verschiedenen Räumen in Frage, wähle einen Raum von $first_items oder $last_item", "RequestChoiceRoom": "Es kommen mehrere Geräte in verschiedenen Räumen in Frage, wähle einen Raum von $first_items oder $last_item",
"DefaultError": "Da ist leider etwas schief gegangen", "DefaultError": "Da ist leider etwas schief gegangen",
"NoValidData": "Tut mir leid, aber ich habe zu wenig Daten um den Vorgang auszuführen", "NoValidData": "ich habe leider zu wenig Daten um den Vorgang auszuführen",
"NoDeviceFound": "Tut mir leid, ich konnte kein passendes Gerät finden", "NoDeviceFound": "Tut mir leid, ich konnte kein passendes Gerät finden",
"NoMappingFound": "Tut mir leid, aber ich konnte kein passendes Mäpping finden", "NoMappingFound": "Tut mir leid, ich konnte kein passendes Mäpping finden",
"NoNewValDerived": "Tut mir leid, aber ich konnte den Zielwert nicht ausrechnen", "NoNewValDerived": "Tut mir leid, ich konnte den Zielwert nicht ausrechnen",
"NoTimedOnDeviceFound": "Das gewählte Gerät unterstützt leider keine taimer Kommandos",
"NoActiveMediaDevice": "Tut mir leid, es ist kein Wiedergabegerät aktiv", "NoActiveMediaDevice": "Tut mir leid, es ist kein Wiedergabegerät aktiv",
"NoMediaChannelFound": "Tut mir leid, der angefragte Kanal scheint nicht zu existieren.", "NoMediaChannelFound": "Tut mir leid, der angefragte Kanal scheint nicht zu existieren.",
"duration_not_understood": "Tut mir leid, ich habe die Dauer nicht verstanden", "duration_not_understood": "Tut mir leid, ich habe die Dauer nicht verstanden",
@ -68,7 +70,7 @@
}, },
"timerCancellation": "$label im $room gelöscht", "timerCancellation": "$label im $room gelöscht",
"timeRequest": "Es ist $hour Uhr $min", "timeRequest": "Es ist $hour Uhr $min",
"weekdayRequest": "Heute ist $weekDay", "weekdayRequest": "Heute ist $weekDay, der $day. $month $year",
"reSpeak_failed": "Tut mir leid, ich kann mich nicht erinnern", "reSpeak_failed": "Tut mir leid, ich kann mich nicht erinnern",
"Change": { "Change": {
"volume": "$deviceName ist auf $value gestellt", "volume": "$deviceName ist auf $value gestellt",
@ -109,16 +111,42 @@
} }
} }
}, },
#Der Bereich "user" ist dazu gedacht, die obigen Standardsätze ggf. einzeln zu ersetzen, so dass bei Updates einfach der "default"-Bereich getauscht werden kann, ohne dass eigene Einstellungen überschrieben werden. Die Struktur muss dabei mit dem obigen Bereich übereinstimmen, wobei nur "Keywords" zugelassen sind, die vom Modul vorgegeben werden, und nur die Variablen aufgelöst werden können, die auch im (englischen) Ausgangstext vorgesehen sind. Er kann in diesem Rahmen entsprechend der nachfolgenden Muster im Prinzip beliebig erweitert werden.
"user": "user":
{ {
"units": { "units": {
"unitSeconds": { "unitSeconds": {
"0": "Test einige Sekunden", "0": "Beispiel 1a: einige Sekunden",
"1": "Test genau eine Sekunde" "1": "Beispiel 1b: genau eine Sekunde"
} }
},
"responses": {
"DefaultConfirmation": "Gerne!",
"DefaultError": "Da paßt irgend was nicht"
},
"words": {
"Monday": "Montag",
"Tuesday": "Dienstag",
"Wednesday": "Mittwoch",
"Thursday": "Montag",
"Friday": "Freitag",
"Saturday": "Samstag",
"Sunday": "Sonntag",
"January": "Jänner",
"February": "Februar",
"March": "März",
"April": "April",
"May": "Mai",
"June": "Juni",
"July": "Juli",
"August": "August",
"September": "September",
"October": "Oktober",
"November": "November",
"December": "Dezember"
} }
}, },
#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.
"slots": "slots":
{ {
"Colors": " "Colors": "