From 88542d67e353574c44611fba9cbef186bcc67288 Mon Sep 17 00:00:00 2001 From: "bernhard.trinnes" Date: Thu, 16 Apr 2020 04:02:43 +0700 Subject: [PATCH] HomeConnect: Devices to things --- debian/nymea-plugin-homeconnect.install.in | 1 + homeconnect/devicepluginhomeconnect.cpp | 253 ----------------- homeconnect/devicepluginhomeconnect.h | 67 ----- homeconnect/homeconnect.cpp | 30 ++ homeconnect/homeconnect.h | 51 ++-- homeconnect/homeconnect.pro | 6 +- homeconnect/integrationpluginhomeconnect.cpp | 258 ++++++++++++++++++ homeconnect/integrationpluginhomeconnect.h | 76 ++++++ ...json => integrationpluginhomeconnect.json} | 10 +- 9 files changed, 403 insertions(+), 349 deletions(-) create mode 100644 debian/nymea-plugin-homeconnect.install.in delete mode 100644 homeconnect/devicepluginhomeconnect.cpp delete mode 100644 homeconnect/devicepluginhomeconnect.h create mode 100644 homeconnect/integrationpluginhomeconnect.cpp create mode 100644 homeconnect/integrationpluginhomeconnect.h rename homeconnect/{devicepluginhomeconnect.json => integrationpluginhomeconnect.json} (85%) diff --git a/debian/nymea-plugin-homeconnect.install.in b/debian/nymea-plugin-homeconnect.install.in new file mode 100644 index 00000000..44410fd7 --- /dev/null +++ b/debian/nymea-plugin-homeconnect.install.in @@ -0,0 +1 @@ +usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_integrationpluginhomeconnect.so diff --git a/homeconnect/devicepluginhomeconnect.cpp b/homeconnect/devicepluginhomeconnect.cpp deleted file mode 100644 index f76b3d0b..00000000 --- a/homeconnect/devicepluginhomeconnect.cpp +++ /dev/null @@ -1,253 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * * - * Copyright (C) 2019 Bernhard Trinnes . * - * * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#include "devicepluginhomeconnect.h" -#include "devices/device.h" -#include "network/networkaccessmanager.h" -#include "plugininfo.h" - -#include -#include -#include -#include - -DevicePluginHomeConnect::DevicePluginHomeConnect() -{ -} - - -DevicePluginHomeConnect::~DevicePluginHomeConnect() -{ - if (m_pluginTimer5sec) - hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer5sec); - if (m_pluginTimer60sec) - hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer60sec); -} - - -Device::DeviceSetupStatus DevicePluginHomeConnect::setupDevice(Device *device) -{ - if (!m_pluginTimer5sec) { - m_pluginTimer5sec = hardwareManager()->pluginTimerManager()->registerTimer(5); - connect(m_pluginTimer5sec, &PluginTimer::timeout, this, [this]() { - - foreach (Device *connectionDevice, myDevices().filterByDeviceClassId(homeConnectConnectionDeviceClassId)) { - HomeConnect *HomeConnect = m_homeConnectConnections.value(connectionDevice); - if (!HomeConnect) { - qWarning(dcHomeConnect()) << "No HomeConnect connection found to device" << connectionDevice->name(); - continue; - } - } - }); - } - - if (!m_pluginTimer60sec) { - m_pluginTimer60sec = hardwareManager()->pluginTimerManager()->registerTimer(60); - connect(m_pluginTimer60sec, &PluginTimer::timeout, this, [this]() { - foreach (Device *device, myDevices().filterByDeviceClassId(homeConnectConnectionDeviceClassId)) { - HomeConnect *homeConnect = m_homeConnectConnections.value(device); - if (!homeConnect) { - qWarning(dcHomeConnect()) << "No HomeConnect connection found to device" << device->name(); - continue; - } - - } - }); - } - - if (device->deviceClassId() == homeConnectConnectionDeviceClassId) { - HomeConnect *homeConnect; - if (m_setupHomeConnectConnections.keys().contains(device->id())) { - //Fresh device setup, has already a fresh access token - qCDebug(dcHomeConnect()) << "HomeConnect OAuth setup complete"; - HomeConnect = m_setupHomeConnectConnections.take(device->id()); - connect(homeConnect, &HomeConnect::connectionChanged, this, &DevicePluginHomeConnect::onConnectionChanged); - connect(homeConnect, &HomeConnect::actionExecuted, this, &DevicePluginHomeConnect::onActionExecuted); - connect(homeConnect, &HomeConnect::authenticationStatusChanged, this, &DevicePluginHomeConnect::onAuthenticationStatusChanged); - m_homeConnectConnections.insert(device, homeConnect); - return Device::DeviceSetupStatusSuccess; - } else { - //device loaded from the device database, needs a new access token; - pluginStorage()->beginGroup(device->id().toString()); - QByteArray refreshToken = pluginStorage()->value("refresh_token").toByteArray(); - pluginStorage()->endGroup(); - - homeConnect = new HomeConnect(hardwareManager()->networkManager(), "TODO", "TODO", this); - connect(homeConnect, &HomeConnect::connectionChanged, this, &DevicePluginHomeConnect::onConnectionChanged); - connect(homeConnect, &HomeConnect::actionExecuted, this, &DevicePluginHomeConnect::onActionExecuted); - connect(homeConnect, &HomeConnect::authenticationStatusChanged, this, &DevicePluginHomeConnect::onAuthenticationStatusChanged); - HomeConnect->getAccessTokenFromRefreshToken(refreshToken); - m_homeConnectConnections.insert(device, homeConnect); - return Device::DeviceSetupStatusAsync; - } - } - return Device::DeviceSetupStatusFailure; -} - -DevicePairingInfo DevicePluginHomeConnect::pairDevice(DevicePairingInfo &devicePairingInfo) -{ - if (devicePairingInfo.deviceClassId() == homeConnectConnectionDeviceClassId) { - - HomeConnect *HomeConnect = new HomeConnect(hardwareManager()->networkManager(), "TODO", "TODO", this); - QUrl url = homeConnect->getLoginUrl(QUrl("https://127.0.0.1:8888"), "TODO Scope"); - qCDebug(dcHomeConnect()) << "HomeConnect url:" << url; - devicePairingInfo.setOAuthUrl(url); - devicePairingInfo.setStatus(Device::DeviceErrorNoError); - m_setupHomeConnectConnections.insert(devicePairingInfo.deviceId(), HomeConnect); - return devicePairingInfo; - } - - qCWarning(dcHomeConnect()) << "Unhandled pairing metod!"; - devicePairingInfo.setStatus(Device::DeviceErrorCreationMethodNotSupported); - return devicePairingInfo; -} - -DevicePairingInfo DevicePluginHomeConnect::confirmPairing(DevicePairingInfo &devicePairingInfo, const QString &username, const QString &secret) -{ - Q_UNUSED(username); - - if (devicePairingInfo.deviceClassId() == homeConnectConnectionDeviceClassId) { - qCDebug(dcHomeConnect()) << "Redirect url is" << secret; - QUrl url(secret); - QUrlQuery query(url); - QByteArray authorizationCode = query.queryItemValue("code").toLocal8Bit(); - QByteArray state = query.queryItemValue("state").toLocal8Bit(); - //TODO evaluate state if it equals the given state - - HomeConnect *HomeConnect = m_setupHomeConnectConnections.value(devicePairingInfo.deviceId()); - - if (!HomeConnect) { - qWarning(dcHomeConnect()) << "No HomeConnect connection found for device:" << devicePairingInfo.deviceName(); - m_setupHomeConnectConnections.remove(devicePairingInfo.deviceId()); - HomeConnect->deleteLater(); - devicePairingInfo.setStatus(Device::DeviceErrorHardwareFailure); - return devicePairingInfo; - } - HomeConnect->getAccessTokenFromAuthorizationCode(authorizationCode); - connect(HomeConnect, &HomeConnect::authenticationStatusChanged, this, [devicePairingInfo, this](bool authenticated){ - HomeConnect *homeConnect = static_cast(sender()); - DevicePairingInfo info(devicePairingInfo); - if(!authenticated) { - qWarning(dcHomeConnect()) << "Authentication process failed" << devicePairingInfo.deviceName(); - m_setupHomeConnectConnections.remove(info.deviceId()); - homeConnect->deleteLater(); - info.setStatus(Device::DeviceErrorSetupFailed); - emit pairingFinished(info); - return; - } - QByteArray accessToken = homeConnect->accessToken(); - QByteArray refreshToken = homeConnect->refreshToken(); - qCDebug(dcHomeConnect()) << "Token:" << accessToken << refreshToken; - - pluginStorage()->beginGroup(info.deviceId().toString()); - pluginStorage()->setValue("refresh_token", refreshToken); - pluginStorage()->endGroup(); - - info.setStatus(Device::DeviceErrorNoError); - emit pairingFinished(info); - }); - devicePairingInfo.setStatus(Device::DeviceErrorAsync); - return devicePairingInfo; - } - qCWarning(dcHomeConnect()) << "Invalid deviceclassId -> no pairing possible with this device"; - devicePairingInfo.setStatus(Device::DeviceErrorHardwareFailure); - return devicePairingInfo; -} - -void DevicePluginHomeConnect::postSetupDevice(Device *device) -{ - if (device->deviceClassId() == homeConnectConnectionDeviceClassId) { - HomeConnect *homeConnect = m_HomeConnectConnections.value(device); - Q_UNUSED(homeConnect) - } -} - - -void DevicePluginHomeConnect::startMonitoringAutoDevices() -{ - -} - -void DevicePluginHomeConnect::deviceRemoved(Device *device) -{ - qCDebug(dcHomeConnect) << "Delete " << device->name(); - if (myDevices().empty()) { - hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer5sec); - hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer60sec); - m_pluginTimer5sec = nullptr; - m_pluginTimer60sec = nullptr; - } -} - - -Device::DeviceError DevicePluginHomeConnect::executeAction(Device *device, const Action &action) -{ - Q_UNUSED(device) - Q_UNUSED(action) - return Device::DeviceErrorDeviceClassNotFound; -} - -void DevicePluginHomeConnect::onConnectionChanged(bool connected) -{ - HomeConnect *HomeConnect = static_cast(sender()); - Device *device = m_homeConnectConnections.key(HomeConnect); - if (!device) - return; - device->setStateValue(homeConnectConnectionConnectedStateTypeId, connected); -} - -void DevicePluginHomeConnect::onAuthenticationStatusChanged(bool authenticated) -{ - HomeConnect *HomeConnectConnection = static_cast(sender()); - Device *device = m_homeConnectConnections.key(HomeConnectConnection); - if (!device) - return; - - if (!device->setupComplete()) { - if (authenticated) { - emit deviceSetupFinished(device, Device::DeviceSetupStatusSuccess); - } else { - emit deviceSetupFinished(device, Device::DeviceSetupStatusFailure); - } - } else { - device->setStateValue(homeConnectConnectionLoggedInStateTypeId, authenticated); - if (!authenticated) { - //refresh access token needs to be refreshed - pluginStorage()->beginGroup(device->id().toString()); - QByteArray refreshToken = pluginStorage()->value("refresh_token").toByteArray(); - pluginStorage()->endGroup(); - HomeConnectConnection->getAccessTokenFromRefreshToken(refreshToken); - } - } -} - -void DevicePluginHomeConnect::onActionExecuted(QUuid HomeConnectActionId, bool success) -{ - if (m_pendingActions.contains(HomeConnectActionId)) { - ActionId nymeaActionId = m_pendingActions.value(HomeConnectActionId); - if (success) { - emit actionExecutionFinished(nymeaActionId, Device::DeviceErrorNoError); - } else { - emit actionExecutionFinished(nymeaActionId, Device::DeviceErrorHardwareFailure); - } - } -} diff --git a/homeconnect/devicepluginhomeconnect.h b/homeconnect/devicepluginhomeconnect.h deleted file mode 100644 index 6d9f6cce..00000000 --- a/homeconnect/devicepluginhomeconnect.h +++ /dev/null @@ -1,67 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * * - * Copyright (C) 2019 Bernhard Trinnes . * - * * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#ifndef DEVICEPLUGINHOMECONNECT_H -#define DEVICEPLUGINHOMECONNECt_H - -#include "devices/deviceplugin.h" -#include "plugintimer.h" -#include "homeconnect.h" - -#include -#include - -class DevicePluginHomeConnect : public DevicePlugin -{ - Q_OBJECT - Q_PLUGIN_METADATA(IID "io.nymea.DevicePlugin" FILE "devicepluginHomeConnect.json") - Q_INTERFACES(DevicePlugin) - -public: - explicit DevicePluginHomeConnect(); - ~DevicePluginHomeConnect() 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 executeAction(Device *device, const Action &action) override; - -private: - PluginTimer *m_pluginTimer5sec = nullptr; - PluginTimer *m_pluginTimer60sec = nullptr; - - QHash m_setupHomeConnectConnections; - QHash m_homeConnectConnections; - - QHash m_pendingActions; - -private slots: - void onConnectionChanged(bool connected); - void onAuthenticationStatusChanged(bool authenticated); - void onActionExecuted(QUuid actionId, bool success); -}; - -#endif // DEVICEPLUGINHOMECONNECT_H diff --git a/homeconnect/homeconnect.cpp b/homeconnect/homeconnect.cpp index 2e43f051..69eef3a9 100644 --- a/homeconnect/homeconnect.cpp +++ b/homeconnect/homeconnect.cpp @@ -1,3 +1,33 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* 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 "homeconnect.h" #include "extern-plugininfo.h" diff --git a/homeconnect/homeconnect.h b/homeconnect/homeconnect.h index 195f797b..b670ed7a 100644 --- a/homeconnect/homeconnect.h +++ b/homeconnect/homeconnect.h @@ -1,24 +1,32 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * * - * Copyright (C) 2019 Bernhard Trinnes * - * * - * This file is part of nymea. * - * * - * This library is free software; you can redistribute it and/or * - * modify it under the terms of the GNU Lesser General Public * - * License as published by the Free Software Foundation; either * - * version 2.1 of the License, or (at your option) any later version. * - * * - * This library 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 library; If not, see * - * . * - * * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* 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 HOMECONNECT_H #define HOMECONNECT_H @@ -27,7 +35,6 @@ #include #include "network/networkaccessmanager.h" -#include "devices/device.h" class HomeConnect : public QObject { diff --git a/homeconnect/homeconnect.pro b/homeconnect/homeconnect.pro index 6af5c583..1dde3bbb 100644 --- a/homeconnect/homeconnect.pro +++ b/homeconnect/homeconnect.pro @@ -2,12 +2,12 @@ include(../plugins.pri) QT += network -TARGET = $$qtLibraryTarget(nymea_devicepluginhomeconnect) +TARGET = $$qtLibraryTarget(nymea_integrationpluginhomeconnect) SOURCES += \ - devicepluginhomeconnect.cpp \ + integrationpluginhomeconnect.cpp \ homeconnect.cpp \ HEADERS += \ - devicepluginhomeconnect.h \ + integrationpluginhomeconnect.h \ homeconnect.h \ diff --git a/homeconnect/integrationpluginhomeconnect.cpp b/homeconnect/integrationpluginhomeconnect.cpp new file mode 100644 index 00000000..6259d35d --- /dev/null +++ b/homeconnect/integrationpluginhomeconnect.cpp @@ -0,0 +1,258 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* 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 "integrationpluginhomeconnect.h" +#include "integrations/integrationplugin.h" +#include "network/networkaccessmanager.h" +#include "plugininfo.h" + +#include +#include +#include +#include + +IntegrationPluginHomeConnect::IntegrationPluginHomeConnect() +{ +} + + +IntegrationPluginHomeConnect::~IntegrationPluginHomeConnect() +{ + if (m_pluginTimer5sec) + hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer5sec); + if (m_pluginTimer60sec) + hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer60sec); +} + +void IntegrationPluginHomeConnect::discoverThings(ThingDiscoveryInfo *info) +{ + Q_UNUSED(info) +} + +void IntegrationPluginHomeConnect::startPairing(ThingPairingInfo *info) +{ + if (info->thingClassId() == homeConnectConnectionThingClassId) { + + HomeConnect *homeConnect = new HomeConnect(hardwareManager()->networkManager(), "TODO", "TODO", this); + QUrl url = homeConnect->getLoginUrl(QUrl("https://127.0.0.1:8888"), "TODO Scope"); + qCDebug(dcHomeConnect()) << "HomeConnect url:" << url; + info->setOAuthUrl(url); + info->finish(Thing::ThingErrorNoError); + m_setupHomeConnectConnections.insert(info->thingId(), homeConnect); + } else { + + qCWarning(dcHomeConnect()) << "Unhandled pairing metod!"; + info->finish(Thing::ThingErrorCreationMethodNotSupported); + } +} + +void IntegrationPluginHomeConnect::confirmPairing(ThingPairingInfo *info, const QString &username, const QString &secret) +{ + Q_UNUSED(username); + + if (info->thingClassId() == homeConnectConnectionThingClassId) { + qCDebug(dcHomeConnect()) << "Redirect url is" << secret; + QUrl url(secret); + QUrlQuery query(url); + QByteArray authorizationCode = query.queryItemValue("code").toLocal8Bit(); + QByteArray state = query.queryItemValue("state").toLocal8Bit(); + //TODO evaluate state if it equals the given state + + HomeConnect *HomeConnect = m_setupHomeConnectConnections.value(info->thingId()); + + if (!HomeConnect) { + qWarning(dcHomeConnect()) << "No HomeConnect connection found for device:" << info->thingName(); + m_setupHomeConnectConnections.remove(info->thingId()); + HomeConnect->deleteLater(); + info->finish(Thing::ThingErrorHardwareFailure); + return; + } + HomeConnect->getAccessTokenFromAuthorizationCode(authorizationCode); + connect(HomeConnect, &HomeConnect::authenticationStatusChanged, this, [info, this](bool authenticated){ + HomeConnect *homeConnect = static_cast(sender()); + DevicePairingInfo info(devicePairingInfo); + if(!authenticated) { + qWarning(dcHomeConnect()) << "Authentication process failed" << devicePairingInfo.deviceName(); + m_setupHomeConnectConnections.remove(info.deviceId()); + homeConnect->deleteLater(); + info.setStatus(Device::DeviceErrorSetupFailed); + emit pairingFinished(info); + return; + } + QByteArray accessToken = homeConnect->accessToken(); + QByteArray refreshToken = homeConnect->refreshToken(); + qCDebug(dcHomeConnect()) << "Token:" << accessToken << refreshToken; + + pluginStorage()->beginGroup(info.deviceId().toString()); + pluginStorage()->setValue("refresh_token", refreshToken); + pluginStorage()->endGroup(); + + info.setStatus(Device::DeviceErrorNoError); + emit pairingFinished(info); + }); + devicePairingInfo.setStatus(Device::DeviceErrorAsync); + + } else { + qCWarning(dcHomeConnect()) << "Invalid thingClassId -> no pairing possible with this device"; + info->finish(Thing::ThingErrorThingClassNotFound); + } +} + + +void IntegrationPluginHomeConnect::setupThing(ThingSetupInfo *info) +{ + if (!m_pluginTimer5sec) { + m_pluginTimer5sec = hardwareManager()->pluginTimerManager()->registerTimer(5); + connect(m_pluginTimer5sec, &PluginTimer::timeout, this, [this]() { + + foreach (Thing *connectionThing, myThings().filterByThingClassId(homeConnectConnectionThingClassId)) { + HomeConnect *HomeConnect = m_homeConnectConnections.value(connectionThing); + if (!HomeConnect) { + qWarning(dcHomeConnect()) << "No HomeConnect account found for" << connectionThing->name(); + continue; + } + } + }); + } + + if (!m_pluginTimer60sec) { + m_pluginTimer60sec = hardwareManager()->pluginTimerManager()->registerTimer(60); + connect(m_pluginTimer60sec, &PluginTimer::timeout, this, [this]() { + foreach (Thing *thing, myThings().filterByThingClassId(homeConnectConnectionThingClassId)) { + HomeConnect *homeConnect = m_homeConnectConnections.value(thing); + if (!homeConnect) { + qWarning(dcHomeConnect()) << "No HomeConnect account found for" << thing->name(); + continue; + } + + } + }); + } + + if (info->thing()->thingClassId() == homeConnectConnectionThingClassId) { + HomeConnect *homeConnect; + if (m_setupHomeConnectConnections.keys().contains(thing->id())) { + //Fresh device setup, has already a fresh access token + qCDebug(dcHomeConnect()) << "HomeConnect OAuth setup complete"; + HomeConnect = m_setupHomeConnectConnections.take(device->id()); + connect(homeConnect, &HomeConnect::connectionChanged, this, &IntegrationPluginHomeConnect::onConnectionChanged); + connect(homeConnect, &HomeConnect::actionExecuted, this, &IntegrationPluginHomeConnect::onActionExecuted); + connect(homeConnect, &HomeConnect::authenticationStatusChanged, this, &IntegrationPluginHomeConnect::onAuthenticationStatusChanged); + m_homeConnectConnections.insert(thing, homeConnect); + return; + } else { + //device loaded from the device database, needs a new access token; + pluginStorage()->beginGroup(thing->id().toString()); + QByteArray refreshToken = pluginStorage()->value("refresh_token").toByteArray(); + pluginStorage()->endGroup(); + + homeConnect = new HomeConnect(hardwareManager()->networkManager(), "TODO", "TODO", this); + connect(homeConnect, &HomeConnect::connectionChanged, this, &IntegrationPluginHomeConnect::onConnectionChanged); + connect(homeConnect, &HomeConnect::actionExecuted, this, &IntegrationPluginHomeConnect::onActionExecuted); + connect(homeConnect, &HomeConnect::authenticationStatusChanged, this, &IntegrationPluginHomeConnect::onAuthenticationStatusChanged); + homeConnect->getAccessTokenFromRefreshToken(refreshToken); + m_homeConnectConnections.insert(thing, homeConnect); + info->finish(Thing::ThingErrorNoError); + } + } else { + info->finish(Thing::ThingErrorThingClassNotFound); + } +} + +void IntegrationPluginHomeConnect::postSetupThing(Thing *thing) +{ + if (thing->thingClassId() == homeConnectConnectionThingClassId) { + HomeConnect *homeConnect = m_homeConnectConnections.value(thing); + Q_UNUSED(homeConnect) + } +} + +void IntegrationPluginHomeConnect::executeAction(ThingActionInfo *info) +{ + +} + +void IntegrationPluginHomeConnect::thingRemoved(Thing *thing) +{ + qCDebug(dcHomeConnect) << "Delete " << thing->name(); + if (myThings().empty()) { + hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer5sec); + hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer60sec); + m_pluginTimer5sec = nullptr; + m_pluginTimer60sec = nullptr; + } +} +Device *device + +void IntegrationPluginHomeConnect::onConnectionChanged(bool connected) +{ + HomeConnect *homeConnect = static_cast(sender()); + Thing *thing = m_homeConnectConnections.key(homeConnect); + if (!thing) + return; + thing->setStateValue(homeConnectConnectionConnectedStateTypeId, connected); +} + +void IntegrationPluginHomeConnect::onAuthenticationStatusChanged(bool authenticated) +{ + HomeConnect *HomeConnectConnection = static_cast(sender()); + Thing *thing = m_homeConnectConnections.key(HomeConnectConnection); + if (!thing) + return; + + if (!thing->setupComplete()) { + if (authenticated) { + //emit deviceSetupFinished(device, Device::DeviceSetupStatusSuccess); + } else { + //emit deviceSetupFinished(device, Device::DeviceSetupStatusFailure); + } + } else { + thing->setStateValue(homeConnectConnectionLoggedInStateTypeId, authenticated); + if (!authenticated) { + //refresh access token needs to be refreshed + pluginStorage()->beginGroup(thing->id().toString()); + QByteArray refreshToken = pluginStorage()->value("refresh_token").toByteArray(); + pluginStorage()->endGroup(); + HomeConnectConnection->getAccessTokenFromRefreshToken(refreshToken); + } + } +} + +void IntegrationPluginHomeConnect::onActionExecuted(QUuid HomeConnectActionId, bool success) +{ + if (m_pendingActions.contains(HomeConnectActionId)) { + ActionId nymeaActionId = m_pendingActions.value(HomeConnectActionId); + if (success) { + //emit actionExecutionFinished(nymeaActionId, Device::DeviceErrorNoError); + } else { + //emit actionExecutionFinished(nymeaActionId, Device::DeviceErrorHardwareFailure); + } + } +} diff --git a/homeconnect/integrationpluginhomeconnect.h b/homeconnect/integrationpluginhomeconnect.h new file mode 100644 index 00000000..68712607 --- /dev/null +++ b/homeconnect/integrationpluginhomeconnect.h @@ -0,0 +1,76 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* 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 INTEGRATIONPLUGINHOMECONNECT_H +#define INTEGRATIONPLUGINHOMECONNECt_H + +#include "integrations/integrationplugin.h" +#include "plugintimer.h" +#include "homeconnect.h" + +#include +#include + +class IntegrationPluginHomeConnect : public IntegrationPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "io.nymea.IntegrationPlugin" FILE "IntegrationPluginHomeConnect.json") + Q_INTERFACES(IntegrationPlugin) + +public: + explicit IntegrationPluginHomeConnect(); + ~IntegrationPluginHomeConnect() override; + + void discoverThings(ThingDiscoveryInfo *info) override; + + void startPairing(ThingPairingInfo *info) override; + void confirmPairing(ThingPairingInfo *info, const QString &username, const QString &secret) override; + + void setupThing(ThingSetupInfo *info) override; + void postSetupThing(Thing *thing) override; + void executeAction(ThingActionInfo *info) override; + void thingRemoved(Thing *thing) override; + +private: + PluginTimer *m_pluginTimer5sec = nullptr; + PluginTimer *m_pluginTimer60sec = nullptr; + + QHash m_setupHomeConnectConnections; + QHash m_homeConnectConnections; + + QHash m_pendingActions; + +private slots: + void onConnectionChanged(bool connected); + void onAuthenticationStatusChanged(bool authenticated); + void onActionExecuted(QUuid actionId, bool success); +}; + +#endif // INTEGRATIONPLUGINHOMECONNECT_H diff --git a/homeconnect/devicepluginhomeconnect.json b/homeconnect/integrationpluginhomeconnect.json similarity index 85% rename from homeconnect/devicepluginhomeconnect.json rename to homeconnect/integrationpluginhomeconnect.json index abefdd53..41186b7b 100644 --- a/homeconnect/devicepluginhomeconnect.json +++ b/homeconnect/integrationpluginhomeconnect.json @@ -7,12 +7,12 @@ "id": "43cfb7a4-402f-4315-86b5-ce095697fd13", "name": "homeConnect", "displayName": "Home Connect", - "deviceClasses": [ + "thingClasses": [ { "id": "babc1a39-730a-4516-95bf-ff51a8ce887a", "name": "homeConnectConnection", "displayName": "Home Connect connection", - "interfaces": ["gateway"], + "interfaces": ["account"], "createMethods": ["user"], "setupMethod": "oauth", "paramTypes": [ @@ -21,9 +21,10 @@ { "id": "1180576a-1de2-4815-b442-877b572ce586", "name": "connected", - "displayName": "connected", - "displayNameEvent": "connected changed", + "displayName": "Connected", + "displayNameEvent": "Connected changed", "defaultValue": true, + "cached": false, "type": "bool" }, { @@ -39,6 +40,7 @@ "name": "userDisplayName", "displayName": "User name", "displayNameEvent": "User name changed", + "defaultValue": "", "type": "QString" } ]