Add support for controlling Somfy smart home devices through their Tahoma box (and the Somfy API). The plugin currently contains support for roller shutters as well as for venetian blinds.
459 lines
25 KiB
C++
459 lines
25 KiB
C++
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
*
|
|
* Copyright 2013 - 2020, nymea GmbH
|
|
* Contact: contact@nymea.io
|
|
*
|
|
* This file is part of nymea.
|
|
* This project including source code and documentation is protected by
|
|
* copyright law, and remains the property of nymea GmbH. All rights, including
|
|
* reproduction, publication, editing and translation, are reserved. The use of
|
|
* this project is subject to the terms of a license agreement to be concluded
|
|
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
|
* under https://nymea.io/license
|
|
*
|
|
* GNU Lesser General Public License Usage
|
|
* Alternatively, this project may be redistributed and/or modified under the
|
|
* terms of the GNU Lesser General Public License as published by the Free
|
|
* Software Foundation; version 3. This project is distributed in the hope that
|
|
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
|
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with this project. If not, see <https://www.gnu.org/licenses/>.
|
|
*
|
|
* For any further details and any questions please contact us under
|
|
* contact@nymea.io or see our FAQ/Licensing Information on
|
|
* https://nymea.io/license/faq
|
|
*
|
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
|
|
#include "integrationpluginsomfytahoma.h"
|
|
|
|
#include <QJsonDocument>
|
|
#include <QJsonObject>
|
|
#include <QJsonArray>
|
|
|
|
#include "network/networkaccessmanager.h"
|
|
|
|
#include "plugininfo.h"
|
|
#include "somfytahomarequests.h"
|
|
|
|
void IntegrationPluginSomfyTahoma::startPairing(ThingPairingInfo *info)
|
|
{
|
|
info->finish(Thing::ThingErrorNoError, QT_TR_NOOP("Please enter the login credentials for Somfy Tahoma."));
|
|
}
|
|
|
|
void IntegrationPluginSomfyTahoma::confirmPairing(ThingPairingInfo *info, const QString &username, const QString &password)
|
|
{
|
|
SomfyTahomaLoginRequest *request = new SomfyTahomaLoginRequest(hardwareManager()->networkManager(), username, password, this);
|
|
connect(request, &SomfyTahomaLoginRequest::error, info, [info](){
|
|
info->finish(Thing::ThingErrorAuthenticationFailure, QT_TR_NOOP("Failed to login to Somfy Tahoma."));
|
|
});
|
|
connect(request, &SomfyTahomaLoginRequest::finished, info, [this, info, username, password](const QVariant &/*result*/){
|
|
pluginStorage()->beginGroup(info->thingId().toString());
|
|
pluginStorage()->setValue("username", username);
|
|
pluginStorage()->setValue("password", password);
|
|
pluginStorage()->endGroup();
|
|
info->finish(Thing::ThingErrorNoError);
|
|
});
|
|
}
|
|
|
|
void IntegrationPluginSomfyTahoma::setupThing(ThingSetupInfo *info)
|
|
{
|
|
if (info->thing()->thingClassId() == tahomaThingClassId) {
|
|
SomfyTahomaLoginRequest *request = createLoginRequestWithStoredCredentials(info->thing());
|
|
connect(request, &SomfyTahomaLoginRequest::error, info, [info](){
|
|
info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("Failed to login to Somfy Tahoma."));
|
|
});
|
|
connect(request, &SomfyTahomaLoginRequest::finished, info, [this, info](const QVariant &/*result*/){
|
|
QUuid accountId = info->thing()->id();
|
|
SomfyTahomaGetRequest *request = new SomfyTahomaGetRequest(hardwareManager()->networkManager(), "/setup", this);
|
|
connect(request, &SomfyTahomaGetRequest::finished, this, [this, accountId](const QVariant &result){
|
|
QList<ThingDescriptor> unknownDevices;
|
|
foreach (const QVariant &gatewayVariant, result.toMap()["gateways"].toList()) {
|
|
QVariantMap gatewayMap = gatewayVariant.toMap();
|
|
QString gatewayId = gatewayMap.value("gatewayId").toString();
|
|
Thing *thing = myThings().findByParams(ParamList() << Param(gatewayThingGatewayIdParamTypeId, gatewayId));
|
|
if (thing) {
|
|
qCDebug(dcSomfyTahoma()) << "Found existing gateway:" << gatewayId;
|
|
} else {
|
|
qCInfo(dcSomfyTahoma()) << "Found new gateway:" << gatewayId;
|
|
ThingDescriptor descriptor(gatewayThingClassId, "TaHoma Gateway", QString(), accountId);
|
|
descriptor.setParams(ParamList() << Param(gatewayThingGatewayIdParamTypeId, gatewayId));
|
|
unknownDevices.append(descriptor);
|
|
}
|
|
}
|
|
foreach (const QVariant &deviceVariant, result.toMap()["devices"].toList()) {
|
|
QVariantMap deviceMap = deviceVariant.toMap();
|
|
QString type = deviceMap.value("uiClass").toString();
|
|
QString deviceUrl = deviceMap.value("deviceURL").toString();
|
|
QString label = deviceMap.value("label").toString();
|
|
if (type == QStringLiteral("RollerShutter")) {
|
|
Thing *thing = myThings().findByParams(ParamList() << Param(rollershutterThingDeviceUrlParamTypeId, deviceUrl));
|
|
if (thing) {
|
|
qCDebug(dcSomfyTahoma()) << "Found existing roller shutter:" << label << deviceUrl;
|
|
} else {
|
|
qCInfo(dcSomfyTahoma()) << "Found new roller shutter:" << label << deviceUrl;
|
|
ThingDescriptor descriptor(rollershutterThingClassId, label, QString(), accountId);
|
|
descriptor.setParams(ParamList() << Param(rollershutterThingDeviceUrlParamTypeId, deviceUrl));
|
|
unknownDevices.append(descriptor);
|
|
}
|
|
} else if (type == QStringLiteral("ExteriorVenetianBlind")) {
|
|
Thing *thing = myThings().findByParams(ParamList() << Param(venetianblindThingDeviceUrlParamTypeId, deviceUrl));
|
|
if (thing) {
|
|
qCDebug(dcSomfyTahoma()) << "Found existing venetian blind:" << label << deviceUrl;
|
|
} else {
|
|
qCInfo(dcSomfyTahoma()) << "Found new venetian blind:" << label << deviceUrl;
|
|
ThingDescriptor descriptor(venetianblindThingClassId, label, QString(), accountId);
|
|
descriptor.setParams(ParamList() << Param(venetianblindThingDeviceUrlParamTypeId, deviceUrl));
|
|
unknownDevices.append(descriptor);
|
|
}
|
|
} else {
|
|
qCInfo(dcSomfyTahoma()) << "Found unsupperted Somfy device:" << label << type << deviceUrl;
|
|
}
|
|
}
|
|
if (!unknownDevices.isEmpty()) {
|
|
emit autoThingsAppeared(unknownDevices);
|
|
}
|
|
});
|
|
info->finish(Thing::ThingErrorNoError);
|
|
});
|
|
}
|
|
|
|
else if (info->thing()->thingClassId() == gatewayThingClassId ||
|
|
info->thing()->thingClassId() == rollershutterThingClassId ||
|
|
info->thing()->thingClassId() == venetianblindThingClassId) {
|
|
info->finish(Thing::ThingErrorNoError);
|
|
}
|
|
}
|
|
|
|
void IntegrationPluginSomfyTahoma::postSetupThing(Thing *thing)
|
|
{
|
|
if (thing->thingClassId() == tahomaThingClassId) {
|
|
pluginStorage()->beginGroup(thing->id().toString());
|
|
thing->setStateValue(tahomaUserDisplayNameStateTypeId, pluginStorage()->value("username"));
|
|
pluginStorage()->endGroup();
|
|
|
|
refreshAccount(thing);
|
|
}
|
|
|
|
// Set parent of all devices to the respective gateway. We create all devices in setup() of the account.
|
|
// But we don't have the ThingIds of the gateways, because they're created in setup() as well.
|
|
QUrl deviceUrl;
|
|
if (thing->thingClassId() == rollershutterThingClassId) {
|
|
deviceUrl = QUrl(thing->paramValue(rollershutterThingDeviceUrlParamTypeId).toString());
|
|
} else if (thing->thingClassId() == venetianblindThingClassId) {
|
|
deviceUrl = QUrl(thing->paramValue(venetianblindThingDeviceUrlParamTypeId).toString());
|
|
}
|
|
if (!deviceUrl.isEmpty()) {
|
|
Thing *gateway = myThings().findByParams(ParamList() << Param(gatewayThingGatewayIdParamTypeId, deviceUrl.host()));
|
|
if (gateway) {
|
|
thing->setParentId(gateway->parentId());
|
|
} else {
|
|
qCWarning(dcSomfyTahoma()) << "Couldn't find gateway for thing" << thing;
|
|
}
|
|
}
|
|
}
|
|
|
|
void IntegrationPluginSomfyTahoma::refreshAccount(Thing *thing)
|
|
{
|
|
// Ensure that even't polling doesn't interfere the refreshing.
|
|
if (m_eventPollTimer.contains(thing)) {
|
|
hardwareManager()->pluginTimerManager()->unregisterTimer(m_eventPollTimer[thing]);
|
|
}
|
|
|
|
SomfyTahomaGetRequest *setupRequest = new SomfyTahomaGetRequest(hardwareManager()->networkManager(), "/setup", this);
|
|
connect(setupRequest, &SomfyTahomaGetRequest::error, this, [this, thing](){
|
|
markDisconnected(thing);
|
|
});
|
|
connect(setupRequest, &SomfyTahomaGetRequest::finished, this, [this, thing](const QVariant &result){
|
|
thing->setStateValue(tahomaLoggedInStateTypeId, true);
|
|
thing->setStateValue(tahomaConnectedStateTypeId, true);
|
|
foreach (const QVariant &gatewayVariant, result.toMap()["gateways"].toList()) {
|
|
QVariantMap gatewayMap = gatewayVariant.toMap();
|
|
QString gatewayId = gatewayMap.value("gatewayId").toString();
|
|
Thing *thing = myThings().findByParams(ParamList() << Param(gatewayThingGatewayIdParamTypeId, gatewayId));
|
|
if (thing) {
|
|
qCDebug(dcSomfyTahoma()) << "Setting initial state for gateway:" << gatewayId;
|
|
thing->setStateValue(gatewayConnectedStateTypeId, gatewayMap["connectivity"].toMap()["status"] == "OK");
|
|
pluginStorage()->beginGroup(thing->id().toString());
|
|
pluginStorage()->setValue("connected", gatewayMap["connectivity"].toMap()["status"] == "OK");
|
|
pluginStorage()->endGroup();
|
|
}
|
|
}
|
|
foreach (const QVariant &deviceVariant, result.toMap()["devices"].toList()) {
|
|
updateThingStates(deviceVariant.toMap()["deviceURL"].toString(), deviceVariant.toMap()["states"].toList());
|
|
}
|
|
});
|
|
|
|
SomfyTahomaPostRequest *eventRegistrationRequest = new SomfyTahomaPostRequest(hardwareManager()->networkManager(), "/events/register", "application/json", QByteArray(), this);
|
|
connect(eventRegistrationRequest, &SomfyTahomaPostRequest::error, this, [this, thing](){
|
|
qCWarning(dcSomfyTahoma()) << "Failed to register for events.";
|
|
markDisconnected(thing);
|
|
});
|
|
connect(eventRegistrationRequest, &SomfyTahomaPostRequest::finished, this, [this, thing](const QVariant &result){
|
|
thing->setStateValue(tahomaConnectedStateTypeId, true);
|
|
QString eventListenerId = result.toMap()["id"].toString();
|
|
m_eventPollTimer[thing] = hardwareManager()->pluginTimerManager()->registerTimer(2 /*sec*/);
|
|
connect(m_eventPollTimer[thing], &PluginTimer::timeout, thing, [this, thing, eventListenerId](){
|
|
SomfyTahomaEventFetchRequest *eventFetchRequest = new SomfyTahomaEventFetchRequest(hardwareManager()->networkManager(), eventListenerId, this);
|
|
connect(eventFetchRequest, &SomfyTahomaEventFetchRequest::error, thing, [this, thing](QNetworkReply::NetworkError error){
|
|
markDisconnected(thing);
|
|
if (error == QNetworkReply::AuthenticationRequiredError) {
|
|
qCInfo(dcSomfyTahoma()) << "Failed to fetch events: Authentication expired, reauthenticating";
|
|
SomfyTahomaLoginRequest *request = createLoginRequestWithStoredCredentials(thing);
|
|
connect(request, &SomfyTahomaLoginRequest::error, this, [this, thing](){
|
|
// This is a fatal error. The user needs to reconfigure the account to provide new credentials.
|
|
qCWarning(dcSomfyTahoma()) << "Failed to reauthenticate";
|
|
hardwareManager()->pluginTimerManager()->unregisterTimer(m_eventPollTimer[thing]);
|
|
m_eventPollTimer.remove(thing);
|
|
});
|
|
connect(request, &SomfyTahomaLoginRequest::finished, this, [this, thing](const QVariant &/*result*/){
|
|
qCInfo(dcSomfyTahoma()) << "Reauthentication successful";
|
|
refreshAccount(thing);
|
|
});
|
|
} else {
|
|
qCWarning(dcSomfyTahoma()) << "Failed to fetch events:" << error;
|
|
}
|
|
});
|
|
connect(eventFetchRequest, &SomfyTahomaEventFetchRequest::finished, thing, [this, thing](const QVariant &result){
|
|
thing->setStateValue(tahomaConnectedStateTypeId, true);
|
|
restoreChildConnectedState(thing);
|
|
if (!result.toList().empty()) {
|
|
qCDebug(dcSomfyTahoma()) << "Got events:" << qUtf8Printable(QJsonDocument::fromVariant(result).toJson());
|
|
}
|
|
handleEvents(result.toList());
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
void IntegrationPluginSomfyTahoma::thingRemoved(Thing *thing)
|
|
{
|
|
m_eventPollTimer.remove(thing);
|
|
}
|
|
|
|
void IntegrationPluginSomfyTahoma::handleEvents(const QVariantList &eventList)
|
|
{
|
|
Thing *thing;
|
|
foreach (const QVariant &eventVariant, eventList) {
|
|
QVariantMap eventMap = eventVariant.toMap();
|
|
if (eventMap["name"] == "DeviceStateChangedEvent") {
|
|
updateThingStates(eventMap["deviceURL"].toString(), eventMap["deviceStates"].toList());
|
|
} else if (eventMap["name"] == "ExecutionRegisteredEvent") {
|
|
QList<Thing *> things;
|
|
foreach (const QVariant &action, eventMap["actions"].toList()) {
|
|
thing = myThings().findByParams(ParamList() << Param(rollershutterThingDeviceUrlParamTypeId, action.toMap()["deviceURL"]));
|
|
if (thing) {
|
|
thing->setStateValue(rollershutterMovingStateTypeId, true);
|
|
things.append(thing);
|
|
continue;
|
|
}
|
|
thing = myThings().findByParams(ParamList() << Param(venetianblindThingDeviceUrlParamTypeId, action.toMap()["deviceURL"]));
|
|
if (thing) {
|
|
thing->setStateValue(venetianblindMovingStateTypeId, true);
|
|
things.append(thing);
|
|
}
|
|
}
|
|
qCDebug(dcSomfyTahoma()) << "ExecutionRegisteredEvent" << eventMap["execId"];
|
|
m_currentExecutions.insert(eventMap["execId"].toString(), things);
|
|
} else if (eventMap["name"] == "ExecutionStateChangedEvent" &&
|
|
(eventMap["newState"] == "COMPLETED" || eventMap["newState"] == "FAILED")) {
|
|
QList<Thing *> things = m_currentExecutions.take(eventMap["execId"].toString());
|
|
foreach (Thing *thing, things) {
|
|
if (thing->thingClassId() == rollershutterThingClassId) {
|
|
thing->setStateValue(rollershutterMovingStateTypeId, false);
|
|
} else if (thing->thingClassId() == venetianblindThingClassId) {
|
|
thing->setStateValue(venetianblindMovingStateTypeId, false);
|
|
}
|
|
}
|
|
|
|
QPointer<ThingActionInfo> thingActionInfo = m_pendingActions.take(eventMap["execId"].toString());
|
|
if (!thingActionInfo.isNull()) {
|
|
if (eventMap["newState"] == "COMPLETED") {
|
|
qCDebug(dcSomfyTahoma()) << "Action finished" << thingActionInfo->thing() << thingActionInfo->action().actionTypeId();
|
|
thingActionInfo->finish(Thing::ThingErrorNoError);
|
|
} else if (eventMap["newState"] == "FAILED") {
|
|
qCWarning(dcSomfyTahoma()) << "Action failed" << thingActionInfo->thing() << thingActionInfo->action().actionTypeId();
|
|
thingActionInfo->finish(Thing::ThingErrorHardwareFailure);
|
|
} else {
|
|
qCWarning(dcSomfyTahoma()) << "Action in unknown state" << thingActionInfo->thing() << thingActionInfo->action().actionTypeId() << eventMap["newState"].toString();
|
|
thingActionInfo->finish(Thing::ThingErrorHardwareFailure);
|
|
}
|
|
}
|
|
} else if (eventMap["name"] == "GatewayAliveEvent") {
|
|
thing = myThings().findByParams(ParamList() << Param(gatewayThingGatewayIdParamTypeId, eventMap["gatewayId"]));
|
|
if (thing) {
|
|
qCInfo(dcSomfyTahoma()) << "Gateway connected event received:" << eventMap["gatewayId"];
|
|
thing->setStateValue(gatewayConnectedStateTypeId, true);
|
|
pluginStorage()->beginGroup(thing->id().toString());
|
|
pluginStorage()->setValue("connected", true);
|
|
pluginStorage()->endGroup();
|
|
restoreChildConnectedState(thing);
|
|
} else {
|
|
qCDebug(dcSomfyTahoma()) << "Ignoring gateway connected event for unknown gateway" << eventMap["gatewayId"];
|
|
}
|
|
} else if (eventMap["name"] == "GatewayDownEvent") {
|
|
thing = myThings().findByParams(ParamList() << Param(gatewayThingGatewayIdParamTypeId, eventMap["gatewayId"]));
|
|
if (thing) {
|
|
qCInfo(dcSomfyTahoma()) << "Gateway disconnected event received:" << eventMap["gatewayId"];
|
|
thing->setStateValue(gatewayConnectedStateTypeId, false);
|
|
pluginStorage()->beginGroup(thing->id().toString());
|
|
pluginStorage()->setValue("connected", false);
|
|
pluginStorage()->endGroup();
|
|
markDisconnected(thing);
|
|
} else {
|
|
qCDebug(dcSomfyTahoma()) << "Ignoring gateway disconnected event for unknown gateway" << eventMap["gatewayId"];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void IntegrationPluginSomfyTahoma::updateThingStates(const QString &deviceUrl, const QVariantList &stateList)
|
|
{
|
|
Thing *thing = myThings().findByParams(ParamList() << Param(rollershutterThingDeviceUrlParamTypeId, deviceUrl));
|
|
if (thing) {
|
|
foreach (const QVariant &stateVariant, stateList) {
|
|
QVariantMap stateMap = stateVariant.toMap();
|
|
if (stateMap["name"] == "core:ClosureState") {
|
|
thing->setStateValue(rollershutterPercentageStateTypeId, stateMap["value"]);
|
|
} else if (stateMap["name"] == "core:StatusState") {
|
|
thing->setStateValue(rollershutterConnectedStateTypeId, stateMap["value"] == "available");
|
|
pluginStorage()->beginGroup(thing->id().toString());
|
|
pluginStorage()->setValue("connected", stateMap["value"] == "available");
|
|
pluginStorage()->endGroup();
|
|
} else if (stateMap["name"] == "core:RSSILevelState") {
|
|
thing->setStateValue(rollershutterSignalStrengthStateTypeId, stateMap["value"]);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
thing = myThings().findByParams(ParamList() << Param(venetianblindThingDeviceUrlParamTypeId, deviceUrl));
|
|
if (thing) {
|
|
foreach (const QVariant &stateVariant, stateList) {
|
|
QVariantMap stateMap = stateVariant.toMap();
|
|
if (stateMap["name"] == "core:ClosureState") {
|
|
thing->setStateValue(venetianblindPercentageStateTypeId, stateMap["value"]);
|
|
} else if (stateMap["name"] == "core:SlateOrientationState") {
|
|
// Convert percentage (0%/100%, 50%=open) into degree (-90/+90)
|
|
int degree = (stateMap["value"].toInt() * 1.8) - 90;
|
|
thing->setStateValue(venetianblindAngleStateTypeId, degree);
|
|
} else if (stateMap["name"] == "core:StatusState") {
|
|
thing->setStateValue(venetianblindConnectedStateTypeId, stateMap["value"] == "available");
|
|
pluginStorage()->beginGroup(thing->id().toString());
|
|
pluginStorage()->setValue("connected", stateMap["value"] == "available");
|
|
pluginStorage()->endGroup();
|
|
} else if (stateMap["name"] == "core:RSSILevelState") {
|
|
thing->setStateValue(venetianblindSignalStrengthStateTypeId, stateMap["value"]);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
void IntegrationPluginSomfyTahoma::executeAction(ThingActionInfo *info)
|
|
{
|
|
qCInfo(dcSomfyTahoma()) << "Action request:" << info->thing() << info->action().actionTypeId() << info->action().params();
|
|
|
|
QString deviceUrl;
|
|
QString actionName;
|
|
QJsonArray actionParameters;
|
|
|
|
if (info->thing()->thingClassId() == rollershutterThingClassId) {
|
|
deviceUrl = info->thing()->paramValue(rollershutterThingDeviceUrlParamTypeId).toString();
|
|
if (info->action().actionTypeId() == rollershutterPercentageActionTypeId) {
|
|
actionName = "setClosureAndLinearSpeed";
|
|
actionParameters = { info->action().param(rollershutterPercentageActionPercentageParamTypeId).value().toInt(), "lowspeed" };
|
|
} else if (info->action().actionTypeId() == rollershutterOpenActionTypeId) {
|
|
actionName = "setClosureAndLinearSpeed";
|
|
actionParameters = { 0, "lowspeed" };
|
|
} else if (info->action().actionTypeId() == rollershutterCloseActionTypeId) {
|
|
actionName = "setClosureAndLinearSpeed";
|
|
actionParameters = { 100, "lowspeed" };
|
|
} else if (info->action().actionTypeId() == rollershutterStopActionTypeId) {
|
|
actionName = "stop";
|
|
}
|
|
} else if (info->thing()->thingClassId() == venetianblindThingClassId) {
|
|
deviceUrl = info->thing()->paramValue(venetianblindThingDeviceUrlParamTypeId).toString();
|
|
if (info->action().actionTypeId() == venetianblindPercentageActionTypeId) {
|
|
actionName = "setClosure";
|
|
actionParameters = { info->action().param(venetianblindPercentageActionPercentageParamTypeId).value().toInt() };
|
|
} else if (info->action().actionTypeId() == venetianblindAngleActionTypeId) {
|
|
actionName = "setOrientation";
|
|
// Convert degree (-90/+90) into percentage (0%/100%, 50%=open)
|
|
int degree = (info->action().param(venetianblindAngleActionAngleParamTypeId).value().toInt() + 90) / 1.8;
|
|
actionParameters = { degree };
|
|
} else if (info->action().actionTypeId() == venetianblindOpenActionTypeId) {
|
|
actionName = "open";
|
|
} else if (info->action().actionTypeId() == venetianblindCloseActionTypeId) {
|
|
actionName = "close";
|
|
} else if (info->action().actionTypeId() == venetianblindStopActionTypeId) {
|
|
actionName = "stop";
|
|
}
|
|
}
|
|
|
|
if (!actionName.isEmpty()) {
|
|
QJsonDocument jsonRequest{QJsonObject
|
|
{
|
|
{"label", info->thing()->name()},
|
|
{"actions", QJsonArray{QJsonObject{{"deviceURL", deviceUrl},
|
|
{"commands", QJsonArray{QJsonObject{{"name", actionName},
|
|
{"parameters", actionParameters}}}}}}}
|
|
}};
|
|
SomfyTahomaPostRequest *request = new SomfyTahomaPostRequest(hardwareManager()->networkManager(), "/exec/apply", "application/json", jsonRequest.toJson(QJsonDocument::Compact), this);
|
|
connect(request, &SomfyTahomaPostRequest::error, info, [info](){
|
|
info->finish(Thing::ThingErrorHardwareFailure);
|
|
});
|
|
connect(request, &SomfyTahomaPostRequest::finished, info, [this, info](const QVariant &result){
|
|
qCInfo(dcSomfyTahoma()) << "Action started" << info->thing() << info->action().actionTypeId();
|
|
m_pendingActions.insert(result.toMap()["execId"].toString(), info);
|
|
});
|
|
} else {
|
|
info->finish(Thing::ThingErrorActionTypeNotFound);
|
|
}
|
|
}
|
|
|
|
SomfyTahomaLoginRequest *IntegrationPluginSomfyTahoma::createLoginRequestWithStoredCredentials(Thing *thing)
|
|
{
|
|
pluginStorage()->beginGroup(thing->id().toString());
|
|
QString username = pluginStorage()->value("username").toString();
|
|
QString password = pluginStorage()->value("password").toString();
|
|
pluginStorage()->endGroup();
|
|
return new SomfyTahomaLoginRequest(hardwareManager()->networkManager(), username, password, this);
|
|
}
|
|
|
|
void IntegrationPluginSomfyTahoma::markDisconnected(Thing *thing)
|
|
{
|
|
if (thing->thingClassId() == tahomaThingClassId) {
|
|
thing->setStateValue(tahomaConnectedStateTypeId, false);
|
|
} else if (thing->thingClassId() == gatewayThingClassId) {
|
|
thing->setStateValue(gatewayConnectedStateTypeId, false);
|
|
} else if (thing->thingClassId() == rollershutterThingClassId) {
|
|
thing->setStateValue(rollershutterConnectedStateTypeId, false);
|
|
} else if (thing->thingClassId() == venetianblindThingClassId) {
|
|
thing->setStateValue(venetianblindConnectedStateTypeId, false);
|
|
}
|
|
foreach (Thing *child, myThings().filterByParentId(thing->id())) {
|
|
markDisconnected(child);
|
|
}
|
|
}
|
|
|
|
void IntegrationPluginSomfyTahoma::restoreChildConnectedState(Thing *thing)
|
|
{
|
|
pluginStorage()->beginGroup(thing->id().toString());
|
|
if (pluginStorage()->contains("connected")) {
|
|
if (thing->thingClassId() == gatewayThingClassId) {
|
|
thing->setStateValue(gatewayConnectedStateTypeId, pluginStorage()->value("connected").toBool());
|
|
} else if (thing->thingClassId() == rollershutterThingClassId) {
|
|
thing->setStateValue(rollershutterConnectedStateTypeId, pluginStorage()->value("connected").toBool());
|
|
} else if (thing->thingClassId() == venetianblindThingClassId) {
|
|
thing->setStateValue(venetianblindConnectedStateTypeId, pluginStorage()->value("connected").toBool());
|
|
}
|
|
}
|
|
pluginStorage()->endGroup();
|
|
foreach (Thing *child, myThings().filterByParentId(thing->id())) {
|
|
restoreChildConnectedState(child);
|
|
}
|
|
}
|