[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:
GiviMAD
2023-03-16 08:07:06 +01:00
committed by GitHub
parent 3e1c0b3259
commit f529a7b77f
3 changed files with 114 additions and 56 deletions
@@ -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.
@@ -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;
}
}
@@ -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() //