diff --git a/fhem/contrib/RHASSPY/10_RHASSPY.pm b/fhem/contrib/RHASSPY/10_RHASSPY.pm index 5ea1b8c81..73f6c0626 100644 --- a/fhem/contrib/RHASSPY/10_RHASSPY.pm +++ b/fhem/contrib/RHASSPY/10_RHASSPY.pm @@ -106,6 +106,7 @@ my $languagevars = { 'DefaultChangeIntentRequestRawInput' => 'change command to $rawInput', 'RequestChoiceDevice' => 'there are several possible devices, choose between $first_items and $last_item', 'RequestChoiceRoom' => 'more than one possible device, please choose one of the following rooms $first_items and $last_item', + 'RequestChoiceGeneric' => 'there are several options, choose between $options', 'DefaultChoiceNoOutstanding' => "no choice expected", 'NoMinConfidence' => 'minimum confidence not given, level is $confidence', 'timerSet' => { @@ -243,7 +244,7 @@ BEGIN { Log3 defs attr cmds modules L DAYSECONDS HOURSECONDS MINUTESECONDS - init_done + init_done fhem_started InternalTimer RemoveInternalTimer AssignIoPort @@ -334,7 +335,7 @@ sub Define { $hash->{defaultRoom} = $defaultRoom; my $language = $h->{language} // shift @{$anon} // lc AttrVal('global','language','en'); - $hash->{MODULE_VERSION} = '0.5.25'; + $hash->{MODULE_VERSION} = '0.5.26a'; $hash->{baseUrl} = $Rhasspy; initialize_Language($hash, $language) if !defined $hash->{LANGUAGE} || $hash->{LANGUAGE} ne $language; $hash->{LANGUAGE} = $language; @@ -875,7 +876,7 @@ sub initialize_rhasspyTweaks { sub configure_DialogManager { my $hash = shift // return; my $siteId = shift // 'null'; #ReadingsVal( $hash->{NAME}, 'siteIds', 'default' ) // return; - my $toDisable = shift // [qw(ConfirmAction CancelAction ChoiceRoom ChoiceDevice)]; + my $toDisable = shift // [qw(ConfirmAction CancelAction Choice ChoiceRoom ChoiceDevice)]; my $enable = shift // q{false}; my $timer = shift; my $retArr = shift; @@ -989,7 +990,7 @@ sub init_custom_intents { sub initialize_devicemap { my $hash = shift // return; - + Log3($hash->{NAME}, 5, "initialize_devicemap called"); my $devspec = $hash->{devspec}; delete $hash->{helper}{devicemap}; @@ -1002,6 +1003,7 @@ sub initialize_devicemap { _analyze_genDevType($hash, $_) if $hash->{useGenericAttrs}; _analyze_rhassypAttr($hash, $_); } + InternalTimer(time+125, \&initialize_devicemap, $hash ) if $fhem_started + 90 > time; return; } @@ -1326,11 +1328,16 @@ sub _analyze_genDevType { } 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; } + + if ( $gdt eq 'scene' ) { + $currentMapping = _analyze_genDevType_setter( $hash, $device, $allset, $currentMapping ); + $hash->{helper}{devicemap}{devices}{$device}{intents} = $currentMapping; + } + return; } @@ -1372,10 +1379,12 @@ sub _analyze_genDevType_setter { my $mapping = shift // {}; my $allValMappings = { - MediaControls => { + MediaControls => { cmdPlay => 'play', cmdPause => 'pause' ,cmdStop => 'stop', cmdBack => 'previous', cmdFwd => 'next', chanUp => 'channelUp', chanDown => 'channelDown' }, - GetState => { + GetState => { update => 'reread|update|reload' }, + SetScene => { + cmdBack => 'previousScene', cmdFwd => 'nextScene' } }; for my $okey ( keys %{$allValMappings} ) { my $ikey = $allValMappings->{$okey}; @@ -1420,7 +1429,6 @@ sub _analyze_genDevType_setter { my $clscene = $scname; # cleanup HUE scenes if ($clscene =~ m{[#]}xms) { - #next 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 if $scname =~ m{[#]\[id}xms; @@ -1914,7 +1922,7 @@ sub getAllRhasspyScenes { push @names, split m{,}x, $hash->{helper}{devicemap}{devices}{$device}->{names}; my $scenes = $hash->{helper}{devicemap}{devices}{$device}{intents}{SetScene}->{SetScene}; for (keys %{$scenes}) { - push @sentences, qq{( $scenes->{$_} ){Scene:$_}}; + push @sentences, qq{( $scenes->{$_} ){Scene:$_}} if $_ ne 'cmdBack' && $_ ne 'cmdFwd' ; } } @@ -2773,10 +2781,13 @@ my $dispatchFns = { GetTime => \&handleIntentGetTime, GetDate => \&handleIntentGetDate, SetTimer => \&handleIntentSetTimer, + GetTimer => \&handleIntentGetTimer, + Timer => \&handleIntentSetTimer, ConfirmAction => \&handleIntentConfirmAction, CancelAction => \&handleIntentCancelAction, ChoiceRoom => \&handleIntentChoiceRoom, ChoiceDevice => \&handleIntentChoiceDevice, + Choice => \&handleIntentChoice, MsgDialog => \&handleIntentMsgDialog, ReSpeak => \&handleIntentReSpeak }; @@ -3379,7 +3390,7 @@ sub respond { } elsif ( $delay ) { $sendData->{text} = $response; $topic = 'continueSession'; - my @ca_strings = configure_DialogManager($hash,$data->{siteId}, [qw(ConfirmAction ChoiceRoom ChoiceDevice)], 'false', undef, 1 ); + my @ca_strings = configure_DialogManager($hash,$data->{siteId}, [qw(ConfirmAction Choice ChoiceRoom ChoiceDevice)], 'false', undef, 1 ); $sendData->{intentFilter} = [@ca_strings]; } else { $sendData->{text} = $response; @@ -3463,7 +3474,7 @@ sub sendSpeakCommand { my $hash = shift; my $cmd = shift; - my $sendData = { + my $sendData = { init => { type => 'notification', canBeEnqueued => 'true', @@ -3472,13 +3483,13 @@ sub sendSpeakCommand { }; if (ref $cmd eq 'HASH') { return 'speak with explicite params needs siteId and text as arguments!' if !defined $cmd->{siteId} || !defined $cmd->{text}; - $sendData->{siteId} = $cmd->{siteId}; + $sendData->{siteId} = _getSiteIdbyRoom($hash, $cmd->{siteId}); $sendData->{init}->{text} = $cmd->{text}; } else { my($unnamedParams, $namedParams) = parseParams($cmd); if (defined $namedParams->{siteId} && defined $namedParams->{text}) { - $sendData->{siteId} = $namedParams->{siteId}; + $sendData->{siteId} = _getSiteIdbyRoom($hash, $namedParams->{siteId}); $sendData->{init}->{text} = $namedParams->{text}; } else { return 'speak needs siteId and text as arguments!'; @@ -3488,12 +3499,25 @@ sub sendSpeakCommand { return IOWrite($hash, 'publish', qq{hermes/dialogueManager/startSession $json}); } +sub _getSiteIdbyRoom { + my $hash = shift // return; + my $siteId = shift // return; + + my $siteIdList = ReadingsVal($hash->{NAME}, 'siteIds', $siteId); + my $siteId2 = ReadingsVal($hash->{NAME}, "room2siteId_$siteId", $siteId); + for my $id ($siteId2, $siteId) { + return $1 if $siteIdList =~ m{\b($id)(?:[,]|\Z)}xmsi; + return $1 if $siteIdList =~ m{\b($id[^,]+)(?:[,]|\Z)}xmsi; + } + return $siteId; +} + # start intent recognition by Rhasspy service, see https://rhasspy.readthedocs.io/en/latest/reference/#nlu_query sub msgDialog { my $hash = shift; my $cmd = shift; - readingsSingleUpdate($hash,"enableMsgDialog", $cmd eq 'enable' ? 1 : 0 ,1); + readingsSingleUpdate($hash,'enableMsgDialog', $cmd eq 'enable' ? 1 : 0 ,1); return initialize_msgDialog($hash) if $cmd eq 'enable'; return disable_msgDialog($hash); @@ -4416,7 +4440,7 @@ sub handleIntentSetNumeric { my $all = $device->[2]; my $choice = $device->[3]; $data->{customData} = $all; - my $toActivate = $choice eq 'RequestChoiceDevice' ? [qw(ChoiceDevice CancelAction)] : [qw(ChoiceRoom CancelAction)]; + my $toActivate = $choice eq 'RequestChoiceDevice' ? [qw(ChoiceDevice Choice CancelAction)] : [qw(ChoiceRoom Choice CancelAction)]; $device = $first; Log3($hash->{NAME}, 5, "More than one device possible, response is $response, first is $first, all are $all, type is $choice"); return setDialogTimeout($hash, $data, _getDialogueTimeout($hash), $response, $toActivate); @@ -4575,7 +4599,7 @@ sub handleIntentGetNumeric { my $all = $device->[2]; my $choice = $device->[3]; $data->{customData} = $all; - my $toActivate = $choice eq 'RequestChoiceDevice' ? [qw(ChoiceDevice CancelAction)] : [qw(ChoiceRoom CancelAction)]; + my $toActivate = $choice eq 'RequestChoiceDevice' ? [qw(ChoiceDevice Choice CancelAction)] : [qw(ChoiceRoom Choice CancelAction)]; $device = $first; Log3($hash->{NAME}, 5, "More than one device possible, response is $response, first is $first, all are $all, type is $choice"); return setDialogTimeout($hash, $data, _getDialogueTimeout($hash), $response, $toActivate); @@ -4655,6 +4679,7 @@ sub handleIntentGetState { my $room = getRoomName($hash, $data); my $type = $data->{Type} // $data->{type}; + my @scenes; my $deviceNames; my $sceneNames; if ($device eq 'RHASSPY') { $type //= 'generic'; return respond( $hash, $data, getResponse($hash, 'NoValidData')) if $type !~ m{\Ageneric|control|info|scenes|rooms\z}; @@ -4663,11 +4688,12 @@ sub handleIntentGetState { if ( $type eq 'rooms' ) { my @rooms = getAllRhasspyMainRooms($hash); $roomNames = _array2andString( $hash, \@rooms); + $response =~ s{(\$\w+)}{$1}eegx; + return respond( $hash, $data, $response); } - my @names; my @scenes; + my @names; my @intents = qw(SetNumeric SetOnOff GetNumeric GetOnOff MediaControls GetState SetScene); - @intents = [] if $type eq 'rooms'; @intents = qw(GetState GetNumeric) if $type eq 'info'; @intents = qw(SetScene) if $type eq 'scenes'; @@ -4690,8 +4716,9 @@ sub handleIntentGetState { @names = uniq(@names); @scenes = uniq(@scenes) if @scenes; - my $deviceNames = _array2andString( $hash, \@names ); - my $sceneNames = !@scenes ? '' : _array2andString( $hash, \@scenes ); + $deviceNames = _array2andString( $hash, \@names ); + $sceneNames = !@scenes ? '' : _array2andString( $hash, \@scenes ); + $response =~ s{(\$\w+)}{$1}eegx; return respond( $hash, $data, $response); } @@ -4699,7 +4726,18 @@ sub handleIntentGetState { my $deviceName = $device; my $intent = 'GetState'; - $device = getDeviceByName($hash, $room, $device); + $device = getDeviceByName($hash, $room, $device) // return respond( $hash, $data, getResponse($hash, 'NoDeviceFound') ); + + if ( $type eq 'scenes' ) { + $response = getResponse( $hash, 'getRHASSPYOptions', $type ); + @scenes = values %{$hash->{helper}{devicemap}{devices}{$device}{intents}{SetScene}->{SetScene}}; + @scenes = uniq(@scenes) if @scenes; + $sceneNames = !@scenes ? '' : _array2andString( $hash, \@scenes ); + $deviceNames = $deviceName; + $response =~ s{(\$\w+)}{$1}eegx; + return respond( $hash, $data, $response); + } + $type //= 'GetState'; my $mapping = getMapping($hash, $device, 'GetState', $type) // return respond( $hash, $data, getResponse($hash, 'NoMappingFound') ); @@ -4712,7 +4750,7 @@ sub handleIntentGetState { } 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} || $data->{Type} ) { + } elsif ( defined $data->{type} || defined $data->{Type} ) { my $reading = $data->{Reading} // 'STATE'; $response = getResponse( $hash, 'getStateResponses', $type ) // getResponse( $hash, 'NoValidIntentResponse') ; $response =~ s{(\$\w+)}{$1}eegx; @@ -4777,7 +4815,7 @@ sub handleIntentSetScene{ my $data = shift // return; Log3($hash->{NAME}, 5, "handleIntentSetScene called"); - return respond( $hash, $data, getResponse( $hash, 'NoValidData' ) ) if !defined $data->{Scene}; + return respond( $hash, $data, getResponse( $hash, 'NoValidData' ) ) if !defined $data->{Scene} && (!defined $data->{Get} || $data->{Get} ne 'scenes'); # Device AND Scene are optimum exist @@ -4787,6 +4825,22 @@ sub handleIntentSetScene{ my $scene = $data->{Scene}; my $device = getDeviceByName($hash, $room, $data->{Device}); my $mapping = getMapping($hash, $device, 'SetScene'); + + #Welche (Szenen | Szenarien | Einstellungen){Get:scenes} (kennt|kann) [(der | die | das)] $de.fhem.Device-scene{Device} + if ( defined $data->{Get} && $data->{Get} eq 'scenes' ) { + delete $data->{Get}; + my $response = getResponse( $hash, 'RequestChoiceGeneric' ); + my @scenes = values %{$hash->{helper}{devicemap}{devices}{$device}{intents}{SetScene}->{SetScene}}; + @scenes = uniq(@scenes) if @scenes; + my $options = !@scenes ? '' : _array2andString( $hash, \@scenes ); + $response =~ s{(\$\w+)}{$1}eegx; + + #until now: only extended test code + $data->{customData} = join q{,}, @scenes; + my $toActivate = [qw(Choice CancelAction)]; + return setDialogTimeout($hash, $data, _getDialogueTimeout($hash), $response, $toActivate); + } + # restore HUE scenes $scene = qq([$scene]) if $scene =~ m{id=.+}xms; @@ -4797,6 +4851,7 @@ sub handleIntentSetScene{ return $hash->{NAME} if !$data->{Confirmation} && getNeedsConfirmation( $hash, $data, 'SetScene' ); my $cmd = qq(scene $scene); + $cmd = $scene if $scene eq 'cmdBack' || $scene eq 'cmdFwd'; # execute Cmd analyzeAndRunCmd($hash, $device, $cmd); @@ -5123,8 +5178,8 @@ sub handleIntentSetColorGroup { -# Handle incoming SetTimer intents -sub handleIntentSetTimer { +# Handle incoming Timer, SetTimer and GetTimer intents +sub handleIntentTimer { my $hash = shift; my $data = shift // return; my $siteId = $data->{siteId} // return; @@ -5256,6 +5311,23 @@ sub handleIntentSetTimer { return $name; } +sub handleIntentGetTimer { + my $hash = shift; + my $data = shift // return; + my $siteId = $data->{siteId} // return; + $data->{GetTimer} = 'redirected from intent GetTimer'; + return handleIntentTimer($hash, $data); +} + +sub handleIntentSetTimer { + my $hash = shift; + my $data = shift // return; + my $siteId = $data->{siteId} // return; + $data->{'.remark'} = 'redirected from intent SetTimer'; + return handleIntentTimer($hash, $data); +} + + sub handleIntentNotRecognized { my $hash = shift // return; @@ -5381,11 +5453,11 @@ sub handleIntentConfirmAction { return $device; } -sub handleIntentChoiceRoom { +sub handleIntentChoice { my $hash = shift // return; my $data = shift // return; - Log3($hash->{NAME}, 5, 'handleIntentChoiceRoom called'); + Log3($hash->{NAME}, 5, 'handleIntentChoice called'); my $identity = qq($data->{sessionId}); my $data_old = $hash->{helper}{'.delayed'}->{$identity}; @@ -5394,10 +5466,9 @@ sub handleIntentChoiceRoom { return respond( $hash, $data, getResponse( $hash, 'DefaultChoiceNoOutstanding' ) ) if !defined $data_old; - $data_old->{siteId} = $data->{siteId}; - $data_old->{sessionId} = $data->{sessionId}; - $data_old->{requestType} = $data->{requestType}; - $data_old->{Room} = $data->{Room}; + for ( qw( siteId sessionId requestType Room Device Scene ) ) { + $data_old->{$_} = $data->{$_} if defined $data->{$_}; + } my $intent = $data_old->{intent}; my $device = $hash->{NAME}; @@ -5410,34 +5481,23 @@ sub handleIntentChoiceRoom { return $device; } + +sub handleIntentChoiceRoom { + my $hash = shift // return; + my $data = shift // return; + + Log3($hash->{NAME}, 5, 'handleIntentChoiceRoom called'); + + return handleIntentChoice($hash, $data); +} + sub handleIntentChoiceDevice { my $hash = shift // return; my $data = shift // return; Log3($hash->{NAME}, 5, 'handleIntentChoiceDevice called'); - #my $data_old = $data->{customData}; - my $identity = qq($data->{sessionId}); - my $data_old = $hash->{helper}{'.delayed'}->{$identity}; - delete $hash->{helper}{'.delayed'}{$identity}; - deleteSingleRegIntTimer($identity, $hash); - - return respond( $hash, $data, getResponse( $hash, 'DefaultChoiceNoOutstanding' ) ) if ! defined $data_old; - - $data_old->{siteId} = $data->{siteId}; - $data_old->{sessionId} = $data->{sessionId}; - $data_old->{requestType} = $data->{requestType}; - $data_old->{Device} = $data->{Device}; - - my $intent = $data_old->{intent}; - my $device = $hash->{NAME}; - - # Passenden Intent-Handler aufrufen - if (ref $dispatchFns->{$intent} eq 'CODE') { - $device = $dispatchFns->{$intent}->($hash, $data_old); - } - - return $device; + return handleIntentChoice($hash, $data); } @@ -5463,7 +5523,7 @@ sub setPlayWav { return 'playWav needs siteId and path to file as parameters!' if !defined $cmd->{siteId} || !defined $cmd->{path}; - my $siteId = $cmd->{siteId}; + my $siteId = _getSiteIdbyRoom($hash, $cmd->{siteId}); my $filename = $cmd->{path}; my $repeats = $cmd->{repeats}; my $encoding = q{:raw :bytes}; @@ -5708,7 +5768,12 @@ So all parameters in define should be provided in the key=value form. In
  • prefix: May be used to distinguishe between different instances of RHASSPY on the FHEM-internal side.
    Might be usefull, if you have several instances of RHASSPY in one FHEM running and want e.g. to use different identifier for groups and rooms (e.g. a different language). Not recommended to be set if just one RHASSPY device is defined.
  • -
  • useGenericAttrs: Formerly, RHASSPY only used it's own attributes (see list below) to identifiy options for the subordinated devices you want to control. Today, it is capable to deal with a couple of commonly used genericDeviceType (switch, light, thermostat, thermometer, blind and media), so it will add genericDeviceType to the global attribute list and activate RHASSPY's feature to estimate appropriate settings - similar to rhasspyMapping. useGenericAttrs=0 will deactivate this. (do not set this unless you know what you are doing!). Note: homebridgeMapping atm. is not used as source for appropriate mappings in RHASSPY.
  • +
  • useGenericAttrs: Formerly, RHASSPY only used it's own attributes (see list below) to identifiy options for the subordinated devices you want to control. Today, it is capable to deal with a couple of commonly used genericDeviceType (switch, light, thermostat, thermometer, blind, media, scene and info), so it will add genericDeviceType to the global attribute list and activate RHASSPY's feature to estimate appropriate settings - similar to rhasspyMapping. useGenericAttrs=0 will deactivate this. (do not set this unless you know what you are doing!). Notes: + +
  • handleHotword: Trigger Reading hotword in case of a hotword is detected. See attribute rhasspyHotwords for further reference.
  • Babble: experimental! Points to a Babble device. Atm. only used in case if text input from an AMADCommBridge is processed, see rhasspySTT for details.
  • encoding: most likely deprecated! May be helpfull in case you experience problems in conversion between RHASSPY (module) and Rhasspy (service). Example: encoding=cp-1252. Do not set this unless you experience encoding problems!
  • @@ -6284,14 +6349,17 @@ yellow=rgb FFFF00

  • SetColor
  • {Device} and one Color option are mandatory, {Room} is optional. Color options are {Hue} (0-360), {Colortemp} (0-100), {Saturation} (as understood by your device) or {Rgb} (hex value from 000000 to FFFFFF)
  • SetColorGroup
  • (as SetColor, except for {Group} instead of {Device}). -
  • SetScene
  • {Device} and {Scene} (it's recommended to use the $lng.fhemId.Scenes slot to get that generated automatically!). +
  • SetScene
  • {Device} and {Scene} (it's recommended to use the $lng.fhemId.Scenes slot to get that generated automatically!), {Room} is optional, {Get} with value scenes may be used to request all possible scenes for a device prior to make a choice.
  • GetTime
  • GetDate
  • -
  • SetTimer
  • Timer info as described in SetTimedOnOff is mandatory, {Room} and/or {Label} are optional to distinguish between different timers. {CancelTimer} key will force RHASSPY to try to remove a running timer (using optional {Room} and/or {Label} key to identify the respective timer). +
  • Timer
  • Timer info as described in SetTimedOnOff is mandatory, {Room} and/or {Label} are optional to distinguish between different timers. {CancelTimer} key will force RHASSPY to try to remove a running timer (using optional {Room} and/or {Label} key to identify the respective timer), {GetTimer} key will be treated as request if there's a timer running (optionally also identified by {Room} and/or {Label} keys). Required tags to set a timer: at least one of {Hour}, {Hourabs}, {Min} or {Sec}. {Label} and {Room} are optional to distinguish between different timers. If {Hourabs} is provided, all timer info will be regarded as absolute time of day info, otherwise everything is calculated using a "from now" logic. +
  • SetTimer
  • Set a timer, required info as mentionned in Timer +
  • GetTimer
  • Get timer info as mentionned in Timer, key {GetTimer} is not explicitely required.
  • ConfirmAction
  • {Mode} with value 'OK'. All other calls will be interpreted as CancelAction intent call.
  • CancelAction
  • {Mode} is recommended. +
  • Choice
  • One or more of {Room}, {Device} or {Scene}
  • ChoiceRoom
  • {Room}
  • ChoiceDevice
  • {Device}
  • ReSpeak
  • @@ -6304,6 +6372,7 @@ yellow=rgb FFFF00

  • siteId2room_<siteId>
  • Typically, RHASSPY derives room info from the name of the siteId. So naming a satellite bedroom will let RHASSPY assign this satellite to the same room, using the group sheme is also supported, e.g. kitchen.front will refer to kitchen as room (if not explicitly given).
    You may overwrite that behaviour by setting values to siteId2room readings: setreading siteId2room_mobile_phone1 kitchen will force RHASSPY to link your satellite phone1 kitchen to kitchen as room. +
  • room2siteId_<room>
  • Used to identify the satellite to speak messages addressed to a room (same for playing sound files). Should deliver exactly one possible siteId, e.g. <lingingroom.04>
  • siteId2doubleSpeak_<siteId>
  • 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.
  • sessionTimeout_<siteId>
  • diff --git a/fhem/contrib/RHASSPY/rhasspy-de.cfg b/fhem/contrib/RHASSPY/rhasspy-de.cfg index 369e7b453..c41fa9428 100644 --- a/fhem/contrib/RHASSPY/rhasspy-de.cfg +++ b/fhem/contrib/RHASSPY/rhasspy-de.cfg @@ -43,7 +43,7 @@ "DefaultConfirmationTimeout": "Tut mir leid, da hat etwas zu lange gedauert", "NoValidResponse": "Fehler. Die respond Funktion wurde ohne Antworttext aufgerufen", "NoValidIntentResponse": "Fehler. Die respond Funktion wurde von $intent ohne Antworttext aufgerufen", - "NoIntentRecognized": "Ich konnte leider keinen passenden Intent finden", + "NoIntentRecognized": "Ich konnte leider keinen passenden Intent finden", "DefaultConfirmationNoOutstanding": "'Warte grade nicht auf eine Bestätigung", "DefaultCancelConfirmation": "Habe abgebrochen", "DefaultConfirmationBack": "Also nochmal", @@ -54,6 +54,7 @@ "DefaultChangeIntentRequestRawInput": "Wechseln zu $rawInput", "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 zwischen $first_items oder $last_item", + "RequestChoiceGeneric": "Es gibt diese Möglichkeiten, unter denen du wählen kannst: $options", "DefaultError": "Da ist leider etwas schief gegangen", "NoValidData": "Ich habe leider zu wenig Daten um den Vorgang auszuführen", "NoDeviceFound": "Tut mir leid, ich konnte kein passendes Gerät finden",