[amazonechocontrol] fix unnecessary refresh and add text commands (#9328)
* fixed: removed unnecessary refresh on push activity Signed-off-by: Jan N. Klug <jan.n.klug@rub.de> Also-By: Tom Blum <trinitus01@googlemail.com> Co-authored-by: Tom Blum <trinitus01@googlemail.com>pull/9335/head
parent
578807f452
commit
5d49188e0b
|
@ -5,6 +5,7 @@ This binding can control Amazon Echo devices (Alexa).
|
|||
It provides features to control and view the current state of echo devices:
|
||||
|
||||
- use echo device as text to speech from a rule
|
||||
- execute a text command
|
||||
- volume
|
||||
- pause/continue/next track/previous track
|
||||
- connect/disconnect bluetooth devices
|
||||
|
@ -179,6 +180,7 @@ It will be configured at runtime by using the save channel to store the current
|
|||
| announcement | String | W | echo, echoshow, echospot | Write Only! Display the announcement message on the display. See in the tutorial section to learn how it’s possible to set the title and turn off the sound.
|
||||
| textToSpeech | String | W | echo, echoshow, echospot | Write Only! Write some text to this channel and Alexa will speak it. It is possible to use plain text or SSML: e.g. `<speak>I want to tell you a secret.<amazon:effect name="whispered">I am not a real human.</amazon:effect></speak>`
|
||||
| textToSpeechVolume | Dimmer | R/W | echo, echoshow, echospot | Volume of the textToSpeech channel, if 0 the current volume will be used
|
||||
| textCommand | String | W | echo, echoshow, echospot | Write Only! Execute a text command (like a spoken text)
|
||||
| lastVoiceCommand | String | R/W | echo, echoshow, echospot | Last voice command spoken to the device. Writing to the channel starts voice output.
|
||||
| mediaProgress | Dimmer | R/W | echo, echoshow, echospot | Media progress in percent
|
||||
| mediaProgressTime | Number:Time | R/W | echo, echoshow, echospot | Media play time
|
||||
|
@ -199,7 +201,6 @@ E.g. to read out the history call from an installation on openhab:8080 with an a
|
|||
|
||||
http://openhab:8080/amazonechocontrol/account1/PROXY/api/activities?startTime=&size=50&offset=1
|
||||
|
||||
|
||||
### Example
|
||||
|
||||
#### echo.things
|
||||
|
|
|
@ -76,6 +76,7 @@ public class AmazonEchoControlBindingConstants {
|
|||
public static final String CHANNEL_AMAZON_MUSIC_PLAY_LIST_ID = "amazonMusicPlayListId";
|
||||
public static final String CHANNEL_TEXT_TO_SPEECH = "textToSpeech";
|
||||
public static final String CHANNEL_TEXT_TO_SPEECH_VOLUME = "textToSpeechVolume";
|
||||
public static final String CHANNEL_TEXT_COMMAND = "textCommand";
|
||||
public static final String CHANNEL_REMIND = "remind";
|
||||
public static final String CHANNEL_PLAY_ALARM_SOUND = "playAlarmSound";
|
||||
public static final String CHANNEL_START_ROUTINE = "startRoutine";
|
||||
|
|
|
@ -162,6 +162,8 @@ public class Connection {
|
|||
|
||||
private Map<Integer, AnnouncementWrapper> announcements = Collections.synchronizedMap(new LinkedHashMap<>());
|
||||
private Map<Integer, TextToSpeech> textToSpeeches = Collections.synchronizedMap(new LinkedHashMap<>());
|
||||
private Map<Integer, TextCommand> textCommands = Collections.synchronizedMap(new LinkedHashMap<>());
|
||||
|
||||
private Map<Integer, Volume> volumes = Collections.synchronizedMap(new LinkedHashMap<>());
|
||||
private Map<String, LinkedBlockingQueue<QueueObject>> devices = Collections.synchronizedMap(new LinkedHashMap<>());
|
||||
|
||||
|
@ -172,7 +174,8 @@ public class Connection {
|
|||
ANNOUNCEMENT,
|
||||
TTS,
|
||||
VOLUME,
|
||||
DEVICES
|
||||
DEVICES,
|
||||
TEXT_COMMAND
|
||||
}
|
||||
|
||||
public Connection(@Nullable Connection oldConnection, Gson gson) {
|
||||
|
@ -962,6 +965,8 @@ public class Connection {
|
|||
replaceTimer(TimerType.VOLUME, null);
|
||||
volumes.clear();
|
||||
replaceTimer(TimerType.DEVICES, null);
|
||||
textCommands.clear();
|
||||
replaceTimer(TimerType.TTS, null);
|
||||
|
||||
devices.values().forEach((queueObjects) -> {
|
||||
queueObjects.forEach((queueObject) -> {
|
||||
|
@ -1354,7 +1359,7 @@ public class Connection {
|
|||
|
||||
private void sendAnnouncement() {
|
||||
// we lock new announcements until we have dispatched everything
|
||||
Lock lock = locks.computeIfAbsent(TimerType.ANNOUNCEMENT, k -> new ReentrantLock());
|
||||
Lock lock = Objects.requireNonNull(locks.computeIfAbsent(TimerType.ANNOUNCEMENT, k -> new ReentrantLock()));
|
||||
lock.lock();
|
||||
try {
|
||||
Iterator<AnnouncementWrapper> iterator = announcements.values().iterator();
|
||||
|
@ -1396,7 +1401,7 @@ public class Connection {
|
|||
}
|
||||
|
||||
// we lock TTS until we have finished adding this one
|
||||
Lock lock = locks.computeIfAbsent(TimerType.TTS, k -> new ReentrantLock());
|
||||
Lock lock = Objects.requireNonNull(locks.computeIfAbsent(TimerType.TTS, k -> new ReentrantLock()));
|
||||
lock.lock();
|
||||
try {
|
||||
TextToSpeech textToSpeech = Objects
|
||||
|
@ -1414,7 +1419,7 @@ public class Connection {
|
|||
|
||||
private void sendTextToSpeech() {
|
||||
// we lock new TTS until we have dispatched everything
|
||||
Lock lock = locks.computeIfAbsent(TimerType.TTS, k -> new ReentrantLock());
|
||||
Lock lock = Objects.requireNonNull(locks.computeIfAbsent(TimerType.TTS, k -> new ReentrantLock()));
|
||||
lock.lock();
|
||||
try {
|
||||
Iterator<TextToSpeech> iterator = textToSpeeches.values().iterator();
|
||||
|
@ -1424,8 +1429,7 @@ public class Connection {
|
|||
List<Device> devices = textToSpeech.devices;
|
||||
if (!devices.isEmpty()) {
|
||||
String text = textToSpeech.text;
|
||||
Map<String, Object> parameters = new HashMap<>();
|
||||
parameters.put("textToSpeak", text);
|
||||
Map<String, Object> parameters = Map.of("textToSpeak", text);
|
||||
executeSequenceCommandWithVolume(devices, "Alexa.Speak", parameters, textToSpeech.ttsVolumes,
|
||||
textToSpeech.standardVolumes);
|
||||
}
|
||||
|
@ -1441,9 +1445,60 @@ public class Connection {
|
|||
}
|
||||
}
|
||||
|
||||
public void textCommand(Device device, String text, @Nullable Integer ttsVolume, @Nullable Integer standardVolume) {
|
||||
if (text.replaceAll("<.+?>", "").replaceAll("\\s+", " ").trim().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// we lock TextCommands until we have finished adding this one
|
||||
Lock lock = Objects.requireNonNull(locks.computeIfAbsent(TimerType.TEXT_COMMAND, k -> new ReentrantLock()));
|
||||
lock.lock();
|
||||
try {
|
||||
TextCommand textCommand = Objects
|
||||
.requireNonNull(textCommands.computeIfAbsent(Objects.hash(text), k -> new TextCommand(text)));
|
||||
textCommand.devices.add(device);
|
||||
textCommand.ttsVolumes.add(ttsVolume);
|
||||
textCommand.standardVolumes.add(standardVolume);
|
||||
// schedule a TextCommand only if it has not been scheduled before
|
||||
timers.computeIfAbsent(TimerType.TEXT_COMMAND,
|
||||
k -> scheduler.schedule(this::sendTextCommand, 500, TimeUnit.MILLISECONDS));
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void sendTextCommand() {
|
||||
// we lock new TTS until we have dispatched everything
|
||||
Lock lock = Objects.requireNonNull(locks.computeIfAbsent(TimerType.TEXT_COMMAND, k -> new ReentrantLock()));
|
||||
lock.lock();
|
||||
|
||||
try {
|
||||
Iterator<TextCommand> iterator = textCommands.values().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
TextCommand textCommand = iterator.next();
|
||||
try {
|
||||
List<Device> devices = textCommand.devices;
|
||||
if (!devices.isEmpty()) {
|
||||
String text = textCommand.text;
|
||||
Map<String, Object> parameters = Map.of("text", text);
|
||||
executeSequenceCommandWithVolume(devices, "Alexa.TextCommand", parameters,
|
||||
textCommand.ttsVolumes, textCommand.standardVolumes);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.warn("send textCommand fails with unexpected error", e);
|
||||
}
|
||||
iterator.remove();
|
||||
}
|
||||
} finally {
|
||||
// the timer is done anyway immediately after we unlock
|
||||
timers.remove(TimerType.TEXT_COMMAND);
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void volume(Device device, int vol) {
|
||||
// we lock volume until we have finished adding this one
|
||||
Lock lock = locks.computeIfAbsent(TimerType.VOLUME, k -> new ReentrantLock());
|
||||
Lock lock = Objects.requireNonNull(locks.computeIfAbsent(TimerType.VOLUME, k -> new ReentrantLock()));
|
||||
lock.lock();
|
||||
try {
|
||||
Volume volume = Objects.requireNonNull(volumes.computeIfAbsent(vol, k -> new Volume(vol)));
|
||||
|
@ -1459,7 +1514,7 @@ public class Connection {
|
|||
|
||||
private void sendVolume() {
|
||||
// we lock new volume until we have dispatched everything
|
||||
Lock lock = locks.computeIfAbsent(TimerType.VOLUME, k -> new ReentrantLock());
|
||||
Lock lock = Objects.requireNonNull(locks.computeIfAbsent(TimerType.VOLUME, k -> new ReentrantLock()));
|
||||
lock.lock();
|
||||
try {
|
||||
Iterator<Volume> iterator = volumes.values().iterator();
|
||||
|
@ -1504,7 +1559,7 @@ public class Connection {
|
|||
|
||||
if (command != null && !parameters.isEmpty()) {
|
||||
JsonArray commandNodesToExecute = new JsonArray();
|
||||
if ("Alexa.Speak".equals(command)) {
|
||||
if ("Alexa.Speak".equals(command) || "Alexa.TextCommand".equals(command)) {
|
||||
for (Device device : devices) {
|
||||
commandNodesToExecute.add(createExecutionNode(device, command, parameters));
|
||||
}
|
||||
|
@ -1565,7 +1620,7 @@ public class Connection {
|
|||
}
|
||||
|
||||
private void handleExecuteSequenceNode() {
|
||||
Lock lock = locks.computeIfAbsent(TimerType.DEVICES, k -> new ReentrantLock());
|
||||
Lock lock = Objects.requireNonNull(locks.computeIfAbsent(TimerType.DEVICES, k -> new ReentrantLock()));
|
||||
if (lock.tryLock()) {
|
||||
try {
|
||||
for (String serialNumber : devices.keySet()) {
|
||||
|
@ -1707,6 +1762,9 @@ public class Connection {
|
|||
JsonObject nodeToExecute = new JsonObject();
|
||||
nodeToExecute.addProperty("@type", "com.amazon.alexa.behaviors.model.OpaquePayloadOperationNode");
|
||||
nodeToExecute.addProperty("type", command);
|
||||
if ("Alexa.TextCommand".equals(command)) {
|
||||
nodeToExecute.addProperty("skillId", "amzn1.ask.1p.tellalexa");
|
||||
}
|
||||
nodeToExecute.add("operationPayload", operationPayload);
|
||||
return nodeToExecute;
|
||||
}
|
||||
|
@ -2047,6 +2105,17 @@ public class Connection {
|
|||
}
|
||||
}
|
||||
|
||||
private static class TextCommand {
|
||||
public List<Device> devices = new ArrayList<>();
|
||||
public String text;
|
||||
public List<@Nullable Integer> ttsVolumes = new ArrayList<>();
|
||||
public List<@Nullable Integer> standardVolumes = new ArrayList<>();
|
||||
|
||||
public TextCommand(String text) {
|
||||
this.text = text;
|
||||
}
|
||||
}
|
||||
|
||||
private static class Volume {
|
||||
public List<Device> devices = new ArrayList<>();
|
||||
public int volume;
|
||||
|
|
|
@ -743,11 +743,6 @@ public class AccountHandler extends BaseBridgeHandler implements IWebSocketComma
|
|||
switch (command) {
|
||||
case "PUSH_ACTIVITY":
|
||||
handlePushActivity(pushCommand.payload);
|
||||
if (refreshDataDelayed != null) {
|
||||
refreshDataDelayed.cancel(false);
|
||||
}
|
||||
this.refreshAfterCommandJob = scheduler.schedule(this::refreshAfterCommand, 700,
|
||||
TimeUnit.MILLISECONDS);
|
||||
break;
|
||||
case "PUSH_DOPPLER_CONNECTION_CHANGE":
|
||||
case "PUSH_BLUETOOTH_STATE_CHANGE":
|
||||
|
|
|
@ -114,6 +114,7 @@ public class EchoHandler extends BaseThingHandler implements IEchoThingHandler {
|
|||
private boolean disableUpdate = false;
|
||||
private boolean updateRemind = true;
|
||||
private boolean updateTextToSpeech = true;
|
||||
private boolean updateTextCommand = true;
|
||||
private boolean updateAlarm = true;
|
||||
private boolean updateRoutine = true;
|
||||
private boolean updatePlayMusicVoiceCommand = true;
|
||||
|
@ -589,6 +590,16 @@ public class EchoHandler extends BaseThingHandler implements IEchoThingHandler {
|
|||
}
|
||||
this.updateState(channelId, new PercentType(textToSpeechVolume));
|
||||
}
|
||||
if (channelId.equals(CHANNEL_TEXT_COMMAND)) {
|
||||
if (command instanceof StringType) {
|
||||
String text = command.toFullString();
|
||||
if (!text.isEmpty()) {
|
||||
waitForUpdate = 1000;
|
||||
updateTextCommand = true;
|
||||
startTextCommand(connection, device, text);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (channelId.equals(CHANNEL_LAST_VOICE_COMMAND)) {
|
||||
if (command instanceof StringType) {
|
||||
String text = command.toFullString();
|
||||
|
@ -713,6 +724,15 @@ public class EchoHandler extends BaseThingHandler implements IEchoThingHandler {
|
|||
connection.textToSpeech(device, text, volume, lastKnownVolume);
|
||||
}
|
||||
|
||||
private void startTextCommand(Connection connection, Device device, String text)
|
||||
throws IOException, URISyntaxException {
|
||||
Integer volume = null;
|
||||
if (textToSpeechVolume != 0) {
|
||||
volume = textToSpeechVolume;
|
||||
}
|
||||
connection.textCommand(device, text, volume, lastKnownVolume);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startAnnouncement(Device device, String speak, String bodyText, @Nullable String title,
|
||||
@Nullable Integer volume) throws IOException, URISyntaxException {
|
||||
|
@ -770,11 +790,11 @@ public class EchoHandler extends BaseThingHandler implements IEchoThingHandler {
|
|||
String type = currentNotification.type;
|
||||
if (type != null) {
|
||||
if (type.equals("Reminder")) {
|
||||
updateState(CHANNEL_REMIND, new StringType(""));
|
||||
updateState(CHANNEL_REMIND, StringType.EMPTY);
|
||||
updateRemind = false;
|
||||
}
|
||||
if (type.equals("Alarm")) {
|
||||
updateState(CHANNEL_PLAY_ALARM_SOUND, new StringType(""));
|
||||
updateState(CHANNEL_PLAY_ALARM_SOUND, StringType.EMPTY);
|
||||
updateAlarm = false;
|
||||
}
|
||||
}
|
||||
|
@ -919,7 +939,7 @@ public class EchoHandler extends BaseThingHandler implements IEchoThingHandler {
|
|||
}
|
||||
} catch (HttpException e) {
|
||||
if (e.getCode() == 400) {
|
||||
updateState(CHANNEL_RADIO_STATION_ID, new StringType(""));
|
||||
updateState(CHANNEL_RADIO_STATION_ID, StringType.EMPTY);
|
||||
} else {
|
||||
logger.info("getMediaState fails", e);
|
||||
}
|
||||
|
@ -1069,27 +1089,31 @@ public class EchoHandler extends BaseThingHandler implements IEchoThingHandler {
|
|||
// Update states
|
||||
if (updateRemind && currentNotifcationUpdateTimer == null) {
|
||||
updateRemind = false;
|
||||
updateState(CHANNEL_REMIND, new StringType(""));
|
||||
updateState(CHANNEL_REMIND, StringType.EMPTY);
|
||||
}
|
||||
if (updateAlarm && currentNotifcationUpdateTimer == null) {
|
||||
updateAlarm = false;
|
||||
updateState(CHANNEL_PLAY_ALARM_SOUND, new StringType(""));
|
||||
updateState(CHANNEL_PLAY_ALARM_SOUND, StringType.EMPTY);
|
||||
}
|
||||
if (updateRoutine) {
|
||||
updateRoutine = false;
|
||||
updateState(CHANNEL_START_ROUTINE, new StringType(""));
|
||||
updateState(CHANNEL_START_ROUTINE, StringType.EMPTY);
|
||||
}
|
||||
if (updateTextToSpeech) {
|
||||
updateTextToSpeech = false;
|
||||
updateState(CHANNEL_TEXT_TO_SPEECH, new StringType(""));
|
||||
updateState(CHANNEL_TEXT_TO_SPEECH, StringType.EMPTY);
|
||||
}
|
||||
if (updateTextCommand) {
|
||||
updateTextCommand = false;
|
||||
updateState(CHANNEL_TEXT_COMMAND, StringType.EMPTY);
|
||||
}
|
||||
if (updatePlayMusicVoiceCommand) {
|
||||
updatePlayMusicVoiceCommand = false;
|
||||
updateState(CHANNEL_PLAY_MUSIC_VOICE_COMMAND, new StringType(""));
|
||||
updateState(CHANNEL_PLAY_MUSIC_VOICE_COMMAND, StringType.EMPTY);
|
||||
}
|
||||
if (updateStartCommand) {
|
||||
updateStartCommand = false;
|
||||
updateState(CHANNEL_START_COMMAND, new StringType(""));
|
||||
updateState(CHANNEL_START_COMMAND, StringType.EMPTY);
|
||||
}
|
||||
|
||||
updateState(CHANNEL_MUSIC_PROVIDER_ID, new StringType(musicProviderId));
|
||||
|
@ -1225,7 +1249,7 @@ public class EchoHandler extends BaseThingHandler implements IEchoThingHandler {
|
|||
}
|
||||
|
||||
if (lastSpokenText.isEmpty() || lastSpokenText.equals(spokenText)) {
|
||||
updateState(CHANNEL_LAST_VOICE_COMMAND, new StringType(""));
|
||||
updateState(CHANNEL_LAST_VOICE_COMMAND, StringType.EMPTY);
|
||||
}
|
||||
lastSpokenText = spokenText;
|
||||
updateState(CHANNEL_LAST_VOICE_COMMAND, new StringType(spokenText));
|
||||
|
|
|
@ -88,6 +88,9 @@ channel-type.amazonechocontrol.textToSpeech.description = Spricht den Text (Nur
|
|||
channel-type.amazonechocontrol.textToSpeechVolume.label = Sprich Lautstärke
|
||||
channel-type.amazonechocontrol.textToSpeechVolume.description = Lautstärke des Sprich Kanals. Wenn 0 wird die aktuelle Lautstärke verwendet.
|
||||
|
||||
channel-type.amazonechocontrol.textCommand.label = Befehl
|
||||
channel-type.amazonechocontrol.textCommand.description = Führt einen Befehl aus (Nur schreiben). Der Befehl wird wie ein gesprochener Befehl ausgeführt.
|
||||
|
||||
channel-type.amazonechocontrol.lastVoiceCommand.label = Letzter Sprachbefehl
|
||||
channel-type.amazonechocontrol.lastVoiceCommand.description = Befehl der zum Gerät gesprochen wurde. Schreiben zum Kanal started die Sprachausgabe.
|
||||
|
||||
|
|
|
@ -73,6 +73,7 @@
|
|||
<channel id="announcement" typeId="announcement"/>
|
||||
<channel id="textToSpeech" typeId="textToSpeech"/>
|
||||
<channel id="textToSpeechVolume" typeId="textToSpeechVolume"/>
|
||||
<channel id="textCommand" typeId="textCommand"/>
|
||||
<channel id="remind" typeId="remind"/>
|
||||
<channel id="nextReminder" typeId="nextReminder"/>
|
||||
<channel id="playAlarmSound" typeId="playAlarmSound"/>
|
||||
|
@ -127,6 +128,7 @@
|
|||
<channel id="announcement" typeId="announcement"/>
|
||||
<channel id="textToSpeech" typeId="textToSpeech"/>
|
||||
<channel id="textToSpeechVolume" typeId="textToSpeechVolume"/>
|
||||
<channel id="textCommand" typeId="textCommand"/>
|
||||
<channel id="remind" typeId="remind"/>
|
||||
<channel id="nextReminder" typeId="nextReminder"/>
|
||||
<channel id="playAlarmSound" typeId="playAlarmSound"/>
|
||||
|
@ -181,6 +183,7 @@
|
|||
<channel id="announcement" typeId="announcement"/>
|
||||
<channel id="textToSpeech" typeId="textToSpeech"/>
|
||||
<channel id="textToSpeechVolume" typeId="textToSpeechVolume"/>
|
||||
<channel id="textCommand" typeId="textCommand"/>
|
||||
<channel id="remind" typeId="remind"/>
|
||||
<channel id="nextReminder" typeId="nextReminder"/>
|
||||
<channel id="playAlarmSound" typeId="playAlarmSound"/>
|
||||
|
@ -452,6 +455,11 @@
|
|||
<label>Speak Volume</label>
|
||||
<description>Volume of the Speak channel. If 0, the current volume will be used.</description>
|
||||
</channel-type>
|
||||
<channel-type id="textCommand" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>TextCommand</label>
|
||||
<description>Run a command (Write only). The command can run like a spoken command.</description>
|
||||
</channel-type>
|
||||
<channel-type id="lastVoiceCommand" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>Last Voice Command</label>
|
||||
|
|
Loading…
Reference in New Issue