Skip to content

Commit

Permalink
further work
Browse files Browse the repository at this point in the history
Signed-off-by: Jan N. Klug <github@klug.nrw>
  • Loading branch information
J-N-K committed Oct 25, 2023
1 parent be38815 commit 49939d6
Show file tree
Hide file tree
Showing 19 changed files with 408 additions and 589 deletions.
97 changes: 46 additions & 51 deletions bundles/org.smarthomej.binding.amazonechocontrol/README.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,5 @@ public class AccountHandlerConfig {
public int discoverSmartHome = 0;
public int pollingIntervalSmartHomeAlexa = 60;
public int pollingIntervalSmartSkills = 120;
public int activityRequestDelay = 10;
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,7 @@ public class AmazonEchoControlBindingConstants {
public static final String CHANNEL_EQUALIZER_TREBLE = "equalizerTreble";
public static final String CHANNEL_EQUALIZER_MIDRANGE = "equalizerMidrange";
public static final String CHANNEL_EQUALIZER_BASS = "equalizerBass";
public static final String CHANNEL_ERROR = "error";
public static final String CHANNEL_SHUFFLE = "shuffle";
public static final String CHANNEL_LOOP = "loop";
public static final String CHANNEL_IMAGE_URL = "imageUrl";
public static final String CHANNEL_TITLE = "title";
public static final String CHANNEL_SUBTITLE1 = "subtitle1";
Expand All @@ -74,11 +72,6 @@ public class AmazonEchoControlBindingConstants {
public static final String CHANNEL_BLUETOOTH_MAC = "bluetoothMAC";
public static final String CHANNEL_BLUETOOTH = "bluetooth";
public static final String CHANNEL_BLUETOOTH_DEVICE_NAME = "bluetoothDeviceName";
public static final String CHANNEL_RADIO_STATION_ID = "radioStationId";
public static final String CHANNEL_RADIO = "radio";
public static final String CHANNEL_AMAZON_MUSIC_TRACK_ID = "amazonMusicTrackId";
public static final String CHANNEL_AMAZON_MUSIC = "amazonMusic";
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";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.thing.ChannelUID;
Expand All @@ -30,7 +29,6 @@
import org.slf4j.LoggerFactory;
import org.smarthomej.binding.amazonechocontrol.internal.dto.DeviceTO;
import org.smarthomej.binding.amazonechocontrol.internal.dto.NotificationSoundTO;
import org.smarthomej.binding.amazonechocontrol.internal.dto.response.PlayListTO;
import org.smarthomej.binding.amazonechocontrol.internal.handler.EchoHandler;
import org.smarthomej.binding.amazonechocontrol.internal.handler.FlashBriefingProfileHandler;

Expand Down Expand Up @@ -111,27 +109,6 @@ public void setEchoHandlerAlarmSounds(EchoHandler echoHandler, List<Notification
}
}

public void setEchoHandlerPlaylists(EchoHandler echoHandler, Map<String, List<PlayListTO>> playlists) {
List<CommandOption> options = new ArrayList<>();
for (List<PlayListTO> innerLists : playlists.values()) {
if (!innerLists.isEmpty()) {
PlayListTO playList = innerLists.get(0);
final String value = playList.playlistId;
if (value != null && playList.title != null) {
options.add(
new CommandOption(value, String.format("%s (%d)", playList.title, playList.trackCount)));
}
}
}

ChannelUID channelUID = new ChannelUID(echoHandler.getThing().getUID(), CHANNEL_AMAZON_MUSIC_PLAY_LIST_ID);
if (options.isEmpty()) {
channelOptionsMap.remove(channelUID);
} else {
channelOptionsMap.put(channelUID, options);
}
}

public void removeCommandDescriptionForThing(ThingUID thingUID) {
logger.trace("removing state description for thing {}", thingUID);
channelOptionsMap.entrySet().removeIf(entry -> entry.getKey().getThingUID().equals(thingUID));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
Expand Down Expand Up @@ -92,15 +91,15 @@ public class AmazonEchoControlServlet extends HttpServlet {
private final Logger logger = LoggerFactory.getLogger(AmazonEchoControlServlet.class);

private final Map<String, Connection> connectionToInitializeMap = new ConcurrentHashMap<>();
private final AmazonEchoControlHandlerFactory handlerFactory;
// private final Template thingDetailTemplate;
private final VelocityEngine velocityEngine = new VelocityEngine();

private final AmazonEchoControlHandlerFactory handlerFactory;

@Activate
public AmazonEchoControlServlet(@Reference AmazonEchoControlHandlerFactory handlerFactory) {
this.handlerFactory = handlerFactory;

velocityEngine.setProperty("runtime.introspector.uberspect",
velocityEngine.setProperty("introspector.uberspect.class",
UberspectImpl.class.getName() + ", " + UberspectPublicFields.class.getName());
velocityEngine.init();
}
Expand Down Expand Up @@ -186,19 +185,18 @@ private void doAccountDeletePostPut(HttpMethod method, ServletUri uriParts, Http
Connection connection = accountHandler.getConnection();
if (uri.startsWith(PROXY_URI_PART)) {
// handle proxy request

if (connection == null) {
returnError(resp, uriParts, "Account not online");
return;
}
String getUrl = connection.getAlexaServer() + "/" + uri.substring(PROXY_URI_PART.length());
String proxyUrl = connection.getAlexaServer() + "/" + uri.substring(PROXY_URI_PART.length());

Object postData = null;
if (HttpMethod.PUT.equals(method) || HttpMethod.POST.equals(method)) {
postData = req.getReader().lines().collect(Collectors.joining());
}

this.handleProxyRequest(accountHandler, connection, resp, uriParts, method, getUrl, null, postData,
this.handleProxyRequest(accountHandler, connection, resp, uriParts, method, proxyUrl, null, postData,
postData != null, connection.getRetailDomain());
return;
}
Expand Down Expand Up @@ -247,7 +245,7 @@ private void doAccountDeletePostPut(HttpMethod method, ServletUri uriParts, Http
retailDomain);
}

protected void doAccountGet(ServletUri uriParts, HttpServletRequest req, HttpServletResponse resp)
private void doAccountGet(ServletUri uriParts, HttpServletRequest req, HttpServletResponse resp)
throws IOException {
String uri = uriParts.request();
String queryString = req.getQueryString();
Expand All @@ -273,14 +271,13 @@ protected void doAccountGet(ServletUri uriParts, HttpServletRequest req, HttpSer
connection = accountHandler.getConnection();
if (uri.startsWith(PROXY_URI_PART)) {
// handle proxy request

if (connection == null) {
returnError(resp, uriParts, "Account not online");
return;
}
String getUrl = connection.getAlexaServer() + "/" + uri.substring(PROXY_URI_PART.length());
String proxyUrl = connection.getAlexaServer() + "/" + uri.substring(PROXY_URI_PART.length());

this.handleProxyRequest(accountHandler, connection, resp, uriParts, HttpMethod.GET, getUrl, null, null,
this.handleProxyRequest(accountHandler, connection, resp, uriParts, HttpMethod.GET, proxyUrl, null, null,
false, connection.getRetailDomain());
return;
}
Expand Down Expand Up @@ -349,7 +346,6 @@ private void createAccountPage(HttpServletResponse resp, ServletUri uriParts, Ac
ctx.put("DEVICE_TYPES", DEVICE_TYPES);

StringWriter stringWriter = evaluateTemplate("WEB-INF/account-detail.vm", ctx);

resp.addHeader(HttpHeader.CONTENT_TYPE.asString(), MimeTypes.Type.TEXT_HTML_UTF_8.asString());
resp.getWriter().write(stringWriter.toString());
}
Expand All @@ -366,11 +362,6 @@ private void createDeviceDetailsResponse(HttpServletResponse resp, ServletUri ur
.sorted(Comparator.comparing(o -> o.value)).toList();
channels.put(CHANNEL_PLAY_ALARM_SOUND, alarmSounds);

List<ChannelOption> playLists = connection.getPlaylists(device).values().stream().flatMap(Collection::stream)
.filter(this::isValidPlaylist).map(p -> new ChannelOption(p.playlistId, p.title))
.sorted(Comparator.comparing(o -> o.value)).toList();
channels.put(CHANNEL_AMAZON_MUSIC_PLAY_LIST_ID, playLists);

List<BluetoothStateTO> states = connection.getBluetoothConnectionStates();
List<ChannelOption> pairedDevices = findIn(states, k -> k.deviceSerialNumber, device.serialNumber)
.map(state -> state.pairedDeviceList)
Expand All @@ -387,7 +378,6 @@ private void createDeviceDetailsResponse(HttpServletResponse resp, ServletUri ur
ctx.put("capabilities", device.capabilities.stream().sorted().toList());

StringWriter stringWriter = evaluateTemplate("WEB-INF/device-detail.vm", ctx);

resp.addHeader(HttpHeader.CONTENT_TYPE.asString(), MimeTypes.Type.TEXT_HTML_UTF_8.asString());
resp.getWriter().write(stringWriter.toString());
}
Expand All @@ -401,10 +391,6 @@ private boolean isValidAlarmSound(NotificationSoundTO sound) {
return sound.folder == null && sound.providerId != null && sound.id != null && sound.displayName != null;
}

private boolean isValidPlaylist(PlayListTO playList) {
return playList.playlistId != null && playList.title != null;
}

private void handleProxyRequest(AccountHandler accountHandler, Connection connection, HttpServletResponse resp,
ServletUri uriParts, HttpMethod method, String url, @Nullable String referer, @Nullable Object postData,
boolean isJson, String retailDomain) throws IOException {
Expand Down Expand Up @@ -480,7 +466,6 @@ private void returnHtml(HttpServletResponse resp, ServletUri uriParts, String ht
.replace("https:&#x2F;&#x2F;www." + retailDomain + ":443" + "&#x2F;", servletUrl)
.replace("http://www." + retailDomain + "/", servletUrl)
.replace("http:&#x2F;&#x2F;www." + retailDomain + "&#x2F;", servletUrl);

resp.addHeader(HttpHeader.CONTENT_TYPE.asString(), MimeTypes.Type.TEXT_HTML_UTF_8.asString());
resp.getWriter().write(resultHtml);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -215,14 +215,14 @@ public boolean isSequenceNodeQueueRunning() {
(queueObjects) -> (queueObjects.stream().anyMatch(queueObject -> queueObject.future != null)));
}

public boolean tryRestoreLogin(@Nullable String data, @Nullable String overloadedDomain) {
public boolean restoreLogin(@Nullable String data, @Nullable String overloadedDomain) {
try {
// verify stored data
if (data != null && !data.isEmpty() && loginData.deserialize(data)) {
if (overloadedDomain != null) {
loginData.setRetailDomain(overloadedDomain);
}
checkRenewSession();
renewTokens();
if (verifyLogin()) {
return true;
}
Expand Down Expand Up @@ -362,7 +362,7 @@ public String getAccessToken() throws ConnectionException {
* @return {@code true} when the session tokens have been renewed, {@code false} otherwise
* @throws ConnectionException when an error occurred
*/
public boolean checkRenewSession() throws ConnectionException {
public boolean renewTokens() throws ConnectionException {
if (System.currentTimeMillis() >= this.accessTokenExpiryTime) {
String renewTokenPostData = "app_name=Amazon%20Alexa" //
+ "&app_version=" + AmazonEchoControlBindingConstants.API_VERSION //
Expand Down Expand Up @@ -392,7 +392,6 @@ public boolean checkRenewSession() throws ConnectionException {
if (System.currentTimeMillis() > this.connectionExpiryTime) {
exchangeToken(loginData.getRetailDomain());
}
return true;
}
return false;
}
Expand Down Expand Up @@ -739,44 +738,6 @@ public void bluetooth(DeviceTO device, @Nullable String address) throws Connecti
}
}

public void playRadio(DeviceTO device, @Nullable String stationId) throws ConnectionException {
if (stationId == null || stationId.isEmpty()) {
command(device, Map.of("type", "PauseCommand"));
} else {
String content = "[\"music/tuneIn/stationId\",\"" + stationId + "\"]|{\"previousPageId\":null}";
String contentToken = new String(
Base64.getEncoder().encode(Base64.getEncoder().encode(content.getBytes(StandardCharsets.UTF_8))));
String url = getAlexaServer() + "/api/entertainment/v1/player/queue?deviceSerialNumber="
+ device.serialNumber + "&deviceType=" + device.deviceType;
requestBuilder.put(url).withContent(Map.of("contentToken", "music:" + contentToken)).retry(false)
.syncSend();
}
}

public void playAmazonMusicTrack(DeviceTO device, @Nullable String trackId) throws ConnectionException {
if (trackId == null || trackId.isEmpty()) {
command(device, Map.of("type", "PauseCommand"));
} else {
Map<String, Object> command = Map.of("trackId", trackId, "playQueuePrime", true);
String url = getAlexaServer() + "/api/cloudplayer/queue-and-play?deviceSerialNumber=" + device.serialNumber
+ "&deviceType=" + device.deviceType + "&mediaOwnerCustomerId=" + loginData.getAccountCustomerId()
+ "&shuffle=false";
requestBuilder.post(url).withContent(command).retry(false).syncSend();
}
}

public void playAmazonMusicPlayList(DeviceTO device, @Nullable String playListId) throws ConnectionException {
if (playListId == null || playListId.isEmpty()) {
command(device, Map.of("type", "PauseCommand"));
} else {
Map<String, Object> command = Map.of("playlistId", playListId, "playQueuePrime", true);
String url = getAlexaServer() + "/api/cloudplayer/queue-and-play?deviceSerialNumber=" + device.serialNumber
+ "&deviceType=" + device.deviceType + "&mediaOwnerCustomerId=" + loginData.getAccountCustomerId()
+ "&shuffle=false";
requestBuilder.post(url).withContent(command).retry(false).syncSend();
}
}

public void announcement(DeviceTO device, String speak, String bodyText, @Nullable String title,
@Nullable Integer ttsVolume, @Nullable Integer standardVolume) {
String trimmedSpeak = speak.replaceAll("\\s+", " ").trim();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ public class MediaStateTO {
public List<MediaStateQueueEntryTO> queue = List.of();
public String queueId;
public int queueSize;
public String radioStationId;
public int radioVariety;
public String referenceId;
public String service;
Expand All @@ -53,8 +52,7 @@ public class MediaStateTO {
+ ", isLiked=" + isLiked + ", looping=" + looping + ", mediaOwnerCustomerId='" + mediaOwnerCustomerId
+ "', muted=" + muted + ", programId='" + programId + "', progressSeconds=" + progressSeconds
+ ", providerId='" + providerId + "', queue=" + queue + ", queueId='" + queueId + "', queueSize="
+ queueSize + ", radioStationId='" + radioStationId + "', radioVariety=" + radioVariety
+ ", referenceId='" + referenceId + "', service='" + service + "', shuffling=" + shuffling + ", volume="
+ volume + "}";
+ queueSize + "', radioVariety=" + radioVariety + ", referenceId='" + referenceId + "', service='"
+ service + "', shuffling=" + shuffling + ", volume=" + volume + "}";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -344,21 +344,21 @@ private void checkLogin() {

try {
if (currentConnection.isLoggedIn()) {
if (currentConnection.checkRenewSession()) {
setConnection(currentConnection);
if (currentConnection.renewTokens()) {
storeSession(currentConnection);
}
} else {
// read session data from property
String sessionStore = this.stateStorage.get("sessionStorage");
String sessionStore = stateStorage.get("sessionStorage");

// try to use the session data
if (currentConnection.tryRestoreLogin(sessionStore, null)) {
if (currentConnection.restoreLogin(sessionStore, null)) {
setConnection(currentConnection);
}
}
if (!currentConnection.isLoggedIn()) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_PENDING,
"Please login in through web site: http(s)://<YOUROPENHAB>:<YOURPORT>/amazonechocontrol/"
"Please login in through servlet: http(s)://<YOUROPENHAB>:<YOURPORT>/amazonechocontrol/"
+ URLEncoder.encode(uid.getId(), StandardCharsets.UTF_8));
}

Expand Down Expand Up @@ -386,9 +386,7 @@ public void unsetConnection() {
public void setConnection(Connection connection) {
pushConnection.close();
this.connection = connection;

String serializedStorage = connection.getLoginData().serializeLoginData();
this.stateStorage.put("sessionStorage", serializedStorage);
storeSession(connection);

updateDeviceList();
updateSmartHomeDeviceList(false);
Expand All @@ -398,6 +396,11 @@ public void setConnection(Connection connection) {
checkData();
}

private void storeSession(Connection connection) {
String serializedStorage = connection.getLoginData().serializeLoginData();
this.stateStorage.put("sessionStorage", serializedStorage);
}

private void checkData() {
synchronized (synchronizeConnection) {
try {
Expand Down Expand Up @@ -466,9 +469,6 @@ private void refreshData() {
List<NotificationSoundTO> notificationSounds = currentConnection.getNotificationSounds(device);
commandDescriptionProvider.setEchoHandlerAlarmSounds(child, notificationSounds);

// update playlists
commandDescriptionProvider.setEchoHandlerPlaylists(child, currentConnection.getPlaylists(device));

BluetoothStateTO bluetoothState = findIn(bluetoothStates, k -> k.deviceSerialNumber,
device.serialNumber).orElse(null);

Expand Down Expand Up @@ -642,7 +642,8 @@ public void onPushCommandReceived(PushCommandTO pushCommand) {
refreshActivityJob.cancel(false);
}
this.refreshActivityJob = scheduler.schedule(
() -> handlePushActivity(dopplerId, pushCommand.timeStamp), 10, TimeUnit.SECONDS);
() -> handlePushActivity(dopplerId, pushCommand.timeStamp),
handlerConfig.activityRequestDelay, TimeUnit.SECONDS);
}
}
}
Expand Down
Loading

0 comments on commit 49939d6

Please sign in to comment.