diff --git a/debian/control b/debian/control index db1cc724..03b23488 100644 --- a/debian/control +++ b/debian/control @@ -700,6 +700,15 @@ Depends: ${shlibs:Depends}, nymea-plugins-translations, Description: nymea.io plugin for Telegram This plugin allows nymea to send messages to telegram via the bot API. + + +Package: nymea-plugin-tempo +Architecture: any +Depends: ${shlibs:Depends}, + ${misc:Depends}, + nymea-plugins-translations, +Description: nymea.io plugin for Tempo time tracking + This package will install the nymea.io plugin for Tempo time tracking. Package: nymea-plugin-tplink diff --git a/debian/nymea-plugin-tempo.install.in b/debian/nymea-plugin-tempo.install.in new file mode 100644 index 00000000..c63e05ef --- /dev/null +++ b/debian/nymea-plugin-tempo.install.in @@ -0,0 +1 @@ +usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_integrationpluginstempo.so diff --git a/nymea-plugins.pro b/nymea-plugins.pro index eb037d01..a92e5782 100644 --- a/nymea-plugins.pro +++ b/nymea-plugins.pro @@ -58,6 +58,7 @@ PLUGIN_DIRS = \ tasmota \ tcpcommander \ telegram \ + tempo \ texasinstruments \ tplink \ tuya \ diff --git a/tempo/README.md b/tempo/README.md new file mode 100644 index 00000000..cc12441a --- /dev/null +++ b/tempo/README.md @@ -0,0 +1,17 @@ +# Tempo + +Add Tempo time tracking accounts to nymea. + +## Supported Things + +* Tempo Atlassian Account + * Time Accounts + +## Requirements + +* Package 'nymea-plugin-tempo' must be installed +* A atlassian account and client credentials + +## More + +https://apidocs.tempo.io/ diff --git a/tempo/integrationplugintempo.cpp b/tempo/integrationplugintempo.cpp new file mode 100644 index 00000000..9cc92cb4 --- /dev/null +++ b/tempo/integrationplugintempo.cpp @@ -0,0 +1,181 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2020, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU Lesser General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU Lesser General Public License as published by the Free +* Software Foundation; version 3. This project is distributed in the hope that +* it will be useful, but WITHOUT ANY WARRANTY; without even the implied +* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this project. If not, see . +* +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "integrationplugintempo.h" +#include "plugininfo.h" + +#include "network/networkaccessmanager.h" + +#include +#include +#include +#include +#include + +IntegrationPluginTempo::IntegrationPluginTempo() +{ +} + +void IntegrationPluginTempo::startPairing(ThingPairingInfo *info) +{ + qCDebug(dcTempo()) << "Start pairing"; + + if (info->thingClassId() == tempoConnectionThingClassId) { + + QByteArray clientId = configValue(tempoPluginCustomClientIdParamTypeId).toByteArray(); + QByteArray clientSecret = configValue(tempoPluginCustomClientSecretParamTypeId).toByteArray(); + if (clientId.isEmpty() || clientSecret.isEmpty()) { + clientId = apiKeyStorage()->requestKey("tempo").data("clientId"); + clientSecret = apiKeyStorage()->requestKey("tempo").data("clientSecret"); + } else { + qCDebug(dcTempo()) << "Using custom client secret and id"; + } + if (clientId.isEmpty() || clientSecret.isEmpty()) { + info->finish(Thing::ThingErrorAuthenticationFailure, tr("Client id and/or seceret is not available.")); + return; + } + + QString jiraCloudInstanceName = info->params().paramValue(tempoConnectionAtlassianAccountParamTypeId).toString(); + QUrl url = Tempo::getLoginUrl(QUrl("https://127.0.0.1:8888"), jiraCloudInstanceName, clientId); + qCDebug(dcTempo()) << "Checking if the Tempo server is reachable"; + QNetworkReply *reply = hardwareManager()->networkManager()->get(QNetworkRequest(url)); + connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); + connect(reply, &QNetworkReply::finished, info, [reply, info, url, this] { + + if (reply->error() != QNetworkReply::NetworkError::HostNotFoundError) { + qCDebug(dcTempo()) << "Tempo server is reachable"; + info->setOAuthUrl(url); + info->finish(Thing::ThingErrorNoError); + } else { + qCWarning(dcTempo()) << "Got online check error" << reply->error() << reply->errorString(); + info->finish(Thing::ThingErrorSetupFailed, tr("Tempo server not reachable, please check the internet connection")); + } + }); + } else { + qCWarning(dcTempo()) << "Unhandled pairing method!"; + info->finish(Thing::ThingErrorCreationMethodNotSupported); + } +} + +void IntegrationPluginTempo::confirmPairing(ThingPairingInfo *info, const QString &username, const QString &secret) +{ + Q_UNUSED(username); + qCDebug(dcTempo()) << "Confirm pairing"; + if (info->thingClassId() == tempoConnectionThingClassId) { + + QUrl url(secret); + QUrlQuery query(url); + QByteArray authorizationCode = query.queryItemValue("code").toLocal8Bit(); + if (authorizationCode.isEmpty()) { + qCWarning(dcTempo()) << "No authorization code received."; + return info->finish(Thing::ThingErrorAuthenticationFailure); + } + + Tempo *tempo = m_setupTempoConnections.value(info->thingId()); + if (!tempo) { + qWarning(dcTempo()) << "No Tempo connection found for device:" << info->thingName(); + m_setupTempoConnections.remove(info->thingId()); + return info->finish(Thing::ThingErrorHardwareFailure); + } + qCDebug(dcTempo()) << "Authorization code" << authorizationCode.mid(0, 4)+QString().fill('*', authorizationCode.length()-4) ; + tempo->getAccessTokenFromAuthorizationCode(authorizationCode); + connect(tempo, &Tempo::receivedRefreshToken, info, [info, this](const QByteArray &refreshToken){ + qCDebug(dcTempo()) << "Token:" << refreshToken.mid(0, 4)+QString().fill('*', refreshToken.length()-4) ; + + pluginStorage()->beginGroup(info->thingId().toString()); + pluginStorage()->setValue("refresh_token", refreshToken); + pluginStorage()->endGroup(); + + info->finish(Thing::ThingErrorNoError); + }); + } else { + Q_ASSERT_X(false, "confirmPairing", QString("Unhandled thingClassId: %1").arg(info->thingClassId().toString()).toUtf8()); + } +} + +void IntegrationPluginTempo::setupThing(ThingSetupInfo *info) +{ + Thing *thing = info->thing(); + qCDebug(dcTempo()) << "Setup thing"; + + if (thing->thingClassId() == tempoConnectionThingClassId) { + + } +} + +void IntegrationPluginTempo::executeAction(ThingActionInfo *info) +{ + Thing *thing = info->thing(); + Action action = info->action(); + + if (thing->thingClassId() == tempoConnectionThingClassId) { + + } else if (thing->thingClassId() == accountThingClassId) { + + } +} + +void IntegrationPluginTempo::thingRemoved(Thing *thing) +{ + qCDebug(dcTempo()) << "Thing removed" << thing->name(); +} + +void IntegrationPluginTempo::onReceivedAccounts(const QList &accounts) +{ + qCDebug(dcTempo()) << "Received" << accounts.count() << "accounts"; + + Tempo *tempoConnection = static_cast(sender()); + Thing *parentThing = m_tempoConnections.key(tempoConnection); + if (!parentThing) + return; + + ThingDescriptors desciptors; + Q_FOREACH(Tempo::Account account, accounts) { + ThingClassId thingClassId; + + Thing * existingThing = myThings().findByParams(ParamList() << Param(m_idParamTypeIds.value(thingClassId), appliance.homeApplianceId)); + if (existingThing) { + qCDebug(dcTempo()) << "Thing is already added to system" << existingThing->name(); + //Set connected state; + //existingThing->setStateValue(m_connectedStateTypeIds.value(thingClassId), appliance.connected); + continue; + } + qCDebug(dcTempo()) << "Found new account:" << account.name << "key:" << account.key << "id:" << account.id; + ThingDescriptor descriptor(thingClassId, account.name, account.key, parentThing->id()); + + ParamList params; + //params << Param(m_idParamTypeIds.value(thingClassId), appliance.homeApplianceId); + descriptor.setParams(params); + desciptors.append(descriptor); + } + if (!desciptors.isEmpty()) + emit autoThingsAppeared(desciptors); +} + diff --git a/tempo/integrationplugintempo.h b/tempo/integrationplugintempo.h new file mode 100644 index 00000000..5ad3bd88 --- /dev/null +++ b/tempo/integrationplugintempo.h @@ -0,0 +1,68 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2020, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU Lesser General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU Lesser General Public License as published by the Free +* Software Foundation; version 3. This project is distributed in the hope that +* it will be useful, but WITHOUT ANY WARRANTY; without even the implied +* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this project. If not, see . +* +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef INTEGRATIONPLUGINTEMPO_H +#define INTEGRATIONPLUGINTEMPO_H + +#include "integrations/integrationplugin.h" +#include "plugintimer.h" + +#include "tempo.h" + +class IntegrationPluginTempo : public IntegrationPlugin +{ + Q_OBJECT + + Q_PLUGIN_METADATA(IID "io.nymea.IntegrationPlugin" FILE "integrationplugintempo.json") + Q_INTERFACES(IntegrationPlugin) + +public: + explicit IntegrationPluginTempo(); + + void startPairing(ThingPairingInfo *info) override; + void confirmPairing(ThingPairingInfo *info, const QString &username, const QString &secret) override; + + void setupThing(ThingSetupInfo *info) override; + void executeAction(ThingActionInfo *info) override; + void thingRemoved(Thing *thing) override; + +private: + PluginTimer *m_pluginTimer15min = nullptr; + + QHash m_setupTempoConnections; + QHash m_tempoConnections; + +private slots: + void onConnectionChanged(bool connected); + void onAuthenticationStatusChanged(bool authenticated); + void onRequestExecuted(QUuid requestId, bool success); + void onReceivedAccounts(const QList &accounts); +}; +#endif // INTEGRATIONPLUGINTEMPO_H diff --git a/tempo/integrationplugintempo.json b/tempo/integrationplugintempo.json new file mode 100644 index 00000000..1aaede7d --- /dev/null +++ b/tempo/integrationplugintempo.json @@ -0,0 +1,172 @@ +{ + "id": "809bc4ca-d1cd-4279-9e0d-7324537ccb5a", + "name": "tempo", + "displayName": "Tempo", + "apiKeys": ["tempo"], + "paramTypes": [ + { + "id": "c130b2b7-6d30-406e-899b-669a065daee3", + "name": "customClientId", + "displayName": "Custom client id", + "defaultValue": "", + "type": "QString" + }, + { + "id": "9c759711-e772-44ce-9d86-6a3af89c2d94", + "name": "customClientSecret", + "displayName": "Custom client secret", + "defaultValue": "", + "type": "QString" + } + ], + "vendors": [ + { + "id": "58fc1ab7-b8b5-4e52-8388-72957ce5852d", + "name": "tempo", + "displayName": "Tempo", + "thingClasses": [ + { + "id": "878eae0a-6217-4b36-bd46-72c911e52e73", + "name": "tempoConnection", + "displayName": "Tempo connection", + "interfaces": ["account"], + "createMethods": ["user"], + "setupMethod": "oauth", + "paramTypes": [ + { + "id": "b4110c37-8331-4057-8e9f-12f34c2623fe", + "name": "atlassianAccountName", + "displayName": "Atlassian account name", + "type": "QString", + "defaultValue": "" + } + ], + "stateTypes": [ + { + "id": "15f45315-5419-4e1b-ace3-fc21503d3b70", + "name": "connected", + "displayName": "Connected", + "displayNameEvent": "Connected changed", + "defaultValue": true, + "cached": false, + "type": "bool" + }, + { + "id": "e4b5be87-dbc9-481e-88da-608c71be8bda", + "name": "loggedIn", + "displayName": "Logged in", + "displayNameEvent": "Logged in changed", + "defaultValue": true, + "type": "bool" + }, + { + "id": "f3b9581b-7828-4fbe-be5f-3e8aad78a71e", + "name": "userDisplayName", + "displayName": "User name", + "displayNameEvent": "User name changed", + "defaultValue": "", + "type": "QString" + }, + { + "id": "db70444d-bf67-4133-b2de-54aefbdd7149", + "name": "autoAddAccounts", + "displayName": "Auto add accounts", + "displayNameEvent": "Auto add accounts", + "defaultValue": true, + "type": "bool" + } + ] + }, + { + "id": "8be71352-bdfd-450b-903e-79a4ed203701", + "name": "account", + "displayName": "Account", + "interfaces": ["connectable"], + "createMethods": ["auto"], + "browsable": true, + "paramTypes": [ + { + "id": "c6aeddae-56af-496d-a419-1635ff9bae50", + "name": "id", + "displayName": "ID", + "defaultValue": "-", + "type": "QString" + } + ], + "stateTypes": [ + { + "id": "0b776bc1-9e56-4205-9bc3-b356026f5b64", + "name": "connected", + "displayName": "Connected", + "displayNameEvent": "Connected changed", + "defaultValue": true, + "cached": false, + "type": "bool" + }, + { + "id": "7948f15b-7243-404e-9e67-18e915e8b328", + "name": "status", + "displayName": "Status", + "displayNameEvent": "Status changed", + "defaultValue": "OPEN", + "possibleValues": [ + "OPEN", + "CLOSED", + "ARCHIVED" + ], + "type": "QString" + }, + { + "id": "abd55ea0-ad4e-413e-bc77-3e8b7f0a9be4", + "name": "global", + "displayName": "Global", + "displayNameEvent": "Global changed", + "defaultValue": false, + "type": "bool" + }, + { + "id": "44ebbc18-7511-48c0-860b-c4de5f634ed6", + "name": "monthlyBudget", + "displayName": "Monthly budget", + "displayNameEvent": "Monthly budget changed", + "defaultValue": 0, + "type": "int" + }, + { + "id": "f1f2af66-d09a-4242-9058-401145f662c4", + "name": "lead", + "displayName": "Lead", + "displayNameEvent": "Lead changed", + "defaultValue": "", + "type": "QString" + }, + { + "id": "ece43b12-4a0d-4e25-b811-b1aca610bea8", + "name": "contact", + "displayName": "Contact", + "displayNameEvent": "Contact changed", + "defaultValue": "", + "type": "QString" + }, + { + "id": "3af6d1c0-bb0a-406f-809b-2c367e1a16bb", + "name": "category", + "displayName": "Category", + "displayNameEvent": "Category changed", + "defaultValue": "", + "type": "QString" + }, + { + "id": "3dcc1426-51f8-46fa-9967-5a93d7bb2633", + "name": "Customer", + "displayName": "Customer", + "displayNameEvent": "Customer changed", + "defaultValue": "", + "type": "QString" + } + ] + } + ] + } + ] +} diff --git a/tempo/meta.json b/tempo/meta.json new file mode 100644 index 00000000..e69de29b diff --git a/tempo/tempo.cpp b/tempo/tempo.cpp new file mode 100644 index 00000000..fc497ce9 --- /dev/null +++ b/tempo/tempo.cpp @@ -0,0 +1,155 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2020, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU Lesser General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU Lesser General Public License as published by the Free +* Software Foundation; version 3. This project is distributed in the hope that +* it will be useful, but WITHOUT ANY WARRANTY; without even the implied +* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this project. If not, see . +* +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include +#include + +#include "tempo.h" +#include "extern-plugininfo.h" + +Tempo::Tempo(NetworkAccessManager *networkmanager, const QByteArray &clientId, const QByteArray &clientSecret, QObject *parent) : +QObject(parent), + m_clientId(clientId), + m_clientSecret(clientSecret), + m_networkManager(networkmanager) + +{ + m_tokenRefreshTimer = new QTimer(this); + m_tokenRefreshTimer->setSingleShot(true); + connect(m_tokenRefreshTimer, &QTimer::timeout, this, &Tempo::onRefreshTimer); +} + +QByteArray Tempo::accessToken() +{ + return m_accessToken; +} + +QByteArray Tempo::refreshToken() +{ + return m_refreshToken; +} + +QUrl Tempo::getLoginUrl(const QUrl &redirectUrl, const QString &jiraCloudInstanceName, const QByteArray &clientId) +{ + QUrl url; + url.setScheme("https"); + url.setHost(jiraCloudInstanceName+"atlassian.net"); + url.setPath("/plugins/servlet/ac/io.tempo.jira/oauth-authorize/"); + QUrlQuery query; + query.addQueryItem("client_id", clientId); + query.addQueryItem("redirect_uri", redirectUrl.toString()); + query.addQueryItem("access_type", "tenant_user"); + url.setQuery(query); + return url; +} + +void Tempo::getAccounts() +{ + QUrl url = QUrl(m_baseControlUrl+"/accounts"); + + QNetworkRequest request(url); + request.setRawHeader("Authorization", "Bearer "+m_accessToken); + + QNetworkReply *reply = m_networkManager->get(request); + connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); + connect(reply, &QNetworkReply::finished, this, [this, reply]{ + + QByteArray rawData = reply->readAll(); + if (!checkStatusCode(reply, rawData)) { + return; + } + QVariantMap dataMap = QJsonDocument::fromJson(rawData).toVariant().toMap(); + QVariantList accountList = dataMap.value("results").toList(); + QList accounts; + Q_FOREACH(QVariant var, accountList) { + QVariantMap map = var.toMap(); + Account account; + account.self = map["self"].toString(); + account.key = map["key"].toString(); + account.id = map["id"].toInt(); + account.name = map["name"].toString(); + + if (map["status"] == "OPEN") { + account.status = Status::Open; + } else if (map["status"] == "CLOSED") { + account.status = Status::Closed; + } else if (map["status"] == "ARCHIVED") { + account.status = Status::Archived; + } + account.global = map["global"].toBool(); + account.monthlyBudget = map["monthlyBudget"].toInt(); + + QVariantMap lead = map["lead"].toMap(); + account.lead.self = lead["self"].toString(); + account.lead.accountId = lead["accountId"].toString(); + account.lead.displayName = lead["displayName"].toString(); + //TODO Customer + QVariantMap customer = map["customer"].toMap(); + account.customer.self = lead["self"].toString(); + //TODO Category + QVariantMap category = map["category"].toMap(); + account.category.self = lead["self"].toString(); + //TODO Contact + QVariantMap contact = map["contact"].toMap(); + account.contact.self = lead["self"].toString(); + + accounts.append(account); + } + if (!accounts.isEmpty()) { + + } + }); +} + +void Tempo::getWorkloadByAccount(const QString &accountKey, QDate from, QDate to) +{ + +} + +void Tempo::onRefreshTimer() +{ + +} + +void Tempo::setAuthenticated(bool state) +{ + if (state != m_authenticated) { + m_authenticated = state; + emit authenticationStatusChanged(state); + } +} + +void Tempo::setConnected(bool state) +{ + if (state != m_connected) { + m_connected = state; + emit connectionChanged(state); + } +} diff --git a/tempo/tempo.h b/tempo/tempo.h new file mode 100644 index 00000000..c6316c1f --- /dev/null +++ b/tempo/tempo.h @@ -0,0 +1,135 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2020, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU Lesser General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU Lesser General Public License as published by the Free +* Software Foundation; version 3. This project is distributed in the hope that +* it will be useful, but WITHOUT ANY WARRANTY; without even the implied +* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this project. If not, see . +* +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef TEMPO_H +#define TEMPO_H + +#include +#include +#include + +#include "network/networkaccessmanager.h" + +class Tempo : public QObject +{ + Q_OBJECT +public: + + enum Status { + Open, + Closed, + Archived + }; + Q_ENUM(Status) + struct Lead { + QUrl self; + QString accountId; + QString displayName; + }; + + struct Contact { + QUrl self; + QString accountId; + QString displayName; + QString type; + }; + + struct Category { + QUrl self; + QString accountId; + QString displayName; + }; + + struct Customer { + QUrl self; + QString key; + int id; + QString name; + }; + + struct Account { + QUrl self; + QString key; + int id; + QString name; + Status status; + bool global; + int monthlyBudget; + Lead lead; + Contact contact; + Category category; + Customer customer; + }; + + explicit Tempo(NetworkAccessManager *networkmanager, const QByteArray &clientId, const QByteArray &clientSecret, QObject *parent = nullptr); + QByteArray accessToken(); + QByteArray refreshToken(); + + static QUrl getLoginUrl(const QUrl &redirectUrl, const QString &jiraCloudInstanceName, const QByteArray &clientId); + + void getAccessTokenFromRefreshToken(const QByteArray &refreshToken); + void getAccessTokenFromAuthorizationCode(const QByteArray &authorizationCode); + + void getAccounts(); + void getWorkloadByAccount(const QString &accountKey, QDate from, QDate to); + +private: + + QByteArray m_baseTokenUrl = "https://api.tempo.io/oauth/token/"; + QByteArray m_baseControlUrl = "https://api.tempo.io/core/3/"; + QByteArray m_clientId; + QByteArray m_clientSecret; + + QByteArray m_accessToken; + QByteArray m_refreshToken; + QByteArray m_redirectUri = "https://127.0.0.1:8888"; + + NetworkAccessManager *m_networkManager = nullptr; + QTimer *m_tokenRefreshTimer = nullptr; + + void setAuthenticated(bool state); + void setConnected(bool state); + + bool m_authenticated = false; + bool m_connected = false; + + bool checkStatusCode(QNetworkReply *reply, const QByteArray &rawData); +private slots: + void onRefreshTimer(); + +signals: + void authenticationStatusChanged(bool state); + void connectionChanged(bool connected); + void receivedRefreshToken(const QByteArray &refreshToken); + void receivedAccessToken(const QByteArray &accessToken); + void accountsReceived(const QList accounts); +}; + +#endif // TEMPO_H diff --git a/tempo/tempo.png b/tempo/tempo.png new file mode 100644 index 00000000..28852c46 Binary files /dev/null and b/tempo/tempo.png differ diff --git a/tempo/tempo.pro b/tempo/tempo.pro new file mode 100644 index 00000000..f649c4e5 --- /dev/null +++ b/tempo/tempo.pro @@ -0,0 +1,14 @@ +include(../plugins.pri) + +QT += network + +TARGET = $$qtLibraryTarget(nymea_integrationplugintempo) + +SOURCES += \ + integrationplugintempo.cpp \ + tempo.cpp + +HEADERS += \ + integrationplugintempo.h \ + tempo.h +