diff --git a/philipshue/bridgeconnection.cpp b/philipshue/bridgeconnection.cpp new file mode 100644 index 00000000..8e528b32 --- /dev/null +++ b/philipshue/bridgeconnection.cpp @@ -0,0 +1,513 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2014 Michael Zanetti * + * Copyright (C) 2015 - 2019 Simon Stürz * + * Copyright (C) 2019 Bernhard Trinns * + * * + * 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 * + * . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + + +#include "bridgeconnection.h" +#include "devices/device.h" +#include "types/param.h" +#include "plugininfo.h" +#include "network/upnp/upnpdiscovery.h" +#include "network/upnp/upnpdiscoveryreply.h" + +#include +#include +#include +#include +#include + + +BridgeConnection::BridgeConnection(HueBridge *bridge, HardwareManager *hardwareManager, QObject *parent) : + QObject(parent), + m_hardwareManager(hardwareManager), + m_bridge(bridge) +{ + m_bridge = new HueBridge(this); + +} + +BridgeConnection::~BridgeConnection() +{ + m_hardwareManager->pluginTimerManager()->unregisterTimer(m_pluginTimer1Sec); + m_hardwareManager->pluginTimerManager()->unregisterTimer(m_pluginTimer5Sec); + m_hardwareManager->pluginTimerManager()->unregisterTimer(m_pluginTimer15Sec); +} + +void BridgeConnection::init() +{ + m_pluginTimer1Sec = m_hardwareManager->pluginTimerManager()->registerTimer(1); + connect(m_pluginTimer1Sec, &PluginTimer::timeout, this, [this]() { + // refresh sensors every second + if (m_remotes.isEmpty()) { + return; + } + refreshSensors(); + }); + m_pluginTimer5Sec = m_hardwareManager->pluginTimerManager()->registerTimer(5); + connect(m_pluginTimer5Sec, &PluginTimer::timeout, this, [this]() { + // refresh lights every 5 seconds + refreshLights(); + + }); + m_pluginTimer15Sec = m_hardwareManager->pluginTimerManager()->registerTimer(15); + connect(m_pluginTimer15Sec, &PluginTimer::timeout, this, [this]() { + // refresh bridges every 15 seconds + refreshBridge(); + // search for new devices connected to the bridge + + }); +} + +void BridgeConnection::refreshLight(const QString &uuid) +{ + HueLight *light = m_lights.value(uuid); + + QNetworkRequest request(QUrl("http://" + light->hostAddress().toString() + "/api/" + light->apiKey() + "/lights/" + QString::number(light->id()))); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + QNetworkReply *reply = m_hardwareManager->networkManager()->get(request); + connect(reply, &QNetworkReply::finished, this, &BridgeConnection::networkManagerReplyReady); + + //m_lightRefreshRequests.insert(reply); +} + +void BridgeConnection::refreshBridge() +{ + // qCDebug(dcPhilipsHue()) << "refreshing bridge"; + + QNetworkRequest request(QUrl("http://" + m_bridge->hostAddress().toString() + "/api/" + m_bridge->apiKey() + "/config")); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + QNetworkReply *reply = m_hardwareManager->networkManager()->get(request); + connect(reply, &QNetworkReply::finished, this, &BridgeConnection::networkManagerReplyReady); + //TODO abort old request and start new one + //m_bridgeRefreshRequests.insert(reply); +} + +void BridgeConnection::refreshLights() +{ + // qCDebug(dcPhilipsHue()) << "refreshing lights"; + QNetworkRequest request(QUrl("http://" + m_bridge->hostAddress().toString() + "/api/" + m_bridge->apiKey() + "/lights")); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + QNetworkReply *reply = m_hardwareManager->networkManager()->get(request); + connect(reply, &QNetworkReply::finished, this, &BridgeConnection::networkManagerReplyReady); + //m_lightsRefreshRequests.insert(reply, device); +} + +void BridgeConnection::refreshSensors() +{ + // qCDebug(dcPhilipsHue()) << "refreshing sensors"; + + QNetworkRequest request(QUrl("http://" + m_bridge->hostAddress().toString() + "/api/" + m_bridge->apiKey() + "/sensors")); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + QNetworkReply *reply = m_hardwareManager->networkManager()->get(request); + connect(reply, &QNetworkReply::finished, this, &BridgeConnection::networkManagerReplyReady); + //m_sensorsRefreshRequests.insert(reply, device); +} + +void BridgeConnection::discoverBridgeDevices() +{ + qCDebug(dcPhilipsHue) << "Discover bridge devices" << m_bridge->hostAddress(); + + QPair lightsRequest = m_bridge->createDiscoverLightsRequest(); + QNetworkReply *lightsReply = m_hardwareManager->networkManager()->get(lightsRequest.first); + connect(lightsReply, &QNetworkReply::finished, this, &BridgeConnection::networkManagerReplyReady); + //m_bridgeLightsDiscoveryRequests.insert(lightsReply, device); + + QPair sensorsRequest = m_bridge->createSearchSensorsRequest(); + QNetworkReply *reply = m_hardwareManager->networkManager()->get(sensorsRequest.first); + connect(reply, &QNetworkReply::finished, this, &BridgeConnection::networkManagerReplyReady); + //m_bridgeSensorsDiscoveryRequests.insert(reply, device); +} + +void BridgeConnection::searchNewDevices(const QString &serialNumber) +{ + qCDebug(dcPhilipsHue) << "Discover bridge devices" << m_bridge->hostAddress(); + + QPair request = m_bridge->createSearchLightsRequest(serialNumber); + QNetworkReply *reply = m_hardwareManager->networkManager()->post(request.first, request.second); + connect(reply, &QNetworkReply::finished, this, &BridgeConnection::networkManagerReplyReady); + //m_bridgeSearchDevicesRequests.insert(reply, device); +} + +void BridgeConnection::setPower(const QString &uuid, bool power) +{ + HueLight *light = m_lights.value(uuid); + QPair request = light->createSetPowerRequest(power); + QNetworkReply *reply = m_hardwareManager->networkManager()->put(request.first, request.second); + connect(reply, &QNetworkReply::finished, this, &BridgeConnection::networkManagerReplyReady); +} + +void BridgeConnection::setBrightness(const QString &uuid, quint8 percent) +{ + HueLight *light = m_lights.value(uuid); + QPair request = light->createSetBrightnessRequest(percent); + QNetworkReply *reply = m_hardwareManager->networkManager()->put(request.first, request.second); + connect(reply, &QNetworkReply::finished, this, &BridgeConnection::networkManagerReplyReady); +} + +void BridgeConnection::setColor(const QString &uuid, QColor color) +{ + HueLight *light = m_lights.value(uuid); + QPair request = light->createSetColorRequest(color); + QNetworkReply *reply = m_hardwareManager->networkManager()->put(request.first, request.second); + connect(reply, &QNetworkReply::finished, this, &BridgeConnection::networkManagerReplyReady); +} + +void BridgeConnection::setEffect(const QString &uuid, const QString &effect) +{ + HueLight *light = m_lights.value(uuid); + QPair request = light->createSetEffectRequest(effect); + QNetworkReply *reply = m_hardwareManager->networkManager()->put(request.first, request.second); + connect(reply, &QNetworkReply::finished, this, &BridgeConnection::networkManagerReplyReady); +} + +void BridgeConnection::setTemperature(const QString &uuid, quint16 temperature) +{ + HueLight *light = m_lights.value(uuid); + QPair request = light->createSetTemperatureRequest(temperature); + QNetworkReply *reply = m_hardwareManager->networkManager()->put(request.first, request.second); + connect(reply, &QNetworkReply::finished, this, &BridgeConnection::networkManagerReplyReady); +} + +void BridgeConnection::setFlash(const QString &uuid, const QString &mode) +{ + HueLight *light = m_lights.value(uuid); + QPair request = light->createFlashRequest(mode); + QNetworkReply *reply = m_hardwareManager->networkManager()->put(request.first, request.second); + connect(reply, &QNetworkReply::finished, this, &BridgeConnection::networkManagerReplyReady); +} + + +void BridgeConnection::processLightRefreshResponse(QString uuid, const QByteArray &data) +{ + QJsonParseError error; + QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error); + + // check JSON error + if (error.error != QJsonParseError::NoError) { + qCWarning(dcPhilipsHue) << "Hue Bridge json error in response" << error.errorString(); + return; + } + + // check response error + if (data.contains("error")) { + if (!jsonDoc.toVariant().toList().isEmpty()) { + qCWarning(dcPhilipsHue) << "Failed to refresh Hue Light:" << jsonDoc.toVariant().toList().first().toMap().value("error").toMap().value("description").toString(); + } else { + qCWarning(dcPhilipsHue) << "Failed to refresh Hue Light: Invalid error message format"; + } + return; + } + + HueLight *light = m_lights.value(uuid); + light->updateStates(jsonDoc.toVariant().toMap().value("state").toMap()); +} + +void BridgeConnection::processBridgeRefreshResponse(const QByteArray &data) +{ + QJsonParseError error; + QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error); + + // check JSON error + if (error.error != QJsonParseError::NoError) { + qCWarning(dcPhilipsHue) << "Hue Bridge json error in response" << error.errorString(); + return; + } + + if (!jsonDoc.toVariant().toList().isEmpty()) { + qCWarning(dcPhilipsHue) << "Failed to refresh Hue Bridge:" << jsonDoc.toVariant().toList().first().toMap().value("error").toMap().value("description").toString(); + bridgeReachableChanged(false); + return; + } + + //qCDebug(dcPhilipsHue()) << "Bridge refresh response" << qUtf8Printable(jsonDoc.toJson(QJsonDocument::Indented)); + + QVariantMap configMap = jsonDoc.toVariant().toMap(); + + // mark bridge as reachable + bridgeReachableChanged(true); + //device->setStateValue(bridgeApiVersionStateTypeId, configMap.value("apiversion").toString()); + //device->setStateValue(bridgeSoftwareVersionStateTypeId, configMap.value("swversion").toString()); + + int updateStatus = configMap.value("swupdate").toMap().value("updatestate").toInt(); + switch (updateStatus) { + case 0: + //device->setStateValue(bridgeUpdateStatusStateTypeId, "Up to date"); + break; + case 1: + //device->setStateValue(bridgeUpdateStatusStateTypeId, "Downloading updates"); + break; + case 2: + //device->setStateValue(bridgeUpdateStatusStateTypeId, "Updates ready to install"); + break; + case 3: + //device->setStateValue(bridgeUpdateStatusStateTypeId, "Installing updates"); + break; + default: + break; + } +} + +void BridgeConnection::processLightsRefreshResponse(const QByteArray &data) +{ + QJsonParseError error; + QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error); + + // check JSON error + if (error.error != QJsonParseError::NoError) { + qCWarning(dcPhilipsHue) << "Hue Bridge json error in response" << error.errorString(); + return; + } + + // check response error + if (data.contains("error")) { + if (!jsonDoc.toVariant().toList().isEmpty()) { + qCWarning(dcPhilipsHue) << "Failed to refresh Hue Lights:" << jsonDoc.toVariant().toList().first().toMap().value("error").toMap().value("description").toString(); + } else { + qCWarning(dcPhilipsHue) << "Failed to refresh Hue Lights: Invalid error message format"; + } + return; + } + + // Update light states + QVariantMap lightsMap = jsonDoc.toVariant().toMap(); + foreach (const QString &lightId, lightsMap.keys()) { + QVariantMap lightMap = lightsMap.value(lightId).toMap(); + // get the light of this bridge + foreach (HueLight *light, m_lights.values()) { + if (light->id() == lightId.toInt()) { + light->updateStates(lightMap.value("state").toMap()); + } + } + } +} + +void BridgeConnection::processSensorsRefreshResponse(const QByteArray &data) +{ + QJsonParseError error; + QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error); + + // check JSON error + if (error.error != QJsonParseError::NoError) { + qCWarning(dcPhilipsHue) << "Hue Bridge json error in response" << error.errorString(); + return; + } + + // check response error + if (!jsonDoc.toVariant().toList().isEmpty()) { + qCWarning(dcPhilipsHue) << "Failed to refresh Hue Sensors:" << jsonDoc.toVariant().toList().first().toMap().value("error").toMap().value("description").toString(); + return; + } + + // Update sensor states + QVariantMap sensorsMap = jsonDoc.toVariant().toMap(); + foreach (const QString &sensorId, sensorsMap.keys()) { + QVariantMap sensorMap = sensorsMap.value(sensorId).toMap(); + + // Remotes + foreach (HueRemote *remote, m_remotes.values()) { + if (remote->id() == sensorId.toInt()) { + remote->updateStates(sensorMap.value("state").toMap(), sensorMap.value("config").toMap()); + } + } + + // Outdoor sensors + foreach (HueMotionSensor *motionSensor, m_motionSensors.values()) { + if (motionSensor->hasSensor(sensorId.toInt())) { + motionSensor->updateStates(sensorMap); + } + } + } +} + +void BridgeConnection::processSetNameResponse(const QByteArray &data) +{ + QJsonParseError error; + QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error); + + // check JSON error + if (error.error != QJsonParseError::NoError) { + qCWarning(dcPhilipsHue) << "Hue Bridge json error in response" << error.errorString(); + //emit deviceSetupFinished(device, Device::DeviceSetupStatusFailure); + return; + } + + // check response error + if (data.contains("error")) { + if (!jsonDoc.toVariant().toList().isEmpty()) { + qCWarning(dcPhilipsHue) << "Failed to set name of Hue:" << jsonDoc.toVariant().toList().first().toMap().value("error").toMap().value("description").toString(); + } else { + qCWarning(dcPhilipsHue) << "Failed to set name of Hue: Invalid error message format"; + } + //emit setNameFinished(true); + return; + } +} + + +void BridgeConnection::networkManagerReplyReady() +{ + QNetworkReply *reply = static_cast(sender()); + reply->deleteLater(); + + int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + + // qCDebug(dcPhilipsHue()) << "Hue reply:" << status << reply->error() << reply->errorString(); + + // create user finished + if (m_pairingRequests.contains(reply)) { + PairingInfo *pairingInfo = m_pairingRequests.take(reply); + + // check HTTP status code + if (status != 200 || reply->error() != QNetworkReply::NoError) { + qCWarning(dcPhilipsHue) << "Request error:" << status << reply->errorString(); + pairingInfo->deleteLater(); + return; + } + processPairingResponse(pairingInfo, reply->readAll()); + + } else if (m_informationRequests.contains(reply)) { + PairingInfo *pairingInfo = m_informationRequests.take(reply); + + // check HTTP status code + if (status != 200 || reply->error() != QNetworkReply::NoError) { + qCWarning(dcPhilipsHue) << "Request error:" << status << reply->errorString(); + pairingInfo->deleteLater(); + return; + } + processInformationResponse(pairingInfo, reply->readAll()); + + } else if (reply == m_bridgeLightsDiscoveryRequests) { + + // check HTTP status code + if (status != 200 || reply->error() != QNetworkReply::NoError) { + qCWarning(dcPhilipsHue) << "Bridge light discovery error:" << status << reply->errorString(); + bridgeReachableChanged(false); + return; + } + processBridgeLightDiscoveryResponse(reply->readAll()); + + } else if (reply == m_bridgeSensorsDiscoveryRequests) { + + // check HTTP status code + if (status != 200 || reply->error() != QNetworkReply::NoError) { + qCWarning(dcPhilipsHue) << "Bridge sensor discovery error:" << status << reply->errorString(); + bridgeReachableChanged(false); + return; + } + processBridgeSensorDiscoveryResponse(reply->readAll()); + + } else if (reply == m_bridgeSearchDevicesRequests) { + + // check HTTP status code + if (status != 200 || reply->error() != QNetworkReply::NoError) { + qCWarning(dcPhilipsHue) << "Bridge search new devices error:" << status << reply->errorString(); + bridgeReachableChanged(false); + return; + } + discoverBridgeDevices(); + + } else if (m_bridgeRefreshRequests == reply) {; + + // check HTTP status code + if (status != 200 || reply->error() != QNetworkReply::NoError) { + qCWarning(dcPhilipsHue) << "Refresh Hue Bridge request error:" << status << reply->errorString(); + bridgeReachableChanged(false); + return; + } + processBridgeRefreshResponse(reply->readAll()); + + } else if (reply == m_lightRefreshRequests) { + + // check HTTP status code + if (status != 200 || reply->error() != QNetworkReply::NoError) { + qCWarning(dcPhilipsHue) << "Refresh Hue Light request error:" << status << reply->errorString(); + bridgeReachableChanged(false); + return; + } + processLightRefreshResponse(reply->readAll()); + + } else if (reply == m_lightsRefreshRequests) { + + // check HTTP status code + if (status != 200 || reply->error() != QNetworkReply::NoError) { + qCWarning(dcPhilipsHue) << "Refresh Hue lights request error:" << status << reply->errorString(); + bridgeReachableChanged(false); + return; + } + processLightsRefreshResponse(reply->readAll()); + + } else if (reply == m_sensorsRefreshRequests) { + + // check HTTP status code + if (status != 200 || reply->error() != QNetworkReply::NoError) { + qCWarning(dcPhilipsHue) << "Refresh Hue sensors request error:" << status << reply->errorString(); + bridgeReachableChanged(false); + return; + } + processSensorsRefreshResponse(reply->readAll()); + + } else if (m_asyncActions.contains(reply)) { + QPair actionInfo = m_asyncActions.take(reply); + + // check HTTP status code + if (status != 200 || reply->error() != QNetworkReply::NoError) { + qCWarning(dcPhilipsHue) << "Execute Hue Light action request error:" << status << reply->errorString(); + bridgeReachableChanged(false); + //emit actionExecutionFinished(actionInfo.second, Device::DeviceErrorHardwareNotAvailable); + return; + } + processActionResponse(actionInfo.second, reply->readAll()); + + } else if (reply == m_setNameRequests) { + + // check HTTP status code + if (status != 200 || reply->error() != QNetworkReply::NoError) { + qCWarning(dcPhilipsHue) << "Set name of Hue Light request error:" << status << reply->errorString(); + bridgeReachableChanged(false); + return; + } + processSetNameResponse(reply->readAll()); + } else { + qCWarning(dcPhilipsHue()) << "Unhandled bridge reply" << reply->error() << reply->readAll(); + } +} + + +int BridgeConnection::brightnessToPercentage(int brightness) +{ + return qRound((100.0 * brightness) / 255.0); +} + +int BridgeConnection::percentageToBrightness(int percentage) +{ + return qRound((255.0 * percentage) / 100.0); +} + +void BridgeConnection::abortRequests(QHash requestList, Device *device) +{ + foreach (QNetworkReply* reply, requestList.keys()) { + if (requestList.value(reply) == device) { + reply->abort(); + } + } +} diff --git a/philipshue/bridgeconnection.h b/philipshue/bridgeconnection.h new file mode 100644 index 00000000..46f2767e --- /dev/null +++ b/philipshue/bridgeconnection.h @@ -0,0 +1,135 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2014 Michael Zanetti * + * Copyright (C) 2015 - 2019 Simon Stürz * + * Copyright (C) 2019 Bernhard Trinns * + * * + * 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 * + * . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + + +#ifndef BRIDGECONNECTION_H +#define BRIDGECONNECTION_H + +#include "devices/deviceplugin.h" +#include "huebridge.h" +#include "huelight.h" +#include "hueremote.h" +#include "pairinginfo.h" +#include "huemotionsensor.h" + +#include "plugintimer.h" +#include "network/networkaccessmanager.h" +#include "network/upnp/upnpdiscovery.h" +#include "devices/devicemanager.h" + +#include + +class BridgeConnection : public QObject +{ + Q_OBJECT +public: + explicit BridgeConnection(HueBridge *bridge, HardwareManager *hardwaremanager, QObject *parent = nullptr); + ~BridgeConnection(); + void init(); + + //bridge actions + void discoverBridgeDevices(); + void searchNewDevices(const QString &serialNumber); + + //light actions + void setPower(const QString &uuid, bool power); + void setBrightness(const QString &uuid, quint8 percent); + void setColor(const QString &uuid, QColor percent); + void setEffect(const QString &uuid, const QString &effect); + void setFlash(const QString &uuid, const QString &flashMode); + void setTemperature(const QString &uuid, quint16 temperature); + + void setLightName(); + void setRemoteName(); + +private slots: + void lightStateChanged(); + void remoteStateChanged(); + void onRemoteButtonEvent(int buttonCode); + + // Motion sensor + void onMotionSensorReachableChanged(bool reachable); + void onMotionSensorBatteryLevelChanged(int batteryLevel); + void onMotionSensorTemperatureChanged(double temperature); + void onMotionSensorPresenceChanged(bool presence); + void onMotionSensorLightIntensityChanged(double lightIntensity); + +private slots: + void networkManagerReplyReady(); + void onDeviceNameChanged(); + +private: + HardwareManager *m_hardwareManager = nullptr; + + PluginTimer *m_pluginTimer1Sec = nullptr; + PluginTimer *m_pluginTimer5Sec = nullptr; + PluginTimer *m_pluginTimer15Sec = nullptr; + + QHash m_pairingRequests; + QHash m_informationRequests; + + QNetworkReply *m_lightRefreshRequests; + QNetworkReply *m_setNameRequests; + QNetworkReply *m_bridgeRefreshRequests; + QNetworkReply *m_lightsRefreshRequests; + QNetworkReply *m_sensorsRefreshRequests; + QNetworkReply *m_bridgeLightsDiscoveryRequests; + QNetworkReply *m_bridgeSensorsDiscoveryRequests; + QNetworkReply *m_bridgeSearchDevicesRequests; + + QHash > m_asyncActions; + + HueBridge *m_bridge; + QHash m_lights; + QHash m_remotes; + QHash m_motionSensors; + + void refreshLight(const QString &uuid); // + void refreshBridge(); + void refreshLights(); + void refreshSensors(); + + void processBridgeLightDiscoveryResponse(const QByteArray &data); + void processBridgeSensorDiscoveryResponse(const QByteArray &data); + void processLightRefreshResponse(const QByteArray &data); + void processBridgeRefreshResponse(const QByteArray &data); + void processLightsRefreshResponse(const QByteArray &data); + void processSensorsRefreshResponse(const QByteArray &data); + void processSetNameResponse(const QByteArray &data); + void processPairingResponse(PairingInfo *pairingInfo, const QByteArray &data); + void processInformationResponse(PairingInfo *pairingInfo, const QByteArray &data); + void processActionResponse(const ActionId actionId, const QByteArray &data); + + void bridgeReachableChanged(const bool &reachable); + + bool lightAlreadyAdded(const QString &uuid); + bool sensorAlreadyAdded(const QString &uuid); + + int brightnessToPercentage(int brightness); + int percentageToBrightness(int percentage); + + void abortRequests(QHash requestList); +}; + +#endif // BRIDGECONNECTION_H diff --git a/philipshue/devicepluginphilipshue.cpp b/philipshue/devicepluginphilipshue.cpp index b57a17f2..7b462e40 100644 --- a/philipshue/devicepluginphilipshue.cpp +++ b/philipshue/devicepluginphilipshue.cpp @@ -1,8 +1,9 @@ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright (C) 2014 Michael Zanetti * - * Copyright (C) 2015 - 2019 Simon Stürz * - * Copyright (C) 2018 Michael Zanetti * + * Copyright (C) 2015 - 2019 Simon Stürz * + * Copyright (C) 2018 Michael Zanetti * + * Copyright (C) 2019 Bernhard Trinnes * * * * This file is part of nymea. * * * @@ -41,42 +42,6 @@ DevicePluginPhilipsHue::DevicePluginPhilipsHue() } -DevicePluginPhilipsHue::~DevicePluginPhilipsHue() -{ - hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer1Sec); - hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer5Sec); - hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer15Sec); -} - -void DevicePluginPhilipsHue::init() -{ - m_pluginTimer1Sec = hardwareManager()->pluginTimerManager()->registerTimer(1); - connect(m_pluginTimer1Sec, &PluginTimer::timeout, this, [this]() { - // refresh sensors every second - if (m_remotes.isEmpty()) { - return; - } - foreach (HueBridge *bridge, m_bridges.keys()) { - refreshSensors(bridge); - } - }); - m_pluginTimer5Sec = hardwareManager()->pluginTimerManager()->registerTimer(5); - connect(m_pluginTimer5Sec, &PluginTimer::timeout, this, [this]() { - // refresh lights every 5 seconds - foreach (HueBridge *bridge, m_bridges.keys()) { - refreshLights(bridge); - } - }); - m_pluginTimer15Sec = hardwareManager()->pluginTimerManager()->registerTimer(15); - connect(m_pluginTimer15Sec, &PluginTimer::timeout, this, [this]() { - // refresh bridges every 15 seconds - foreach (Device *device, m_bridges.values()) { - refreshBridge(device); - } - }); - -} - Device::DeviceError DevicePluginPhilipsHue::discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms) { Q_UNUSED(params) @@ -117,7 +82,6 @@ Device::DeviceError DevicePluginPhilipsHue::discoverDevices(const DeviceClassId discovery->results.append(descriptor); } } - finishDiscovery(discovery); }); @@ -160,10 +124,8 @@ Device::DeviceError DevicePluginPhilipsHue::discoverDevices(const DeviceClassId qCDebug(dcPhilipsHue()) << "N-UPnP: Found Hue bridge:" << bridgeId; discovery->results.append(descriptor); } - finishDiscovery(discovery); }); - return Device::DeviceErrorAsync; } @@ -183,7 +145,7 @@ Device::DeviceSetupStatus DevicePluginPhilipsHue::setupDevice(Device *device) device->setParamValue(bridgeDeviceApiKeyParamTypeId, b->apiKey()); device->setParamValue(bridgeDeviceZigbeeChannelParamTypeId, b->zigbeeChannel()); device->setParamValue(bridgeDeviceMacParamTypeId, b->macAddress()); - m_bridges.insert(b, device); + m_bridgeConnections.insert(device->id(), ); device->setStateValue(bridgeConnectedStateTypeId, true); discoverBridgeDevices(b); return Device::DeviceSetupStatusSuccess; @@ -193,15 +155,18 @@ Device::DeviceSetupStatus DevicePluginPhilipsHue::setupDevice(Device *device) // Loaded bridge qCDebug(dcPhilipsHue) << "Setup Hue Bridge" << device->params(); + HueBridge *bridge = new HueBridge(this); bridge->setId(device->paramValue(bridgeDeviceIdParamTypeId).toString()); bridge->setApiKey(device->paramValue(bridgeDeviceApiKeyParamTypeId).toString()); bridge->setHostAddress(QHostAddress(device->paramValue(bridgeDeviceHostParamTypeId).toString())); bridge->setMacAddress(device->paramValue(bridgeDeviceMacParamTypeId).toString()); bridge->setZigbeeChannel(device->paramValue(bridgeDeviceZigbeeChannelParamTypeId).toInt()); - m_bridges.insert(bridge, device); - discoverBridgeDevices(bridge); + BridgeConnection *bridgeConnection = new BridgeConnection(bridge, hardwareManager(), this); + m_bridgeConnections.insert(device->id(), bridgeConnection); + + bridgeConnection->discoverBridgeDevices(); return Device::DeviceSetupStatusSuccess; } @@ -209,7 +174,7 @@ Device::DeviceSetupStatus DevicePluginPhilipsHue::setupDevice(Device *device) if (device->deviceClassId() == colorLightDeviceClassId) { qCDebug(dcPhilipsHue) << "Setup Hue color light" << device->params(); - HueBridge *bridge = m_bridges.key(myDevices().findById(device->parentId())); + BridgeConnection *bridge = m_bridgeConnections; HueLight *hueLight = new HueLight(this); hueLight->setHostAddress(bridge->hostAddress()); hueLight->setApiKey(bridge->apiKey()); @@ -297,7 +262,7 @@ Device::DeviceSetupStatus DevicePluginPhilipsHue::setupDevice(Device *device) if (device->deviceClassId() == remoteDeviceClassId) { qCDebug(dcPhilipsHue) << "Setup Hue remote" << device->params() << device->deviceClassId(); - HueBridge *bridge = m_bridges.key(myDevices().findById(device->parentId())); + BridgeConnection *bridge = m_bridgeConnections.value(device->parentId()); HueRemote *hueRemote = new HueRemote(this); hueRemote->setHostAddress(bridge->hostAddress()); hueRemote->setApiKey(bridge->apiKey()); @@ -486,148 +451,6 @@ Device::DeviceSetupStatus DevicePluginPhilipsHue::confirmPairing(const PairingTr return Device::DeviceSetupStatusAsync; } -void DevicePluginPhilipsHue::networkManagerReplyReady() -{ - QNetworkReply *reply = static_cast(sender()); - reply->deleteLater(); - - int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - - // qCDebug(dcPhilipsHue()) << "Hue reply:" << status << reply->error() << reply->errorString(); - - // create user finished - if (m_pairingRequests.contains(reply)) { - PairingInfo *pairingInfo = m_pairingRequests.take(reply); - - // check HTTP status code - if (status != 200 || reply->error() != QNetworkReply::NoError) { - qCWarning(dcPhilipsHue) << "Request error:" << status << reply->errorString(); - pairingInfo->deleteLater(); - return; - } - processPairingResponse(pairingInfo, reply->readAll()); - - } else if (m_informationRequests.contains(reply)) { - PairingInfo *pairingInfo = m_informationRequests.take(reply); - - // check HTTP status code - if (status != 200 || reply->error() != QNetworkReply::NoError) { - qCWarning(dcPhilipsHue) << "Request error:" << status << reply->errorString(); - pairingInfo->deleteLater(); - return; - } - processInformationResponse(pairingInfo, reply->readAll()); - - } else if (m_bridgeLightsDiscoveryRequests.contains(reply)) { - Device *device = m_bridgeLightsDiscoveryRequests.take(reply); - - // check HTTP status code - if (status != 200 || reply->error() != QNetworkReply::NoError) { - qCWarning(dcPhilipsHue) << "Bridge light discovery error:" << status << reply->errorString(); - bridgeReachableChanged(device, false); - return; - } - processBridgeLightDiscoveryResponse(device, reply->readAll()); - - } else if (m_bridgeSensorsDiscoveryRequests.contains(reply)) { - Device *device = m_bridgeSensorsDiscoveryRequests.take(reply); - - // check HTTP status code - if (status != 200 || reply->error() != QNetworkReply::NoError) { - qCWarning(dcPhilipsHue) << "Bridge sensor discovery error:" << status << reply->errorString(); - bridgeReachableChanged(device, false); - return; - } - processBridgeSensorDiscoveryResponse(device, reply->readAll()); - - } else if (m_bridgeSearchDevicesRequests.contains(reply)) { - Device *device = m_bridgeSearchDevicesRequests.take(reply); - - // check HTTP status code - if (status != 200 || reply->error() != QNetworkReply::NoError) { - qCWarning(dcPhilipsHue) << "Bridge search new devices error:" << status << reply->errorString(); - bridgeReachableChanged(device, false); - return; - } - discoverBridgeDevices(m_bridges.key(device)); - - } else if (m_bridgeRefreshRequests.contains(reply)) { - Device *device = m_bridgeRefreshRequests.take(reply); - - // check HTTP status code - if (status != 200 || reply->error() != QNetworkReply::NoError) { - if (device->stateValue(bridgeConnectedStateTypeId).toBool()) { - qCWarning(dcPhilipsHue) << "Refresh Hue Bridge request error:" << status << reply->errorString(); - bridgeReachableChanged(device, false); - } - return; - } - processBridgeRefreshResponse(device, reply->readAll()); - - } else if (m_lightRefreshRequests.contains(reply)) { - Device *device = m_lightRefreshRequests.take(reply); - - // check HTTP status code - if (status != 200 || reply->error() != QNetworkReply::NoError) { - qCWarning(dcPhilipsHue) << "Refresh Hue Light request error:" << status << reply->errorString(); - bridgeReachableChanged(device, false); - return; - } - processLightRefreshResponse(device, reply->readAll()); - - } else if (m_lightsRefreshRequests.contains(reply)) { - Device *device = m_lightsRefreshRequests.take(reply); - - // check HTTP status code - if (status != 200 || reply->error() != QNetworkReply::NoError) { - if (device->stateValue(bridgeConnectedStateTypeId).toBool()) { - qCWarning(dcPhilipsHue) << "Refresh Hue lights request error:" << status << reply->errorString(); - bridgeReachableChanged(device, false); - } - return; - } - processLightsRefreshResponse(device, reply->readAll()); - - } else if (m_sensorsRefreshRequests.contains(reply)) { - Device *device = m_sensorsRefreshRequests.take(reply); - - // check HTTP status code - if (status != 200 || reply->error() != QNetworkReply::NoError) { - if (device->stateValue(bridgeConnectedStateTypeId).toBool()) { - qCWarning(dcPhilipsHue) << "Refresh Hue sensors request error:" << status << reply->errorString(); - bridgeReachableChanged(device, false); - } - return; - } - processSensorsRefreshResponse(device, reply->readAll()); - - } else if (m_asyncActions.contains(reply)) { - QPair actionInfo = m_asyncActions.take(reply); - - // check HTTP status code - if (status != 200 || reply->error() != QNetworkReply::NoError) { - qCWarning(dcPhilipsHue) << "Execute Hue Light action request error:" << status << reply->errorString(); - bridgeReachableChanged(actionInfo.first, false); - emit actionExecutionFinished(actionInfo.second, Device::DeviceErrorHardwareNotAvailable); - return; - } - processActionResponse(actionInfo.first, actionInfo.second, reply->readAll()); - - } else if (m_setNameRequests.contains(reply)) { - Device *device = m_setNameRequests.take(reply); - - // check HTTP status code - if (status != 200 || reply->error() != QNetworkReply::NoError) { - qCWarning(dcPhilipsHue) << "Set name of Hue Light request error:" << status << reply->errorString(); - bridgeReachableChanged(device, false); - return; - } - processSetNameResponse(device, reply->readAll()); - } else { - qCWarning(dcPhilipsHue()) << "Unhandled bridge reply" << reply->error() << reply->readAll(); - } -} - void DevicePluginPhilipsHue::onDeviceNameChanged() { Device *device = static_cast(sender()); @@ -672,48 +495,39 @@ Device::DeviceError DevicePluginPhilipsHue::executeAction(Device *device, const // Color light if (device->deviceClassId() == colorLightDeviceClassId) { - HueLight *light = m_lights.key(device); - if (!light->reachable()) { + BridgeConnection* bridge = m_bridgeConnections.value(device->parentId()); + QString uuid = device->paramValue(colorLightDeviceUuidParamTypeId).toString(); + + /*if (!light->reachable()) { qCWarning(dcPhilipsHue) << "Light" << light->name() << "not reachable"; return Device::DeviceErrorHardwareNotAvailable; - } + }*/ if (action.actionTypeId() == colorLightPowerActionTypeId) { - QPair request = light->createSetPowerRequest(action.param(colorLightPowerActionPowerParamTypeId).value().toBool()); - QNetworkReply *reply = hardwareManager()->networkManager()->put(request.first, request.second); - connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady); - m_asyncActions.insert(reply, QPair(device, action.id())); + //get bridge + bridge->setPower(uuid, action.param(colorLightPowerActionPowerParamTypeId).value().toBool()) + //m_asyncActions.insert(device, action.id()); return Device::DeviceErrorAsync; } else if (action.actionTypeId() == colorLightColorActionTypeId) { - QPair request = light->createSetColorRequest(action.param(colorLightColorActionColorParamTypeId).value().value()); - QNetworkReply *reply = hardwareManager()->networkManager()->put(request.first, request.second); - connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady); - m_asyncActions.insert(reply,QPair(device, action.id())); + bridge->setColor(uuid, action.param(colorLightColorActionColorParamTypeId).value().value()); + //m_asyncActions.insert(reply,QPair(device, action.id())); return Device::DeviceErrorAsync; } else if (action.actionTypeId() == colorLightBrightnessActionTypeId) { - QPair request = light->createSetBrightnessRequest(percentageToBrightness(action.param(colorLightBrightnessActionBrightnessParamTypeId).value().toInt())); - QNetworkReply *reply = hardwareManager()->networkManager()->put(request.first, request.second); - connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady); - m_asyncActions.insert(reply, QPair(device, action.id())); + bridge->setBrightness(uuid, action.param(colorLightBrightnessActionBrightnessParamTypeId).value().toInt()); + //m_asyncActions.insert(reply, QPair(device, action.id())); return Device::DeviceErrorAsync; } else if (action.actionTypeId() == colorLightEffectActionTypeId) { - QPair request = light->createSetEffectRequest(action.param(colorLightEffectActionEffectParamTypeId).value().toString()); - QNetworkReply *reply = hardwareManager()->networkManager()->put(request.first, request.second); - connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady); - m_asyncActions.insert(reply, QPair(device, action.id())); + bridge->setEffect(uuid, action.param(colorLightEffectActionEffectParamTypeId).value().toString()); + //m_asyncActions.insert(reply, QPair(device, action.id())); return Device::DeviceErrorAsync; } else if (action.actionTypeId() == colorLightAlertActionTypeId) { - QPair request = light->createFlashRequest(action.param(colorLightAlertActionAlertParamTypeId).value().toString()); - QNetworkReply *reply = hardwareManager()->networkManager()->put(request.first, request.second); - connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady); - m_asyncActions.insert(reply, QPair(device, action.id())); + bridge->setFlash(uuid, action.param(colorLightAlertActionAlertParamTypeId).value().toString()); + //m_asyncActions.insert(reply, QPair(device, action.id())); return Device::DeviceErrorAsync; } else if (action.actionTypeId() == colorLightColorTemperatureActionTypeId) { - QPair request = light->createSetTemperatureRequest(action.param(colorLightColorTemperatureActionColorTemperatureParamTypeId).value().toInt()); - QNetworkReply *reply = hardwareManager()->networkManager()->put(request.first, request.second); - connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady); - m_asyncActions.insert(reply, QPair(device, action.id())); + bridge->setTemperature(uuid, action.param(colorLightColorTemperatureActionColorTemperatureParamTypeId).value().toInt()); + //m_asyncActions.insert(reply, QPair(device, action.id())); return Device::DeviceErrorAsync; } return Device::DeviceErrorActionTypeNotFound; @@ -721,36 +535,29 @@ Device::DeviceError DevicePluginPhilipsHue::executeAction(Device *device, const // Color temperature light if (device->deviceClassId() == colorTemperatureLightDeviceClassId) { - HueLight *light = m_lights.key(device); + BridgeConnection* bridge = m_bridgeConnections.value(device->parentId()); + QString uuid = device->paramValue(colorTemperatureLightDeviceUuidParamTypeId).toString(); - if (!light->reachable()) { + /*if (!light->reachable()) { qCWarning(dcPhilipsHue) << "Light" << light->name() << "not reachable"; return Device::DeviceErrorHardwareNotAvailable; - } + }*/ //TODO if (action.actionTypeId() == colorTemperatureLightPowerActionTypeId) { - QPair request = light->createSetPowerRequest(action.param(colorTemperatureLightPowerActionPowerParamTypeId).value().toBool()); - QNetworkReply *reply = hardwareManager()->networkManager()->put(request.first, request.second); - connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady); - m_asyncActions.insert(reply, QPair(device, action.id())); + bridge->setTemperature(uuid, action.param(colorTemperatureLightPowerActionPowerParamTypeId).value().toBool()); + //m_asyncActions.insert(reply, QPair(device, action.id())); return Device::DeviceErrorAsync; } else if (action.actionTypeId() == colorTemperatureLightBrightnessActionTypeId) { - QPair request = light->createSetBrightnessRequest(percentageToBrightness(action.param(colorTemperatureLightBrightnessActionBrightnessParamTypeId).value().toInt())); - QNetworkReply *reply = hardwareManager()->networkManager()->put(request.first, request.second); - connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady); - m_asyncActions.insert(reply, QPair(device, action.id())); + bridge->setBrightness(uuid, action.param(colorTemperatureLightBrightnessActionBrightnessParamTypeId).value().toInt()); + //m_asyncActions.insert(reply, QPair(device, action.id())); return Device::DeviceErrorAsync; } else if (action.actionTypeId() == colorTemperatureLightAlertActionTypeId) { - QPair request = light->createFlashRequest(action.param(colorTemperatureLightAlertActionAlertParamTypeId).value().toString()); - QNetworkReply *reply = hardwareManager()->networkManager()->put(request.first, request.second); - connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady); - m_asyncActions.insert(reply, QPair(device, action.id())); + bridge->setFlash(uuid, action.param(colorTemperatureLightAlertActionAlertParamTypeId).value().toString()); + //m_asyncActions.insert(reply, QPair(device, action.id())); return Device::DeviceErrorAsync; } else if (action.actionTypeId() == colorTemperatureLightColorTemperatureActionTypeId) { - QPair request = light->createSetTemperatureRequest(action.param(colorTemperatureLightColorTemperatureActionColorTemperatureParamTypeId).value().toInt()); - QNetworkReply *reply = hardwareManager()->networkManager()->put(request.first, request.second); - connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady); - m_asyncActions.insert(reply, QPair(device, action.id())); + bridge->setTemperature(uuid, action.param(colorTemperatureLightColorTemperatureActionColorTemperatureParamTypeId).value().toInt()); + //m_asyncActions.insert(reply, QPair(device, action.id())); return Device::DeviceErrorAsync; } return Device::DeviceErrorActionTypeNotFound; @@ -758,30 +565,28 @@ Device::DeviceError DevicePluginPhilipsHue::executeAction(Device *device, const // Dimmable light if (device->deviceClassId() == dimmableLightDeviceClassId) { - HueLight *light = m_lights.key(device); - if (!light->reachable()) { + BridgeConnection* bridge = m_bridgeConnections.value(device->parentId()); + QString uuid = device->paramValue(dimmableLightDeviceUuidParamTypeId).toString(); + + /*if (!light->reachable()) { qCWarning(dcPhilipsHue) << "Light" << light->name() << "not reachable"; return Device::DeviceErrorHardwareNotAvailable; - } + }*/ //TODO if (action.actionTypeId() == dimmableLightPowerActionTypeId) { - QPair request = light->createSetPowerRequest(action.param(dimmableLightPowerActionPowerParamTypeId).value().toBool()); + bridge->setPower(uuid, action.param(dimmableLightPowerActionPowerParamTypeId).value().toBool()); QNetworkReply *reply = hardwareManager()->networkManager()->put(request.first, request.second); connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady); - m_asyncActions.insert(reply, QPair(device, action.id())); + //m_asyncActions.insert(reply, QPair(device, action.id())); return Device::DeviceErrorAsync; } else if (action.actionTypeId() == dimmableLightBrightnessActionTypeId) { - QPair request = light->createSetBrightnessRequest(percentageToBrightness(action.param(dimmableLightBrightnessActionBrightnessParamTypeId).value().toInt())); - QNetworkReply *reply = hardwareManager()->networkManager()->put(request.first, request.second); - connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady); - m_asyncActions.insert(reply, QPair(device, action.id())); + bridge->setBrightness(uuid, action.param(dimmableLightBrightnessActionBrightnessParamTypeId).value().toInt()); + //m_asyncActions.insert(reply, QPair(device, action.id())); return Device::DeviceErrorAsync; } else if (action.actionTypeId() == dimmableLightAlertActionTypeId) { - QPair request = light->createFlashRequest(action.param(dimmableLightAlertActionAlertParamTypeId).value().toString()); - QNetworkReply *reply = hardwareManager()->networkManager()->put(request.first, request.second); - connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady); - m_asyncActions.insert(reply, QPair(device, action.id())); + bridge->setFlash(uuid, action.param(dimmableLightAlertActionAlertParamTypeId).value().toString()); + //m_asyncActions.insert(reply, QPair(device, action.id())); return Device::DeviceErrorAsync; } return Device::DeviceErrorActionTypeNotFound; @@ -789,14 +594,15 @@ Device::DeviceError DevicePluginPhilipsHue::executeAction(Device *device, const // Hue bridge if (device->deviceClassId() == bridgeDeviceClassId) { - HueBridge *bridge = m_bridges.key(device); + BridgeConnection* bridge = m_bridgeConnections.value(device->id()); + if (!device->stateValue(bridgeConnectedStateTypeId).toBool()) { qCWarning(dcPhilipsHue) << "Bridge" << bridge->hostAddress().toString() << "not reachable"; return Device::DeviceErrorHardwareNotAvailable; } if (action.actionTypeId() == bridgeSearchNewDevicesActionTypeId) { - searchNewDevices(bridge, action.param(bridgeSearchNewDevicesActionSerialParamTypeId).value().toString()); + bridge->searchNewDevices(action.param(bridgeSearchNewDevicesActionSerialParamTypeId).value().toString()); return Device::DeviceErrorNoError; } else if (action.actionTypeId() == bridgeCheckForUpdatesActionTypeId) { QPair request = bridge->createCheckUpdatesRequest(); @@ -962,79 +768,7 @@ void DevicePluginPhilipsHue::onMotionSensorLightIntensityChanged(double lightInt sensorDevice->setStateValue(sensor->lightIntensityStateTypeId(), lightIntensity); } -void DevicePluginPhilipsHue::refreshLight(Device *device) -{ - HueLight *light = m_lights.key(device); - QNetworkRequest request(QUrl("http://" + light->hostAddress().toString() + "/api/" + light->apiKey() + "/lights/" + QString::number(light->id()))); - request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); - QNetworkReply *reply = hardwareManager()->networkManager()->get(request); - connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady); - m_lightRefreshRequests.insert(reply, device); -} - -void DevicePluginPhilipsHue::refreshBridge(Device *device) -{ - HueBridge *bridge = m_bridges.key(device); - // qCDebug(dcPhilipsHue()) << "refreshing bridge"; - - QNetworkRequest request(QUrl("http://" + bridge->hostAddress().toString() + "/api/" + bridge->apiKey() + "/config")); - request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); - QNetworkReply *reply = hardwareManager()->networkManager()->get(request); - connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady); - m_bridgeRefreshRequests.insert(reply, device); -} - -void DevicePluginPhilipsHue::refreshLights(HueBridge *bridge) -{ - Device *device = m_bridges.value(bridge); - // qCDebug(dcPhilipsHue()) << "refreshing lights"; - - QNetworkRequest request(QUrl("http://" + bridge->hostAddress().toString() + "/api/" + bridge->apiKey() + "/lights")); - request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); - QNetworkReply *reply = hardwareManager()->networkManager()->get(request); - connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady); - m_lightsRefreshRequests.insert(reply, device); -} - -void DevicePluginPhilipsHue::refreshSensors(HueBridge *bridge) -{ - Device *device = m_bridges.value(bridge); - // qCDebug(dcPhilipsHue()) << "refreshing sensors"; - - QNetworkRequest request(QUrl("http://" + bridge->hostAddress().toString() + "/api/" + bridge->apiKey() + "/sensors")); - request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); - QNetworkReply *reply = hardwareManager()->networkManager()->get(request); - connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady); - m_sensorsRefreshRequests.insert(reply, device); -} - -void DevicePluginPhilipsHue::discoverBridgeDevices(HueBridge *bridge) -{ - Device *device = m_bridges.value(bridge); - qCDebug(dcPhilipsHue) << "Discover bridge devices" << bridge->hostAddress(); - - QPair lightsRequest = bridge->createDiscoverLightsRequest(); - QNetworkReply *lightsReply = hardwareManager()->networkManager()->get(lightsRequest.first); - connect(lightsReply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady); - m_bridgeLightsDiscoveryRequests.insert(lightsReply, device); - - QPair sensorsRequest = bridge->createSearchSensorsRequest(); - QNetworkReply *reply = hardwareManager()->networkManager()->get(sensorsRequest.first); - connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady); - m_bridgeSensorsDiscoveryRequests.insert(reply, device); -} - -void DevicePluginPhilipsHue::searchNewDevices(HueBridge *bridge, const QString &serialNumber) -{ - Device *device = m_bridges.value(bridge); - qCDebug(dcPhilipsHue) << "Discover bridge devices" << bridge->hostAddress(); - - QPair request = bridge->createSearchLightsRequest(serialNumber); - QNetworkReply *reply = hardwareManager()->networkManager()->post(request.first, request.second); - connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady); - m_bridgeSearchDevicesRequests.insert(reply, device); -} void DevicePluginPhilipsHue::setLightName(Device *device) { @@ -1321,175 +1055,7 @@ void DevicePluginPhilipsHue::processBridgeSensorDiscoveryResponse(Device *device } } -void DevicePluginPhilipsHue::processLightRefreshResponse(Device *device, const QByteArray &data) -{ - QJsonParseError error; - QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error); - // check JSON error - if (error.error != QJsonParseError::NoError) { - qCWarning(dcPhilipsHue) << "Hue Bridge json error in response" << error.errorString(); - return; - } - - // check response error - if (data.contains("error")) { - if (!jsonDoc.toVariant().toList().isEmpty()) { - qCWarning(dcPhilipsHue) << "Failed to refresh Hue Light:" << jsonDoc.toVariant().toList().first().toMap().value("error").toMap().value("description").toString(); - } else { - qCWarning(dcPhilipsHue) << "Failed to refresh Hue Light: Invalid error message format"; - } - return; - } - - HueLight *light = m_lights.key(device); - light->updateStates(jsonDoc.toVariant().toMap().value("state").toMap()); -} - -void DevicePluginPhilipsHue::processBridgeRefreshResponse(Device *device, const QByteArray &data) -{ - QJsonParseError error; - QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error); - - // check JSON error - if (error.error != QJsonParseError::NoError) { - qCWarning(dcPhilipsHue) << "Hue Bridge json error in response" << error.errorString(); - return; - } - - if (!jsonDoc.toVariant().toList().isEmpty()) { - qCWarning(dcPhilipsHue) << "Failed to refresh Hue Bridge:" << jsonDoc.toVariant().toList().first().toMap().value("error").toMap().value("description").toString(); - bridgeReachableChanged(device, false); - return; - } - - //qCDebug(dcPhilipsHue()) << "Bridge refresh response" << qUtf8Printable(jsonDoc.toJson(QJsonDocument::Indented)); - - QVariantMap configMap = jsonDoc.toVariant().toMap(); - - // mark bridge as reachable - bridgeReachableChanged(device, true); - device->setStateValue(bridgeApiVersionStateTypeId, configMap.value("apiversion").toString()); - device->setStateValue(bridgeSoftwareVersionStateTypeId, configMap.value("swversion").toString()); - - int updateStatus = configMap.value("swupdate").toMap().value("updatestate").toInt(); - switch (updateStatus) { - case 0: - device->setStateValue(bridgeUpdateStatusStateTypeId, "Up to date"); - break; - case 1: - device->setStateValue(bridgeUpdateStatusStateTypeId, "Downloading updates"); - break; - case 2: - device->setStateValue(bridgeUpdateStatusStateTypeId, "Updates ready to install"); - break; - case 3: - device->setStateValue(bridgeUpdateStatusStateTypeId, "Installing updates"); - break; - default: - break; - } -} - -void DevicePluginPhilipsHue::processLightsRefreshResponse(Device *device, const QByteArray &data) -{ - QJsonParseError error; - QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error); - - // check JSON error - if (error.error != QJsonParseError::NoError) { - qCWarning(dcPhilipsHue) << "Hue Bridge json error in response" << error.errorString(); - return; - } - - // check response error - if (data.contains("error")) { - if (!jsonDoc.toVariant().toList().isEmpty()) { - qCWarning(dcPhilipsHue) << "Failed to refresh Hue Lights:" << jsonDoc.toVariant().toList().first().toMap().value("error").toMap().value("description").toString(); - } else { - qCWarning(dcPhilipsHue) << "Failed to refresh Hue Lights: Invalid error message format"; - } - return; - } - - // Update light states - QVariantMap lightsMap = jsonDoc.toVariant().toMap(); - foreach (const QString &lightId, lightsMap.keys()) { - QVariantMap lightMap = lightsMap.value(lightId).toMap(); - // get the light of this bridge - foreach (HueLight *light, m_lights.keys()) { - if (light->id() == lightId.toInt() && m_lights.value(light)->parentId() == device->id()) { - light->updateStates(lightMap.value("state").toMap()); - } - } - } -} - -void DevicePluginPhilipsHue::processSensorsRefreshResponse(Device *device, const QByteArray &data) -{ - QJsonParseError error; - QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error); - - // check JSON error - if (error.error != QJsonParseError::NoError) { - qCWarning(dcPhilipsHue) << "Hue Bridge json error in response" << error.errorString(); - return; - } - - // check response error - if (!jsonDoc.toVariant().toList().isEmpty()) { - qCWarning(dcPhilipsHue) << "Failed to refresh Hue Sensors:" << jsonDoc.toVariant().toList().first().toMap().value("error").toMap().value("description").toString(); - return; - } - - // Update sensor states - QVariantMap sensorsMap = jsonDoc.toVariant().toMap(); - foreach (const QString &sensorId, sensorsMap.keys()) { - QVariantMap sensorMap = sensorsMap.value(sensorId).toMap(); - - // Remotes - foreach (HueRemote *remote, m_remotes.keys()) { - if (remote->id() == sensorId.toInt() && m_remotes.value(remote)->parentId() == device->id()) { - remote->updateStates(sensorMap.value("state").toMap(), sensorMap.value("config").toMap()); - } - } - - // Outdoor sensors - foreach (HueMotionSensor *motionSensor, m_motionSensors.keys()) { - if (motionSensor->hasSensor(sensorId.toInt()) && m_motionSensors.value(motionSensor)->parentId() == device->id()) { - motionSensor->updateStates(sensorMap); - } - } - } -} - -void DevicePluginPhilipsHue::processSetNameResponse(Device *device, const QByteArray &data) -{ - QJsonParseError error; - QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error); - - // check JSON error - if (error.error != QJsonParseError::NoError) { - qCWarning(dcPhilipsHue) << "Hue Bridge json error in response" << error.errorString(); - emit deviceSetupFinished(device, Device::DeviceSetupStatusFailure); - return; - } - - // check response error - if (data.contains("error")) { - if (!jsonDoc.toVariant().toList().isEmpty()) { - qCWarning(dcPhilipsHue) << "Failed to set name of Hue:" << jsonDoc.toVariant().toList().first().toMap().value("error").toMap().value("description").toString(); - } else { - qCWarning(dcPhilipsHue) << "Failed to set name of Hue: Invalid error message format"; - } - emit deviceSetupFinished(device, Device::DeviceSetupStatusFailure); - return; - } - - if (device->deviceClassId() == colorLightDeviceClassId || device->deviceClassId() == dimmableLightDeviceClassId) - refreshLight(device); - -} void DevicePluginPhilipsHue::processPairingResponse(PairingInfo *pairingInfo, const QByteArray &data) { diff --git a/philipshue/devicepluginphilipshue.h b/philipshue/devicepluginphilipshue.h index 5dc275ab..04f83cde 100644 --- a/philipshue/devicepluginphilipshue.h +++ b/philipshue/devicepluginphilipshue.h @@ -1,7 +1,8 @@ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright (C) 2014 Michael Zanetti * - * Copyright (C) 2015 - 2019 Simon Stürz * + * Copyright (C) 2015 - 2019 Simon Stürz * + * Copyright (C) 2019 Bernhard Trinns * * * * This file is part of nymea. * * * @@ -25,12 +26,12 @@ #define DEVICEPLUGINPHILIPSHUE_H #include "devices/deviceplugin.h" +#include "bridgeconnection.h" #include "huebridge.h" #include "huelight.h" #include "hueremote.h" #include "pairinginfo.h" #include "huemotionsensor.h" -#include "huemotionsensor.h" #include "plugintimer.h" #include "network/networkaccessmanager.h" @@ -47,31 +48,28 @@ class DevicePluginPhilipsHue: public DevicePlugin public: explicit DevicePluginPhilipsHue(); - ~DevicePluginPhilipsHue(); - void init() override; Device::DeviceSetupStatus setupDevice(Device *device) override; Device::DeviceError discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms) override; void deviceRemoved(Device *device) override; Device::DeviceSetupStatus confirmPairing(const PairingTransactionId &pairingTransactionId, const DeviceClassId &deviceClassId, const ParamList ¶ms, const QString &secret) override; - -public slots: - Device::DeviceError executeAction(Device *device, const Action &action); + Device::DeviceError executeAction(Device *device, const Action &action) override; private slots: - void lightStateChanged(); - void remoteStateChanged(); + // Light Device Events + void onLightStateChanged(); + + // Remote Device Eventes + void onRemoteStateChanged(); void onRemoteButtonEvent(int buttonCode); - // Motion sensor + // Motion Sensor Events void onMotionSensorReachableChanged(bool reachable); void onMotionSensorBatteryLevelChanged(int batteryLevel); void onMotionSensorTemperatureChanged(double temperature); void onMotionSensorPresenceChanged(bool presence); void onMotionSensorLightIntensityChanged(double lightIntensity); -private slots: - void networkManagerReplyReady(); void onDeviceNameChanged(); private: @@ -83,66 +81,18 @@ private: }; void finishDiscovery(DiscoveryJob* job); - PluginTimer *m_pluginTimer1Sec = nullptr; - PluginTimer *m_pluginTimer5Sec = nullptr; - PluginTimer *m_pluginTimer15Sec = nullptr; - QHash m_pairingRequests; QHash m_informationRequests; + QHash m_bridgeConnections; + QList m_unconfiguredBridges; QList m_unconfiguredLights; - QHash m_lightRefreshRequests; - QHash m_setNameRequests; - QHash m_bridgeRefreshRequests; - QHash m_lightsRefreshRequests; - QHash m_sensorsRefreshRequests; - QHash m_bridgeLightsDiscoveryRequests; - QHash m_bridgeSensorsDiscoveryRequests; - QHash m_bridgeSearchDevicesRequests; - QHash > m_asyncActions; - QHash m_bridges; - QHash m_lights; - QHash m_remotes; - QHash m_motionSensors; - - void refreshLight(Device *device); - void refreshBridge(Device *device); - - void refreshLights(HueBridge *bridge); - void refreshSensors(HueBridge *bridge); - - void discoverBridgeDevices(HueBridge *bridge); - void searchNewDevices(HueBridge *bridge, const QString &serialNumber); - - void setLightName(Device *device); - void setRemoteName(Device *device); - - void processNUpnpResponse(const QByteArray &data); - void processBridgeLightDiscoveryResponse(Device *device, const QByteArray &data); - void processBridgeSensorDiscoveryResponse(Device *device, const QByteArray &data); - void processLightRefreshResponse(Device *device, const QByteArray &data); - void processBridgeRefreshResponse(Device *device, const QByteArray &data); - void processLightsRefreshResponse(Device *device, const QByteArray &data); - void processSensorsRefreshResponse(Device *device, const QByteArray &data); - void processSetNameResponse(Device *device, const QByteArray &data); - void processPairingResponse(PairingInfo *pairingInfo, const QByteArray &data); - void processInformationResponse(PairingInfo *pairingInfo, const QByteArray &data); - void processActionResponse(Device *device, const ActionId actionId, const QByteArray &data); - - void bridgeReachableChanged(Device *device, const bool &reachable); - - Device* bridgeForBridgeId(const QString &id); bool lightAlreadyAdded(const QString &uuid); bool sensorAlreadyAdded(const QString &uuid); - - int brightnessToPercentage(int brightness); - int percentageToBrightness(int percentage); - - void abortRequests(QHash requestList, Device* device); }; -#endif // DEVICEPLUGINBOBLIGHT_H +#endif // DEVICEPLUGINPHILIPSHUE_H diff --git a/philipshue/huelight.h b/philipshue/huelight.h index 1971d237..2e094006 100644 --- a/philipshue/huelight.h +++ b/philipshue/huelight.h @@ -103,7 +103,6 @@ private: signals: void stateChanged(); - }; #endif // HUELIGHT_H diff --git a/philipshue/philipshue.pro b/philipshue/philipshue.pro index d666a866..b406a22a 100644 --- a/philipshue/philipshue.pro +++ b/philipshue/philipshue.pro @@ -13,7 +13,8 @@ SOURCES += \ huemotionsensor.cpp \ pairinginfo.cpp \ hueremote.cpp \ - huedevice.cpp + huedevice.cpp \ + bridgeconnection.cpp HEADERS += \ devicepluginphilipshue.h \ @@ -25,7 +26,8 @@ HEADERS += \ huemotionsensor.h \ pairinginfo.h \ hueremote.h \ - huedevice.h + huedevice.h \ + bridgeconnection.h