added new nymea auth method
parent
85060d152c
commit
34b97e89d4
|
|
@ -22,10 +22,13 @@
|
|||
|
||||
#include "devicepluginsonos.h"
|
||||
#include "devices/device.h"
|
||||
#include "network/networkaccessmanager.h"
|
||||
#include "plugininfo.h"
|
||||
|
||||
#include <QNetworkRequest>
|
||||
#include <QNetworkReply>
|
||||
#include <QUrlQuery>
|
||||
#include <QJsonDocument>
|
||||
|
||||
DevicePluginSonos::DevicePluginSonos()
|
||||
{
|
||||
|
|
@ -45,23 +48,13 @@ Device::DeviceSetupStatus DevicePluginSonos::setupDevice(Device *device)
|
|||
|
||||
}
|
||||
if (device->deviceClassId() == sonosConnectionDeviceClassId) {
|
||||
|
||||
Sonos *sonos = new Sonos("0a8f6d44-d9d1-4474-bcfa-cfb41f8b66e8", this);
|
||||
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();
|
||||
pluginStorage()->endGroup();
|
||||
|
||||
sonos->authenticate(username, password);
|
||||
|
||||
m_sonosConnections.insert(device->id(), sonos);
|
||||
connect(sonos, &Sonos::authenticationFinished, this, [this, sonos](bool success){
|
||||
if (success) {
|
||||
} else {
|
||||
qCWarning(dcSonos()) << "Cannot authenticate to Sonos api";
|
||||
}
|
||||
});
|
||||
|
||||
m_sonosConnections.insert(device, sonos);
|
||||
}
|
||||
|
||||
if (device->deviceClassId() == sonosGroupDeviceClassId) {
|
||||
|
|
@ -71,6 +64,88 @@ Device::DeviceSetupStatus DevicePluginSonos::setupDevice(Device *device)
|
|||
return Device::DeviceSetupStatusSuccess;
|
||||
}
|
||||
|
||||
DevicePairingInfo DevicePluginSonos::pairDevice(DevicePairingInfo &devicePairingInfo)
|
||||
{
|
||||
|
||||
if (devicePairingInfo.deviceClassId() == sonosConnectionDeviceClassId) {
|
||||
QString clientId = "b15cbf8c-a39c-47aa-bd93-635a96e9696c";
|
||||
QString clientSecret = "c086ba71-e562-430b-a52f-867c6482fd11";
|
||||
|
||||
QUrl url("https://api.sonos.com/login/v3/oauth");
|
||||
QUrlQuery queryParams;
|
||||
queryParams.addQueryItem("client_id", clientId);
|
||||
queryParams.addQueryItem("redirect_uri", "https://127.0.0.1:8888");
|
||||
queryParams.addQueryItem("response_type", "code");
|
||||
queryParams.addQueryItem("scope", "playback-control-all");
|
||||
queryParams.addQueryItem("state", QUuid::createUuid().toString());
|
||||
url.setQuery(queryParams);
|
||||
|
||||
qCDebug(dcSonos()) << "Sonos url:" << url;
|
||||
|
||||
devicePairingInfo.setOAuthUrl(url);
|
||||
devicePairingInfo.setStatus(Device::DeviceErrorNoError);
|
||||
return devicePairingInfo;
|
||||
}
|
||||
|
||||
qCWarning(dcSonos()) << "Unhandled pairing metod!";
|
||||
devicePairingInfo.setStatus(Device::DeviceErrorCreationMethodNotSupported);
|
||||
return devicePairingInfo;
|
||||
}
|
||||
|
||||
DevicePairingInfo DevicePluginSonos::confirmPairing(DevicePairingInfo &devicePairingInfo, const QString &username, const QString &secret)
|
||||
{
|
||||
Q_UNUSED(username);
|
||||
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");
|
||||
|
||||
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);
|
||||
|
||||
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 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();
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
devicePairingInfo.setStatus(Device::DeviceErrorAsync);
|
||||
return devicePairingInfo;
|
||||
}
|
||||
|
||||
|
||||
qCWarning(dcSonos()) << "Invalid deviceclassId -> no pairing possible with this device";
|
||||
devicePairingInfo.setStatus(Device::DeviceErrorHardwareFailure);
|
||||
return devicePairingInfo;
|
||||
}
|
||||
|
||||
void DevicePluginSonos::postSetupDevice(Device *device)
|
||||
{
|
||||
if (device->deviceClassId() == sonosConnectionDeviceClassId) {
|
||||
|
|
@ -82,6 +157,16 @@ void DevicePluginSonos::postSetupDevice(Device *device)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void DevicePluginSonos::startMonitoringAutoDevices()
|
||||
{
|
||||
foreach (Device *device, myDevices()) {
|
||||
if (device->deviceClassId() == sonosGroupDeviceClassId) {
|
||||
return; // We already have a Auto Mock device... do nothing.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DevicePluginSonos::deviceRemoved(Device *device)
|
||||
{
|
||||
qCDebug(dcSonos) << "Delete " << device->name();
|
||||
|
|
@ -94,8 +179,8 @@ Device::DeviceError DevicePluginSonos::executeAction(Device *device, const Actio
|
|||
{
|
||||
Q_UNUSED(action)
|
||||
if (device->deviceClassId() == sonosGroupDeviceClassId) {
|
||||
Sonos *sonos = m_sonosConnections.value(device->parentId());
|
||||
int groupId = device->paramValue(sonosGroupDe)
|
||||
Sonos *sonos = m_sonosConnections.value(device);
|
||||
//int groupId = device->paramValue(sonosGroupDe)
|
||||
if (!sonos)
|
||||
return Device::DeviceErrorInvalidParameter;
|
||||
|
||||
|
|
@ -131,41 +216,33 @@ Device::DeviceError DevicePluginSonos::executeAction(Device *device, const Actio
|
|||
}
|
||||
|
||||
if (action.actionTypeId() == sonosGroupStopActionTypeId) {
|
||||
sonos->stop();
|
||||
//sonos->stop();
|
||||
return Device::DeviceErrorNoError;
|
||||
}
|
||||
|
||||
if (action.actionTypeId() == sonosGroupMuteActionTypeId) {
|
||||
bool mute = action.param(sonosGroupMuteActionMuteParamTypeId).value().toBool();
|
||||
//bool mute = action.param(sonosGroupMuteActionMuteParamTypeId).value().toBool();
|
||||
|
||||
sonos->setGroupMute()
|
||||
//sonos->setGroupMute();
|
||||
return Device::DeviceErrorNoError;
|
||||
}
|
||||
|
||||
if (action.actionTypeId() == sonosSkipNextActionTypeId) {
|
||||
if (!m_sonosSystem->GetPlayer()->Next()) {
|
||||
if (action.actionTypeId() == sonosGroupSkipNextActionTypeId) {
|
||||
/*if (!m_sonosSystem->GetPlayer()->Next()) {
|
||||
return Device::DeviceErrorHardwareFailure;
|
||||
}
|
||||
}*/
|
||||
return Device::DeviceErrorNoError;
|
||||
}
|
||||
|
||||
if (action.actionTypeId() == sonosSkipBackActionTypeId) {
|
||||
if(!m_sonosSystem->GetPlayer()->Previous()) {
|
||||
if (action.actionTypeId() == sonosGroupSkipBackActionTypeId) {
|
||||
/* if(!m_sonosSystem->GetPlayer()->Previous()) {
|
||||
return Device::DeviceErrorHardwareFailure;
|
||||
}
|
||||
}*/
|
||||
return Device::DeviceErrorNoError;
|
||||
}
|
||||
|
||||
if (action.actionTypeId() == sonosSkipBackActionTypeId) {
|
||||
int volume = action.param(sonosVolumeActionVolumeParamTypeId).value().toInt();
|
||||
|
||||
SONOS::ZonePtr pl = m_sonosSystem->GetConnectedZone();
|
||||
for (SONOS::Zone::iterator ip = pl->begin(); ip != pl->end(); ++ip) {
|
||||
if (!m_sonosSystem->GetPlayer()->SetVolume((*ip)->GetUUID(), volume)) {
|
||||
qWarning(dcSonos()) << "Could not set volume for" << (*ip)->GetHost().c_str();
|
||||
return Device::DeviceErrorHardwareFailure;
|
||||
}
|
||||
}
|
||||
if (action.actionTypeId() == sonosGroupSkipBackActionTypeId) {
|
||||
//int volume = action.param(sonosVolumeActionVolumeParamTypeId).value().toInt();
|
||||
return Device::DeviceErrorNoError;
|
||||
}
|
||||
return Device::DeviceErrorActionTypeNotFound;
|
||||
|
|
@ -177,18 +254,11 @@ void DevicePluginSonos::onPluginTimer()
|
|||
{
|
||||
}
|
||||
|
||||
void DevicePluginSonos::handleEventCB(void* handle)
|
||||
void DevicePluginSonos::onConnectionChanged()
|
||||
{
|
||||
Q_UNUSED(handle);
|
||||
/*unsigned char mask = m_sonosSystem->LastEvents();
|
||||
if ((mask & SONOS::SVCEvent_TransportChanged))
|
||||
qDebug(dcSonos()) << "Event Transport changed";
|
||||
if ((mask & SONOS::SVCEvent_AlarmClockChanged))
|
||||
qDebug(dcSonos()) << "Alarm clock changed";
|
||||
if ((mask & SONOS::SVCEvent_ZGTopologyChanged))
|
||||
qDebug(dcSonos()) << "ZG Topology changed";
|
||||
if ((mask & SONOS::SVCEvent_ContentDirectoryChanged))
|
||||
qDebug(dcSonos()) << "Content directory changed";
|
||||
if ((mask & SONOS::SVCEvent_RenderingControlChanged))
|
||||
qDebug(dcSonos()) << "Rendering control changed";*/
|
||||
Sonos *sonos = static_cast<Sonos *>(sender());
|
||||
Device *device = m_sonosConnections.key(sonos);
|
||||
device->setStateValue(sonosConnectionConnectedStateTypeId, false); //TODO
|
||||
|
||||
//TODO set all groups
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,15 +42,17 @@ public:
|
|||
~DevicePluginSonos() override;
|
||||
|
||||
Device::DeviceSetupStatus setupDevice(Device *device) override;
|
||||
DevicePairingInfo pairDevice(DevicePairingInfo &devicePairingInfo) override;
|
||||
DevicePairingInfo confirmPairing(DevicePairingInfo &devicePairingInfo, const QString &username, const QString &secret) override;
|
||||
|
||||
void postSetupDevice(Device *device) override;
|
||||
void startMonitoringAutoDevices() override;
|
||||
void deviceRemoved(Device *device) override;
|
||||
Device::DeviceError discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms) override;
|
||||
Device::DeviceError executeAction(Device *device, const Action &action) override;
|
||||
|
||||
private:
|
||||
PluginTimer *m_pluginTimer = nullptr;
|
||||
QHash<DeviceId, Sonos *> m_sonosConnections;
|
||||
|
||||
QHash<Device *, Sonos *> m_sonosConnections;
|
||||
|
||||
private slots:
|
||||
void onPluginTimer();
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
"displayName": "Sonos connection",
|
||||
"interfaces": ["gateway"],
|
||||
"createMethods": ["user"],
|
||||
"setupMethod": "userandpassword",
|
||||
"setupMethod": "oauth",
|
||||
"stateTypes": [
|
||||
{
|
||||
"id": "5aa4360c-61de-47d0-a72e-a19d57712e1c",
|
||||
|
|
@ -28,11 +28,11 @@
|
|||
]
|
||||
},
|
||||
{
|
||||
"id": "22df416d-7732-44f1-b6b9-e41296211178",
|
||||
"id": "72d9332b-2b25-4136-87a6-e534eae4cc80",
|
||||
"name": "sonosGroup",
|
||||
"displayName": "Sonos group",
|
||||
"interfaces": ["extendedvolumecontroller", "mediametadataprovider", "shufflerepeat", "connectable"],
|
||||
"createMethods": ["discovery"],
|
||||
"createMethods": ["auto"],
|
||||
"paramTypes": [
|
||||
{
|
||||
"id": "defc44cd-2ffb-4af1-b348-d6a3474c7515",
|
||||
|
|
|
|||
190
sonos/sonos.cpp
190
sonos/sonos.cpp
|
|
@ -22,35 +22,30 @@
|
|||
|
||||
#include "sonos.h"
|
||||
#include "extern-plugininfo.h"
|
||||
#include "network/networkaccessmanager.h"
|
||||
|
||||
#include <QJsonDocument>
|
||||
|
||||
Sonos::Sonos(QByteArray apiKey, QObject *parent) :
|
||||
Sonos::Sonos(NetworkAccessManager *networkmanager, const QByteArray &accessToken, QObject *parent) :
|
||||
QObject(parent),
|
||||
m_apiKey(apiKey)
|
||||
m_accessToken(accessToken),
|
||||
m_networkManager(networkmanager)
|
||||
{
|
||||
}
|
||||
|
||||
void Sonos::authenticate(const QString &username, const QString &password)
|
||||
void Sonos::setAccessToken(const QByteArray &accessToken)
|
||||
{
|
||||
Q_UNUSED(username)
|
||||
Q_UNUSED(password)
|
||||
|
||||
m_OAuth = new OAuth(m_apiKey, this);
|
||||
m_OAuth->setUrl(QUrl(m_baseAuthorizationUrl));
|
||||
m_OAuth->setScope("playback-control-all");
|
||||
m_OAuth->startAuthentication();
|
||||
m_accessToken = accessToken;
|
||||
}
|
||||
|
||||
void Sonos::getHouseholds()
|
||||
{
|
||||
QNetworkRequest request;
|
||||
request.setHeader(QNetworkRequest::KnownHeaders::ContentTypeHeader, "application/json");
|
||||
request.setRawHeader("Authorization", "Bearer" + m_OAuth->bearerToken());
|
||||
request.setRawHeader("Authorization", "Bearer" + m_accessToken);
|
||||
request.setUrl(QUrl(m_baseControlUrl + "/households"));
|
||||
QNetworkReply *reply = QNetworkAccessManager.get(request);
|
||||
connect(reply, &QNetworkReply::finished, this [this] {
|
||||
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
|
||||
|
|
@ -58,13 +53,176 @@ void Sonos::getHouseholds()
|
|||
qCWarning(dcSonos()) << "Request error:" << status << reply->errorString();
|
||||
return;
|
||||
}
|
||||
QJsonDocument data = reply->readAll();
|
||||
|
||||
qDebug(dcSonos()) << "Received response from Sonos" << reply->readAll();
|
||||
/*QJsonDocument data = reply->readAll();
|
||||
if (!data.isObject())
|
||||
return;
|
||||
|
||||
|
||||
QList<HouseholdObject> households;
|
||||
emit householdObjectsReceived(households);
|
||||
emit householdObjectsReceived(households);*/
|
||||
});
|
||||
}
|
||||
|
||||
void Sonos::cancelAudioClip()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Sonos::loadAudioClip()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Sonos::getFavorites()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Sonos::loadFavorite()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Sonos::getGroups()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Sonos::createGroup()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Sonos::modifyGroupMembers()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Sonos::setGroupMembers()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Sonos::getGroupVolume(int groupId)
|
||||
{
|
||||
Q_UNUSED(groupId)
|
||||
}
|
||||
|
||||
void Sonos::setGroupVolume(int groupId, int volume)
|
||||
{
|
||||
Q_UNUSED(groupId)
|
||||
Q_UNUSED(volume)
|
||||
}
|
||||
|
||||
void Sonos::setGroupMute(int groupId, bool mute)
|
||||
{
|
||||
Q_UNUSED(groupId)
|
||||
Q_UNUSED(mute)
|
||||
}
|
||||
|
||||
void Sonos::setGroupRelativeVolume(int groupId, int volumeDelta)
|
||||
{
|
||||
Q_UNUSED(groupId)
|
||||
Q_UNUSED(volumeDelta)
|
||||
}
|
||||
|
||||
void Sonos::getPlaybackStatus()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Sonos::loadLineIn()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Sonos::play()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Sonos::pause()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Sonos::seek()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Sonos::seekRelative()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Sonos::setPlayModes()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Sonos::skipToNextTrack()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Sonos::skipToPreviousTrack()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Sonos::togglePlayPause()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Sonos::getPlayerVolume(int playerId)
|
||||
{
|
||||
Q_UNUSED(playerId)
|
||||
}
|
||||
|
||||
void Sonos::setPlayerVolume(int playerId, int volume)
|
||||
{
|
||||
Q_UNUSED(playerId)
|
||||
Q_UNUSED(volume)
|
||||
}
|
||||
|
||||
void Sonos::setPlayerRelativeVolume(int playerId, int volumeDelta)
|
||||
{
|
||||
Q_UNUSED(playerId)
|
||||
Q_UNUSED(volumeDelta)
|
||||
}
|
||||
|
||||
void Sonos::setPlayerMute(int playerId, bool mute)
|
||||
{
|
||||
Q_UNUSED(playerId)
|
||||
Q_UNUSED(mute)
|
||||
}
|
||||
|
||||
void Sonos::getPlaylist()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Sonos::getPlaylists()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Sonos::loadPlaylist()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Sonos::getPlayerSettings()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Sonos::setPlayerSettings()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,10 +24,9 @@
|
|||
#define SONOS_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QNetworkAccessManager>
|
||||
|
||||
#include "network/networkaccessmanager.h"
|
||||
#include "devices/device.h"
|
||||
#include "oauth.h"
|
||||
|
||||
class Sonos : public QObject
|
||||
{
|
||||
|
|
@ -149,10 +148,9 @@ public:
|
|||
//tags enum
|
||||
};
|
||||
|
||||
explicit Sonos(QByteArray apiKey, QObject *parent = nullptr);
|
||||
explicit Sonos(NetworkAccessManager *networkManager, const QByteArray &accessToken, QObject *parent = nullptr);
|
||||
|
||||
void setApiKey(QByteArray apiKey);
|
||||
void authenticate(const QString &username, const QString &password);
|
||||
void setAccessToken(const QByteArray &accessToken);
|
||||
|
||||
void getHouseholds();
|
||||
|
||||
|
|
@ -205,19 +203,14 @@ public:
|
|||
private:
|
||||
QByteArray m_baseAuthorizationUrl = "api.sonos.com/login/v3/oauth";
|
||||
QByteArray m_baseControlUrl = "api.ws.sonos.com/control/api/v1";
|
||||
QByteArray m_apiKey;
|
||||
QByteArray m_accessToken;
|
||||
|
||||
OAuth *m_OAuth = nullptr;
|
||||
NetworkAccessManager *m_networkManager = nullptr;
|
||||
|
||||
private slots:
|
||||
|
||||
|
||||
signals:
|
||||
void authenticationFinished();
|
||||
void authenticationFailed(const QString &reason);
|
||||
|
||||
|
||||
public slots:
|
||||
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -7,11 +7,9 @@ TARGET = $$qtLibraryTarget(nymea_devicepluginsonos)
|
|||
SOURCES += \
|
||||
devicepluginsonos.cpp \
|
||||
sonos.cpp \
|
||||
oauth.cpp
|
||||
|
||||
HEADERS += \
|
||||
devicepluginsonos.h \
|
||||
sonos.h \
|
||||
oauth.h
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue