Withings : Use new API

This commit is contained in:
grauser
2017-03-02 07:33:54 +01:00
parent 676fc9acb8
commit e65ef941bc
9 changed files with 294 additions and 55 deletions

View File

@@ -228,6 +228,7 @@ void KQOAuthManager::executeRequest(KQOAuthRequest *request) {
urlWithParams.setQuery(query);
#endif
networkRequest.setUrl(urlWithParams);
qDebug() << "urlWithParams:" << urlWithParams;
// Submit the request including the params.
QNetworkReply *reply = d->networkManager->get(networkRequest);

View File

@@ -150,10 +150,46 @@ OAuthDialog::OAuthDialog(Context *context, OAuthSite site, QString baseURL, QStr
if (baseURL=="") baseURL="https://whats.todaysplan.com.au";
urlstr = QString("%1/authorize/").arg(baseURL);
urlstr.append(GC_TODAYSPLAN_CLIENT_ID);
} else if (site == WITHINGS) {
#ifdef GC_HAVE_KQOAUTH
oauthRequest = new KQOAuthRequest;
oauthManager = new KQOAuthManager(this);
connect(oauthManager, SIGNAL(temporaryTokenReceived(QString,QString)),
this, SLOT(onTemporaryTokenReceived(QString, QString)));
connect(oauthManager, SIGNAL(authorizationReceived(QString,QString)),
this, SLOT( onAuthorizationReceived(QString, QString)));
connect(oauthManager, SIGNAL(accessTokenReceived(QString,QString)),
this, SLOT(onAccessTokenReceived(QString,QString)));
connect(oauthManager, SIGNAL(requestReady(QByteArray)),
this, SLOT(onRequestReady(QByteArray)));
connect(oauthManager, SIGNAL(authorizationPageRequested(QUrl)),
this, SLOT(onAuthorizationPageRequested(QUrl)));
oauthRequest->initRequest(KQOAuthRequest::TemporaryCredentials, QUrl("https://oauth.withings.com/account/request_token"));
//oauthRequest->setEnableDebugOutput(true);
oauthRequest->setHttpMethod(KQOAuthRequest::GET);
oauthRequest->setConsumerKey(GC_WITHINGS_CONSUMER_KEY);
oauthRequest->setConsumerSecretKey(GC_WITHINGS_CONSUMER_SECRET);
//oauthRequest->setCallbackUrl(QUrl("http://www.goldencheetah.org"));
oauthManager->setHandleUserAuthorization(true); // false to use callback
oauthManager->setHandleAuthorizationPageOpening(false);
oauthManager->executeRequest(oauthRequest);
#endif
}
// different process to get the token for STRAVA, CYCLINGANALYTICS vs.
// TWITTER
// TWITTER or WITHINGS
if (site == DROPBOX || site == STRAVA || site == CYCLING_ANALYTICS ||
site == GOOGLE_CALENDAR || site == GOOGLE_DRIVE || site == TODAYSPLAN) {
url = QUrl(urlstr);
@@ -167,22 +203,35 @@ OAuthDialog::OAuthDialog(Context *context, OAuthSite site, QString baseURL, QStr
}
#ifdef GC_HAVE_KQOAUTH
// ****************** Twitter OAUTH ******************************************************
// ****************** OAUTH V1 (TWITTER, WITHINGS) ************************************************
void OAuthDialog::onTemporaryTokenReceived(QString, QString)
{
QUrl userAuthURL("https://api.twitter.com/oauth/authorize");
//qDebug() << "onTemporaryTokenReceived";
QUrl userAuthURL;
if (site == TWITTER) {
userAuthURL = "https://api.twitter.com/oauth/authorize";
} else if (site == WITHINGS) {
userAuthURL = "https://oauth.withings.com/account/authorize";
}
if( oauthManager->lastError() == KQOAuthManager::NoError) {
oauthManager->getUserAuthorization(userAuthURL);
}
} else
qDebug() << "error" << oauthManager->lastError();
}
void OAuthDialog::onAuthorizationReceived(QString, QString) {
// qDebug() << "Authorization token received: " << token << verifier;
//qDebug() << "Authorization token received: " << token << verifier;
if (site == TWITTER) {
oauthManager->getUserAccessTokens(QUrl("https://api.twitter.com/oauth/access_token"));
} else if (site == WITHINGS) {
oauthManager->getUserAccessTokens(QUrl("https://oauth.withings.com/account/access_token"));
}
oauthManager->getUserAccessTokens(QUrl("https://api.twitter.com/oauth/access_token"));
if( oauthManager->lastError() != KQOAuthManager::NoError) {
QString error = QString(tr("Error fetching OAuth credentials - Endpoint: /oauth/access_token"));
QMessageBox oautherr(QMessageBox::Critical, tr("Authorization Error"), error);
@@ -192,23 +241,34 @@ void OAuthDialog::onAuthorizationReceived(QString, QString) {
}
void OAuthDialog::onAccessTokenReceived(QString token, QString tokenSecret) {
// qDebug() << "Access token received: " << token << tokenSecret;
//qDebug() << "Access token received: " << token << tokenSecret;
QString info;
if (site == TWITTER) {
appsettings->setCValue(context->athlete->cyclist, GC_TWITTER_TOKEN, token);
appsettings->setCValue(context->athlete->cyclist, GC_TWITTER_SECRET, tokenSecret);
info = QString(tr("Twitter authorization was successful."));
} else if (site == WITHINGS) {
appsettings->setCValue(context->athlete->cyclist, GC_WITHINGS_TOKEN, token);
appsettings->setCValue(context->athlete->cyclist, GC_WITHINGS_SECRET, tokenSecret);
info = QString(tr("Withings authorization was successful."));
}
appsettings->setCValue(context->athlete->cyclist, GC_TWITTER_TOKEN, token);
appsettings->setCValue(context->athlete->cyclist, GC_TWITTER_SECRET, tokenSecret);
QString info = QString(tr("Twitter authorization was successful."));
QMessageBox information(QMessageBox::Information, tr("Information"), info);
information.exec();
accept();
}
void OAuthDialog::onAuthorizedRequestDone() {
// request sent to Twitter - do nothing
// request sent - do nothing
}
void OAuthDialog::onRequestReady(QByteArray response) {
// qDebug() << "Response received: " << response;
//qDebug() << "Response received: " << response;
QString r = response;
if (r.contains("\"errors\"", Qt::CaseInsensitive))
{
@@ -216,11 +276,30 @@ void OAuthDialog::onRequestReady(QByteArray response) {
tr("There was an error during authorization. Please check the error description."));
oautherr.setDetailedText(r); // probably blank
oautherr.exec();
} else {
if (site == WITHINGS) {
QString userid;
#if QT_VERSION > 0x050000
QUrlQuery params;
params.setQuery(response);
#else
QUrl params;
params.setEncodedQuery(response);
#endif
userid = params.queryItemValue("userid");
if (userid.isEmpty() == false) {
appsettings->setCValue(context->athlete->cyclist, GC_WIUSER, userid);
}
}
}
}
void OAuthDialog::onAuthorizationPageRequested(QUrl url) {
//qDebug() << "AuthorizationPageRequested: " << url;
// open Authorization page in view
view->setUrl(url);

View File

@@ -22,33 +22,122 @@
#include "RideCache.h"
#include <QMessageBox>
#ifdef GC_HAVE_KQOAUTH
#include <kqoauthmanager.h>
#include <kqoauthrequest.h>
#endif
WithingsDownload::WithingsDownload(Context *context) : context(context)
{
nam = new QNetworkAccessManager(this);
connect(nam, SIGNAL(finished(QNetworkReply*)), this, SLOT(downloadFinished(QNetworkReply*)));
parser = new WithingsParser;
#ifdef GC_HAVE_KQOAUTH
oauthRequest = new KQOAuthRequest();
oauthManager = new KQOAuthManager();
connect(oauthManager, SIGNAL(requestReady(QByteArray)),
this, SLOT(onRequestReady(QByteArray)));
connect(oauthManager, SIGNAL(authorizedRequestDone()),
this, SLOT(onAuthorizedRequestDone()));
#endif
}
bool
WithingsDownload::download()
{
// account for trailing slash, remove it if it is there (it was the default in preferences)
QString server = appsettings->cvalue(context->athlete->cyclist, GC_WIURL, "http://wbsapi.withings.net").toString();
if (server.endsWith("/")) server=server.mid(0, server.length()-1);
// New API (OAuth)
QString strToken = "";
QString strSecret = "";
#ifdef GC_HAVE_KQOAUTH
strToken =appsettings->cvalue(context->athlete->cyclist, GC_WITHINGS_TOKEN).toString();
strSecret= appsettings->cvalue(context->athlete->cyclist, GC_WITHINGS_SECRET).toString();
#endif
QString request = QString("%1/measure?action=getmeas&userid=%2&publickey=%3")
.arg(server)
.arg(appsettings->cvalue(context->athlete->cyclist, GC_WIUSER, "").toString())
.arg(appsettings->cvalue(context->athlete->cyclist, GC_WIKEY, "").toString());
QString strOldKey = appsettings->cvalue(context->athlete->cyclist, GC_WIKEY).toString();
QNetworkReply *reply = nam->get(QNetworkRequest(QUrl(request)));
if (reply->error() != QNetworkReply::NoError) {
QMessageBox::warning(context->mainWindow, tr("Withings Data Download"), reply->errorString());
if((strToken.isEmpty() || strSecret.isEmpty() ||
strToken == "" || strToken == "0" ||
strSecret == "" || strSecret == "0" ) &&
(strOldKey.isEmpty() || strOldKey == "" || strOldKey == "0" ))
{
#ifdef Q_OS_MACX
#define GC_PREF tr("Golden Cheetah->Preferences")
#else
#define GC_PREF tr("Tools->Options")
#endif
QString advise = QString(tr("Error fetching OAuth credentials. Please make sure to complete the Withings authorization procedure found under %1.")).arg(GC_PREF);
QMessageBox oautherr(QMessageBox::Critical, tr("OAuth Error"), advise);
oautherr.exec();
return false;
}
return true;
if(!strToken.isEmpty() &&! strSecret.isEmpty() &&
strToken != "" && strToken != "0" &&
strSecret != "" && strSecret != "0" ) {
#ifdef GC_HAVE_KQOAUTH
oauthRequest->initRequest(KQOAuthRequest::AuthorizedRequest, QUrl("http://wbsapi.withings.net/measure"));
oauthRequest->setHttpMethod(KQOAuthRequest::GET);
//oauthRequest->setEnableDebugOutput(true);
oauthRequest->setConsumerKey(GC_WITHINGS_CONSUMER_KEY);
oauthRequest->setConsumerSecretKey(GC_WITHINGS_CONSUMER_SECRET);
// set the user token and secret
oauthRequest->setToken(strToken);
oauthRequest->setTokenSecret(strSecret);
KQOAuthParameters params;
params.insert("action", "getmeas");
params.insert("userid", appsettings->cvalue(context->athlete->cyclist, GC_WIUSER, "").toString());
// Hack...
// Why should we add params manually (GET) ????
QList<QByteArray> requestParameters = oauthRequest->requestParameters();
for (int i=0; i<requestParameters.count(); i++) {
//qDebug() << requestParameters.at(i);
#if QT_VERSION > 0x050000
QUrlQuery _params;
_params.setQuery(requestParameters.at(i));
#else
QUrl _params;
_params.setEncodedQuery(requestParameters.at(i));
#endif
QString value = _params.queryItems().at(0).second;
value = value.replace("\"", "");
params.insert(_params.queryItems().at(0).first, value);
}
oauthRequest->setAdditionalParameters(params);
oauthManager->executeRequest(oauthRequest);
#endif
} else {
// account for trailing slash, remove it if it is there (it was the default in preferences)
QString server = appsettings->cvalue(context->athlete->cyclist, GC_WIURL, "http://wbsapi.withings.net").toString();
if (server.endsWith("/")) server=server.mid(0, server.length()-1);
QString request = QString("%1/measure?action=getmeas&userid=%2&publickey=%3")
.arg(server)
.arg(appsettings->cvalue(context->athlete->cyclist, GC_WIUSER, "").toString())
.arg(appsettings->cvalue(context->athlete->cyclist, GC_WIKEY, "").toString());
QNetworkReply *reply = nam->get(QNetworkRequest(QUrl(request)));
if (reply->error() != QNetworkReply::NoError) {
QMessageBox::warning(context->mainWindow, tr("Withings Data Download"), reply->errorString());
return false;
}
return true;
}
return false;
}
void
@@ -59,7 +148,12 @@ WithingsDownload::downloadFinished(QNetworkReply *reply)
QMessageBox::warning(context->mainWindow, tr("Network Problem"), tr("No Withings Data downloaded"));
return;
}
QString text = reply->readAll();
parse(reply->readAll());
}
void
WithingsDownload::parse(QString text)
{
QStringList errors;
// parse it
@@ -105,3 +199,27 @@ WithingsDownload::downloadFinished(QNetworkReply *reply)
return;
}
void
WithingsDownload::onAuthorizedRequestDone() {
// qDebug() << "Request sent to Withings!";
}
void
WithingsDownload::onRequestReady(QByteArray response) {
//qDebug() << "Response from the Withings's service: " << response;
QString r = response;
if (r.contains("\"status\":0", Qt::CaseInsensitive))
{
parse(response);
} else {
QMessageBox oautherr(QMessageBox::Critical, tr("Error"),
tr("There was an error during fetching. Please check the error description."));
oautherr.setDetailedText(r); // probably blank
oautherr.exec();
}
}

View File

@@ -27,6 +27,10 @@
#include "Settings.h"
#include "WithingsParser.h"
#ifdef GC_HAVE_KQOAUTH
#include <kqoauthmanager.h>
#endif
class WithingsDownload : public QObject
{
Q_OBJECT
@@ -47,5 +51,18 @@ private:
int allMeasures;
int newMeasures;
#ifdef GC_HAVE_KQOAUTH
KQOAuthManager *oauthManager;
KQOAuthRequest *oauthRequest;
#endif
void parse(QString text);
private slots:
#ifdef GC_HAVE_KQOAUTH
void onRequestReady(QByteArray);
void onAuthorizedRequestDone();
#endif
};
#endif

View File

@@ -54,10 +54,13 @@
#define GC_GOOGLE_DRIVE_API_KEY "__GC_GOOGLE_DRIVE_API_KEY__"
#endif
// used by OAuthDialog.cpp (but currently public in Settings.h (!!)
//#ifndef GC_TWITTER_CONSUMER_SECRET
//#define GC_TWITTER_CONSUMER_SECRET "__GC_TWITTER_CONSUMER_SECRET__"
//#endif
#ifndef GC_TWITTER_CONSUMER_SECRET
#define GC_TWITTER_CONSUMER_SECRET "__GC_TWITTER_CONSUMER_SECRET__"
#endif
#ifndef GC_WITHINGS_CONSUMER_SECRET
#define GC_WITHINGS_CONSUMER_SECRET "__GC_WITHINGS_CONSUMER_SECRET__"
#endif
#ifndef GC_DROPBOX_CLIENT_SECRET
#define GC_DROPBOX_CLIENT_SECRET "__GC_DROPBOX_CLIENT_SECRET__"

View File

@@ -37,7 +37,6 @@
//Twitter oauth keys / see also Athlete parameter
#define GC_TWITTER_CONSUMER_KEY "qbbmhDt8bG8ZBcT3r9nYw" //< consumer key
#define GC_TWITTER_CONSUMER_SECRET "IWXu2G6mQC5xvhM8V0ohA0mPTUOqAFutiuKIva3LQg"
//Google Calendar-CALDAV oauthkeys / see also Athlete parameter
#define GC_GOOGLE_CALENDAR_CLIENT_ID "426009671216-c588t1u6hafep30tfs7g0g1nuo72s8ko.apps.googleusercontent.com"
@@ -48,6 +47,9 @@
//Cycling Analytics / see also Athlete parameter
#define GC_CYCLINGANALYTICS_CLIENT_ID "1504958" // app id
//Withings oauth keys
#define GC_WITHINGS_CONSUMER_KEY "292875b6883b87e27cefd2555d1cb872b2282f02fee25b4906871314934" //< consumer key
// Dropbox id
#ifndef GC_DROPBOX_CLIENT_ID
#define GC_DROPBOX_CLIENT_ID "753fbblhri06ah3"
@@ -294,8 +296,12 @@
#define GC_GOOGLE_DRIVE_FOLDER "<athlete-private>google-drive/folder"
#define GC_GOOGLE_DRIVE_FOLDER_ID "<athlete-private>google-drive/folder_id"
//Twitter
#define GC_TWITTER_TOKEN "<athlete-private>twitter_token"
#define GC_TWITTER_SECRET "<athlete-private>twitter_secret"
//Withings
#define GC_WITHINGS_TOKEN "<athlete-private>withings_token"
#define GC_WITHINGS_SECRET "<athlete-private>withings_secret"
//Google Calendar-CALDAV oauthkeys
#define GC_GOOGLE_CALENDAR_REFRESH_TOKEN "<athlete-private>google_cal_refresh_token"
//Strava

View File

@@ -705,34 +705,35 @@ CredentialsPage::CredentialsPage(QWidget *parent, Context *context) : QScrollAre
grid->addWidget(rideWithGPSPass, row, 1, Qt::AlignLeft | Qt::AlignVCenter);
//////////////////////////////////////////////////
// Withings Wifi Scales
// Withings
//////////////////////////////////////////////////
QLabel *wip = new QLabel(tr("Withings Wifi Scales"));
wip->setFont(current);
#ifdef GC_HAVE_KQOAUTH
QLabel *withingsLabel = new QLabel(tr("Withings"));
withingsLabel->setFont(current);
QLabel *wiurlLabel = new QLabel(tr("Website"));
QLabel *wiuserLabel = new QLabel(tr("User Id"));
QLabel *wipassLabel = new QLabel(tr("Public Key"));
QLabel *wipauthLabel = new QLabel(tr("Authorise"));
wiURL = new QLineEdit(this);
wiURL->setText(appsettings->cvalue(context->athlete->cyclist, GC_WIURL, "http://wbsapi.withings.net/").toString());
withingsAuthorise = new QPushButton(tr("Authorise"), this);
withingsAuthorised = new QPushButton(this);
withingsAuthorised->setContentsMargins(0,0,0,0);
withingsAuthorised->setIcon(passwords.scaled(16,16));
withingsAuthorised->setIconSize(QSize(16,16));
withingsAuthorised->setFixedHeight(16);
withingsAuthorised->setFixedWidth(16);
wiUser = new QLineEdit(this);
wiUser->setText(appsettings->cvalue(context->athlete->cyclist, GC_WIUSER, "").toString());
grid->addWidget(withingsLabel, ++row, 0);
wiPass = new QLineEdit(this);
wiPass->setText(appsettings->cvalue(context->athlete->cyclist, GC_WIKEY, "").toString());
grid->addWidget(wipauthLabel, ++row, 0);
grid->addWidget(withingsAuthorise, row, 1, Qt::AlignLeft | Qt::AlignVCenter);
if (appsettings->cvalue(context->athlete->cyclist, GC_WITHINGS_TOKEN, "")!="")
grid->addWidget(withingsAuthorised, row, 1, Qt::AlignLeft | Qt::AlignVCenter);
else
withingsAuthorised->hide(); // if no token no show
grid->addWidget(wip, ++row, 0);
connect(withingsAuthorise, SIGNAL(clicked()), this, SLOT(authoriseWithings()));
#endif
grid->addWidget(wiurlLabel, ++row, 0);
grid->addWidget(wiURL, row, 1, 0);
grid->addWidget(wiuserLabel, ++row, 0);
grid->addWidget(wiUser, row, 1, Qt::AlignLeft | Qt::AlignVCenter);
grid->addWidget(wipassLabel, ++row, 0);
grid->addWidget(wiPass, row, 1, Qt::AlignLeft | Qt::AlignVCenter);
//////////////////////////////////////////////////
// Web Calendar
@@ -1041,6 +1042,17 @@ void CredentialsPage::authoriseTwitter()
oauthDialog->exec();
}
}
void CredentialsPage::authoriseWithings()
{
OAuthDialog *oauthDialog = new OAuthDialog(context, OAuthDialog::WITHINGS);
if (oauthDialog->sslLibMissing()) {
delete oauthDialog;
} else {
oauthDialog->setWindowModality(Qt::ApplicationModal);
oauthDialog->exec();
}
}
#endif
#if QT_VERSION >= 0x050000 // only in QT5 or higher
@@ -1154,9 +1166,6 @@ CredentialsPage::saveClicked()
appsettings->setCValue(context->athlete->cyclist, GC_SPORTPLUSHEALTHPASS, sphPass->text());
appsettings->setCValue(context->athlete->cyclist, GC_SELUSER, selUser->text());
appsettings->setCValue(context->athlete->cyclist, GC_SELPASS, selPass->text());
appsettings->setCValue(context->athlete->cyclist, GC_WIURL, wiURL->text());
appsettings->setCValue(context->athlete->cyclist, GC_WIUSER, wiUser->text());
appsettings->setCValue(context->athlete->cyclist, GC_WIKEY, wiPass->text());
appsettings->setCValue(context->athlete->cyclist, GC_WEBCAL_URL, webcalURL->text());
appsettings->setCValue(context->athlete->cyclist, GC_WEBCAL_URL, webcalURL->text());
appsettings->setCValue(context->athlete->cyclist, GC_TODAYSPLAN_URL, tdpURL->text());

View File

@@ -227,6 +227,7 @@ class CredentialsPage : public QScrollArea
public slots:
#ifdef GC_HAVE_KQOAUTH
void authoriseTwitter();
void authoriseWithings();
#endif
#if QT_VERSION >= 0x050000
void authoriseDropbox();
@@ -253,6 +254,7 @@ class CredentialsPage : public QScrollArea
#ifdef GC_HAVE_KQOAUTH
QPushButton *twitterAuthorise;
QPushButton *withingsAuthorise;
#endif
#if QT_VERSION >= 0x050000
QPushButton *dropboxAuthorise;
@@ -265,7 +267,7 @@ class CredentialsPage : public QScrollArea
#endif
QComboBox *dvCALDAVType;
QPushButton *stravaAuthorise, *stravaAuthorised, *twitterAuthorised;
QPushButton *stravaAuthorise, *stravaAuthorised, *twitterAuthorised, *withingsAuthorised;
QPushButton *tdpAuthorise, *tdpAuthorised;
QPushButton *networkFileStoreFolderBrowse;
QLineEdit *networkFileStoreFolder;

View File

@@ -277,6 +277,10 @@ macx {
# if you have your own MAPQUEST KEY
#DEFINES += GC_MAPQUESTAPI_KEY=\\\"xxxxxxxxxxxxxxxxxxxxxx\\\"
# USING THE WITHINGS API (http://oauth.withings.com/api)
#DEFINES += GC_WITHINGS_CLIENT_ID=\\\"xxxxxxxxxxxxxxx\\\"
#DEFINES += GC_WITHINGS_CLIENT_SECRET=\\\"xxxxxxxxxxxxxxx\\\"
# What video playback do you want?
DEFINES += GC_VIDEO_NONE # dont add any video playback support
#DEFINES += GC_VIDEO_QUICKTIME # mac only and the default