mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-03-03 10:46:53 +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:
parent
5e5dd8602c
commit
b685cb73f6
@ -39,7 +39,7 @@ use HttpUtils;
|
||||
use utf8;
|
||||
use List::Util 1.45 qw(max min uniq);
|
||||
use Data::Dumper;
|
||||
use Scalar::Util qw(weaken);
|
||||
use FHEM::Core::Timer::Register qw(:ALL);
|
||||
|
||||
sub ::RHASSPY_Initialize { goto &Initialize }
|
||||
|
||||
@ -80,6 +80,7 @@ my $languagevars = {
|
||||
'DefaultError' => "Sorry but something seems not to work as expected",
|
||||
'NoValidData' => "Sorry but the received data is not sufficient to derive any action",
|
||||
'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",
|
||||
'NoNewValDerived' => "Sorry but I could not calculate a new value to set",
|
||||
'NoActiveMediaDevice' => "Sorry no active playback device",
|
||||
@ -108,7 +109,7 @@ my $languagevars = {
|
||||
},
|
||||
'timerCancellation' => '$label for $room deleted',
|
||||
'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",
|
||||
'reSpeak_failed' => 'i am sorry i can not remember',
|
||||
'Change' => {
|
||||
@ -311,7 +312,7 @@ sub Initialize {
|
||||
$hash->{DefFn} = \&Define;
|
||||
$hash->{UndefFn} = \&Undefine;
|
||||
$hash->{DeleteFn} = \&Delete;
|
||||
$hash->{RenameFn} = \&Rename;
|
||||
#$hash->{RenameFn} = \&Rename;
|
||||
$hash->{SetFn} = \&Set;
|
||||
$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;
|
||||
@ -336,7 +337,7 @@ sub Define {
|
||||
|
||||
my @unknown;
|
||||
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;
|
||||
return "unknown key(s) in DEF: $err" if @unknown && $init_done;
|
||||
@ -344,14 +345,12 @@ sub Define {
|
||||
|
||||
$hash->{defaultRoom} = $defaultRoom;
|
||||
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->{helper}{defaultRoom} = $defaultRoom;
|
||||
initialize_Language($hash, $language) if !defined $hash->{LANGUAGE} || $hash->{LANGUAGE} ne $language;
|
||||
$hash->{LANGUAGE} = $language;
|
||||
$hash->{devspec} = $h->{devspec} // q{room=Rhasspy};
|
||||
$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};
|
||||
$hash->{prefix} = $h->{prefix} // q{rhasspy};
|
||||
$hash->{encoding} = $h->{encoding} // q{utf8};
|
||||
@ -378,6 +377,7 @@ sub firstInit {
|
||||
|
||||
return if !$init_done || !defined $IODev;
|
||||
RemoveInternalTimer($hash);
|
||||
deleteAllRegIntTimer($hash);
|
||||
|
||||
IOWrite($hash, 'subscriptions', join q{ }, @topics) if InternalVal($IODev,'TYPE',undef) eq 'MQTT2_CLIENT';
|
||||
|
||||
@ -460,10 +460,9 @@ sub initialize_prefix {
|
||||
sub Undefine {
|
||||
my $hash = shift // return;
|
||||
|
||||
deleteAllRegisteredInternalTimer($hash);
|
||||
deleteAllRegIntTimer($hash);
|
||||
RemoveInternalTimer($hash);
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -471,7 +470,7 @@ sub Delete {
|
||||
my $hash = shift // return;
|
||||
#my $prefix = $hash->{prefix} // return;
|
||||
|
||||
deleteAllRegisteredInternalTimer($hash);
|
||||
deleteAllRegIntTimer($hash);
|
||||
RemoveInternalTimer($hash);
|
||||
|
||||
# DELETE POD AFTER TESTS ARE COMPLETED
|
||||
@ -505,13 +504,6 @@ sub Delete {
|
||||
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
|
||||
sub Set {
|
||||
@ -948,7 +940,7 @@ sub _analyze_rhassypAttr {
|
||||
my($unnamed, $named) = parseParams($val);
|
||||
my $combined = _combineHashes( $hash->{helper}{devicemap}{devices}{$device}{intents}{SetScene}->{SetScene}, $named);
|
||||
for (keys %{$combined}) {
|
||||
delete $combined->{$_} if $combined->{$_} eq 'none';
|
||||
delete $combined->{$_} if $combined->{$_} eq 'none' || $named->{all} eq 'none';
|
||||
}
|
||||
keys %{$combined} ?
|
||||
$hash->{helper}{devicemap}{devices}{$device}{intents}{SetScene}->{SetScene} = $combined
|
||||
@ -1039,7 +1031,7 @@ sub _analyze_genDevType {
|
||||
$currentMapping->{SetNumeric} = {
|
||||
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;
|
||||
}
|
||||
elsif ( $gdt eq 'thermostat' ) {
|
||||
@ -1138,7 +1130,6 @@ sub _analyze_genDevType_setter {
|
||||
my $ikey = $allKeyMappings->{$okey};
|
||||
for ( keys %{$ikey} ) {
|
||||
$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 ) {
|
||||
my $col = $_;
|
||||
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) {
|
||||
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;
|
||||
@ -1189,7 +1187,7 @@ sub RHASSPY_DialogTimeout {
|
||||
|
||||
my $data = shift // $hash->{helper}{'.delayed'}->{$identiy};
|
||||
delete $hash->{helper}{'.delayed'}{$identiy};
|
||||
deleteSingleRegisteredInternalTimer($identiy, $hash);
|
||||
deleteSingleRegIntTimer($identiy, $hash, 1);
|
||||
|
||||
my $siteId = $data->{siteId};
|
||||
my $toDisable = defined $data->{'.ENABLED'} ? $data->{'.ENABLED'} : [qw(ConfirmAction CancelAction)];
|
||||
@ -1203,7 +1201,7 @@ sub RHASSPY_DialogTimeout {
|
||||
|
||||
sub setDialogTimeout {
|
||||
my $hash = shift // return;
|
||||
my $data = shift // return; # $hash->{helper}{'.delayed'};
|
||||
my $data = shift // return;
|
||||
my $timeout = shift;
|
||||
my $response = shift;
|
||||
my $toEnable = shift // [qw(ConfirmAction CancelAction)];
|
||||
@ -1215,8 +1213,7 @@ sub setDialogTimeout {
|
||||
$response = $hash->{helper}{lng}->{responses}->{DefaultConfirmationReceived} if $response eq 'default';
|
||||
$hash->{helper}{'.delayed'}{$identiy} = $data;
|
||||
|
||||
resetRegisteredInternalTimer( $identiy, time + $timeout, \&RHASSPY_DialogTimeout, $hash, 0);
|
||||
#InternalTimer(time + $timeout, \&RHASSPY_DialogTimeout, $hash, 0);
|
||||
resetRegIntTimer( $identiy, 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
|
||||
my @ca_strings;
|
||||
@ -1467,9 +1464,12 @@ sub getRoomName {
|
||||
$siteId =~ s{\A([^.]+).*}{$1}xms;
|
||||
utf8::downgrade($siteId, 1);
|
||||
$room = ReadingsVal($hash->{NAME}, $rreading, lc $siteId);
|
||||
#$room = ReadingsVal($hash->{NAME}, $rreading, $siteId);
|
||||
$room = $hash->{defaultRoom} if $room eq 'default' || !(length $room);
|
||||
if ($room eq 'default' || !(length $room)) {
|
||||
$room = $hash->{defaultRoom};
|
||||
Log3($hash->{NAME}, 5, "default room used");
|
||||
} else {
|
||||
Log3($hash->{NAME}, 5, "room is identified using siteId as $room");
|
||||
}
|
||||
|
||||
return $room;
|
||||
}
|
||||
@ -1488,7 +1488,7 @@ sub getDeviceByName {
|
||||
return if !defined $hash->{helper}{devicemap};
|
||||
|
||||
$device = $hash->{helper}{devicemap}{rhasspyRooms}{$room}{$name};
|
||||
#return $device if $device;
|
||||
|
||||
if ($device) {
|
||||
Log3($hash->{NAME}, 5, "Device selected (by hash, with room and name): $device");
|
||||
return $device ;
|
||||
@ -1617,8 +1617,8 @@ sub getDeviceByIntentAndType {
|
||||
} else {
|
||||
push @{$device}, join q{,}, @rooms;
|
||||
$last_item = pop @rooms;
|
||||
my $first_items = join q{ }, @rooms;
|
||||
my $response = getResponse ($hash, 'RequestChoiceRoom');
|
||||
$first_items = join q{ }, @rooms;
|
||||
$response = getResponse ($hash, 'RequestChoiceRoom');
|
||||
$response =~ s{(\$\w+)}{$1}eegx;
|
||||
unshift @{$device}, $response;
|
||||
unshift @{$device}, $matchesOutsideRoom->[0];
|
||||
@ -1709,16 +1709,17 @@ sub getDevicesByGroup {
|
||||
my $data = shift // return;
|
||||
|
||||
my $group = $data->{Group} // return;
|
||||
my $room = $data->{Room} // return;
|
||||
#my $room = $data->{Room} // return;
|
||||
my $room = getRoomName($hash, $data);
|
||||
|
||||
my $devices = {};
|
||||
|
||||
for my $dev (keys %{$hash->{helper}{devicemap}{devices}}) {
|
||||
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;
|
||||
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 $label = $specials->{partOf} // $dev;
|
||||
@ -1989,11 +1990,6 @@ sub parseJSONPayload {
|
||||
for my $key (qw(sessionId siteId input rawInput customData)) {
|
||||
$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
|
||||
if (exists $decoded->{slots}) {
|
||||
@ -2077,6 +2073,8 @@ my $dispatchFns = {
|
||||
Shortcuts => \&handleIntentShortcuts,
|
||||
SetOnOff => \&handleIntentSetOnOff,
|
||||
SetOnOffGroup => \&handleIntentSetOnOffGroup,
|
||||
SetTimedOnOff => \&handleIntentSetTimedOnOff,
|
||||
SetTimedOnOffGroup => \&handleIntentSetTimedOnOffGroup,
|
||||
GetOnOff => \&handleIntentGetOnOff,
|
||||
SetNumeric => \&handleIntentSetNumeric,
|
||||
SetNumericGroup => \&handleIntentSetNumericGroup,
|
||||
@ -2777,6 +2775,163 @@ sub handleIntentSetOnOffGroup {
|
||||
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
|
||||
sub handleIntentGetOnOff {
|
||||
my $hash = shift // return;
|
||||
@ -2833,7 +2988,6 @@ sub isValidData {
|
||||
&& (defined $internal_mappings->{Change}->{$data->{Change}} ||defined $de_mappings->{ToEn}->{$data->{Change}})
|
||||
#$data->{Change}= =~ m/^(lauter|leiser)$/i);
|
||||
|
||||
|
||||
# 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} =~
|
||||
#m{\A$hash->{helper}{lng}->{Change}->{regex}->{volume}\z}xim;
|
||||
@ -2903,7 +3057,6 @@ sub handleIntentSetNumeric {
|
||||
my $hash = shift // return;
|
||||
my $data = shift // return;
|
||||
my $device = $data->{'.DevName'};
|
||||
#my $mapping;
|
||||
my $response;
|
||||
|
||||
Log3($hash->{NAME}, 5, "handleIntentSetNumeric called");
|
||||
@ -3242,6 +3395,18 @@ sub handleIntentSetScene{
|
||||
$scene = $data->{Scene};
|
||||
$device = getDeviceByName($hash, $room, $data->{Device});
|
||||
$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?
|
||||
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");
|
||||
|
||||
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};
|
||||
$response =~ s{(\$\w+)}{$1}eegx;
|
||||
|
||||
@ -3649,12 +3819,9 @@ sub handleIntentSetTimer {
|
||||
$responseEnd =~ s{(\$\w+)}{$1}eegx;
|
||||
|
||||
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};
|
||||
$addtrigger .= " $label" if defined $label;
|
||||
# : q{};
|
||||
|
||||
if ( !defined $soundoption ) {
|
||||
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};
|
||||
|
||||
#might lead to problems, if there's more than one timeout running...
|
||||
#RemoveInternalTimer( $hash, \&RHASSPY_DialogTimeout );
|
||||
my $identiy = qq($data->{sessionId});
|
||||
deleteSingleRegisteredInternalTimer($identiy, $hash);
|
||||
deleteSingleRegIntTimer($identiy, $hash);
|
||||
$response = $hash->{helper}{lng}->{responses}->{ 'DefaultCancelConfirmation' };
|
||||
configure_DialogManager($hash, $data->{siteId}, $toDisable, 'false');
|
||||
|
||||
@ -3737,25 +3902,20 @@ sub handleIntentConfirmAction {
|
||||
Log3($hash->{NAME}, 5, 'handleIntentConfirmAction called');
|
||||
|
||||
#cancellation case
|
||||
#return RHASSPY_DialogTimeout($hash, 1, $data) if $data->{Mode} ne 'OK';
|
||||
return handleIntentCancelAction($hash, $data) if $data->{Mode} ne 'OK';
|
||||
|
||||
#confirmed case
|
||||
my $identiy = qq($data->{sessionId});
|
||||
my $data_saved = $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 $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)];
|
||||
configure_DialogManager($hash, $data->{siteId}, $toDisable, 'false');
|
||||
|
||||
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->{sessionId} = $data->{sessionId};
|
||||
@ -3779,11 +3939,10 @@ sub handleIntentChoiceRoom {
|
||||
|
||||
Log3($hash->{NAME}, 5, 'handleIntentChoiceRoom called');
|
||||
|
||||
#my $data_old = $data->{customData};
|
||||
my $identiy = qq($data->{sessionId});
|
||||
my $data_old = $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;
|
||||
|
||||
@ -3813,7 +3972,7 @@ sub handleIntentChoiceDevice {
|
||||
my $identiy = qq($data->{sessionId});
|
||||
my $data_old = $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;
|
||||
|
||||
@ -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;
|
||||
|
||||
__END__
|
||||
@ -4076,7 +4174,7 @@ __END__
|
||||
# "rhasspySpecials" bzw. rhasspyTweaks als weitere Attribute
|
||||
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)
|
||||
- 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
|
||||
- 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
|
||||
|
||||
# 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
|
||||
|
||||
@ -4375,37 +4475,14 @@ i="i am hungry" f="set Stove on" d="Stove" c="would you like roast pork"</code><
|
||||
|
||||
<p> </p>
|
||||
<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>There are two ways to tell RHASSPY which devices it should control:</p>
|
||||
<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>
|
||||
<p><b>For the subordinated devices</b>, a list of the possible attributes is automatically extended by several further entries</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>
|
||||
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>
|
||||
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>
|
||||
<li>
|
||||
<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>
|
||||
</li>
|
||||
<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><code>attr lamp1 rhasspySpecials colorCommandMap:0='rgb FF0000' 120='rgb 00FF00' 240='rgb 0000FF'</code></p>
|
||||
</li>
|
||||
@ -4500,6 +4577,12 @@ yellow=rgb FFFF00</code></p>
|
||||
<p>Example:</p>
|
||||
<p><code>attr sensor_outside_main rhasspySpecials priority:inRoom=temperature outsideRoom=temperature,humidity,pressure</code></p>
|
||||
</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>
|
||||
</li>
|
||||
</ul>
|
||||
@ -4512,6 +4595,8 @@ yellow=rgb FFFF00</code></p>
|
||||
<li>Shortcuts</li>
|
||||
<li>SetOnOff</li>
|
||||
<li>SetOnOffGroup</li>
|
||||
<li>SetTimedOnOff</li>
|
||||
<li>SetTimedOnOffGroup</li>
|
||||
<li>GetOnOff</li>
|
||||
<li>SetNumeric</li>
|
||||
<li>SetNumericGroup</li>
|
||||
|
@ -6,7 +6,7 @@
|
||||
# "commaconversion", "units" und "mutated_vowels" sind optional, wenn nicht
|
||||
# angegeben, werden die englischen Werte/Gepflogenheiten verwendet bzw. keine
|
||||
# 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":
|
||||
{
|
||||
"commaconversion": "1",
|
||||
@ -43,14 +43,16 @@
|
||||
"DefaultConfirmationNoOutstanding": "warte grade nicht auf eine bestätigung",
|
||||
"DefaultCancelConfirmation": "Habe abgebrochen",
|
||||
"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",
|
||||
"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",
|
||||
"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",
|
||||
"NoMappingFound": "Tut mir leid, aber ich konnte kein passendes Mäpping finden",
|
||||
"NoNewValDerived": "Tut mir leid, aber ich konnte den Zielwert nicht ausrechnen",
|
||||
"NoMappingFound": "Tut mir leid, ich konnte kein passendes Mäpping finden",
|
||||
"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",
|
||||
"NoMediaChannelFound": "Tut mir leid, der angefragte Kanal scheint nicht zu existieren.",
|
||||
"duration_not_understood": "Tut mir leid, ich habe die Dauer nicht verstanden",
|
||||
@ -68,7 +70,7 @@
|
||||
},
|
||||
"timerCancellation": "$label im $room gelöscht",
|
||||
"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",
|
||||
"Change": {
|
||||
"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":
|
||||
{
|
||||
"units": {
|
||||
"unitSeconds": {
|
||||
"0": "Test einige Sekunden",
|
||||
"1": "Test genau eine Sekunde"
|
||||
"0": "Beispiel 1a: einige Sekunden",
|
||||
"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":
|
||||
{
|
||||
"Colors": "
|
||||
|
Loading…
x
Reference in New Issue
Block a user