diff --git a/common/zigbeeintegrationplugin.cpp b/common/zigbeeintegrationplugin.cpp index 51a8f78..ba8c44d 100644 --- a/common/zigbeeintegrationplugin.cpp +++ b/common/zigbeeintegrationplugin.cpp @@ -51,6 +51,7 @@ #include #include #include +#include #include #include @@ -83,10 +84,16 @@ void ZigbeeIntegrationPlugin::handleRemoveNode(ZigbeeNode *node, const QUuid &ne { Q_UNUSED(networkUuid) foreach (Thing *thing, m_thingNodes.keys(node)) { - emit autoThingDisappeared(thing->id()); - // Removing it from our map to prevent a loop that would ask the zigbee network to remove this node (see thingRemoved()) - m_thingNodes.remove(thing); + // Not removing thing as this destroys magic too easily if a device decides to leave and rejoin for whatever reason + // While in theory that's not a use case, in practice it turns out that this is a common thing to do when fiddling with + // ZigBee network instabilites. Instead we're just marking things as disconnected. + +// emit autoThingDisappeared(thing->id()); +// // Removing it from our map to prevent a loop that would ask the zigbee network to remove this node (see thingRemoved()) +// m_thingNodes.remove(thing); + + thing->setStateValue("connected", false); } } @@ -113,39 +120,7 @@ ZigbeeNode* ZigbeeIntegrationPlugin::manageNode(Thing *thing) return nullptr; } - m_thingNodes.insert(thing, node); - - // Update connected state - thing->setStateValue("connected", node->reachable()); - connect(node, &ZigbeeNode::reachableChanged, thing, [thing](bool reachable){ - thing->setStateValue("connected", reachable); - }); - - // Update signal strength - thing->setStateValue("signalStrength", qRound(node->lqi() * 100.0 / 255.0)); - connect(node, &ZigbeeNode::lqiChanged, thing, [thing](quint8 lqi){ - uint signalStrength = qRound(lqi * 100.0 / 255.0); - thing->setStateValue("signalStrength", signalStrength); - }); - - connect(node, &ZigbeeNode::lastSeenChanged, this, [=](){ - while (!m_delayedWriteRequests.value(node).isEmpty()) { - DelayedAttributeWriteRequest request = m_delayedWriteRequests[node].takeFirst(); - ZigbeeClusterReply *reply = request.cluster->writeAttributes(request.records, request.manufacturerCode); - connect(reply, &ZigbeeClusterReply::finished, this, [=](){ - if (reply->error() != ZigbeeClusterReply::ErrorNoError) { - qCWarning(m_dc) << "Error writing attributes on" << thing->name(); - } - }); - } - while (!m_delayedReadRequests.value(node).isEmpty()) { - DelayedAttributeReadRequest request = m_delayedReadRequests[node].takeFirst(); - ZigbeeClusterReply *reply = request.cluster->readAttributes(request.attributes, request.manufacturerCode); - if (reply->error() != ZigbeeClusterReply::ErrorNoError) { - qCWarning(m_dc) << "Error writing attributes on" << thing->name(); - } - } - }); + setupNode(node, thing); return node; } @@ -160,7 +135,7 @@ ZigbeeNode *ZigbeeIntegrationPlugin::nodeForThing(Thing *thing) return m_thingNodes.value(thing); } -void ZigbeeIntegrationPlugin::createThing(const ThingClassId &thingClassId, ZigbeeNode *node, const ParamList &additionalParams) +Thing *ZigbeeIntegrationPlugin::createThing(const ThingClassId &thingClassId, ZigbeeNode *node, const ParamList &additionalParams) { ThingDescriptor descriptor(thingClassId); QString deviceClassName = supportedThings().findById(thingClassId).displayName(); @@ -172,7 +147,15 @@ void ZigbeeIntegrationPlugin::createThing(const ThingClassId &thingClassId, Zigb params.append(Param(tc.paramTypes().findByName("ieeeAddress").id(), node->extendedAddress().toString())); params.append(additionalParams); descriptor.setParams(params); - emit autoThingsAppeared({descriptor}); + + Thing *existingThing = myThings().findByParams(params); + if (!existingThing) { + emit autoThingsAppeared({descriptor}); + } else { + qCInfo(m_dc) << "Thing for node" << node << "already existing. Not recreating."; + setupNode(node, existingThing); + } + return existingThing; } void ZigbeeIntegrationPlugin::bindCluster(ZigbeeNodeEndpoint *endpoint, ZigbeeClusterLibrary::ClusterId clusterId, int retries) @@ -605,6 +588,27 @@ void ZigbeeIntegrationPlugin::configureWindowCoveringInputClusterLiftPercentageA }); } +void ZigbeeIntegrationPlugin::configureDoorLockInputClusterAttributeReporting(ZigbeeNodeEndpoint *endpoint) +{ + ZigbeeClusterLibrary::AttributeReportingConfiguration reportingConfig; + reportingConfig.attributeId = ZigbeeClusterDoorLock::AttributeLockState; + reportingConfig.dataType = Zigbee::Enum8; + reportingConfig.minReportingInterval = 60; + reportingConfig.maxReportingInterval = 120; + reportingConfig.reportableChange = ZigbeeDataType(static_cast(1)).data(); + + qCDebug(m_dc()) << "Configuring attribute reporting for door lock cluster lock state"; + ZigbeeClusterReply *reportingReply = endpoint->getInputCluster(ZigbeeClusterLibrary::ClusterIdDoorLock)->configureReporting({reportingConfig}); + connect(reportingReply, &ZigbeeClusterReply::finished, this, [=](){ + if (reportingReply->error() != ZigbeeClusterReply::ErrorNoError) { + qCWarning(m_dc()) << "Failed to door lock cluster door state attribute reporting" << reportingReply->error(); + } else { + qCDebug(m_dc()) << "Attribute reporting configuration finished for door lock cluster lock state" << ZigbeeClusterLibrary::parseAttributeReportingStatusRecords(reportingReply->responseFrame().payload); + } + }); + +} + void ZigbeeIntegrationPlugin::connectToPowerConfigurationInputCluster(Thing *thing, ZigbeeNodeEndpoint *endpoint, qreal maxVoltage, qreal minVoltage) { ZigbeeClusterPowerConfiguration *powerCluster = endpoint->inputCluster(ZigbeeClusterLibrary::ClusterIdPowerConfiguration); @@ -1083,6 +1087,10 @@ void ZigbeeIntegrationPlugin::connectToOtaOutputCluster(Thing *thing, ZigbeeNode otaCluster->setProperty("lastFirmwareCheck", QDateTime::currentDateTime()); ZigbeeNode *node = nodeForThing(thing); + if (!node) { + qCWarning(m_dc) << "Node for thing" << thing << "not found. Cannot continue with OTA"; + return; + } FirmwareIndexEntry newInfo = checkFirmwareAvailability(m_firmwareIndex, manufacturerCode, imageType, currentFileVersion, node->modelName()); ZigbeeClusterOta::FileVersion currentParsed = ZigbeeClusterOta::parseFileVersion(currentFileVersion); thing->setStateValue("currentVersion", QString("%0.%1.%2.%3") @@ -1664,6 +1672,48 @@ void ZigbeeIntegrationPlugin::updateFirmwareIndex() }); } +void ZigbeeIntegrationPlugin::setupNode(ZigbeeNode *node, Thing *thing) +{ + m_thingNodes.insert(thing, node); + + // Delaying the connection setup to only set state values after the thing setup finished + QTimer::singleShot(0, thing, [=](){ + // Update connected state + thing->setStateValue("connected", node->reachable()); + connect(node, &ZigbeeNode::reachableChanged, thing, [thing](bool reachable){ + thing->setStateValue("connected", reachable); + }); + + // Update signal strength + thing->setStateValue("signalStrength", qRound(node->lqi() * 100.0 / 255.0)); + connect(node, &ZigbeeNode::lqiChanged, thing, [thing](quint8 lqi){ + uint signalStrength = qRound(lqi * 100.0 / 255.0); + thing->setStateValue("signalStrength", signalStrength); + }); + + connect(node, &ZigbeeNode::lastSeenChanged, this, [=](){ + while (!m_delayedWriteRequests.value(node).isEmpty()) { + DelayedAttributeWriteRequest request = m_delayedWriteRequests[node].takeFirst(); + ZigbeeClusterReply *reply = request.cluster->writeAttributes(request.records, request.manufacturerCode); + connect(reply, &ZigbeeClusterReply::finished, this, [=](){ + if (reply->error() != ZigbeeClusterReply::ErrorNoError) { + qCWarning(m_dc) << "Error writing attributes on" << thing->name(); + } + }); + } + while (!m_delayedReadRequests.value(node).isEmpty()) { + DelayedAttributeReadRequest request = m_delayedReadRequests[node].takeFirst(); + ZigbeeClusterReply *reply = request.cluster->readAttributes(request.attributes, request.manufacturerCode); + if (reply->error() != ZigbeeClusterReply::ErrorNoError) { + qCWarning(m_dc) << "Error writing attributes on" << thing->name(); + } + } + }); + + createConnections(thing); + }); +} + ZigbeeIntegrationPlugin::FirmwareIndexEntry ZigbeeIntegrationPlugin::firmwareInfo(quint16 manufacturerId, quint16 imageType, quint32 fileVersion) const { foreach (const FirmwareIndexEntry &entry, m_firmwareIndex) { diff --git a/common/zigbeeintegrationplugin.h b/common/zigbeeintegrationplugin.h index b3f856f..926f4a3 100644 --- a/common/zigbeeintegrationplugin.h +++ b/common/zigbeeintegrationplugin.h @@ -73,7 +73,9 @@ class ZigbeeIntegrationPlugin: public IntegrationPlugin, public ZigbeeHandler Thing *thingForNode(ZigbeeNode *node); ZigbeeNode *nodeForThing(Thing *thing); - virtual void createThing(const ThingClassId &thingClassId, ZigbeeNode *node, const ParamList &additionalParams = ParamList()); + virtual Thing *createThing(const ThingClassId &thingClassId, ZigbeeNode *node, const ParamList &additionalParams = ParamList()); + virtual void createConnections(Thing *thing) = 0; + void bindCluster(ZigbeeNodeEndpoint *endpoint, ZigbeeClusterLibrary::ClusterId clusterId, int retries = 3); @@ -94,6 +96,7 @@ class ZigbeeIntegrationPlugin: public IntegrationPlugin, public ZigbeeHandler void configureFanControlInputClusterAttributeReporting(ZigbeeNodeEndpoint *endpoint); void configureIasZoneInputClusterAttributeReporting(ZigbeeNodeEndpoint *endpoint); void configureWindowCoveringInputClusterLiftPercentageAttributeReporting(ZigbeeNodeEndpoint *endpoint); + void configureDoorLockInputClusterAttributeReporting(ZigbeeNodeEndpoint *endpoint); void connectToPowerConfigurationInputCluster(Thing *thing, ZigbeeNodeEndpoint *endpoint, qreal maxVoltage = 0, qreal minVoltage = 0); void connectToThermostatCluster(Thing *thing, ZigbeeNodeEndpoint *endpoint); @@ -145,6 +148,7 @@ private slots: virtual void updateFirmwareIndex(); private: + void setupNode(ZigbeeNode *node, Thing *thing); FirmwareIndexEntry firmwareInfo(quint16 manufacturerId, quint16 imageType, quint32 fileVersion) const; QString firmwareFileName(const FirmwareIndexEntry &info) const; FetchFirmwareReply *fetchFirmware(const FirmwareIndexEntry &info); diff --git a/zigbeedevelco/integrationpluginzigbeedevelco.cpp b/zigbeedevelco/integrationpluginzigbeedevelco.cpp index 8545cc4..cd80e20 100644 --- a/zigbeedevelco/integrationpluginzigbeedevelco.cpp +++ b/zigbeedevelco/integrationpluginzigbeedevelco.cpp @@ -173,14 +173,23 @@ void IntegrationPluginZigbeeDevelco::setupThing(ThingSetupInfo *info) info->finish(Thing::ThingErrorHardwareNotAvailable); return; } + + info->finish(Thing::ThingErrorNoError); +} + +void IntegrationPluginZigbeeDevelco::createConnections(Thing *thing) +{ ZigbeeNode *node = nodeForThing(thing); + if (!node) { + qCWarning(dcZigbeeDevelco()) << "Node for thing" << thing << "not found."; + return; + } if (thing->thingClassId() == ioModuleThingClassId) { // Set the version from the manufacturer specific attribute in base cluster ZigbeeNodeEndpoint *primaryEndpoint = node->getEndpoint(IO_MODULE_EP_INPUT1); if (!primaryEndpoint) { qCWarning(dcZigbeeDevelco()) << "Failed to set up IO module" << thing << ". Could not find endpoint for version parsing."; - info->finish(Thing::ThingErrorSetupFailed); return; } @@ -326,7 +335,6 @@ void IntegrationPluginZigbeeDevelco::setupThing(ThingSetupInfo *info) ZigbeeNodeEndpoint *sensorEndpoint = node->getEndpoint(DEVELCO_EP_TEMPERATURE_SENSOR); if (!sensorEndpoint) { qCWarning(dcZigbeeDevelco()) << "Failed to set up air quality sensor" << thing << ". Could not find endpoint for version parsing."; - info->finish(Thing::ThingErrorSetupFailed); return; } @@ -430,14 +438,13 @@ void IntegrationPluginZigbeeDevelco::setupThing(ThingSetupInfo *info) connectToTemperatureMeasurementInputCluster(thing, temperatureSensorEndpoint); connectToIlluminanceMeasurementInputCluster(thing, illuminanceSensorEndpoint); } - - info->finish(Thing::ThingErrorNoError); } void IntegrationPluginZigbeeDevelco::postSetupThing(Thing *thing) { if (thing->thingClassId() == ioModuleThingClassId) { - if (nodeForThing(thing)->reachable()) { + ZigbeeNode *node = nodeForThing(thing); + if (node && node->reachable()) { readIoModuleOutputPowerStates(thing); readIoModuleInputPowerStates(thing); } @@ -453,6 +460,11 @@ void IntegrationPluginZigbeeDevelco::executeAction(ThingActionInfo *info) Thing *thing = info->thing(); ZigbeeNode *node = nodeForThing(info->thing()); + if (!node) { + qCWarning(dcZigbeeDevelco()) << "Node for thing" << thing << "not found."; + info->finish(Thing::ThingErrorHardwareNotAvailable); + return; + } if (thing->thingClassId() == ioModuleThingClassId) { // Identify diff --git a/zigbeedevelco/integrationpluginzigbeedevelco.h b/zigbeedevelco/integrationpluginzigbeedevelco.h index 982ed30..1fc6924 100644 --- a/zigbeedevelco/integrationpluginzigbeedevelco.h +++ b/zigbeedevelco/integrationpluginzigbeedevelco.h @@ -100,6 +100,8 @@ class IntegrationPluginZigbeeDevelco: public ZigbeeIntegrationPlugin void executeAction(ThingActionInfo *info) override; private: + void createConnections(Thing *thing) override; + QString parseDevelcoVersionString(ZigbeeNodeEndpoint *endpoint); void initIoModule(ZigbeeNode *node); diff --git a/zigbeeeurotronic/integrationpluginzigbeeeurotronic.cpp b/zigbeeeurotronic/integrationpluginzigbeeeurotronic.cpp index 1cb886b..51ab26f 100644 --- a/zigbeeeurotronic/integrationpluginzigbeeeurotronic.cpp +++ b/zigbeeeurotronic/integrationpluginzigbeeeurotronic.cpp @@ -79,7 +79,17 @@ void IntegrationPluginZigbeeEurotronic::setupThing(ThingSetupInfo *info) return; } + info->finish(Thing::ThingErrorNoError); +} + +void IntegrationPluginZigbeeEurotronic::createConnections(Thing *thing) +{ ZigbeeNode *node = nodeForThing(thing); + if (!node) { + qCWarning(dcZigbeeEurotronic()) << "Node for thing" << thing << "not found."; + return; + } + ZigbeeNodeEndpoint *endpoint = node->getEndpoint(0x01); thing->setStateValue("currentVersion", endpoint->deviceVersion()); @@ -89,7 +99,6 @@ void IntegrationPluginZigbeeEurotronic::setupThing(ThingSetupInfo *info) ZigbeeClusterThermostat *thermostatCluster = endpoint->inputCluster(ZigbeeClusterLibrary::ClusterIdThermostat); if (!thermostatCluster) { qCWarning(dcZigbeeEurotronic()) << "Failed to read thermostat cluster"; - info->finish(Thing::ThingErrorHardwareFailure); return; } @@ -107,8 +116,6 @@ void IntegrationPluginZigbeeEurotronic::setupThing(ThingSetupInfo *info) } }); thermostatCluster->readAttributes({0x4008}, 0x1037); - - info->finish(Thing::ThingErrorNoError); } void IntegrationPluginZigbeeEurotronic::executeAction(ThingActionInfo *info) @@ -143,6 +150,11 @@ void IntegrationPluginZigbeeEurotronic::sendNextAction() } ZigbeeNode *node = nodeForThing(info->thing()); + if (!node) { + qCWarning(dcZigbeeEurotronic()) << "Node for thing" << info->thing() << "not found."; + info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("ZigBee node not found in network.")); + return; + } ZigbeeNodeEndpoint *endpoint = node->getEndpoint(0x01); ZigbeeClusterThermostat *thermostatCluster = endpoint->inputCluster(ZigbeeClusterLibrary::ClusterIdThermostat); diff --git a/zigbeeeurotronic/integrationpluginzigbeeeurotronic.h b/zigbeeeurotronic/integrationpluginzigbeeeurotronic.h index b0b6746..3027215 100644 --- a/zigbeeeurotronic/integrationpluginzigbeeeurotronic.h +++ b/zigbeeeurotronic/integrationpluginzigbeeeurotronic.h @@ -64,6 +64,8 @@ class IntegrationPluginZigbeeEurotronic: public ZigbeeIntegrationPlugin void executeAction(ThingActionInfo *info) override; private: + void createConnections(Thing *thing) override; + void sendNextAction(); private: diff --git a/zigbeegeneric/integrationpluginzigbeegeneric.cpp b/zigbeegeneric/integrationpluginzigbeegeneric.cpp index e942ab1..8fae132 100644 --- a/zigbeegeneric/integrationpluginzigbeegeneric.cpp +++ b/zigbeegeneric/integrationpluginzigbeegeneric.cpp @@ -64,8 +64,8 @@ bool IntegrationPluginZigbeeGeneric::handleNode(ZigbeeNode *node, const QUuid &/ endpoint->deviceId() == Zigbee::HomeAutomationDeviceOnOffLight)) { qCDebug(dcZigbeeGeneric()) << "Handling on/off light for" << node << endpoint; - createThing(onOffLightThingClassId, node, endpoint->endpointId()); configureOnOffInputClusterAttributeReporting(endpoint); + createThing(onOffLightThingClassId, node, endpoint->endpointId()); handled = true; } @@ -76,10 +76,10 @@ bool IntegrationPluginZigbeeGeneric::handleNode(ZigbeeNode *node, const QUuid &/ endpoint->deviceId() == Zigbee::HomeAutomationDeviceDimmableLight)) { qCDebug(dcZigbeeGeneric()) << "Handling dimmable light for" << node << endpoint; - createThing(dimmableLightThingClassId, node, endpoint->endpointId()); configureOnOffInputClusterAttributeReporting(endpoint); bindCluster(endpoint, ZigbeeClusterLibrary::ClusterIdLevelControl); configureLevelControlInputClusterAttributeReporting(endpoint); + createThing(dimmableLightThingClassId, node, endpoint->endpointId()); handled = true; } @@ -90,10 +90,10 @@ bool IntegrationPluginZigbeeGeneric::handleNode(ZigbeeNode *node, const QUuid &/ endpoint->deviceId() == Zigbee::HomeAutomationDeviceColourTemperatureLight)) { qCDebug(dcZigbeeGeneric()) << "Handling color temperature light for" << node << endpoint; - createThing(colorTemperatureLightThingClassId, node, endpoint->endpointId()); configureOnOffInputClusterAttributeReporting(endpoint); bindCluster(endpoint, ZigbeeClusterLibrary::ClusterIdLevelControl); configureLevelControlInputClusterAttributeReporting(endpoint); + createThing(colorTemperatureLightThingClassId, node, endpoint->endpointId()); handled = true; } @@ -104,10 +104,10 @@ bool IntegrationPluginZigbeeGeneric::handleNode(ZigbeeNode *node, const QUuid &/ (endpoint->profile() == Zigbee::ZigbeeProfileHomeAutomation && endpoint->deviceId() == Zigbee::HomeAutomationDeviceDimmableColorLight)) { qCDebug(dcZigbeeGeneric()) << "Handling color light for" << node << endpoint; - createThing(colorLightThingClassId, node, endpoint->endpointId()); configureOnOffInputClusterAttributeReporting(endpoint); bindCluster(endpoint, ZigbeeClusterLibrary::ClusterIdLevelControl); configureLevelControlInputClusterAttributeReporting(endpoint); + createThing(colorLightThingClassId, node, endpoint->endpointId()); handled = true; } @@ -115,11 +115,11 @@ bool IntegrationPluginZigbeeGeneric::handleNode(ZigbeeNode *node, const QUuid &/ if (endpoint->profile() == Zigbee::ZigbeeProfile::ZigbeeProfileHomeAutomation && endpoint->deviceId() == Zigbee::HomeAutomationDeviceThermostat) { qCDebug(dcZigbeeGeneric()) << "Handling thermostat endpoint for" << node << endpoint; - createThing(thermostatThingClassId, node, endpoint->endpointId()); bindCluster(endpoint, ZigbeeClusterLibrary::ClusterIdPowerConfiguration); configurePowerConfigurationInputClusterAttributeReporting(endpoint); bindCluster(endpoint, ZigbeeClusterLibrary::ClusterIdThermostat); configureThermostatClusterAttributeReporting(endpoint); + createThing(thermostatThingClassId, node, endpoint->endpointId()); handled = true; } @@ -133,19 +133,20 @@ bool IntegrationPluginZigbeeGeneric::handleNode(ZigbeeNode *node, const QUuid &/ // Simple on/off device if (endpoint->hasInputCluster(ZigbeeClusterLibrary::ClusterIdOnOff)) { + bindCluster(endpoint, ZigbeeClusterLibrary::ClusterIdOnOff); + configureOnOffInputClusterAttributeReporting(endpoint); + if (endpoint->hasInputCluster(ZigbeeClusterLibrary::ClusterIdMetering)) { qCDebug(dcZigbeeGeneric()) << "Handling power socket with energy metering for" << node << endpoint; - createThing(powerMeterSocketThingClassId, node, endpoint->endpointId()); bindCluster(endpoint, ZigbeeClusterLibrary::ClusterIdMetering); configureMeteringInputClusterAttributeReporting(endpoint); + createThing(powerMeterSocketThingClassId, node, endpoint->endpointId()); } else { qCDebug(dcZigbeeGeneric()) << "Handling power socket endpoint for" << node << endpoint; createThing(powerSocketThingClassId, node, endpoint->endpointId()); } - bindCluster(endpoint, ZigbeeClusterLibrary::ClusterIdOnOff); - configureOnOffInputClusterAttributeReporting(endpoint); handled = true; } } @@ -157,9 +158,15 @@ bool IntegrationPluginZigbeeGeneric::handleNode(ZigbeeNode *node, const QUuid &/ qCWarning(dcZigbeeGeneric()) << "Endpoint claims to be a door lock, but the appropriate input clusters could not be found" << node << endpoint; } else { qCDebug(dcZigbeeGeneric()) << "Handling door lock endpoint for" << node << endpoint; + + bindCluster(endpoint, ZigbeeClusterLibrary::ClusterIdPowerConfiguration); + configurePowerConfigurationInputClusterAttributeReporting(endpoint); + + bindCluster(endpoint, ZigbeeClusterLibrary::ClusterIdDoorLock); + configureDoorLockInputClusterAttributeReporting(endpoint); + createThing(doorLockThingClassId, node, endpoint->endpointId()); - // Initialize bindings and cluster attributes - initDoorLock(node, endpoint); + handled = true; } } @@ -265,14 +272,17 @@ void IntegrationPluginZigbeeGeneric::setupThing(ThingSetupInfo *info) return; } + info->finish(Thing::ThingErrorNoError); +} + +void IntegrationPluginZigbeeGeneric::createConnections(Thing *thing) +{ ZigbeeNodeEndpoint *endpoint = findEndpoint(thing); if (!endpoint) { qCWarning(dcZigbeeGeneric()) << "Could not find endpoint for" << thing; - info->finish(Thing::ThingErrorSetupFailed); return; } - // Set the version thing->setStateValue("version", endpoint->softwareBuildId()); @@ -387,8 +397,6 @@ void IntegrationPluginZigbeeGeneric::setupThing(ThingSetupInfo *info) connectToOnOffOutputCluster(thing, endpoint); connectToLevelControlOutputCluster(thing, endpoint); } - - info->finish(Thing::ThingErrorNoError); } void IntegrationPluginZigbeeGeneric::executeAction(ThingActionInfo *info) @@ -401,7 +409,8 @@ void IntegrationPluginZigbeeGeneric::executeAction(ThingActionInfo *info) // Get the node Thing *thing = info->thing(); ZigbeeNode *node = nodeForThing(info->thing()); - if (!node->reachable()) { + if (!node || !node->reachable()) { + qCWarning(dcZigbeeGeneric()) << "Node for thing" << thing << "not found."; info->finish(Thing::ThingErrorHardwareNotAvailable); return; } @@ -602,81 +611,3 @@ ZigbeeNodeEndpoint *IntegrationPluginZigbeeGeneric::findEndpoint(Thing *thing) quint8 endpointId = thing->paramValue("endpointId").toUInt(); return node->getEndpoint(endpointId); } - -void IntegrationPluginZigbeeGeneric::initSimplePowerSocket(ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint) -{ - // Get the on/off server cluster from the endpoint - ZigbeeClusterOnOff *onOffCluster = endpoint->inputCluster(ZigbeeClusterLibrary::ClusterIdOnOff); - if (!onOffCluster) - return; - - qCDebug(dcZigbeeGeneric()) << "Reading on/off power value for" << node << endpoint; - ZigbeeClusterReply *reply = onOffCluster->readAttributes({ZigbeeClusterOnOff::AttributeOnOff}); - connect(reply, &ZigbeeClusterReply::finished, node, [=](){ - if (reply->error() != ZigbeeClusterReply::ErrorNoError) { - qCWarning(dcZigbeeGeneric()) << "Failed to read on/off cluster attribute from" << node << endpoint << reply->error(); - return; - } - }); - - ZigbeeDeviceObjectReply *bindPowerReply = node->deviceObject()->requestBindIeeeAddress(endpoint->endpointId(), ZigbeeClusterLibrary::ClusterIdOnOff, - hardwareManager()->zigbeeResource()->coordinatorAddress(node->networkUuid()), 0x01); - connect(bindPowerReply, &ZigbeeDeviceObjectReply::finished, node, [=](){ - if (bindPowerReply->error() != ZigbeeDeviceObjectReply::ErrorNoError) { - qCWarning(dcZigbeeGeneric()) << "Failed to bind power configuration cluster" << bindPowerReply->error(); - } else { - qCDebug(dcZigbeeGeneric()) << "Binding power configuration cluster finished successfully"; - } - - ZigbeeClusterLibrary::AttributeReportingConfiguration batteryPercentageConfig; - batteryPercentageConfig.attributeId = ZigbeeClusterOnOff::AttributeOnOff; - batteryPercentageConfig.dataType = Zigbee::Uint8; - batteryPercentageConfig.minReportingInterval = 60; - batteryPercentageConfig.maxReportingInterval = 120; - batteryPercentageConfig.reportableChange = ZigbeeDataType(static_cast(1)).data(); - - qCDebug(dcZigbeeGeneric()) << "Configuring attribute reporting for OnOff cluster"; - ZigbeeClusterReply *reportingReply = onOffCluster->configureReporting({batteryPercentageConfig}); - connect(reportingReply, &ZigbeeClusterReply::finished, this, [=](){ - if (reportingReply->error() != ZigbeeClusterReply::ErrorNoError) { - qCWarning(dcZigbeeGeneric()) << "Failed to configure OnOff cluster attribute reporting" << reportingReply->error(); - } else { - qCDebug(dcZigbeeGeneric()) << "Attribute reporting configuration finished for OnOff cluster" << ZigbeeClusterLibrary::parseAttributeReportingStatusRecords(reportingReply->responseFrame().payload); - } - }); - }); -} - -void IntegrationPluginZigbeeGeneric::initDoorLock(ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint) -{ - bindCluster(endpoint, ZigbeeClusterLibrary::ClusterIdPowerConfiguration); - configurePowerConfigurationInputClusterAttributeReporting(endpoint); - - qCDebug(dcZigbeeGeneric()) << "Binding door lock cluster "; - ZigbeeDeviceObjectReply * zdoReply = node->deviceObject()->requestBindIeeeAddress(endpoint->endpointId(), ZigbeeClusterLibrary::ClusterIdDoorLock, hardwareManager()->zigbeeResource()->coordinatorAddress(node->networkUuid()), 0x01); - connect(zdoReply, &ZigbeeDeviceObjectReply::finished, node, [=](){ - if (zdoReply->error() != ZigbeeDeviceObjectReply::ErrorNoError) { - qCWarning(dcZigbeeGeneric()) << "Failed to door lock cluster to coordinator" << zdoReply->error(); - } else { - qCDebug(dcZigbeeGeneric()) << "Bind door lock cluster to coordinator finished successfully"; - } - - // Configure attribute reporting for lock state - ZigbeeClusterLibrary::AttributeReportingConfiguration reportingConfig; - reportingConfig.attributeId = ZigbeeClusterDoorLock::AttributeLockState; - reportingConfig.dataType = Zigbee::Enum8; - reportingConfig.minReportingInterval = 60; - reportingConfig.maxReportingInterval = 120; - reportingConfig.reportableChange = ZigbeeDataType(static_cast(1)).data(); - - qCDebug(dcZigbeeGeneric()) << "Configure attribute reporting for door lock cluster to coordinator"; - ZigbeeClusterReply *reportingReply = endpoint->getInputCluster(ZigbeeClusterLibrary::ClusterIdDoorLock)->configureReporting({reportingConfig}); - connect(reportingReply, &ZigbeeClusterReply::finished, this, [=](){ - if (reportingReply->error() != ZigbeeClusterReply::ErrorNoError) { - qCWarning(dcZigbeeGeneric()) << "Failed to door lock cluster attribute reporting" << reportingReply->error(); - } else { - qCDebug(dcZigbeeGeneric()) << "Attribute reporting configuration finished for door lock cluster" << ZigbeeClusterLibrary::parseAttributeReportingStatusRecords(reportingReply->responseFrame().payload); - } - }); - }); -} diff --git a/zigbeegeneric/integrationpluginzigbeegeneric.h b/zigbeegeneric/integrationpluginzigbeegeneric.h index 2a86630..4df10fa 100644 --- a/zigbeegeneric/integrationpluginzigbeegeneric.h +++ b/zigbeegeneric/integrationpluginzigbeegeneric.h @@ -61,8 +61,8 @@ class IntegrationPluginZigbeeGeneric: public ZigbeeIntegrationPlugin private: ZigbeeNodeEndpoint *findEndpoint(Thing *thing); - void initSimplePowerSocket(ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint); - void initDoorLock(ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint); + void createConnections(Thing *thing) override; + }; #endif // INTEGRATIONPLUGINZIGBEEGENERIC_H diff --git a/zigbeegewiss/integrationpluginzigbeegewiss.cpp b/zigbeegewiss/integrationpluginzigbeegewiss.cpp index aa13e58..50aa5c2 100644 --- a/zigbeegewiss/integrationpluginzigbeegewiss.cpp +++ b/zigbeegewiss/integrationpluginzigbeegewiss.cpp @@ -110,8 +110,16 @@ void IntegrationPluginZigbeeGewiss::setupThing(ThingSetupInfo *info) return; } - ZigbeeNode *node = nodeForThing(thing); + info->finish(Thing::ThingErrorNoError); +} +void IntegrationPluginZigbeeGewiss::createConnections(Thing *thing) +{ + ZigbeeNode *node = nodeForThing(thing); + if (!node) { + qCWarning(dcZigbeeGewiss()) << "Node for thing" << thing << "not found."; + return; + } if (thing->thingClassId() == gwa1501BinaryInputThingClassId) { @@ -127,7 +135,6 @@ void IntegrationPluginZigbeeGewiss::setupThing(ThingSetupInfo *info) connectToOnOffOutputCluster(thing, endpoint1, "Toggle 1", "On 1", "Off 1", "input1"); connectToOnOffOutputCluster(thing, endpoint2, "Toggle 2", "On 2", "Off 2", "input2"); - info->finish(Thing::ThingErrorNoError); return; // Single channel relay @@ -151,11 +158,7 @@ void IntegrationPluginZigbeeGewiss::setupThing(ThingSetupInfo *info) thing->setStateValue(gwa1521ActuatorRelayStateTypeId, power); }); } - return info->finish(Thing::ThingErrorNoError); } - qCWarning(dcZigbeeGewiss()) << "Thing class not found" << info->thing()->thingClassId(); - Q_ASSERT_X(false, "ZigbeeGewiss", "Unhandled thing class"); - return info->finish(Thing::ThingErrorThingClassNotFound); } void IntegrationPluginZigbeeGewiss::executeAction(ThingActionInfo *info) @@ -168,7 +171,7 @@ void IntegrationPluginZigbeeGewiss::executeAction(ThingActionInfo *info) Thing *thing = info->thing(); ZigbeeNode *node = nodeForThing(info->thing()); - if (!node->reachable()) { + if (!node || !node->reachable()) { info->finish(Thing::ThingErrorHardwareNotAvailable); return; } diff --git a/zigbeegewiss/integrationpluginzigbeegewiss.h b/zigbeegewiss/integrationpluginzigbeegewiss.h index bdc6392..b0a4a93 100644 --- a/zigbeegewiss/integrationpluginzigbeegewiss.h +++ b/zigbeegewiss/integrationpluginzigbeegewiss.h @@ -54,6 +54,8 @@ class IntegrationPluginZigbeeGewiss: public ZigbeeIntegrationPlugin void executeAction(ThingActionInfo *info) override; private: + void createConnections(Thing *thing) override; + bool initTemperatureCluster(ZigbeeNode *node, Thing *thing); void connectToOnOffOutputCluster(Thing *thing, ZigbeeNodeEndpoint *endpoint, const QString &toggleButton, const QString &onButton, const QString &offButton, const QString &powerStateName); diff --git a/zigbeejung/integrationpluginzigbeejung.cpp b/zigbeejung/integrationpluginzigbeejung.cpp index 31566de..d38026c 100644 --- a/zigbeejung/integrationpluginzigbeejung.cpp +++ b/zigbeejung/integrationpluginzigbeejung.cpp @@ -84,7 +84,16 @@ void IntegrationPluginZigbeeJung::setupThing(ThingSetupInfo *info) return; } + info->finish(Thing::ThingErrorNoError); +} + +void IntegrationPluginZigbeeJung::createConnections(Thing *thing) +{ ZigbeeNode *node = nodeForThing(thing); + if (!node) { + qCWarning(dcZigbeeJung()) << "Node for thing" << thing << "not found."; + return; + } if (thing->thingClassId() == instaThingClassId) { ZigbeeNodeEndpoint *endpoint = node->getEndpoint(0x01); @@ -94,7 +103,6 @@ void IntegrationPluginZigbeeJung::setupThing(ThingSetupInfo *info) ZigbeeClusterScenes *scenesCluster = endpoint->outputCluster(ZigbeeClusterLibrary::ClusterIdScenes); if (!onOffCluster || !levelControlCluster || !scenesCluster) { qCWarning(dcZigbeeJung()) << "Could not find all of the needed clusters for" << thing->name() << "in" << node << "on endpoint" << endpoint->endpointId(); - info->finish(Thing::ThingErrorHardwareNotAvailable); return; } connect(onOffCluster, &ZigbeeClusterOnOff::commandReceived, this, [=](ZigbeeClusterOnOff::Command command, const QByteArray ¶meters){ @@ -132,16 +140,18 @@ void IntegrationPluginZigbeeJung::setupThing(ThingSetupInfo *info) connectToOtaOutputCluster(thing, endpoint); - info->finish(Thing::ThingErrorNoError); return; } - - info->finish(Thing::ThingErrorNoError); } void IntegrationPluginZigbeeJung::executeAction(ThingActionInfo *info) { ZigbeeNode *node = nodeForThing(info->thing()); + if (!node) { + qCWarning(dcZigbeeJung()) << "Node for thing" << info->thing() << "not found."; + info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("ZigBee node not found in network.")); + return; + } if (info->action().actionTypeId() == instaPerformUpdateActionTypeId) { enableFirmwareUpdate(info->thing()); diff --git a/zigbeejung/integrationpluginzigbeejung.h b/zigbeejung/integrationpluginzigbeejung.h index 3de1092..3f7ba7f 100644 --- a/zigbeejung/integrationpluginzigbeejung.h +++ b/zigbeejung/integrationpluginzigbeejung.h @@ -55,6 +55,10 @@ class IntegrationPluginZigbeeJung: public ZigbeeIntegrationPlugin void setupThing(ThingSetupInfo *info) override; void executeAction(ThingActionInfo *info) override; + +private: + void createConnections(Thing *thing) override; + }; #endif // INTEGRATIONPLUGINZIGBEEJUNG_H diff --git a/zigbeelumi/integrationpluginzigbeelumi.cpp b/zigbeelumi/integrationpluginzigbeelumi.cpp index 5553cc8..86f99d3 100644 --- a/zigbeelumi/integrationpluginzigbeelumi.cpp +++ b/zigbeelumi/integrationpluginzigbeelumi.cpp @@ -162,12 +162,19 @@ void IntegrationPluginZigbeeLumi::setupThing(ThingSetupInfo *info) return; } - ZigbeeNode *node = nodeForThing(thing); + info->finish(Thing::ThingErrorNoError); +} +void IntegrationPluginZigbeeLumi::createConnections(Thing *thing) +{ + ZigbeeNode *node = nodeForThing(thing); + if (!node) { + qCWarning(dcZigbeeLumi()) << "Unable to get ZigBee node for thing" << thing; + return; + } ZigbeeNodeEndpoint *endpoint = node->getEndpoint(0x01); if (!endpoint) { qCWarning(dcZigbeeLumi()) << "Zigbee endpoint 1 not found on" << thing; - info->finish(Thing::ThingErrorSetupFailed); return; } @@ -717,14 +724,17 @@ void IntegrationPluginZigbeeLumi::setupThing(ThingSetupInfo *info) qCWarning(dcZigbeeLumi()) << "Could not find endpoint 2 on" << thing << node; } } - - info->finish(Thing::ThingErrorNoError); } void IntegrationPluginZigbeeLumi::executeAction(ThingActionInfo *info) { Thing *thing = info->thing(); ZigbeeNode *node = nodeForThing(info->thing()); + if (!node) { + qCWarning(dcZigbeeLumi()) << "Node for thing" << thing << "not found."; + info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("ZigBee node not found in network.")); + return; + } if (thing->thingClassId() == lumiPowerSocketThingClassId) { ZigbeeNodeEndpoint *endpoint = node->getEndpoint(0x01); diff --git a/zigbeelumi/integrationpluginzigbeelumi.h b/zigbeelumi/integrationpluginzigbeelumi.h index 2398d72..7b14c89 100644 --- a/zigbeelumi/integrationpluginzigbeelumi.h +++ b/zigbeelumi/integrationpluginzigbeelumi.h @@ -57,6 +57,8 @@ class IntegrationPluginZigbeeLumi: public ZigbeeIntegrationPlugin void executeAction(ThingActionInfo *info) override; private: + void createConnections(Thing *thing) override; + QHash m_knownLumiDevices; }; diff --git a/zigbeeosram/integrationpluginzigbeeosram.cpp b/zigbeeosram/integrationpluginzigbeeosram.cpp index 3874231..26c7888 100644 --- a/zigbeeosram/integrationpluginzigbeeosram.cpp +++ b/zigbeeosram/integrationpluginzigbeeosram.cpp @@ -95,6 +95,16 @@ void IntegrationPluginZigbeeOsram::setupThing(ThingSetupInfo *info) return; } + info->finish(Thing::ThingErrorNoError); +} + +void IntegrationPluginZigbeeOsram::createConnections(Thing *thing) +{ + ZigbeeNode *node = nodeForThing(thing); + if (!node) { + qCWarning(dcZigbeeOsram()) << "Failed to claim node during setup."; + return; + } if (thing->thingClassId() == switchMiniThingClassId) { ZigbeeNodeEndpoint *ep1 = node->getEndpoint(1), @@ -200,10 +210,7 @@ void IntegrationPluginZigbeeOsram::setupThing(ThingSetupInfo *info) } }); - } - - info->finish(Thing::ThingErrorNoError); } void IntegrationPluginZigbeeOsram::executeAction(ThingActionInfo *info) diff --git a/zigbeeosram/integrationpluginzigbeeosram.h b/zigbeeosram/integrationpluginzigbeeosram.h index 83a9695..fab7e17 100644 --- a/zigbeeosram/integrationpluginzigbeeosram.h +++ b/zigbeeosram/integrationpluginzigbeeosram.h @@ -56,6 +56,8 @@ class IntegrationPluginZigbeeOsram: public ZigbeeIntegrationPlugin void executeAction(ThingActionInfo *info) override; private: + void createConnections(Thing *thing) override; + bool deduplicate(Thing *thing, quint8 transactionSequenceNumber); QHash m_transactionSequenceNumbers; diff --git a/zigbeephilipshue/integrationpluginzigbeephilipshue.cpp b/zigbeephilipshue/integrationpluginzigbeephilipshue.cpp index 1725492..5acdb5d 100644 --- a/zigbeephilipshue/integrationpluginzigbeephilipshue.cpp +++ b/zigbeephilipshue/integrationpluginzigbeephilipshue.cpp @@ -189,7 +189,17 @@ void IntegrationPluginZigbeePhilipsHue::setupThing(ThingSetupInfo *info) return; } + info->finish(Thing::ThingErrorNoError); +} + +void IntegrationPluginZigbeePhilipsHue::createConnections(Thing *thing) +{ + ZigbeeNode *node = nodeForThing(thing); + if (!node) { + qCWarning(dcZigbeePhilipsHue()) << "Node for thing" << thing << "not found."; + return; + } if (thing->thingClassId() == dimmableLightThingClassId @@ -474,13 +484,16 @@ void IntegrationPluginZigbeePhilipsHue::setupThing(ThingSetupInfo *info) } }); } - - info->finish(Thing::ThingErrorNoError); } void IntegrationPluginZigbeePhilipsHue::executeAction(ThingActionInfo *info) { ZigbeeNode *node = nodeForThing(info->thing()); + if (!node) { + qCWarning(dcZigbeePhilipsHue()) << "Node for thing" << info->thing() << "not found."; + info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("ZigBee node not found in network.")); + return; + } ActionType actionType = info->thing()->thingClass().actionTypes().findById(info->action().actionTypeId()); diff --git a/zigbeephilipshue/integrationpluginzigbeephilipshue.h b/zigbeephilipshue/integrationpluginzigbeephilipshue.h index a3545c6..9575c0f 100644 --- a/zigbeephilipshue/integrationpluginzigbeephilipshue.h +++ b/zigbeephilipshue/integrationpluginzigbeephilipshue.h @@ -52,10 +52,14 @@ class IntegrationPluginZigbeePhilipsHue: public ZigbeeIntegrationPlugin void setupThing(ThingSetupInfo *info) override; void executeAction(ThingActionInfo *info) override; +protected: + void createConnections(Thing *thing) override; + private slots: void pollLight(Thing *thing); private: + void bindManufacturerSpecificPhilipsCluster(ZigbeeNodeEndpoint *endpoint); QHash m_pollTimers; diff --git a/zigbeeschneiderelectric/integrationpluginzigbeeschneiderelectric.cpp b/zigbeeschneiderelectric/integrationpluginzigbeeschneiderelectric.cpp index a19be3c..8388408 100644 --- a/zigbeeschneiderelectric/integrationpluginzigbeeschneiderelectric.cpp +++ b/zigbeeschneiderelectric/integrationpluginzigbeeschneiderelectric.cpp @@ -95,7 +95,16 @@ void IntegrationPluginZigbeeSchneiderElectric::setupThing(ThingSetupInfo *info) return; } + info->finish(Thing::ThingErrorNoError); +} + +void IntegrationPluginZigbeeSchneiderElectric::createConnections(Thing *thing) +{ ZigbeeNode *node = nodeForThing(thing); + if (!node) { + qCWarning(dcZigbeeSchneiderElectric()) << "Node for thing" << thing << "not found."; + return; + } if (thing->thingClassId() == flsAirlink4ThingClassId) { @@ -105,7 +114,6 @@ void IntegrationPluginZigbeeSchneiderElectric::setupThing(ThingSetupInfo *info) if (!endpoint21 || !endpoint22) { qCWarning(dcZigbeeSchneiderElectric()) << "Unable to get endpoints from device."; - info->finish(Thing::ThingErrorHardwareFailure); return; } @@ -115,11 +123,6 @@ void IntegrationPluginZigbeeSchneiderElectric::setupThing(ThingSetupInfo *info) connectToOnOffOutputCluster(thing, endpoint22, "Bottom on", "Bottom off"); connectToLevelControlOutputCluster(thing, endpoint22, "Bottom up", "Bottom down"); - info->finish(Thing::ThingErrorNoError); return; } - - qCWarning(dcZigbeeSchneiderElectric()) << "Thing class not found" << info->thing()->thingClassId(); - Q_ASSERT_X(false, "ZigbeeSchneiderElectric", "Unhandled thing class"); - return info->finish(Thing::ThingErrorThingClassNotFound); } diff --git a/zigbeeschneiderelectric/integrationpluginzigbeeschneiderelectric.h b/zigbeeschneiderelectric/integrationpluginzigbeeschneiderelectric.h index 807e8db..af1266a 100644 --- a/zigbeeschneiderelectric/integrationpluginzigbeeschneiderelectric.h +++ b/zigbeeschneiderelectric/integrationpluginzigbeeschneiderelectric.h @@ -51,6 +51,9 @@ class IntegrationPluginZigbeeSchneiderElectric: public ZigbeeIntegrationPlugin bool handleNode(ZigbeeNode *node, const QUuid &networkUuid) override; void setupThing(ThingSetupInfo *info) override; + +protected: + void createConnections(Thing *thing) override; }; #endif // INTEGRATIONPLUGINZIGBEESCHNEIDERELECTRIC_H diff --git a/zigbeetradfri/integrationpluginzigbeetradfri.cpp b/zigbeetradfri/integrationpluginzigbeetradfri.cpp index 346bb14..c6b5f21 100644 --- a/zigbeetradfri/integrationpluginzigbeetradfri.cpp +++ b/zigbeetradfri/integrationpluginzigbeetradfri.cpp @@ -208,12 +208,20 @@ void IntegrationPluginZigbeeTradfri::setupThing(ThingSetupInfo *info) return; } + info->finish(Thing::ThingErrorNoError); +} + +void IntegrationPluginZigbeeTradfri::createConnections(Thing *thing) +{ ZigbeeNode *node = nodeForThing(thing); + if (!node) { + qCWarning(dcZigbeeTradfri()) << "Node for thing" << thing << "not found."; + return; + } ZigbeeNodeEndpoint *endpoint = node->getEndpoint(1); if (!endpoint) { qCWarning(dcZigbeeTradfri()) << "Could not find endpoint for" << thing; - info->finish(Thing::ThingErrorSetupFailed); return; } @@ -566,12 +574,16 @@ void IntegrationPluginZigbeeTradfri::setupThing(ThingSetupInfo *info) if (thing->thingClassId() == signalRepeaterThingClassId) { connectToOtaOutputCluster(thing, endpoint); } - info->finish(Thing::ThingErrorNoError); } void IntegrationPluginZigbeeTradfri::executeAction(ThingActionInfo *info) { ZigbeeNode *node = nodeForThing(info->thing()); + if (!node) { + qCWarning(dcZigbeeTradfri()) << "Node for thing" << info->thing() << "not found."; + info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("ZigBee node not found in network.")); + return; + } ZigbeeNodeEndpoint *endpoint = node->getEndpoint(1); diff --git a/zigbeetradfri/integrationpluginzigbeetradfri.h b/zigbeetradfri/integrationpluginzigbeetradfri.h index 2b78d05..27e3626 100644 --- a/zigbeetradfri/integrationpluginzigbeetradfri.h +++ b/zigbeetradfri/integrationpluginzigbeetradfri.h @@ -59,6 +59,7 @@ class IntegrationPluginZigbeeTradfri: public ZigbeeIntegrationPlugin void thingRemoved(Thing *thing) override; protected: + void createConnections(Thing *thing) override; QList firmwareIndexFromJson(const QByteArray &data) const override; private slots: diff --git a/zigbeetuya/integrationpluginzigbeetuya.cpp b/zigbeetuya/integrationpluginzigbeetuya.cpp index 97ebebe..4cb3871 100644 --- a/zigbeetuya/integrationpluginzigbeetuya.cpp +++ b/zigbeetuya/integrationpluginzigbeetuya.cpp @@ -183,7 +183,17 @@ void IntegrationPluginZigbeeTuya::setupThing(ThingSetupInfo *info) return; } + info->finish(Thing::ThingErrorNoError); +} + +void IntegrationPluginZigbeeTuya::createConnections(Thing *thing) +{ ZigbeeNode *node = nodeForThing(thing); + if (!node) { + qCWarning(dcZigbeeTuya()) << "Node for thing" << thing << "not found."; + return; + } + ZigbeeNodeEndpoint *endpoint = node->getEndpoint(0x01); connect(node, &ZigbeeNode::lastSeenChanged, this, [this](){ @@ -336,7 +346,6 @@ void IntegrationPluginZigbeeTuya::setupThing(ThingSetupInfo *info) ZigbeeNodeEndpoint *endpoint = node->getEndpoint(1); if (!endpoint) { qCWarning(dcZigbeeTuya()) << "Endpoint 1 not found on" << node; - info->finish(Thing::ThingErrorHardwareNotAvailable); return; } ZigbeeClusterIasZone *iasZoneCluster = endpoint->inputCluster(ZigbeeClusterLibrary::ClusterIdIasZone); @@ -375,13 +384,11 @@ void IntegrationPluginZigbeeTuya::setupThing(ThingSetupInfo *info) ZigbeeNodeEndpoint *endpoint = node->getEndpoint(1); if (!endpoint) { qCWarning(dcZigbeeTuya()) << "Unable to find endpoint 1 on node" << node; - info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("Unable to find endpoint 1 on Zigbee node.")); return; } ZigbeeCluster *cluster = endpoint->getInputCluster(static_cast(CLUSTER_ID_MANUFACTURER_SPECIFIC_TUYA)); if (!cluster) { qCWarning(dcZigbeeTuya()) << "Unable to find Tuya manufacturer specific cliuster on endpoint 1 on node" << node; - info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("Unable to find Tuya cluster on Zigbee node.")); return; } @@ -442,17 +449,15 @@ void IntegrationPluginZigbeeTuya::setupThing(ThingSetupInfo *info) } - if (info->thing()->thingClassId() == htlcdSensorThingClassId) { + if (thing->thingClassId() == htlcdSensorThingClassId) { ZigbeeNodeEndpoint *endpoint = node->getEndpoint(1); if (!endpoint) { qCWarning(dcZigbeeTuya()) << "Unable to find endpoint 1 on node" << node; - info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("Unable to find endpoint 1 on Zigbee node.")); return; } ZigbeeCluster *cluster = endpoint->getInputCluster(static_cast(CLUSTER_ID_MANUFACTURER_SPECIFIC_TUYA)); if (!cluster) { qCWarning(dcZigbeeTuya()) << "Unable to find Tuya manufacturer specific cliuster on endpoint 1 on node" << node; - info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("Unable to find Tuya cluster on Zigbee node.")); return; } @@ -532,17 +537,15 @@ void IntegrationPluginZigbeeTuya::setupThing(ThingSetupInfo *info) } - if (info->thing()->thingClassId() == airHousekeeperThingClassId) { + if (thing->thingClassId() == airHousekeeperThingClassId) { ZigbeeNodeEndpoint *endpoint = node->getEndpoint(1); if (!endpoint) { qCWarning(dcZigbeeTuya()) << "Unable to find endpoint 1 on node" << node; - info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("Unable to find endpoint 1 on Zigbee node.")); return; } ZigbeeCluster *cluster = endpoint->getInputCluster(static_cast(CLUSTER_ID_MANUFACTURER_SPECIFIC_TUYA)); if (!cluster) { qCWarning(dcZigbeeTuya()) << "Unable to find Tuya manufacturer specific cluster on endpoint 1 on node" << node; - info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("Unable to find Tuya cluster on Zigbee node.")); return; } @@ -600,17 +603,15 @@ void IntegrationPluginZigbeeTuya::setupThing(ThingSetupInfo *info) }); } - if (info->thing()->thingClassId() == smokeSensorThingClassId) { + if (thing->thingClassId() == smokeSensorThingClassId) { ZigbeeNodeEndpoint *endpoint = node->getEndpoint(1); if (!endpoint) { qCWarning(dcZigbeeTuya()) << "Unable to find endpoint 1 on node" << node; - info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("Unable to find endpoint 1 on Zigbee node.")); return; } ZigbeeCluster *cluster = endpoint->getInputCluster(static_cast(CLUSTER_ID_MANUFACTURER_SPECIFIC_TUYA)); if (!cluster) { qCWarning(dcZigbeeTuya()) << "Unable to find Tuya manufacturer specific cluuster on endpoint 1 on node" << node; - info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("Unable to find Tuya cluster on Zigbee node.")); return; } @@ -666,8 +667,6 @@ void IntegrationPluginZigbeeTuya::setupThing(ThingSetupInfo *info) }); } - - info->finish(Thing::ThingErrorNoError); } void IntegrationPluginZigbeeTuya::executeAction(ThingActionInfo *info) @@ -679,7 +678,7 @@ void IntegrationPluginZigbeeTuya::executeAction(ThingActionInfo *info) Thing *thing = info->thing(); ZigbeeNode *node = nodeForThing(info->thing()); - if (!node->reachable()) { + if (!node || !node->reachable()) { info->finish(Thing::ThingErrorHardwareNotAvailable); return; } @@ -719,6 +718,10 @@ void IntegrationPluginZigbeeTuya::pollEnergyMeters() { foreach (Thing *thing, myThings().filterByThingClassId(powerSocketThingClassId)) { ZigbeeNode *node = nodeForThing(thing); + if (!node) { + qCDebug(dcZigbeeTuya()) << "Node for thing" << thing << "not found. Cannot poll energy meter."; + continue; + } ZigbeeNodeEndpoint *endpoint = node->getEndpoint(0x01); ZigbeeClusterMetering *meteringCluster = endpoint->inputCluster(ZigbeeClusterLibrary::ClusterIdMetering); meteringCluster->readAttributes({ZigbeeClusterMetering::AttributeCurrentSummationDelivered}); diff --git a/zigbeetuya/integrationpluginzigbeetuya.h b/zigbeetuya/integrationpluginzigbeetuya.h index db68258..5e3c3a5 100644 --- a/zigbeetuya/integrationpluginzigbeetuya.h +++ b/zigbeetuya/integrationpluginzigbeetuya.h @@ -57,6 +57,9 @@ class IntegrationPluginZigbeeTuya: public ZigbeeIntegrationPlugin void executeAction(ThingActionInfo *info) override; void thingRemoved(Thing *thing) override; +protected: + void createConnections(Thing *thing) override; + private slots: void pollEnergyMeters();