Skip to content
This repository has been archived by the owner on Sep 21, 2020. It is now read-only.

Commit

Permalink
[amazonechocontrol] Announcement volume (#6097)
Browse files Browse the repository at this point in the history
Signed-off-by: Michael Geramb <mail@michael-geramb.at> (github: mgeramb)
  • Loading branch information
Michael Geramb authored and kaikreuzer committed Nov 16, 2019
1 parent 56b882b commit 1dd0323
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 28 deletions.
10 changes: 7 additions & 3 deletions bundles/org.openhab.binding.amazonechocontrol/README.md
Expand Up @@ -363,13 +363,17 @@ end
```

Expert:
You can use a json formatted string to control the title and the sound:
You can use a json formatted string to control title, sound and volume:

```php
{ "sound": true, "speak":"<Speak>" "title": "<Title>", "body": "<Body Text>"}
{ "sound": true, "speak":"<Speak>" "title": "<Title>", "body": "<Body Text>", "volume": 20}
```

The combination of sound=true and speak in SSML syntax is not allowed
The combination of `sound=true` and `speak` in SSML syntax is not allowed.
Not all properties need to be specified.
The value for `volume` can be between 0 and 100 to set the volume.
A volume value smaller then 0 means that the current alexa volume should be used.
No specification uses the volume from the `textToSpeechVolume` channel.

Note: If you turn off the sound and Alexa is playing music, it will anyway turn down the volume for a moment. This behavior can not be changed.

Expand Down
Expand Up @@ -1100,8 +1100,8 @@ public void sendNotificationToMobileApp(String customerId, String text, @Nullabl
executeSequenceCommand(null, "Alexa.Notifications.SendMobilePush", parameters);
}

public void sendAnnouncement(Device device, String text, String bodyText, @Nullable String title, int ttsVolume,
int standardVolume) throws IOException, URISyntaxException {
public void sendAnnouncement(Device device, String speak, String bodyText, @Nullable String title,
@Nullable Integer ttsVolume, int standardVolume) throws IOException, URISyntaxException {
Map<String, Object> parameters = new HashMap<>();
parameters.put("expireAfter", "PT5S");
JsonAnnouncementContent[] contentArray = new JsonAnnouncementContent[1];
Expand All @@ -1112,10 +1112,10 @@ public void sendAnnouncement(Device device, String text, String bodyText, @Nulla
content.display.title = title;
}
content.display.body = bodyText;
if (text.startsWith("<speak>") && text.endsWith("</speak>")) {
if (speak.startsWith("<speak>") && speak.endsWith("</speak>")) {
content.speak.type = "ssml";
}
content.speak.value = text;
content.speak.value = speak;

contentArray[0] = content;

Expand All @@ -1140,36 +1140,31 @@ public void sendAnnouncement(Device device, String text, String bodyText, @Nulla
executeSequenceCommandWithVolume(device, "AlexaAnnouncement", parameters, ttsVolume, standardVolume);
}

public void textToSpeech(Device device, String text, int ttsVolume, int standardVolume)
public void textToSpeech(Device device, String text, @Nullable Integer ttsVolume, int standardVolume)
throws IOException, URISyntaxException {
Map<String, Object> parameters = new HashMap<>();
parameters.put("textToSpeak", text);
executeSequenceCommandWithVolume(device, "Alexa.Speak", parameters, ttsVolume, standardVolume);
}

private void executeSequenceCommandWithVolume(@Nullable Device device, String command,
@Nullable Map<String, Object> parameters, int ttsVolume, int standardVolume)
@Nullable Map<String, Object> parameters, @Nullable Integer ttsVolume, int standardVolume)
throws IOException, URISyntaxException {
if (ttsVolume != 0) {

if (ttsVolume != null) {
JsonArray nodesToExecute = new JsonArray();

Map<String, Object> volumeParameters = new HashMap<>();
// add tts volume
volumeParameters.clear();
volumeParameters.put("value", ttsVolume);
nodesToExecute.add(createExecutionNode(device, "Alexa.DeviceControls.Volume", volumeParameters));

// add command
nodesToExecute.add(createExecutionNode(device, command, parameters));

// add volume
volumeParameters.clear();
volumeParameters.put("value", standardVolume);
nodesToExecute.add(createExecutionNode(device, "Alexa.DeviceControls.Volume", volumeParameters));

executeSequenceNodes(nodesToExecute);

} else {
executeSequenceCommand(device, command, parameters);
}
Expand Down
Expand Up @@ -33,8 +33,11 @@
public class ChannelHandlerAnnouncement extends ChannelHandler {
private static final String CHANNEL_NAME = "announcement";

public ChannelHandlerAnnouncement(IAmazonThingHandler thingHandler, Gson gson) {
protected final IEchoThingHandler thingHandler;

public ChannelHandlerAnnouncement(IEchoThingHandler thingHandler, Gson gson) {
super(thingHandler, gson);
this.thingHandler = thingHandler;
}

@Override
Expand All @@ -46,6 +49,7 @@ public boolean tryHandleCommand(Device device, Connection connection, String cha
String body = commandValue;
String title = null;
String speak = commandValue;
Integer volume = null;
if (commandValue.startsWith("{") && commandValue.endsWith("}")) {
try {
AnnouncementRequestJson request = parseJson(commandValue, AnnouncementRequestJson.class);
Expand All @@ -54,6 +58,7 @@ public boolean tryHandleCommand(Device device, Connection connection, String cha
if (speak == null || speak.length() == 0) {
speak = " "; // blank generates a beep
}
volume = request.volume;
title = request.title;
body = request.body;
if (body == null) {
Expand All @@ -70,6 +75,9 @@ public boolean tryHandleCommand(Device device, Connection connection, String cha
speak = "<speak><lang xml:lang=\"en-UK\">Error: The combination of sound and speak in <prosody rate=\"x-slow\"><say-as interpret-as=\"characters\">SSML</say-as></prosody> syntax is not allowed</lang></speak>";
}
}
if ("<speak> </speak>".equals(speak)) {
volume = -1; // Do not change volume
}
}
} catch (JsonSyntaxException e) {
body = "Invalid Json." + e.getLocalizedMessage();
Expand All @@ -79,7 +87,7 @@ public boolean tryHandleCommand(Device device, Connection connection, String cha
body = e.getLocalizedMessage();
}
}
connection.sendAnnouncement(device, speak, body, title, 0, 0);
thingHandler.startAnnouncment(device, speak, body, title, volume);
}
RefreshChannel();
}
Expand All @@ -95,5 +103,6 @@ static class AnnouncementRequestJson {
public @Nullable String title;
public @Nullable String body;
public @Nullable String speak;
public @Nullable Integer volume;
}
}
@@ -0,0 +1,31 @@
/**
* Copyright (c) 2010-2019 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.amazonechocontrol.internal.channelhandler;

import java.io.IOException;
import java.net.URISyntaxException;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.amazonechocontrol.internal.jsons.JsonDevices.Device;

/**
* The {@link IEchoThingHandler} is used from ChannelHandlers to communicate with the thing
*
* @author Michael Geramb - Initial contribution
*/
@NonNullByDefault
public interface IEchoThingHandler extends IAmazonThingHandler {
void startAnnouncment(Device device, String speak, String bodyText, @Nullable String title,
@Nullable Integer volume) throws IOException, URISyntaxException;
}
Expand Up @@ -12,7 +12,46 @@
*/
package org.openhab.binding.amazonechocontrol.internal.handler;

import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.*;
import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_AMAZON_MUSIC;
import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_AMAZON_MUSIC_PLAY_LIST_ID;
import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_AMAZON_MUSIC_TRACK_ID;
import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_ASCENDING_ALARM;
import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_BLUETOOTH;
import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_BLUETOOTH_DEVICE_NAME;
import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_BLUETOOTH_MAC;
import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_EQUALIZER_BASS;
import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_EQUALIZER_MIDRANGE;
import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_EQUALIZER_TREBLE;
import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_IMAGE_URL;
import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_LAST_VOICE_COMMAND;
import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_MEDIA_LENGTH;
import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_MEDIA_PROGRESS;
import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_MEDIA_PROGRESS_TIME;
import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_MUSIC_PROVIDER_ID;
import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_NEXT_ALARM;
import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_NEXT_MUSIC_ALARM;
import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_NEXT_REMINDER;
import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_NEXT_TIMER;
import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_NOTIFICATION_VOLUME;
import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_PLAYER;
import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_PLAY_ALARM_SOUND;
import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_PLAY_MUSIC_VOICE_COMMAND;
import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_PLAY_ON_DEVICE;
import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_PROVIDER_DISPLAY_NAME;
import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_RADIO;
import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_RADIO_STATION_ID;
import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_REMIND;
import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_SHUFFLE;
import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_START_COMMAND;
import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_START_ROUTINE;
import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_SUBTITLE1;
import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_SUBTITLE2;
import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_TEXT_TO_SPEECH;
import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_TEXT_TO_SPEECH_VOLUME;
import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_TITLE;
import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_VOLUME;
import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.DEVICE_PROPERTY_SERIAL_NUMBER;
import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.FLASH_BRIEFING_COMMAND_PREFIX;

import java.io.IOException;
import java.net.URISyntaxException;
Expand Down Expand Up @@ -63,7 +102,7 @@
import org.openhab.binding.amazonechocontrol.internal.HttpException;
import org.openhab.binding.amazonechocontrol.internal.channelhandler.ChannelHandler;
import org.openhab.binding.amazonechocontrol.internal.channelhandler.ChannelHandlerAnnouncement;
import org.openhab.binding.amazonechocontrol.internal.channelhandler.IAmazonThingHandler;
import org.openhab.binding.amazonechocontrol.internal.channelhandler.IEchoThingHandler;
import org.openhab.binding.amazonechocontrol.internal.jsons.JsonActivities.Activity;
import org.openhab.binding.amazonechocontrol.internal.jsons.JsonActivities.Activity.Description;
import org.openhab.binding.amazonechocontrol.internal.jsons.JsonAscendingAlarm.AscendingAlarmModel;
Expand Down Expand Up @@ -99,7 +138,7 @@
* @author Michael Geramb - Initial contribution
*/
@NonNullByDefault
public class EchoHandler extends BaseThingHandler implements IAmazonThingHandler {
public class EchoHandler extends BaseThingHandler implements IEchoThingHandler {

private final Logger logger = LoggerFactory.getLogger(EchoHandler.class);
private Gson gson;
Expand Down Expand Up @@ -728,22 +767,38 @@ private boolean handleEqualizerCommands(String channelId, Command command, Conne

private void startTextToSpeech(Connection connection, Device device, String text)
throws IOException, URISyntaxException {
Integer volume = null;
if (textToSpeechVolume != 0) {
@Nullable
ScheduledFuture<?> oldIgnoreVolumeChange = this.ignoreVolumeChange;
if (oldIgnoreVolumeChange != null) {
oldIgnoreVolumeChange.cancel(false);
}
this.ignoreVolumeChange = scheduler.schedule(this::stopIgnoreVolumeChange, 2000, TimeUnit.MILLISECONDS);
startIgnoreVolumeChange();
volume = textToSpeechVolume;
}
if (text.startsWith("<speak>") && text.endsWith("</speak>")) {
String bodyText = text.replaceAll("<[^>]+>", "");
connection.sendAnnouncement(device, text, bodyText, null, textToSpeechVolume, lastKnownVolume);
connection.sendAnnouncement(device, text, bodyText, null, volume, lastKnownVolume);
} else {
connection.textToSpeech(device, text, textToSpeechVolume, lastKnownVolume);
connection.textToSpeech(device, text, volume, lastKnownVolume);
}
}

@Override
public void startAnnouncment(Device device, String speak, String bodyText, @Nullable String title,
@Nullable Integer volume) throws IOException, URISyntaxException {
Connection connection = this.findConnection();
if (connection == null) {
return;
}
if (volume == null && textToSpeechVolume != 0) {
volume = textToSpeechVolume;
}
if (volume != null && volume < 0) {
volume = null; // the meaning of negative values is 'do not use'. The api requires null in this case.
}
if (volume != null) {
startIgnoreVolumeChange();
}
connection.sendAnnouncement(device, speak, bodyText, title, volume, lastKnownVolume);
}

private void stopCurrentNotification() {
ScheduledFuture<?> currentNotifcationUpdateTimer = this.currentNotifcationUpdateTimer;
if (currentNotifcationUpdateTimer != null) {
Expand Down Expand Up @@ -1251,6 +1306,15 @@ public void handlePushActivity(Activity pushActivity) {
}
}

private void startIgnoreVolumeChange() {
@Nullable
ScheduledFuture<?> oldIgnoreVolumeChange = this.ignoreVolumeChange;
if (oldIgnoreVolumeChange != null) {
oldIgnoreVolumeChange.cancel(false);
}
this.ignoreVolumeChange = scheduler.schedule(this::stopIgnoreVolumeChange, 2000, TimeUnit.MILLISECONDS);
}

private void stopIgnoreVolumeChange() {
this.ignoreVolumeChange = null;
}
Expand Down

0 comments on commit 1dd0323

Please sign in to comment.