From 90fd0a6f21c34745bde178afe19319cc57fdd1ea Mon Sep 17 00:00:00 2001 From: J-N-K Date: Wed, 6 Dec 2023 09:00:22 +0100 Subject: [PATCH] Fix customer history retrieval and internal list item support (#538) Signed-off-by: Jan N. Klug --- .../internal/connection/Connection.java | 42 +++++++++++++---- .../internal/connection/TextWrapper.java | 6 +++ .../dto/push/PushListItemChangeTO.java | 26 ++++++++++ .../internal/dto/response/ListItemTO.java | 39 +++++++++++++++ .../dto/response/NamedListsInfoTO.java | 47 +++++++++++++++++++ .../dto/response/NamedListsItemsTO.java | 32 +++++++++++++ .../internal/handler/AccountHandler.java | 8 ++++ 7 files changed, 192 insertions(+), 8 deletions(-) create mode 100644 bundles/org.smarthomej.binding.amazonechocontrol/src/main/java/org/smarthomej/binding/amazonechocontrol/internal/dto/push/PushListItemChangeTO.java create mode 100644 bundles/org.smarthomej.binding.amazonechocontrol/src/main/java/org/smarthomej/binding/amazonechocontrol/internal/dto/response/ListItemTO.java create mode 100644 bundles/org.smarthomej.binding.amazonechocontrol/src/main/java/org/smarthomej/binding/amazonechocontrol/internal/dto/response/NamedListsInfoTO.java create mode 100644 bundles/org.smarthomej.binding.amazonechocontrol/src/main/java/org/smarthomej/binding/amazonechocontrol/internal/dto/response/NamedListsItemsTO.java diff --git a/bundles/org.smarthomej.binding.amazonechocontrol/src/main/java/org/smarthomej/binding/amazonechocontrol/internal/connection/Connection.java b/bundles/org.smarthomej.binding.amazonechocontrol/src/main/java/org/smarthomej/binding/amazonechocontrol/internal/connection/Connection.java index 5cac36ccfa..90327619d1 100644 --- a/bundles/org.smarthomej.binding.amazonechocontrol/src/main/java/org/smarthomej/binding/amazonechocontrol/internal/connection/Connection.java +++ b/bundles/org.smarthomej.binding.amazonechocontrol/src/main/java/org/smarthomej/binding/amazonechocontrol/internal/connection/Connection.java @@ -99,9 +99,12 @@ import org.smarthomej.binding.amazonechocontrol.internal.dto.response.DeviceNotificationStatesTO; import org.smarthomej.binding.amazonechocontrol.internal.dto.response.DoNotDisturbDeviceStatusesTO; import org.smarthomej.binding.amazonechocontrol.internal.dto.response.EndpointTO; +import org.smarthomej.binding.amazonechocontrol.internal.dto.response.ListItemTO; import org.smarthomej.binding.amazonechocontrol.internal.dto.response.ListMediaSessionTO; import org.smarthomej.binding.amazonechocontrol.internal.dto.response.MediaSessionTO; import org.smarthomej.binding.amazonechocontrol.internal.dto.response.MusicProviderTO; +import org.smarthomej.binding.amazonechocontrol.internal.dto.response.NamedListsInfoTO; +import org.smarthomej.binding.amazonechocontrol.internal.dto.response.NamedListsItemsTO; import org.smarthomej.binding.amazonechocontrol.internal.dto.response.NotificationListResponseTO; import org.smarthomej.binding.amazonechocontrol.internal.dto.response.NotificationSoundResponseTO; import org.smarthomej.binding.amazonechocontrol.internal.dto.response.PlayerStateTO; @@ -592,14 +595,35 @@ public List getActivities(long startTime, long endTime) + "&endTime=" + endTime + "&maxRecordSize=1"; CustomerHistoryRecordsTO customerHistoryRecords = requestBuilder.get(url) .syncSend(CustomerHistoryRecordsTO.class); - return customerHistoryRecords.customerHistoryRecords.stream().sorted(Comparator.comparing(r -> r.timestamp)) - .toList(); + return customerHistoryRecords.customerHistoryRecords.stream() + .filter(r -> !"DEVICE_ARBITRATION".equals(r.utteranceType)) + .sorted(Comparator.comparing(r -> r.timestamp)).toList(); } catch (ConnectionException e) { logger.info("getting activities failed", e); } return List.of(); } + public @Nullable NamedListsInfoTO getNamedListInfo(String listId) { + try { + String url = getAlexaServer() + "/api/namedLists/" + listId + "?_=" + System.currentTimeMillis(); + return requestBuilder.get(url).syncSend(NamedListsInfoTO.class); + } catch (ConnectionException e) { + logger.info("getting information for list {} failed", listId, e); + } + return null; + } + + public List getNamedListItems(String listId) { + try { + String url = getAlexaServer() + "/api/namedLists/" + listId + "/items?_=" + System.currentTimeMillis(); + return requestBuilder.get(url).syncSend(NamedListsItemsTO.class).list; + } catch (ConnectionException e) { + logger.info("getting items from list '{}' failed", listId, e); + } + return List.of(); + } + public List getBluetoothConnectionStates() { try { String url = getAlexaServer() + "/api/bluetooth?cached=true"; @@ -841,6 +865,7 @@ private void sendTextToSpeech() { Iterator iterator = textToSpeeches.values().iterator(); while (iterator.hasNext()) { TextWrapper textToSpeech = iterator.next(); + logger.trace("Executing textToSpeech {}", textToSpeech); try { List devices = textToSpeech.getDevices(); if (!devices.isEmpty()) { @@ -848,8 +873,8 @@ private void sendTextToSpeech() { Map.of("textToSpeak", textToSpeech.getText()), textToSpeech.getTtsVolumes(), textToSpeech.getStandardVolumes()); } - } catch (Exception e) { - logger.warn("send textToSpeech fails with unexpected error", e); + } catch (RuntimeException e) { + logger.warn("Send textToSpeech failed with unexpected error", e); } iterator.remove(); } @@ -885,8 +910,8 @@ public void textCommand(DeviceTO device, String text, @Nullable Integer ttsVolum } } - private synchronized void sendTextCommand() { - // we lock new TTS until we have dispatched everything + private void sendTextCommand() { + // we lock new textCommands until we have dispatched everything Lock lock = Objects.requireNonNull(locks.computeIfAbsent(TimerType.TEXT_COMMAND, k -> new ReentrantLock())); lock.lock(); @@ -894,6 +919,7 @@ private synchronized void sendTextCommand() { Iterator iterator = textCommands.values().iterator(); while (iterator.hasNext()) { TextWrapper textCommand = iterator.next(); + logger.trace("Executing textCommand {}", textCommand); try { List devices = textCommand.getDevices(); if (!devices.isEmpty()) { @@ -901,8 +927,8 @@ private synchronized void sendTextCommand() { Map.of("text", textCommand.getText()), textCommand.getTtsVolumes(), textCommand.getStandardVolumes()); } - } catch (Exception e) { - logger.warn("send textCommand fails with unexpected error", e); + } catch (RuntimeException e) { + logger.warn("Sending textCommand failed with unexpected error", e); } iterator.remove(); } diff --git a/bundles/org.smarthomej.binding.amazonechocontrol/src/main/java/org/smarthomej/binding/amazonechocontrol/internal/connection/TextWrapper.java b/bundles/org.smarthomej.binding.amazonechocontrol/src/main/java/org/smarthomej/binding/amazonechocontrol/internal/connection/TextWrapper.java index 8149b4a890..eef7d09682 100644 --- a/bundles/org.smarthomej.binding.amazonechocontrol/src/main/java/org/smarthomej/binding/amazonechocontrol/internal/connection/TextWrapper.java +++ b/bundles/org.smarthomej.binding.amazonechocontrol/src/main/java/org/smarthomej/binding/amazonechocontrol/internal/connection/TextWrapper.java @@ -56,4 +56,10 @@ public List getDevices() { public String getText() { return text; } + + @Override + public String toString() { + return "TextWrapper{" + "devices=" + devices + ", text='" + text + "'" + ", ttsVolumes=" + ttsVolumes + + ", standardVolumes=" + standardVolumes + "}"; + } } diff --git a/bundles/org.smarthomej.binding.amazonechocontrol/src/main/java/org/smarthomej/binding/amazonechocontrol/internal/dto/push/PushListItemChangeTO.java b/bundles/org.smarthomej.binding.amazonechocontrol/src/main/java/org/smarthomej/binding/amazonechocontrol/internal/dto/push/PushListItemChangeTO.java new file mode 100644 index 0000000000..a064dea98f --- /dev/null +++ b/bundles/org.smarthomej.binding.amazonechocontrol/src/main/java/org/smarthomej/binding/amazonechocontrol/internal/dto/push/PushListItemChangeTO.java @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2021-2023 Contributors to the SmartHome/J 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.smarthomej.binding.amazonechocontrol.internal.dto.push; + +/** + * The {@link PushListItemChangeTO} encapsulates a PUSH_LIST_ITEM_CHANGE message + * + * @author Jan N. Klug - Initial contribution + */ +public class PushListItemChangeTO { + public String listId; + public String listItemId; + public int version; + public String eventName; + public String destinationUserId; +} diff --git a/bundles/org.smarthomej.binding.amazonechocontrol/src/main/java/org/smarthomej/binding/amazonechocontrol/internal/dto/response/ListItemTO.java b/bundles/org.smarthomej.binding.amazonechocontrol/src/main/java/org/smarthomej/binding/amazonechocontrol/internal/dto/response/ListItemTO.java new file mode 100644 index 0000000000..cb7fca3077 --- /dev/null +++ b/bundles/org.smarthomej.binding.amazonechocontrol/src/main/java/org/smarthomej/binding/amazonechocontrol/internal/dto/response/ListItemTO.java @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2021-2023 Contributors to the SmartHome/J 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.smarthomej.binding.amazonechocontrol.internal.dto.response; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * The {@link ListItemTO} encapsulates a single list item + * + * @author Jan N. Klug - Initial contribution + */ +public class ListItemTO { + public String listId; + public boolean shoppingListItem; + public String customerId; + public long createdDateTime; + public boolean completed; + public String id; + public String value; + public int version; + public long updatedDateTime; + + @Override + public @NonNull String toString() { + return "ListItemTO{listId='" + listId + "', shoppingListItem=" + shoppingListItem + ", customerId='" + + customerId + "', createdDateTime=" + createdDateTime + ", completed=" + completed + ", id='" + id + + "', value='" + value + "', version=" + version + ", updatedDateTime=" + updatedDateTime + "}"; + } +} diff --git a/bundles/org.smarthomej.binding.amazonechocontrol/src/main/java/org/smarthomej/binding/amazonechocontrol/internal/dto/response/NamedListsInfoTO.java b/bundles/org.smarthomej.binding.amazonechocontrol/src/main/java/org/smarthomej/binding/amazonechocontrol/internal/dto/response/NamedListsInfoTO.java new file mode 100644 index 0000000000..dd019aa511 --- /dev/null +++ b/bundles/org.smarthomej.binding.amazonechocontrol/src/main/java/org/smarthomej/binding/amazonechocontrol/internal/dto/response/NamedListsInfoTO.java @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2021-2023 Contributors to the SmartHome/J 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.smarthomej.binding.amazonechocontrol.internal.dto.response; + +import java.util.List; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * The {@link NamedListsInfoTO} encapsulates the response of /api/namedLists/listId + * + * @author Jan N. Klug - Initial contribution + */ +public class NamedListsInfoTO { + public List listIds; + public long updatedDate; + public String type; + public int version; + public boolean defaultList; + public boolean archived; + public String itemId; + public long createdDate; + public Object listReorderVersion; + public Object originalAudioId; + public String customerId; + public String name; + public Object nbestItems; + + @Override + public @NonNull String toString() { + return "NamedListsInfoTO{listIds=" + listIds + ", updatedDate=" + updatedDate + ", type='" + type + + "', version=" + version + ", defaultList=" + defaultList + ", archived=" + archived + ", itemId='" + + itemId + "', createdDate=" + createdDate + ", listReorderVersion=" + listReorderVersion + + ", originalAudioId=" + originalAudioId + ", customerId='" + customerId + "', name=" + name + + ", nbestItems=" + nbestItems + "}"; + } +} diff --git a/bundles/org.smarthomej.binding.amazonechocontrol/src/main/java/org/smarthomej/binding/amazonechocontrol/internal/dto/response/NamedListsItemsTO.java b/bundles/org.smarthomej.binding.amazonechocontrol/src/main/java/org/smarthomej/binding/amazonechocontrol/internal/dto/response/NamedListsItemsTO.java new file mode 100644 index 0000000000..c2d757af5d --- /dev/null +++ b/bundles/org.smarthomej.binding.amazonechocontrol/src/main/java/org/smarthomej/binding/amazonechocontrol/internal/dto/response/NamedListsItemsTO.java @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2021-2023 Contributors to the SmartHome/J 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.smarthomej.binding.amazonechocontrol.internal.dto.response; + +import java.util.List; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * The {@link NamedListsItemsTO} encapsulate the response of /api/namedLists/listId/items + * + * @author Jan N. Klug - Initial contribution + */ +public class NamedListsItemsTO { + public Object listReorderVersion; + public List list; + + @Override + public @NonNull String toString() { + return "NamedListsItemsTO{listReorderVersion=" + listReorderVersion + ", list=" + list + "}"; + } +} diff --git a/bundles/org.smarthomej.binding.amazonechocontrol/src/main/java/org/smarthomej/binding/amazonechocontrol/internal/handler/AccountHandler.java b/bundles/org.smarthomej.binding.amazonechocontrol/src/main/java/org/smarthomej/binding/amazonechocontrol/internal/handler/AccountHandler.java index f663bdf96f..2815052d1f 100644 --- a/bundles/org.smarthomej.binding.amazonechocontrol/src/main/java/org/smarthomej/binding/amazonechocontrol/internal/handler/AccountHandler.java +++ b/bundles/org.smarthomej.binding.amazonechocontrol/src/main/java/org/smarthomej/binding/amazonechocontrol/internal/handler/AccountHandler.java @@ -75,10 +75,12 @@ import org.smarthomej.binding.amazonechocontrol.internal.dto.push.PushCommandTO; import org.smarthomej.binding.amazonechocontrol.internal.dto.push.PushDeviceTO; import org.smarthomej.binding.amazonechocontrol.internal.dto.push.PushDopplerIdTO; +import org.smarthomej.binding.amazonechocontrol.internal.dto.push.PushListItemChangeTO; import org.smarthomej.binding.amazonechocontrol.internal.dto.request.SendConversationDTO; import org.smarthomej.binding.amazonechocontrol.internal.dto.response.AccountTO; import org.smarthomej.binding.amazonechocontrol.internal.dto.response.BluetoothStateTO; import org.smarthomej.binding.amazonechocontrol.internal.dto.response.CustomerHistoryRecordTO; +import org.smarthomej.binding.amazonechocontrol.internal.dto.response.ListItemTO; import org.smarthomej.binding.amazonechocontrol.internal.dto.response.MusicProviderTO; import org.smarthomej.binding.amazonechocontrol.internal.dto.response.WakeWordTO; import org.smarthomej.binding.amazonechocontrol.internal.dto.smarthome.JsonSmartHomeDevice; @@ -646,6 +648,12 @@ public void onPushCommandReceived(PushCommandTO pushCommand) { // echoHandlers.forEach(e -> e.refreshAudioPlayerState(true)); echoHandlers.values().forEach(EchoHandler::updateMediaSessions); break; + case "PUSH_LIST_ITEM_CHANGE": + PushListItemChangeTO itemChange = Objects + .requireNonNull(gson.fromJson(payload, PushListItemChangeTO.class)); + List lists = connection.getNamedListItems(itemChange.listId); + // TODO: create channels + break; default: logger.warn("Detected unknown command from activity stream: {}", pushCommand); }