/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 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 "integrationpluginsomfytahoma.h" #include #include #include #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 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 if (type == QStringLiteral("GarageDoor")) { Thing *thing = myThings().findByParams(ParamList() << Param(garagedoorThingDeviceUrlParamTypeId, deviceUrl)); if (thing) { qCDebug(dcSomfyTahoma()) << "Found existing garage door:" << label << deviceUrl; } else { qCInfo(dcSomfyTahoma()) << "Found new garage door:" << label << deviceUrl; ThingDescriptor descriptor(garagedoorThingClassId, label, QString(), accountId); descriptor.setParams(ParamList() << Param(garagedoorThingDeviceUrlParamTypeId, 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->thing()->thingClassId() == garagedoorThingClassId) { 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()); } else if (thing->thingClassId() == garagedoorThingClassId) { deviceUrl = QUrl(thing->paramValue(garagedoorThingDeviceUrlParamTypeId).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 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); } thing = myThings().findByParams(ParamList() << Param(garagedoorThingDeviceUrlParamTypeId, action.toMap()["deviceURL"])); if (thing) { thing->setStateValue(garagedoorMovingStateTypeId, 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 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); } else if (thing->thingClassId() == garagedoorThingClassId) { thing->setStateValue(garagedoorMovingStateTypeId, false); } } QPointer 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; } thing = myThings().findByParams(ParamList() << Param(garagedoorThingDeviceUrlParamTypeId, deviceUrl)); if (thing) { foreach (const QVariant &stateVariant, stateList) { QVariantMap stateMap = stateVariant.toMap(); if (stateMap["name"] == "core:ClosureState") { thing->setStateValue(garagedoorPercentageStateTypeId, stateMap["value"]); if (stateMap["value"] == 100) { thing->setStateValue(garagedoorStateStateTypeId, "closed"); } else if (stateMap["value"] == 0) { thing->setStateValue(garagedoorStateStateTypeId, "open"); } else { thing->setStateValue(garagedoorStateStateTypeId, "intermediate"); } } else if (stateMap["name"] == "core:StatusState") { thing->setStateValue(garagedoorConnectedStateTypeId, stateMap["value"] == "available"); pluginStorage()->beginGroup(thing->id().toString()); pluginStorage()->setValue("connected", stateMap["value"] == "available"); pluginStorage()->endGroup(); } else if (stateMap["name"] == "core:RSSILevelState") { thing->setStateValue(garagedoorSignalStrengthStateTypeId, 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"; } } else if (info->thing()->thingClassId() == garagedoorThingClassId) { deviceUrl = info->thing()->paramValue(garagedoorThingDeviceUrlParamTypeId).toString(); if (info->action().actionTypeId() == garagedoorPercentageActionTypeId) { actionName = "setClosure"; actionParameters = { info->action().param(garagedoorPercentageActionPercentageParamTypeId).value().toInt() }; } else if (info->action().actionTypeId() == garagedoorOpenActionTypeId) { actionName = "open"; } else if (info->action().actionTypeId() == garagedoorCloseActionTypeId) { actionName = "close"; } else if (info->action().actionTypeId() == garagedoorStopActionTypeId) { 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); } else if (thing->thingClassId() == garagedoorThingClassId) { thing->setStateValue(garagedoorConnectedStateTypeId, 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()); } else if (thing->thingClassId() == garagedoorThingClassId) { thing->setStateValue(garagedoorConnectedStateTypeId, pluginStorage()->value("connected").toBool()); } } pluginStorage()->endGroup(); foreach (Thing *child, myThings().filterByParentId(thing->id())) { restoreChildConnectedState(child); } }