mirror of
https://github.com/danieldemus/openhab-core.git
synced 2026-07-29 12:34:22 +02:00
[CLI/voice] use parameters for cli voice commands startDialog and listenAndAnswer (#3253)
Signed-off-by: Miguel Álvarez <miguelwork92@gmail.com>
This commit is contained in:
+5
-5
@@ -334,7 +334,7 @@ public interface VoiceManager {
|
||||
* @return a TTS service or null, if no service with this id exists
|
||||
*/
|
||||
@Nullable
|
||||
TTSService getTTS(String id);
|
||||
TTSService getTTS(@Nullable String id);
|
||||
|
||||
/**
|
||||
* Retrieves all TTS services.
|
||||
@@ -361,7 +361,7 @@ public interface VoiceManager {
|
||||
* @return a STT service or null, if no service with this id exists
|
||||
*/
|
||||
@Nullable
|
||||
STTService getSTT(String id);
|
||||
STTService getSTT(@Nullable String id);
|
||||
|
||||
/**
|
||||
* Retrieves all STT services.
|
||||
@@ -388,7 +388,7 @@ public interface VoiceManager {
|
||||
* @return a KS service or null, if no service with this id exists
|
||||
*/
|
||||
@Nullable
|
||||
KSService getKS(String id);
|
||||
KSService getKS(@Nullable String id);
|
||||
|
||||
/**
|
||||
* Retrieves all KS services.
|
||||
@@ -404,7 +404,7 @@ public interface VoiceManager {
|
||||
* @param ids Comma separated list of HLI service ids to use
|
||||
* @return a List<HumanLanguageInterpreter> or empty, if none of the services is available
|
||||
*/
|
||||
List<HumanLanguageInterpreter> getHLIsByIds(String ids);
|
||||
List<HumanLanguageInterpreter> getHLIsByIds(@Nullable String ids);
|
||||
|
||||
/**
|
||||
* Retrieves a HumanLanguageInterpreter collection.
|
||||
@@ -433,7 +433,7 @@ public interface VoiceManager {
|
||||
* @return a HumanLanguageInterpreter or null, if no interpreter with this id exists
|
||||
*/
|
||||
@Nullable
|
||||
HumanLanguageInterpreter getHLI(String id);
|
||||
HumanLanguageInterpreter getHLI(@Nullable String id);
|
||||
|
||||
/**
|
||||
* Retrieves all HumanLanguageInterpreters.
|
||||
|
||||
+106
-48
@@ -16,6 +16,7 @@ import static java.util.Comparator.comparing;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
@@ -23,8 +24,6 @@ import java.util.Objects;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.audio.AudioManager;
|
||||
import org.openhab.core.audio.AudioSink;
|
||||
import org.openhab.core.audio.AudioSource;
|
||||
import org.openhab.core.i18n.LocaleProvider;
|
||||
import org.openhab.core.io.console.Console;
|
||||
import org.openhab.core.io.console.extensions.AbstractConsoleCommandExtension;
|
||||
@@ -33,6 +32,7 @@ import org.openhab.core.items.Item;
|
||||
import org.openhab.core.items.ItemNotFoundException;
|
||||
import org.openhab.core.items.ItemNotUniqueException;
|
||||
import org.openhab.core.items.ItemRegistry;
|
||||
import org.openhab.core.voice.DialogContext;
|
||||
import org.openhab.core.voice.KSService;
|
||||
import org.openhab.core.voice.STTService;
|
||||
import org.openhab.core.voice.TTSService;
|
||||
@@ -87,14 +87,13 @@ public class VoiceConsoleCommandExtension extends AbstractConsoleCommandExtensio
|
||||
return List.of(buildCommandUsage(SUBCMD_SAY + " <text>", "speaks a text"),
|
||||
buildCommandUsage(SUBCMD_INTERPRET + " <command>", "interprets a human language command"),
|
||||
buildCommandUsage(SUBCMD_VOICES, "lists available voices of the TTS services"),
|
||||
buildCommandUsage(
|
||||
SUBCMD_START_DIALOG
|
||||
+ " [<source> [<sink> [<interpreters> [<tts> [<stt> [<ks> {<voice> [<keyword>]]]]]]]]",
|
||||
buildCommandUsage(SUBCMD_START_DIALOG
|
||||
+ " [--source <source>] [--sink <sink>] [--interpreters <comma,separated,interpreters>] [--tts <tts> [--voice <voice>]] [--stt <stt>] [--ks ks [--keyword <ks>]] [--listening-item <listeningItem>]",
|
||||
"start a new dialog processing using the default services or the services identified with provided arguments"),
|
||||
buildCommandUsage(SUBCMD_STOP_DIALOG + " [<source>]",
|
||||
"stop the dialog processing for the default audio source or the audio source identified with provided argument"),
|
||||
buildCommandUsage(
|
||||
SUBCMD_LISTEN_ANSWER + " [<source> [<sink> [<interpreters> [<tts> [<stt> [<voice]]]]]]",
|
||||
buildCommandUsage(SUBCMD_LISTEN_ANSWER
|
||||
+ " [--source <source>] [--sink <sink>] [--interpreters <comma,separated,interpreters>] [--tts <tts> [--voice <voice>]] [--stt <stt>] [--listening-item <listeningItem>]",
|
||||
"Execute a simple dialog sequence without keyword spotting using the default services or the services identified with provided arguments"),
|
||||
buildCommandUsage(SUBCMD_INTERPRETERS, "lists the interpreters"),
|
||||
buildCommandUsage(SUBCMD_KEYWORD_SPOTTERS, "lists the keyword spotters"),
|
||||
@@ -107,21 +106,23 @@ public class VoiceConsoleCommandExtension extends AbstractConsoleCommandExtensio
|
||||
if (args.length > 0) {
|
||||
String subCommand = args[0];
|
||||
switch (subCommand) {
|
||||
case SUBCMD_SAY:
|
||||
case SUBCMD_SAY -> {
|
||||
if (args.length > 1) {
|
||||
say(Arrays.copyOfRange(args, 1, args.length), console);
|
||||
} else {
|
||||
console.println("Specify text to say (e.g. 'say hello')");
|
||||
}
|
||||
return;
|
||||
case SUBCMD_INTERPRET:
|
||||
}
|
||||
case SUBCMD_INTERPRET -> {
|
||||
if (args.length > 1) {
|
||||
interpret(Arrays.copyOfRange(args, 1, args.length), console);
|
||||
} else {
|
||||
console.println("Specify text to interpret (e.g. 'interpret turn all lights off')");
|
||||
}
|
||||
return;
|
||||
case SUBCMD_VOICES:
|
||||
}
|
||||
case SUBCMD_VOICES -> {
|
||||
Locale locale = localeProvider.getLocale();
|
||||
Voice defaultVoice = getDefaultVoice();
|
||||
for (Voice voice : voiceManager.getAllVoices()) {
|
||||
@@ -133,64 +134,71 @@ public class VoiceConsoleCommandExtension extends AbstractConsoleCommandExtensio
|
||||
}
|
||||
}
|
||||
return;
|
||||
case SUBCMD_START_DIALOG:
|
||||
}
|
||||
case SUBCMD_START_DIALOG -> {
|
||||
DialogContext.Builder dialogContextBuilder;
|
||||
try {
|
||||
AudioSource source = args.length < 2 ? null : audioManager.getSource(args[1]);
|
||||
AudioSink sink = args.length < 3 ? null : audioManager.getSink(args[2]);
|
||||
List<HumanLanguageInterpreter> hlis = args.length < 4 ? List.of()
|
||||
: voiceManager.getHLIsByIds(args[3]);
|
||||
TTSService tts = args.length < 5 ? null : voiceManager.getTTS(args[4]);
|
||||
STTService stt = args.length < 6 ? null : voiceManager.getSTT(args[5]);
|
||||
KSService ks = args.length < 7 ? null : voiceManager.getKS(args[6]);
|
||||
Voice voice = args.length < 8 ? null : getVoice(args[7]);
|
||||
String keyword = args.length < 9 ? null : args[8];
|
||||
voiceManager.startDialog(ks, stt, tts, voice, hlis, source, sink, null, keyword, null);
|
||||
dialogContextBuilder = parseDialogParameters(args);
|
||||
} catch (IllegalStateException e) {
|
||||
console.println(Objects.requireNonNullElse(e.getMessage(),
|
||||
"An error occurred while parsing the dialog options"));
|
||||
break;
|
||||
}
|
||||
try {
|
||||
voiceManager.startDialog(dialogContextBuilder.build());
|
||||
} catch (IllegalStateException e) {
|
||||
console.println(Objects.requireNonNullElse(e.getMessage(),
|
||||
"An error occurred while starting the dialog"));
|
||||
}
|
||||
break;
|
||||
case SUBCMD_STOP_DIALOG:
|
||||
return;
|
||||
}
|
||||
case SUBCMD_STOP_DIALOG -> {
|
||||
try {
|
||||
voiceManager.stopDialog(args.length < 2 ? null : audioManager.getSource(args[1]));
|
||||
} catch (IllegalStateException e) {
|
||||
console.println(Objects.requireNonNullElse(e.getMessage(),
|
||||
"An error occurred while stopping the dialog"));
|
||||
}
|
||||
break;
|
||||
case SUBCMD_LISTEN_ANSWER:
|
||||
return;
|
||||
}
|
||||
case SUBCMD_LISTEN_ANSWER -> {
|
||||
DialogContext.Builder dialogContextBuilder;
|
||||
try {
|
||||
AudioSource source = args.length < 2 ? null : audioManager.getSource(args[1]);
|
||||
AudioSink sink = args.length < 3 ? null : audioManager.getSink(args[2]);
|
||||
List<HumanLanguageInterpreter> hlis = args.length < 4 ? List.of()
|
||||
: voiceManager.getHLIsByIds(args[3]);
|
||||
TTSService tts = args.length < 5 ? null : voiceManager.getTTS(args[4]);
|
||||
STTService stt = args.length < 6 ? null : voiceManager.getSTT(args[5]);
|
||||
Voice voice = args.length < 7 ? null : getVoice(args[6]);
|
||||
voiceManager.listenAndAnswer(stt, tts, voice, hlis, source, sink, null, null);
|
||||
dialogContextBuilder = parseDialogParameters(args);
|
||||
} catch (IllegalStateException e) {
|
||||
console.println(Objects.requireNonNullElse(e.getMessage(),
|
||||
"An error occurred while parsing the dialog options"));
|
||||
break;
|
||||
}
|
||||
try {
|
||||
voiceManager.listenAndAnswer(dialogContextBuilder.build());
|
||||
} catch (IllegalStateException e) {
|
||||
console.println(Objects.requireNonNullElse(e.getMessage(),
|
||||
"An error occurred while executing the simple dialog sequence"));
|
||||
}
|
||||
break;
|
||||
case SUBCMD_INTERPRETERS:
|
||||
return;
|
||||
}
|
||||
case SUBCMD_INTERPRETERS -> {
|
||||
listInterpreters(console);
|
||||
return;
|
||||
case SUBCMD_KEYWORD_SPOTTERS:
|
||||
}
|
||||
case SUBCMD_KEYWORD_SPOTTERS -> {
|
||||
listKeywordSpotters(console);
|
||||
return;
|
||||
case SUBCMD_STT_SERVICES:
|
||||
}
|
||||
case SUBCMD_STT_SERVICES -> {
|
||||
listSTTs(console);
|
||||
return;
|
||||
case SUBCMD_TTS_SERVICES:
|
||||
}
|
||||
case SUBCMD_TTS_SERVICES -> {
|
||||
listTTSs(console);
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
default -> {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
printUsage(console);
|
||||
}
|
||||
printUsage(console);
|
||||
}
|
||||
|
||||
private @Nullable Voice getDefaultVoice() {
|
||||
@@ -213,9 +221,7 @@ public class VoiceConsoleCommandExtension extends AbstractConsoleCommandExtensio
|
||||
String msg = sb.toString();
|
||||
try {
|
||||
String result = voiceManager.interpret(msg);
|
||||
if (result != null) {
|
||||
console.println(result);
|
||||
}
|
||||
console.println(result);
|
||||
} catch (InterpretationException ie) {
|
||||
console.println(Objects.requireNonNullElse(ie.getMessage(),
|
||||
String.format("An error occurred while interpreting '%s'", msg)));
|
||||
@@ -229,7 +235,7 @@ public class VoiceConsoleCommandExtension extends AbstractConsoleCommandExtensio
|
||||
String itemName = word.substring(1, word.length() - 1);
|
||||
try {
|
||||
Item item = this.itemRegistry.getItemByPattern(itemName);
|
||||
msg.append(item.getState().toString());
|
||||
msg.append(item.getState());
|
||||
} catch (ItemNotFoundException e) {
|
||||
console.println("Error: Item '" + itemName + "' does not exist.");
|
||||
} catch (ItemNotUniqueException e) {
|
||||
@@ -302,7 +308,59 @@ public class VoiceConsoleCommandExtension extends AbstractConsoleCommandExtensio
|
||||
}
|
||||
}
|
||||
|
||||
private @Nullable Voice getVoice(String id) {
|
||||
return voiceManager.getAllVoices().stream().filter(voice -> voice.getUID().equals(id)).findAny().orElse(null);
|
||||
private @Nullable Voice getVoice(@Nullable String id) {
|
||||
return id == null ? null
|
||||
: voiceManager.getAllVoices().stream().filter(voice -> voice.getUID().equals(id)).findAny()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
private DialogContext.Builder parseDialogParameters(String[] args) {
|
||||
var dialogContextBuilder = voiceManager.getDialogContextBuilder();
|
||||
if (args.length < 2) {
|
||||
return dialogContextBuilder;
|
||||
}
|
||||
var parameters = new HashMap<String, String>();
|
||||
for (int i = 1; i < args.length; i++) {
|
||||
var arg = args[i].trim();
|
||||
if (arg.startsWith("--")) {
|
||||
i++;
|
||||
if (i < args.length) {
|
||||
parameters.put(arg.replace("--", ""), args[i].trim());
|
||||
} else {
|
||||
throw new IllegalStateException("Missing value for argument " + arg);
|
||||
}
|
||||
} else {
|
||||
throw new IllegalStateException("Argument name should start by -- " + arg);
|
||||
}
|
||||
}
|
||||
String sourceId = parameters.remove("source");
|
||||
if (sourceId != null) {
|
||||
var source = audioManager.getSource(sourceId);
|
||||
if (source == null) {
|
||||
throw new IllegalStateException("Audio source not found");
|
||||
}
|
||||
dialogContextBuilder.withSource(source);
|
||||
}
|
||||
String sinkId = parameters.remove("sink");
|
||||
if (sinkId != null) {
|
||||
var sink = audioManager.getSink(sinkId);
|
||||
if (sink == null) {
|
||||
throw new IllegalStateException("Audio sink not found");
|
||||
}
|
||||
dialogContextBuilder.withSink(sink);
|
||||
}
|
||||
dialogContextBuilder //
|
||||
.withSTT(voiceManager.getSTT(parameters.remove("stt"))) //
|
||||
.withTTS(voiceManager.getTTS(parameters.remove("tts"))) //
|
||||
.withVoice(getVoice(parameters.remove("voice"))) //
|
||||
.withHLIs(voiceManager.getHLIsByIds(parameters.remove("hlis"))) //
|
||||
.withKS(voiceManager.getKS(parameters.remove("ks"))) //
|
||||
.withListeningItem(parameters.remove("listening-item")) //
|
||||
.withKeyword(parameters.remove("keyword"));
|
||||
if (!parameters.isEmpty()) {
|
||||
throw new IllegalStateException(
|
||||
"Argument" + parameters.keySet().stream().findAny().orElse("") + "is not supported");
|
||||
}
|
||||
return dialogContextBuilder;
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -941,8 +941,8 @@ public class VoiceManagerImpl implements VoiceManager, ConfigOptionProvider, Dia
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HumanLanguageInterpreter> getHLIsByIds(String ids) {
|
||||
return getHLIsByIds(Arrays.asList(ids.split(",")));
|
||||
public List<HumanLanguageInterpreter> getHLIsByIds(@Nullable String ids) {
|
||||
return ids == null ? List.of() : getHLIsByIds(Arrays.asList(ids.split(",")));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1074,7 +1074,7 @@ public class VoiceManagerImpl implements VoiceManager, ConfigOptionProvider, Dia
|
||||
*/
|
||||
private void buildDialogRegistrations() {
|
||||
synchronized (dialogRegistrationStorage) {
|
||||
dialogRegistrationStorage.getValues().stream().forEach(dr -> {
|
||||
dialogRegistrationStorage.getValues().forEach(dr -> {
|
||||
if (dr != null && !dialogProcessors.containsKey(dr.sourceId)) {
|
||||
try {
|
||||
startDialog(getDialogContextBuilder() //
|
||||
|
||||
Reference in New Issue
Block a user