added favorite browsing

master
nymea 2019-10-11 13:49:48 +02:00
parent 3a40115214
commit 19c0a28366
4 changed files with 196 additions and 77 deletions

View File

@ -48,44 +48,6 @@ void DevicePluginSonos::setupDevice(DeviceSetupInfo *info)
{
Device *device = info->device();
if (!m_pluginTimer5sec) {
m_pluginTimer5sec = hardwareManager()->pluginTimerManager()->registerTimer(5);
connect(m_pluginTimer5sec, &PluginTimer::timeout, this, [this]() {
foreach (Device *connectionDevice, myDevices().filterByDeviceClassId(sonosConnectionDeviceClassId)) {
Sonos *sonos = m_sonosConnections.value(connectionDevice);
if (!sonos) {
qWarning(dcSonos()) << "No sonos connection found to device" << connectionDevice->name();
continue;
}
foreach (Device *groupDevice, myDevices().filterByParentDeviceId(connectionDevice->id())) {
if (groupDevice->deviceClassId() == sonosGroupDeviceClassId) {
//get playback status of each group
QString groupId = groupDevice->paramValue(sonosGroupDeviceGroupIdParamTypeId).toString();
sonos->getGroupPlaybackStatus(groupId);
sonos->getGroupMetadataStatus(groupId);
sonos->getGroupVolume(groupId);
}
}
}
});
}
if (!m_pluginTimer60sec) {
m_pluginTimer60sec = hardwareManager()->pluginTimerManager()->registerTimer(60);
connect(m_pluginTimer60sec, &PluginTimer::timeout, this, [this]() {
foreach (Device *device, myDevices().filterByDeviceClassId(sonosConnectionDeviceClassId)) {
Sonos *sonos = m_sonosConnections.value(device);
if (!sonos) {
qWarning(dcSonos()) << "No sonos connection found to device" << device->name();
continue;
}
//get groups for each household in order to add or remove groups
sonos->getHouseholds();
}
});
}
if (device->deviceClassId() == sonosConnectionDeviceClassId) {
Sonos *sonos;
if (m_setupSonosConnections.keys().contains(device->id())) {
@ -126,6 +88,7 @@ void DevicePluginSonos::setupDevice(DeviceSetupInfo *info)
connect(sonos, &Sonos::volumeReceived, this, &DevicePluginSonos::onVolumeReceived);
connect(sonos, &Sonos::actionExecuted, this, &DevicePluginSonos::onActionExecuted);
connect(sonos, &Sonos::authenticationStatusChanged, this, &DevicePluginSonos::onAuthenticationStatusChanged);
connect(sonos, &Sonos::favouritesReceived, this, &DevicePluginSonos::onFavouritesReceived);
sonos->getAccessTokenFromRefreshToken(refreshToken);
m_sonosConnections.insert(device, sonos);
return info->finish(Device::DeviceErrorNoError);
@ -204,6 +167,44 @@ void DevicePluginSonos::confirmPairing(DevicePairingInfo *info, const QString &u
void DevicePluginSonos::postSetupDevice(Device *device)
{
if (!m_pluginTimer5sec) {
m_pluginTimer5sec = hardwareManager()->pluginTimerManager()->registerTimer(5);
connect(m_pluginTimer5sec, &PluginTimer::timeout, this, [this]() {
foreach (Device *connectionDevice, myDevices().filterByDeviceClassId(sonosConnectionDeviceClassId)) {
Sonos *sonos = m_sonosConnections.value(connectionDevice);
if (!sonos) {
qWarning(dcSonos()) << "No sonos connection found to device" << connectionDevice->name();
continue;
}
foreach (Device *groupDevice, myDevices().filterByParentDeviceId(connectionDevice->id())) {
if (groupDevice->deviceClassId() == sonosGroupDeviceClassId) {
//get playback status of each group
QString groupId = groupDevice->paramValue(sonosGroupDeviceGroupIdParamTypeId).toString();
sonos->getGroupPlaybackStatus(groupId);
sonos->getGroupMetadataStatus(groupId);
sonos->getGroupVolume(groupId);
}
}
}
});
}
if (!m_pluginTimer60sec) {
m_pluginTimer60sec = hardwareManager()->pluginTimerManager()->registerTimer(60);
connect(m_pluginTimer60sec, &PluginTimer::timeout, this, [this]() {
foreach (Device *device, myDevices().filterByDeviceClassId(sonosConnectionDeviceClassId)) {
Sonos *sonos = m_sonosConnections.value(device);
if (!sonos) {
qWarning(dcSonos()) << "No sonos connection found to device" << device->name();
continue;
}
//get groups for each household in order to add or remove groups
sonos->getHouseholds();
}
});
}
if (device->deviceClassId() == sonosConnectionDeviceClassId) {
Sonos *sonos = m_sonosConnections.value(device);
sonos->getHouseholds();
@ -331,6 +332,55 @@ void DevicePluginSonos::executeAction(DeviceActionInfo *info)
info->finish(Device::DeviceErrorDeviceClassNotFound);
}
void DevicePluginSonos::browseDevice(BrowseResult *result)
{
Device *parentDevice = myDevices().findById(result->device()->parentId());
Sonos *sonosConnection = m_sonosConnections.value(parentDevice);
if (!sonosConnection)
return;
qDebug(dcSonos()) << "Browse Device" << result->itemId();
QString householdId = result->device()->paramValue(sonosGroupDeviceHouseholdIdParamTypeId).toString();
if (result->itemId().isEmpty()){
BrowserItem item;
item.setId("favorites");
item.setIcon(BrowserItem::BrowserIconFavorites);
item.setExecutable(false);
item.setBrowsable(true);
item.setDisplayName("Favorites");
result->addItem(item);
result->finish(Device::DeviceErrorNoError);
} else if (result->itemId() == "favorites") {
sonosConnection->getFavorites(householdId);
m_pendingBrowseResult.insert(householdId, result);
} else {
//TODO add media browsing
result->finish(Device::DeviceErrorItemNotFound);
}
}
void DevicePluginSonos::browserItem(BrowserItemResult *result)
{
Q_UNUSED(result)
}
void DevicePluginSonos::executeBrowserItem(BrowserActionInfo *info)
{
Device *parentDevice = myDevices().findById(info->device()->parentId());
Sonos *sonosConnection = m_sonosConnections.value(parentDevice);
if (!sonosConnection)
return;
QString groupId = info->device()->paramValue(sonosGroupDeviceGroupIdParamTypeId).toString();
QUuid requestId = sonosConnection->loadFavorite(groupId, info->browserAction().itemId());
m_pendingBrowserExecution.insert(requestId, info);
}
void DevicePluginSonos::executeBrowserItemAction(BrowserItemActionInfo *info)
{
Q_UNUSED(info)
}
void DevicePluginSonos::onConnectionChanged(bool connected)
{
Sonos *sonos = static_cast<Sonos *>(sender());
@ -366,25 +416,45 @@ void DevicePluginSonos::onHouseholdIdsReceived(QList<QString> householdIds)
Sonos *sonos = static_cast<Sonos *>(sender());
foreach(QString householdId, householdIds) {
sonos->getGroups(householdId);
sonos->getFavorites(householdId);
sonos->getPlaylists(householdId);
}
}
void DevicePluginSonos::onFavouritesReceived(const QString &householdId, QList<Sonos::FavouriteObject> favourites)
{
Q_UNUSED(householdId);
foreach(Sonos::FavouriteObject favourite, favourites) {
qDebug(dcSonos()) << "Favourite: " << favourite.name << favourite.description;
if (m_pendingBrowseResult.contains(householdId)) {
BrowseResult *result = m_pendingBrowseResult.take(householdId);
if (!result)
return;
foreach(Sonos::FavouriteObject favourite, favourites) {
BrowserItem item;
item.setId(favourite.id);
item.setExecutable(true);
item.setBrowsable(false);
if (!favourite.imageUrl.isEmpty()) {
item.setThumbnail(favourite.imageUrl);
} else {
item.setIcon(BrowserItem::BrowserIconFavorites);
}
item.setDisplayName(favourite.name);
item.setDescription(favourite.description);
result->addItem(item);
qDebug(dcSonos()) << "Favourite: " << favourite.name << favourite.description;
}
result->finish(Device::DeviceErrorNoError);
} else {
qDebug(dcSonos()) << "Received unhandled favourites list";
}
}
void DevicePluginSonos::onPlaylistsReceived(const QString &householdId, QList<Sonos::PlaylistObject> playlists)
{
Sonos *sonos = static_cast<Sonos *>(sender());
foreach(Sonos::PlaylistObject playlist, playlists) {
qDebug(dcSonos()) << "Playlist: " << playlist.name << playlist.type << playlist.trackCount;
sonos->getPlaylist(householdId, playlist.id);
sonos->getPlaylist(householdId, playlist.id); //Get the playlist details
}
}
@ -399,7 +469,6 @@ void DevicePluginSonos::onPlaylistSummaryReceived(const QString &householdId, So
void DevicePluginSonos::onGroupsReceived(const QString &householdId, QList<Sonos::GroupObject> groupObjects)
{
Q_UNUSED(householdId);
Sonos *sonos = static_cast<Sonos *>(sender());
Device *parentDevice = m_sonosConnections.key(sonos);
if (!parentDevice)
@ -409,12 +478,16 @@ void DevicePluginSonos::onGroupsReceived(const QString &householdId, QList<Sonos
foreach(Sonos::GroupObject groupObject, groupObjects) {
Device *groupDevice = myDevices().findByParams(ParamList() << Param(sonosGroupDeviceGroupIdParamTypeId, groupObject.groupId));
if (groupDevice) {
groupDevice->setName(groupObject.displayName);
if (groupDevice->name() != groupObject.displayName) {
qDebug(dcSonos()) << "Updating group name" << groupDevice->name() << "to" << groupObject.displayName;
groupDevice->setName(groupObject.displayName);
}
} else {
//new device, add to the system
DeviceDescriptor deviceDescriptor(sonosGroupDeviceClassId, groupObject.displayName, "Sonos Group", parentDevice->id());
ParamList params;
params.append(Param(sonosGroupDeviceGroupIdParamTypeId, groupObject.groupId));
params.append(Param(sonosGroupDeviceHouseholdIdParamTypeId, householdId));
deviceDescriptor.setParams(params);
deviceDescriptors.append(deviceDescriptor);
}
@ -478,8 +551,11 @@ void DevicePluginSonos::onMetadataStatusReceived(const QString &groupId, Sonos::
device->setStateValue(sonosGroupTitleStateTypeId, metaDataStatus.currentItem.track.name);
device->setStateValue(sonosGroupArtistStateTypeId, metaDataStatus.currentItem.track.artist.name);
device->setStateValue(sonosGroupCollectionStateTypeId, metaDataStatus.currentItem.track.album.name);
//device->setStateValue(sonosGroupArtworkStateTypeId, metaDataStatus.currentItem.track.imageUrl);
device->setStateValue(sonosGroupArtworkStateTypeId, metaDataStatus.container.imageUrl);
if (!metaDataStatus.currentItem.track.imageUrl.isEmpty()){
device->setStateValue(sonosGroupArtworkStateTypeId, metaDataStatus.currentItem.track.imageUrl);
} else {
device->setStateValue(sonosGroupArtworkStateTypeId, metaDataStatus.container.imageUrl);
}
}
void DevicePluginSonos::onVolumeReceived(const QString &groupId, Sonos::VolumeObject groupVolume)
@ -506,4 +582,18 @@ void DevicePluginSonos::onActionExecuted(QUuid sonosActionId, bool success)
info->finish(Device::DeviceErrorHardwareFailure);
}
}
if (m_pendingBrowserExecution.contains(sonosActionId)) {
BrowserActionInfo *info = m_pendingBrowserExecution.value(sonosActionId);
if (!info) {
qCWarning(dcSonos()) << "BrowseActionInfo has disappeared. Did it time out?";
return;
}
if (success) {
info->finish(Device::DeviceErrorNoError);
} else {
info->finish(Device::DeviceErrorHardwareFailure);
}
}
}

View File

@ -49,11 +49,16 @@ public:
void deviceRemoved(Device *device) override;
void executeAction(DeviceActionInfo *info) override;
void browseDevice(BrowseResult *result) override;
void browserItem(BrowserItemResult *result) override;
void executeBrowserItem(BrowserActionInfo *info) override;
void executeBrowserItemAction(BrowserItemActionInfo *info) override;
private:
PluginTimer *m_pluginTimer5sec = nullptr;
PluginTimer *m_pluginTimer60sec = nullptr;
QHash<DeviceId, Sonos *> m_setupSonosConnections;
QHash<DeviceId, Sonos *> m_setupSonosConnections;
QHash<Device *, Sonos *> m_sonosConnections;
QList<QByteArray> m_householdIds;
@ -61,6 +66,9 @@ private:
QByteArray m_sonosConnectionRefreshToken;
QHash<QUuid, QPointer<DeviceActionInfo> > m_pendingActions;
QHash<QString, BrowseResult *> m_pendingBrowseResult;
QHash<QUuid, BrowserActionInfo *> m_pendingBrowserExecution;
private slots:
void onConnectionChanged(bool connected);

View File

@ -22,8 +22,8 @@
{
"id": "5aa4360c-61de-47d0-a72e-a19d57712e1c",
"name": "connected",
"displayName": "connected",
"displayNameEvent": "connected changed",
"displayName": "Connected",
"displayNameEvent": "Connected changed",
"defaultValue": true,
"type": "bool"
},
@ -51,28 +51,35 @@
"displayName": "Sonos group",
"interfaces": ["extendedvolumecontroller", "mediametadataprovider", "shufflerepeat", "connectable"],
"createMethods": ["auto"],
"browsable": true,
"paramTypes": [
{
"id": "defc44cd-2ffb-4af1-b348-d6a3474c7515",
"name": "groupId",
"displayName": "Group id",
"type" : "QString"
},
{
"id": "f8a5d3d8-fad9-441d-b345-3484524490a0",
"name": "householdId",
"displayName": "Household id",
"type" : "QString"
}
],
"stateTypes": [
{
"id": "09dfbd40-c97c-4a20-9ecd-f80e389a4864",
"name": "connected",
"displayName": "connected",
"displayNameEvent": "connected changed",
"displayName": "Connected",
"displayNameEvent": "Connected changed",
"defaultValue": false,
"type": "bool"
},
{
"id": "bc98cdb0-4d0e-48ca-afc7-922e49bb7813",
"name": "mute",
"displayName": "mute",
"displayNameEvent": "mute changed",
"displayName": "Mute",
"displayNameEvent": "Mute changed",
"displayNameAction": "Set mute",
"type": "bool",
"defaultValue": true,
@ -81,8 +88,8 @@
{
"id": "9dfe5d78-4c3f-497c-bab1-bb9fdf7e93a9",
"name": "volume",
"displayName": "volume",
"displayNameEvent": "volume changed",
"displayName": "Volume",
"displayNameEvent": "Volume changed",
"displayNameAction": "Set volume",
"unit": "Percentage",
"type": "int",
@ -94,12 +101,12 @@
{
"id": "2dd512b7-40c2-488e-8d4f-6519edaa6f74",
"name": "playbackStatus",
"displayName": "playback status",
"displayName": "Playback status",
"type": "QString",
"possibleValues": ["Playing", "Paused", "Stopped"],
"defaultValue": "Stopped",
"displayNameEvent": "playback status changed",
"displayNameAction": "set playback status",
"displayNameEvent": "Playback status changed",
"displayNameAction": "Set playback status",
"writable": true
},
{
@ -160,49 +167,56 @@
{
"id": "2535a1eb-7643-4874-98f6-b027fdff6311",
"name": "onPlayerPlay",
"displayName": "player play"
"displayName": "Group play"
},
{
"id": "99498b1c-e9c0-480a-9e91-662ee79ba976",
"name": "onPlayerPause",
"displayName": "player pause"
"displayName": "Group pause"
},
{
"id": "a02ce255-3abb-435d-a92e-7f99c952ecb2",
"name": "onPlayerStop",
"displayName": "player stop"
"displayName": "Group stop"
}
],
"actionTypes": [
{
"id": "a180807d-1265-4831-9d86-a421767418dd",
"name": "skipBack",
"displayName": "skip back"
"displayName": "Skip back"
},
{
"id": "7e70b47b-7e79-4521-be34-04a3c427e5b1",
"name": "fastRewind",
"displayName": "rewind"
"displayName": "Rewind"
},
{
"id": "ae3cbe03-ee3e-410e-abbd-efabc2402198",
"name": "stop",
"displayName": "stop"
"displayName": "Stop"
},
{
"id": "4d2ee668-a2e3-4795-8b96-0c800b703b46",
"name": "play",
"displayName": "play"
"displayName": "Play"
},
{
"id": "3cf341cb-fe63-40bc-a450-9678d18e91e3",
"name": "pause",
"displayName": "pause"
"displayName": "Pause"
},
{
"id": "85d7126a-b123-4a28-aeb4-d84bcfb4d14f",
"name": "skipNext",
"displayName": "skipNext"
"displayName": "Skip Next"
}
],
"browserItemActionTypes": [
{
"id": "b056af0f-4d5b-4f75-b0dd-e73246d3a83f",
"name": "updateLibrary",
"displayName": "Update library"
}
]
}

View File

@ -111,7 +111,7 @@ void Sonos::getHouseholds()
QList<QString> households;
foreach (const QVariant &variant, data.toVariant().toMap().value("households").toList()) {
QVariantMap obj = variant.toMap();
qDebug(dcSonos()) << "Household ID received:" << obj["id"].toString();
//qDebug(dcSonos()) << "Household ID received:" << obj["id"].toString();
households.append(obj["id"].toString());
}
emit householdIdsReceived(households);
@ -125,12 +125,14 @@ QUuid Sonos::loadFavorite(const QString &groupId, const QString &favouriteId)
request.setHeader(QNetworkRequest::KnownHeaders::ContentTypeHeader, "application/json");
request.setRawHeader("Authorization", "Bearer " + m_accessToken);
request.setRawHeader("X-Sonos-Api-Key", m_clientKey);
request.setUrl(QUrl(m_baseControlUrl + "/groups/" + groupId + "/favourites"));
request.setUrl(QUrl(m_baseControlUrl + "/groups/" + groupId + "/favorites"));
QUuid actionId = QUuid::createUuid();
QJsonObject object;
object.insert("favoriteId", QJsonValue::fromVariant(favouriteId));
object.insert("favoriteId", favouriteId);
object.insert("playOnCompletion", true);
QJsonDocument doc(object);
qDebug(dcSonos()) << "Sending request" << doc.toJson();
QNetworkReply *reply = m_networkManager->post(request, doc.toJson(QJsonDocument::Compact));
connect(reply, &QNetworkReply::finished, this, [reply, actionId, this] {
@ -193,15 +195,16 @@ void Sonos::getFavorites(const QString &householdId)
return;
QVariantList array = data.toVariant().toMap().value("items").toList();
QList<FavouriteObject> favourites
;
//qDebug(dcSonos()) << "Favourites received:" << data.toJson();
QList<FavouriteObject> favourites;
foreach (const QVariant &variant, array) {
QVariantMap itemObject = variant.toMap();
qDebug(dcSonos()) << "Item ID received:" << itemObject["id"].toString();
FavouriteObject favourite;
favourite.id = itemObject["id"].toString();
favourite.name = itemObject["name"].toString();
favourite.description = itemObject["description"].toString();
favourite.imageUrl = itemObject["imageUrl"].toString();
favourites.append(favourite);
}
emit favouritesReceived(householdId, favourites);
@ -248,10 +251,16 @@ void Sonos::getGroups(const QString &householdId)
QList<GroupObject> groupObjects;
foreach (const QVariant &value, array) {
QVariantMap obj = value.toMap();
qDebug(dcSonos()) << "Group ID received:" << obj["id"].toString();
//qDebug(dcSonos()) << "Group ID received:" << obj["id"].toString();
GroupObject group;
group.groupId = obj["id"].toString();
group.displayName = obj["name"].toString();
group.CoordinatorId = obj["coordinatorId"].toString();
group.playbackState = obj["playbackState"].toString();
QVariantList players = obj.value("playerIds").toList();
foreach (const QVariant &value, players) {
group.playerIds.append(value.toByteArray());
}
groupObjects.append(group);
}
emit groupsReceived(householdId, groupObjects);
@ -1289,7 +1298,6 @@ void Sonos::getPlaylist(const QString &householdId, const QString &playlistId)
QVariantList array = variant["tracks"].toList();
foreach (const QVariant &value, array) {
QVariantMap itemObject = value.toMap();
qDebug(dcSonos()) << "Item ID received:" << itemObject["id"].toString();
PlaylistTrackObject track;
track.name = itemObject["name"].toString();
track.album = itemObject["album"].toString();
@ -1341,7 +1349,6 @@ void Sonos::getPlaylists(const QString &householdId)
QList<PlaylistObject> playlists;
foreach (const QVariant &value, array) {
QVariantMap itemObject = value.toMap();
qDebug(dcSonos()) << "Item ID received:" << itemObject["id"].toString();
PlaylistObject playlist;
playlist.id = itemObject["id"].toString();
playlist.name = itemObject["name"].toString();