added Tempo plugin
This commit is contained in:
parent
68ca8605f3
commit
8150258814
9
debian/control
vendored
9
debian/control
vendored
@ -700,6 +700,15 @@ Depends: ${shlibs:Depends},
|
|||||||
nymea-plugins-translations,
|
nymea-plugins-translations,
|
||||||
Description: nymea.io plugin for Telegram
|
Description: nymea.io plugin for Telegram
|
||||||
This plugin allows nymea to send messages to telegram via the bot API.
|
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
|
Package: nymea-plugin-tplink
|
||||||
|
|||||||
1
debian/nymea-plugin-tempo.install.in
vendored
Normal file
1
debian/nymea-plugin-tempo.install.in
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_integrationpluginstempo.so
|
||||||
@ -58,6 +58,7 @@ PLUGIN_DIRS = \
|
|||||||
tasmota \
|
tasmota \
|
||||||
tcpcommander \
|
tcpcommander \
|
||||||
telegram \
|
telegram \
|
||||||
|
tempo \
|
||||||
texasinstruments \
|
texasinstruments \
|
||||||
tplink \
|
tplink \
|
||||||
tuya \
|
tuya \
|
||||||
|
|||||||
17
tempo/README.md
Normal file
17
tempo/README.md
Normal file
@ -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/
|
||||||
181
tempo/integrationplugintempo.cpp
Normal file
181
tempo/integrationplugintempo.cpp
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* 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 <QTimer>
|
||||||
|
#include <QUrlQuery>
|
||||||
|
#include <QUrl>
|
||||||
|
#include <QNetworkRequest>
|
||||||
|
#include <QNetworkReply>
|
||||||
|
|
||||||
|
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<Tempo::Account> &accounts)
|
||||||
|
{
|
||||||
|
qCDebug(dcTempo()) << "Received" << accounts.count() << "accounts";
|
||||||
|
|
||||||
|
Tempo *tempoConnection = static_cast<Tempo *>(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);
|
||||||
|
}
|
||||||
|
|
||||||
68
tempo/integrationplugintempo.h
Normal file
68
tempo/integrationplugintempo.h
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* 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<ThingId, Tempo *> m_setupTempoConnections;
|
||||||
|
QHash<Thing *, Tempo *> m_tempoConnections;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onConnectionChanged(bool connected);
|
||||||
|
void onAuthenticationStatusChanged(bool authenticated);
|
||||||
|
void onRequestExecuted(QUuid requestId, bool success);
|
||||||
|
void onReceivedAccounts(const QList<Tempo::Account> &accounts);
|
||||||
|
};
|
||||||
|
#endif // INTEGRATIONPLUGINTEMPO_H
|
||||||
172
tempo/integrationplugintempo.json
Normal file
172
tempo/integrationplugintempo.json
Normal file
@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
0
tempo/meta.json
Normal file
0
tempo/meta.json
Normal file
155
tempo/tempo.cpp
Normal file
155
tempo/tempo.cpp
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* 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 <QJsonDocument>
|
||||||
|
#include <QUrlQuery>
|
||||||
|
|
||||||
|
#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<Account> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
135
tempo/tempo.h
Normal file
135
tempo/tempo.h
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* 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 <QObject>
|
||||||
|
#include <QUrl>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
#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<Account> accounts);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // TEMPO_H
|
||||||
BIN
tempo/tempo.png
Normal file
BIN
tempo/tempo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 29 KiB |
14
tempo/tempo.pro
Normal file
14
tempo/tempo.pro
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
include(../plugins.pri)
|
||||||
|
|
||||||
|
QT += network
|
||||||
|
|
||||||
|
TARGET = $$qtLibraryTarget(nymea_integrationplugintempo)
|
||||||
|
|
||||||
|
SOURCES += \
|
||||||
|
integrationplugintempo.cpp \
|
||||||
|
tempo.cpp
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
integrationplugintempo.h \
|
||||||
|
tempo.h
|
||||||
|
|
||||||
Loading…
x
Reference in New Issue
Block a user