From 4254d3cb3cbfaea2041a8c8fda48da9c58efab92 Mon Sep 17 00:00:00 2001 From: nymea Date: Sun, 1 Sep 2019 21:41:10 +0200 Subject: [PATCH] added plugin Storage for tokens --- sonos/devicepluginsonos.cpp | 180 +++++++++++++++++++++++------------ sonos/devicepluginsonos.h | 10 ++ sonos/devicepluginsonos.json | 2 + sonos/sonos.cpp | 147 ++++++++++++++++++---------- sonos/sonos.h | 89 ++++++++++------- 5 files changed, 283 insertions(+), 145 deletions(-) diff --git a/sonos/devicepluginsonos.cpp b/sonos/devicepluginsonos.cpp index bfdbabca..bba6e3a3 100644 --- a/sonos/devicepluginsonos.cpp +++ b/sonos/devicepluginsonos.cpp @@ -45,15 +45,23 @@ DevicePluginSonos::~DevicePluginSonos() Device::DeviceSetupStatus DevicePluginSonos::setupDevice(Device *device) { if (!m_pluginTimer) { - + hardwareManager()->pluginTimerManager()->registerTimer(1); } + + if(!m_tokenRefreshTimer) { + m_tokenRefreshTimer = new QTimer(this); + m_tokenRefreshTimer->setSingleShot(false); + connect(m_tokenRefreshTimer, &QTimer::timeout, this, &DevicePluginSonos::onRefreshTimeout); + } + if (device->deviceClassId() == sonosConnectionDeviceClassId) { qCDebug(dcSonos()) << "Sonos OAuth setup complete"; - Sonos *sonos = new Sonos(hardwareManager()->networkManager(), "0a8f6d44-d9d1-4474-bcfa-cfb41f8b66e8", this); + pluginStorage()->beginGroup(device->id().toString()); - QString username = pluginStorage()->value("username").toString(); - QString password = pluginStorage()->value("password").toString(); + QByteArray accessToken = pluginStorage()->value("access_token").toByteArray(); pluginStorage()->endGroup(); + + Sonos *sonos = new Sonos(hardwareManager()->networkManager(), accessToken, this); m_sonosConnections.insert(device, sonos); } @@ -98,49 +106,66 @@ DevicePairingInfo DevicePluginSonos::confirmPairing(DevicePairingInfo &devicePai qCDebug(dcSonos()) << "Confirm pairing"; if (devicePairingInfo.deviceClassId() == sonosConnectionDeviceClassId) { - qCDebug(dcSonos()) << "Secret is" << secret; - QUrl url(secret); - QUrlQuery query(url); - qCDebug(dcSonos()) << "Acess code is:" << query.queryItemValue("code"); + qCDebug(dcSonos()) << "Secret is" << secret; + QUrl url(secret); + QUrlQuery query(url); + qCDebug(dcSonos()) << "Acess code is:" << query.queryItemValue("code"); - QString accessCode = query.queryItemValue("code"); + QString accessCode = query.queryItemValue("code"); - // Obtaining access token - url = QUrl("https://api.sonos.com/login/v3/oauth/access"); - query.clear(); - query.addQueryItem("grant_type", "authorization_code"); - query.addQueryItem("code", accessCode); - query.addQueryItem("redirect_uri", "https%3A%2F%2F127.0.0.1%3A8888"); - url.setQuery(query); + // Obtaining access token + url = QUrl("https://api.sonos.com/login/v3/oauth/access"); + query.clear(); + query.addQueryItem("grant_type", "authorization_code"); + query.addQueryItem("code", accessCode); + query.addQueryItem("redirect_uri", "https%3A%2F%2F127.0.0.1%3A8888"); + url.setQuery(query); - QNetworkRequest request(url); - request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded;charset=utf-8"); + QNetworkRequest request(url); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded;charset=utf-8"); - QByteArray clientId = "b15cbf8c-a39c-47aa-bd93-635a96e9696c"; - QByteArray clientSecret = "c086ba71-e562-430b-a52f-867c6482fd11"; + QByteArray clientId = "b15cbf8c-a39c-47aa-bd93-635a96e9696c"; + QByteArray clientSecret = "c086ba71-e562-430b-a52f-867c6482fd11"; - QByteArray auth = QByteArray(clientId + ':' + clientSecret).toBase64(QByteArray::Base64Encoding | QByteArray::KeepTrailingEquals); - request.setRawHeader("Authorization", QString("Basic %1").arg(QString(auth)).toUtf8()); + QByteArray auth = QByteArray(clientId + ':' + clientSecret).toBase64(QByteArray::Base64Encoding | QByteArray::KeepTrailingEquals); + request.setRawHeader("Authorization", QString("Basic %1").arg(QString(auth)).toUtf8()); - QNetworkReply *reply = hardwareManager()->networkManager()->post(request, QByteArray()); - connect(reply, &QNetworkReply::finished, this, [this, reply, devicePairingInfo](){ - reply->deleteLater(); + QNetworkReply *reply = hardwareManager()->networkManager()->post(request, QByteArray()); + connect(reply, &QNetworkReply::finished, this, [this, reply, devicePairingInfo](){ + reply->deleteLater(); + DevicePairingInfo info(devicePairingInfo); - QJsonDocument jsonDoc = QJsonDocument::fromJson(reply->readAll()); - qCDebug(dcSonos()) << "Sonos accessToken reply:" << this << reply->error() << reply->errorString() << jsonDoc.toJson(); - qCDebug(dcSonos()) << "Access token:" << jsonDoc.toVariant().toMap().value("access_token").toString(); - qCDebug(dcSonos()) << "expires at" << QDateTime::currentDateTime().addSecs(jsonDoc.toVariant().toMap().value("expires_in").toInt()).toString(); - qCDebug(dcSonos()) << "Refresh token:" << jsonDoc.toVariant().toMap().value("refresh_token").toString(); - DevicePairingInfo info(devicePairingInfo); - info.setStatus(Device::DeviceErrorNoError); - emit pairingFinished(info); - }); + QJsonDocument jsonDoc = QJsonDocument::fromJson(reply->readAll()); + qCDebug(dcSonos()) << "Sonos accessToken reply:" << this << reply->error() << reply->errorString() << jsonDoc.toJson(); + if(!jsonDoc.toVariant().toMap().contains("access_token") || !jsonDoc.toVariant().toMap().contains("refresh_token") ) { + info.setStatus(Device::DeviceErrorSetupFailed); + emit pairingFinished(info); + return; + } + qCDebug(dcSonos()) << "Access token:" << jsonDoc.toVariant().toMap().value("access_token").toString(); + QByteArray accessToken = jsonDoc.toVariant().toMap().value("access_token").toByteArray(); - devicePairingInfo.setStatus(Device::DeviceErrorAsync); - return devicePairingInfo; - } + qCDebug(dcSonos()) << "Refresh token:" << jsonDoc.toVariant().toMap().value("refresh_token").toString(); + QByteArray refreshToken = jsonDoc.toVariant().toMap().value("refresh_token").toByteArray(); + pluginStorage()->beginGroup(info.deviceId().toString()); + pluginStorage()->setValue("access_token", accessToken); + pluginStorage()->setValue("refresh_token", refreshToken); + pluginStorage()->endGroup(); + /*if (jsonDoc.toVariant().toMap().contains("expires_in")) { + int expireTime = jsonDoc.toVariant().toMap().value("expires_in").toInt(); + qCDebug(dcSonos()) << "expires at" << QDateTime::currentDateTime().addSecs(expireTime).toString(); + m_tokenRefreshTimer->start((expireTime - 20) * 1000); + }*/ + + info.setStatus(Device::DeviceErrorNoError); + emit pairingFinished(info); + }); + + devicePairingInfo.setStatus(Device::DeviceErrorAsync); + return devicePairingInfo; + } qCWarning(dcSonos()) << "Invalid deviceclassId -> no pairing possible with this device"; devicePairingInfo.setStatus(Device::DeviceErrorHardwareFailure); return devicePairingInfo; @@ -149,7 +174,8 @@ DevicePairingInfo DevicePluginSonos::confirmPairing(DevicePairingInfo &devicePai void DevicePluginSonos::postSetupDevice(Device *device) { if (device->deviceClassId() == sonosConnectionDeviceClassId) { - + Sonos *sonos = m_sonosConnections.value(device); + sonos->getHouseholds(); } if (device->deviceClassId() == sonosGroupDeviceClassId) { @@ -180,69 +206,59 @@ Device::DeviceError DevicePluginSonos::executeAction(Device *device, const Actio Q_UNUSED(action) if (device->deviceClassId() == sonosGroupDeviceClassId) { Sonos *sonos = m_sonosConnections.value(device); - //int groupId = device->paramValue(sonosGroupDe) + QByteArray groupId = device->paramValue(sonosGroupDeviceGroupIdParamTypeId).toByteArray(); + if (!sonos) return Device::DeviceErrorInvalidParameter; if (action.actionTypeId() == sonosGroupPlayActionTypeId) { - sonos->play(); + sonos->groupPlay(groupId); return Device::DeviceErrorAsync; } if (action.actionTypeId() == sonosGroupShuffleActionTypeId) { - + bool shuffle = action.param(sonosGroupShuffleActionShuffleParamTypeId).value().toBool(); + sonos->groupSetShuffle(groupId, shuffle); return Device::DeviceErrorAsync; } if (action.actionTypeId() == sonosGroupRepeatActionTypeId) { if (action.param(sonosGroupRepeatActionRepeatParamTypeId).value().toString() == "None") { - + sonos->groupSetRepeat(groupId, Sonos::RepeatModeNone); } else if (action.param(sonosGroupShuffleActionShuffleParamTypeId).value().toString() == "One") { - + sonos->groupSetRepeat(groupId, Sonos::RepeatModeOne); } else if (action.param(sonosGroupShuffleActionShuffleParamTypeId).value().toString() == "All") { - + sonos->groupSetRepeat(groupId, Sonos::RepeatModeAll); } else { return Device::DeviceErrorHardwareFailure; } - - return Device::DeviceErrorAsync; } if (action.actionTypeId() == sonosGroupPauseActionTypeId) { - sonos->pause(); + sonos->groupPause(groupId); return Device::DeviceErrorNoError; } if (action.actionTypeId() == sonosGroupStopActionTypeId) { - //sonos->stop(); + sonos->groupPause(groupId); return Device::DeviceErrorNoError; } if (action.actionTypeId() == sonosGroupMuteActionTypeId) { - //bool mute = action.param(sonosGroupMuteActionMuteParamTypeId).value().toBool(); - - //sonos->setGroupMute(); + bool mute = action.param(sonosGroupMuteActionMuteParamTypeId).value().toBool(); + sonos->setGroupMute(groupId, mute); return Device::DeviceErrorNoError; } if (action.actionTypeId() == sonosGroupSkipNextActionTypeId) { - /*if (!m_sonosSystem->GetPlayer()->Next()) { - return Device::DeviceErrorHardwareFailure; - }*/ + sonos->groupSkipToNextTrack(groupId); return Device::DeviceErrorNoError; } if (action.actionTypeId() == sonosGroupSkipBackActionTypeId) { - /* if(!m_sonosSystem->GetPlayer()->Previous()) { - return Device::DeviceErrorHardwareFailure; - }*/ - return Device::DeviceErrorNoError; - } - - if (action.actionTypeId() == sonosGroupSkipBackActionTypeId) { - //int volume = action.param(sonosVolumeActionVolumeParamTypeId).value().toInt(); + sonos->groupSkipToPreviousTrack(groupId); return Device::DeviceErrorNoError; } return Device::DeviceErrorActionTypeNotFound; @@ -262,3 +278,43 @@ void DevicePluginSonos::onConnectionChanged() //TODO set all groups } + +void DevicePluginSonos::onRefreshTimeout() +{ + qCDebug(dcSonos) << "Refresh authentication token"; + + QUrlQuery query; + query.addQueryItem("grant_type", "refresh_token"); + query.addQueryItem("refresh_token", m_sonosConnectionRefreshToken); + + QUrl url("https://api.sonos.com/login/v3/oauth"); + QNetworkRequest request(url); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded; charset=UTF-8"); + QNetworkReply *reply = hardwareManager()->networkManager()->post(request, QByteArray()); + connect(reply, &QNetworkReply::finished, this, [this, reply](){ + reply->deleteLater(); + + QJsonDocument jsonDoc = QJsonDocument::fromJson(reply->readAll()); + qCDebug(dcSonos()) << "Sonos accessToken reply:" << this << reply->error() << reply->errorString() << jsonDoc.toJson(); + if(!jsonDoc.toVariant().toMap().contains("access_token")) { + return; + } + qCDebug(dcSonos()) << "Access token:" << jsonDoc.toVariant().toMap().value("access_token").toString(); + m_sonosConnectionAccessToken = jsonDoc.toVariant().toMap().value("access_token").toByteArray(); + + if (jsonDoc.toVariant().toMap().contains("expires_in")) { + int expireTime = jsonDoc.toVariant().toMap().value("expires_in").toInt(); + qCDebug(dcSonos()) << "expires at" << QDateTime::currentDateTime().addSecs(expireTime).toString(); + m_tokenRefreshTimer->start((expireTime - 20) * 1000); + } + }); +} + +void DevicePluginSonos::onHouseholdIdsReceived(QList householdIds) +{ + qDebug(dcSonos()) << "Household Id received, start to discover groups"; + Sonos *sonos = static_cast(sender()); + foreach(QByteArray householdId, householdIds) { + sonos->getGroups(householdId); + } +} diff --git a/sonos/devicepluginsonos.h b/sonos/devicepluginsonos.h index db84374b..075375b0 100644 --- a/sonos/devicepluginsonos.h +++ b/sonos/devicepluginsonos.h @@ -29,6 +29,7 @@ #include #include +#include class DevicePluginSonos : public DevicePlugin @@ -52,11 +53,20 @@ public: private: PluginTimer *m_pluginTimer = nullptr; + QTimer *m_tokenRefreshTimer = nullptr; + QHash m_sonosConnections; + QList m_householdIds; + + QByteArray m_sonosConnectionAccessToken; + QByteArray m_sonosConnectionRefreshToken; private slots: void onPluginTimer(); void onConnectionChanged(); + void onRefreshTimeout(); + + void onHouseholdIdsReceived(QList householdIds); }; diff --git a/sonos/devicepluginsonos.json b/sonos/devicepluginsonos.json index a59bd854..af19f4d0 100644 --- a/sonos/devicepluginsonos.json +++ b/sonos/devicepluginsonos.json @@ -16,6 +16,8 @@ "interfaces": ["gateway"], "createMethods": ["user"], "setupMethod": "oauth", + "paramTypes": [ + ], "stateTypes": [ { "id": "5aa4360c-61de-47d0-a72e-a19d57712e1c", diff --git a/sonos/sonos.cpp b/sonos/sonos.cpp index 20cf52dc..3d606860 100644 --- a/sonos/sonos.cpp +++ b/sonos/sonos.cpp @@ -41,9 +41,11 @@ void Sonos::getHouseholds() { QNetworkRequest request; request.setHeader(QNetworkRequest::KnownHeaders::ContentTypeHeader, "application/json"); - request.setRawHeader("Authorization", "Bearer" + m_accessToken); + request.setRawHeader("Authorization", "Bearer " + m_accessToken); + request.setRawHeader("X-Sonos-Api-Key", m_apiKey); request.setUrl(QUrl(m_baseControlUrl + "/households")); QNetworkReply *reply = m_networkManager->get(request); + qDebug(dcSonos()) << "Sending request" << request.url() << request.rawHeaderList() << request.rawHeader("Authorization"); connect(reply, &QNetworkReply::finished, this, [reply, this] { reply->deleteLater(); int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); @@ -58,8 +60,6 @@ void Sonos::getHouseholds() /*QJsonDocument data = reply->readAll(); if (!data.isObject()) return; - - QList households; emit householdObjectsReceived(households);*/ }); @@ -85,14 +85,38 @@ void Sonos::loadFavorite() } -void Sonos::getGroups() +void Sonos::getGroups(const QByteArray &householdId) { + QNetworkRequest request; + request.setHeader(QNetworkRequest::KnownHeaders::ContentTypeHeader, "application/json"); + request.setRawHeader("Authorization", "Bearer " + m_accessToken); + request.setRawHeader("X-Sonos-Api-Key", m_apiKey); + request.setUrl(QUrl(m_baseControlUrl + "/households/" + householdId + "/groups")); + QNetworkReply *reply = m_networkManager->get(request); + connect(reply, &QNetworkReply::finished, this, [reply, this] { + reply->deleteLater(); + int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + // Check HTTP status code + if (status != 200 || reply->error() != QNetworkReply::NoError) { + qCWarning(dcSonos()) << "Request error:" << status << reply->errorString(); + return; + } + + qDebug(dcSonos()) << "Received response from Sonos" << reply->readAll(); + /*QJsonDocument data = reply->readAll(); + if (!data.isObject()) + return; + + QList households; + emit householdObjectsReceived(households);*/ + }); } -void Sonos::createGroup() +void Sonos::createGroup(const QByteArray &householdId, QList playerIds) { - + Q_UNUSED(householdId) + Q_UNUSED(playerIds) } void Sonos::modifyGroupMembers() @@ -100,105 +124,126 @@ void Sonos::modifyGroupMembers() } -void Sonos::setGroupMembers() -{ - -} - -void Sonos::getGroupVolume(int groupId) +void Sonos::setGroupMembers(const QByteArray &groupId) { Q_UNUSED(groupId) } -void Sonos::setGroupVolume(int groupId, int volume) +void Sonos::getGroupVolume(const QByteArray &groupId) { - Q_UNUSED(groupId) - Q_UNUSED(volume) + Q_UNUSED(groupId) } -void Sonos::setGroupMute(int groupId, bool mute) +void Sonos::setGroupVolume(const QByteArray &groupId, int volume) { - Q_UNUSED(groupId) - Q_UNUSED(mute) + Q_UNUSED(groupId) + Q_UNUSED(volume) } -void Sonos::setGroupRelativeVolume(int groupId, int volumeDelta) +void Sonos::setGroupMute(const QByteArray &groupId, bool mute) { - Q_UNUSED(groupId) - Q_UNUSED(volumeDelta) + Q_UNUSED(groupId) + Q_UNUSED(mute) } -void Sonos::getPlaybackStatus() +void Sonos::setGroupRelativeVolume(const QByteArray &groupId, int volumeDelta) { + Q_UNUSED(groupId) + Q_UNUSED(volumeDelta) +} + +void Sonos::getGroupPlaybackStatus(const QByteArray &groupId) +{ + Q_UNUSED(groupId) +} + +void Sonos::groupLoadLineIn(const QByteArray &groupId) +{ + Q_UNUSED(groupId) } -void Sonos::loadLineIn() +void Sonos::groupPlay(const QByteArray &groupId) { - + Q_UNUSED(groupId) } -void Sonos::play() +void Sonos::groupPause(const QByteArray &groupId) { - + Q_UNUSED(groupId) } -void Sonos::pause() +void Sonos::groupSeek(const QByteArray &groupId) { - + Q_UNUSED(groupId) } -void Sonos::seek() +void Sonos::groupSeekRelative(const QByteArray &groupId, int millis) { - + Q_UNUSED(groupId) + Q_UNUSED(millis) } -void Sonos::seekRelative() +void Sonos::groupSetPlayModes(const QByteArray &groupId, PlayMode playMode) { - + Q_UNUSED(groupId) + Q_UNUSED(playMode) } -void Sonos::setPlayModes() +void Sonos::groupSetShuffle(const QByteArray &groupId, bool shuffle) { - + Q_UNUSED(groupId) + Q_UNUSED(shuffle) } -void Sonos::skipToNextTrack() +void Sonos::groupSetRepeat(const QByteArray &groupId, RepeatMode repeatMode) { - + Q_UNUSED(groupId) + Q_UNUSED(repeatMode) } -void Sonos::skipToPreviousTrack() +void Sonos::groupSetCrossfade(const QByteArray &groupId, bool crossfade) { - + Q_UNUSED(groupId) + Q_UNUSED(crossfade) } -void Sonos::togglePlayPause() +void Sonos::groupSkipToNextTrack(const QByteArray &groupId) { - + Q_UNUSED(groupId) } -void Sonos::getPlayerVolume(int playerId) +void Sonos::groupSkipToPreviousTrack(const QByteArray &groupId) { - Q_UNUSED(playerId) + Q_UNUSED(groupId) } -void Sonos::setPlayerVolume(int playerId, int volume) +void Sonos::groupTogglePlayPause(const QByteArray &groupId) { - Q_UNUSED(playerId) - Q_UNUSED(volume) + Q_UNUSED(groupId) } -void Sonos::setPlayerRelativeVolume(int playerId, int volumeDelta) +void Sonos::getPlayerVolume(const QByteArray &playerId) { - Q_UNUSED(playerId) - Q_UNUSED(volumeDelta) + Q_UNUSED(playerId) } -void Sonos::setPlayerMute(int playerId, bool mute) +void Sonos::setPlayerVolume(const QByteArray &playerId, int volume) { - Q_UNUSED(playerId) - Q_UNUSED(mute) + Q_UNUSED(playerId) + Q_UNUSED(volume) +} + +void Sonos::setPlayerRelativeVolume(const QByteArray &playerId, int volumeDelta) +{ + Q_UNUSED(playerId) + Q_UNUSED(volumeDelta) +} + +void Sonos::setPlayerMute(const QByteArray &playerId, bool mute) +{ + Q_UNUSED(playerId) + Q_UNUSED(mute) } void Sonos::getPlaylist() diff --git a/sonos/sonos.h b/sonos/sonos.h index 4d98478a..d91c1191 100644 --- a/sonos/sonos.h +++ b/sonos/sonos.h @@ -33,19 +33,38 @@ class Sonos : public QObject Q_OBJECT public: - enum PlayMode { - Repeat, - RepeatOne, - Shuffle, - Crossfade + enum RepeatMode { + RepeatModeOne, + RepeatModeAll, + RepeatModeNone }; + struct PlayMode { + bool repeat; + bool repeatOne; + bool shuffle; + bool crossfade; + }; + + struct PlayerObject { + + }; + + /* Represents a Sonos household.*/ + struct GroupObject { + QByteArray CoordinatorId; //Player acting as the group coordinator for the group + QByteArray groupId; //The ID of the group. + QString playbackState; //The playback state corresponding to the group. + QList playerIds; //The IDs of the primary players in the group. + QString displayName; //The display name for the group, such as “Living Room” or “Kitchen + 2”. + }; + /* * Represents a Sonos household.*/ - struct HouseholdObject { + /*struct HouseholdObject { QString id; //Identifies a Sonos household. QString name; //A user-displayable name of the Sonos household - }; + };*/ /* * The music service identifier or a pseudo-service identifier in the case of local library. */ @@ -160,36 +179,40 @@ public: void getFavorites(); void loadFavorite(); - void getGroups(); - void createGroup(); + void getGroups(const QByteArray &householdId); + void createGroup(const QByteArray &householdId, QList playerIds); void modifyGroupMembers(); - void setGroupMembers(); + void setGroupMembers(const QByteArray &groupId); //group volume - void getGroupVolume(int groupId); //Get the volume and mute state of a group. - void setGroupVolume(int groupId, int volume); //Set group volume to a specific level and unmute the group if muted. - void setGroupMute(int groupId, bool mute); //Mute and unmute the group. - void setGroupRelativeVolume(int groupId, int volumeDelta); //Increase or decrease group volume. + void getGroupVolume(const QByteArray &groupId); //Get the volume and mute state of a group. + void setGroupVolume(const QByteArray &groupId, int volume); //Set group volume to a specific level and unmute the group if muted. + void setGroupMute(const QByteArray &groupId, bool mute); //Mute and unmute the group. + void setGroupRelativeVolume(const QByteArray &groupId, int volumeDelta); //Increase or decrease group volume. - //playback - void getPlaybackStatus(); - void loadLineIn(); - void play(); - void pause(); - void seek(); - void seekRelative(); - void setPlayModes(); - void skipToNextTrack(); - void skipToPreviousTrack(); - void togglePlayPause(); + //group playback + void getGroupPlaybackStatus(const QByteArray &groupId); + void groupLoadLineIn(const QByteArray &groupId); + void groupPlay(const QByteArray &groupId); + void groupPause(const QByteArray &groupId); + void groupSeek(const QByteArray &groupId); + void groupSeekRelative(const QByteArray &groupId, int deltaMillis); + void groupSetPlayModes(const QByteArray &groupId, PlayMode playMode); + void groupSetShuffle(const QByteArray &groupId, bool shuffle); + void groupSetRepeat(const QByteArray &groupId, RepeatMode repeatMode); + void groupSetCrossfade(const QByteArray &groupId, bool crossfade); + + void groupSkipToNextTrack(const QByteArray &groupId); + void groupSkipToPreviousTrack(const QByteArray &groupId); + void groupTogglePlayPause(const QByteArray &groupId); //playbackMetadata // playerVolume - void getPlayerVolume(int playerId); - void setPlayerVolume(int playerId, int volume); - void setPlayerRelativeVolume(int playerId, int volumeDelta); - void setPlayerMute(int playerId, bool mute); + void getPlayerVolume(const QByteArray &playerId); + void setPlayerVolume(const QByteArray &playerId, int volume); + void setPlayerRelativeVolume(const QByteArray &playerId, int volumeDelta); + void setPlayerMute(const QByteArray &playerId, bool mute); //Playlists API namespace void getPlaylist(); @@ -201,16 +224,18 @@ public: void setPlayerSettings(); private: - QByteArray m_baseAuthorizationUrl = "api.sonos.com/login/v3/oauth"; - QByteArray m_baseControlUrl = "api.ws.sonos.com/control/api/v1"; + QByteArray m_baseAuthorizationUrl = "https://api.sonos.com/login/v3/oauth"; + QByteArray m_baseControlUrl = "https://api.ws.sonos.com/control/api/v1"; QByteArray m_accessToken; + QByteArray m_apiKey = "b15cbf8c-a39c-47aa-bd93-635a96e9696c"; NetworkAccessManager *m_networkManager = nullptr; - private slots: signals: + void householdIdsReceived(QList householdIds); + void groupsReceived(QList groups); };