/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright 2013 - 2021, 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 "integrationpluginzigbeedevelco.h" #include "plugininfo.h" #include "hardware/zigbee/zigbeehardwareresource.h" #include #include IntegrationPluginZigbeeDevelco::IntegrationPluginZigbeeDevelco() { m_ieeeAddressParamTypeIds[ioModuleThingClassId] = ioModuleThingIeeeAddressParamTypeId; m_ieeeAddressParamTypeIds[airQualitySensorThingClassId] = airQualitySensorThingIeeeAddressParamTypeId; m_networkUuidParamTypeIds[ioModuleThingClassId] = ioModuleThingNetworkUuidParamTypeId; m_networkUuidParamTypeIds[airQualitySensorThingClassId] = airQualitySensorThingNetworkUuidParamTypeId; m_connectedStateTypeIds[ioModuleThingClassId] = ioModuleConnectedStateTypeId; m_connectedStateTypeIds[airQualitySensorThingClassId] = airQualitySensorConnectedStateTypeId; m_signalStrengthStateTypeIds[ioModuleThingClassId] = ioModuleSignalStrengthStateTypeId; m_signalStrengthStateTypeIds[airQualitySensorThingClassId] = airQualitySensorSignalStrengthStateTypeId; } QString IntegrationPluginZigbeeDevelco::name() const { return "Develco"; } bool IntegrationPluginZigbeeDevelco::handleNode(ZigbeeNode *node, const QUuid &networkUuid) { // Filter for develco manufacturer code if (node->nodeDescriptor().manufacturerCode != Zigbee::Develco) return false; bool handled = false; if (node->modelName() == "IOMZB-110" || node->modelName() == "DIOZB-110") { if (node->hasEndpoint(IO_MODULE_EP_INPUT1) && node->hasEndpoint(IO_MODULE_EP_INPUT2) && node->hasEndpoint(IO_MODULE_EP_INPUT3) && node->hasEndpoint(IO_MODULE_EP_INPUT4) && node->hasEndpoint(IO_MODULE_EP_OUTPUT1 && node->hasEndpoint(IO_MODULE_EP_OUTPUT2))) { qCDebug(dcZigbeeDevelco()) << "Found IO module" << node << networkUuid.toString(); initIoModule(node); createThing(ioModuleThingClassId, networkUuid, node); handled = true; } } else if (node->modelName() == "AQSZB-110") { if (node->hasEndpoint(AIR_QUALITY_SENSOR_EP_SENSOR)) { qCDebug(dcZigbeeDevelco()) << "Found air quality sensor" << node << networkUuid.toString(); initAirQualitySensor(node); createThing(airQualitySensorThingClassId, networkUuid, node); handled = true; } } return handled; } void IntegrationPluginZigbeeDevelco::handleRemoveNode(ZigbeeNode *node, const QUuid &networkUuid) { Q_UNUSED(networkUuid) Thing *thing = m_thingNodes.key(node); if (thing) { qCDebug(dcZigbeeDevelco()) << node << "for" << thing << "has left the network."; 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); } } void IntegrationPluginZigbeeDevelco::init() { hardwareManager()->zigbeeResource()->registerHandler(this, ZigbeeHardwareResource::HandlerTypeVendor); } void IntegrationPluginZigbeeDevelco::setupThing(ThingSetupInfo *info) { qCDebug(dcZigbeeDevelco()) << "Setup" << info->thing(); // Get the node for this thing Thing *thing = info->thing(); QUuid networkUuid = thing->paramValue(m_networkUuidParamTypeIds.value(thing->thingClassId())).toUuid(); ZigbeeAddress zigbeeAddress = ZigbeeAddress(thing->paramValue(m_ieeeAddressParamTypeIds.value(thing->thingClassId())).toString()); ZigbeeNode *node = m_thingNodes.value(thing); if (!node) { node = hardwareManager()->zigbeeResource()->claimNode(this, networkUuid, zigbeeAddress); if (!node) { qCWarning(dcZigbeeDevelco()) << "Coud not find zigbee node for" << thing; info->finish(Thing::ThingErrorHardwareNotAvailable); return; } } m_thingNodes.insert(thing, node); // Update signal strength thing->setStateValue(m_signalStrengthStateTypeIds.value(thing->thingClassId()), qRound(node->lqi() * 100.0 / 255.0)); connect(node, &ZigbeeNode::lqiChanged, thing, [this, thing](quint8 lqi){ uint signalStrength = qRound(lqi * 100.0 / 255.0); qCDebug(dcZigbeeDevelco()) << thing << "signal strength changed" << signalStrength << "%"; thing->setStateValue(m_signalStrengthStateTypeIds.value(thing->thingClassId()), signalStrength); }); 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; } if (primaryEndpoint->hasInputCluster(ZigbeeClusterLibrary::ClusterIdBasic)) { ZigbeeCluster *basicCluster = primaryEndpoint->getInputCluster(ZigbeeClusterLibrary::ClusterIdBasic); if (basicCluster->hasAttribute(DEVELCO_BASIC_ATTRIBUTE_SW_VERSION)) { thing->setStateValue(ioModuleVersionStateTypeId, parseDevelcoVersionString(primaryEndpoint)); } else { readDevelcoFirmwareVersion(node, primaryEndpoint); } connect(basicCluster, &ZigbeeCluster::attributeChanged, this, [=](const ZigbeeClusterAttribute &attribute){ if (attribute.id() == DEVELCO_BASIC_ATTRIBUTE_SW_VERSION) { thing->setStateValue(ioModuleVersionStateTypeId, parseDevelcoVersionString(primaryEndpoint)); } }); } // Handle reachable state from node thing->setStateValue(m_connectedStateTypeIds.value(thing->thingClassId()), node->reachable()); connect(node, &ZigbeeNode::reachableChanged, thing, [=](bool reachable){ thing->setStateValue(m_connectedStateTypeIds.value(thing->thingClassId()), reachable); if (reachable) { readDevelcoFirmwareVersion(node, node->getEndpoint(IO_MODULE_EP_INPUT1)); readIoModuleOutputPowerStates(thing); readIoModuleInputPowerStates(thing); } }); // Output 1 ZigbeeNodeEndpoint *output1Endpoint = node->getEndpoint(IO_MODULE_EP_OUTPUT1); if (!output1Endpoint) { qCWarning(dcZigbeeDevelco()) << "Could not find endpoint for output 1 on" << thing << node; } else { ZigbeeClusterOnOff *onOffCluster = output1Endpoint->inputCluster(ZigbeeClusterLibrary::ClusterIdOnOff); if (!onOffCluster) { qCWarning(dcZigbeeDevelco()) << "Could not find On/Off cluster on" << thing << node << output1Endpoint; } else { if (onOffCluster->hasAttribute(ZigbeeClusterOnOff::AttributeOnOff)) { thing->setStateValue(ioModuleOutput1StateTypeId, onOffCluster->power()); } connect(onOffCluster, &ZigbeeClusterOnOff::powerChanged, thing, [thing](bool power){ qCDebug(dcZigbeeDevelco()) << thing << "output 1 power changed to" << power; thing->setStateValue(ioModuleOutput1StateTypeId, power); }); } } // Output 2 ZigbeeNodeEndpoint *output2Endpoint = node->getEndpoint(IO_MODULE_EP_OUTPUT2); if (!output2Endpoint) { qCWarning(dcZigbeeDevelco()) << "Could not find endpoint for output 2 on" << thing << node; } else { ZigbeeClusterOnOff *onOffCluster = output2Endpoint->inputCluster(ZigbeeClusterLibrary::ClusterIdOnOff); if (!onOffCluster) { qCWarning(dcZigbeeDevelco()) << "Could not find On/Off cluster on" << thing << node << output2Endpoint; } else { if (onOffCluster->hasAttribute(ZigbeeClusterOnOff::AttributeOnOff)) { thing->setStateValue(ioModuleOutput2StateTypeId, onOffCluster->power()); } connect(onOffCluster, &ZigbeeClusterOnOff::powerChanged, thing, [thing](bool power){ qCDebug(dcZigbeeDevelco()) << thing << "output 2 power changed to" << power; thing->setStateValue(ioModuleOutput2StateTypeId, power); }); } } // Input 1 ZigbeeNodeEndpoint *input1Endpoint = node->getEndpoint(IO_MODULE_EP_INPUT1); if (!input1Endpoint) { qCWarning(dcZigbeeDevelco()) << "Could not find endpoint for input 1 on" << thing << node; } else { ZigbeeClusterBinaryInput *binaryInputCluster = input1Endpoint->inputCluster(ZigbeeClusterLibrary::ClusterIdBinaryInput); if (!binaryInputCluster) { qCWarning(dcZigbeeDevelco()) << "Could not find BinaryInput cluster on" << thing << node << input1Endpoint; } else { if (binaryInputCluster->hasAttribute(ZigbeeClusterLibrary::ClusterIdBinaryInput)) { thing->setStateValue(ioModuleInput1StateTypeId, binaryInputCluster->presentValue()); } connect(binaryInputCluster, &ZigbeeClusterBinaryInput::presentValueChanged, thing, [thing](bool presentValue){ qCDebug(dcZigbeeDevelco()) << thing << "input 1 changed to" << presentValue; thing->setStateValue(ioModuleInput1StateTypeId, presentValue); }); } } // Input 2 ZigbeeNodeEndpoint *input2Endpoint = node->getEndpoint(IO_MODULE_EP_INPUT2); if (!input2Endpoint) { qCWarning(dcZigbeeDevelco()) << "Could not find endpoint for input 2 on" << thing << node; } else { ZigbeeClusterBinaryInput *binaryInputCluster = input2Endpoint->inputCluster(ZigbeeClusterLibrary::ClusterIdBinaryInput); if (!binaryInputCluster) { qCWarning(dcZigbeeDevelco()) << "Could not find BinaryInput cluster on" << thing << node << input2Endpoint; } else { if (binaryInputCluster->hasAttribute(ZigbeeClusterLibrary::ClusterIdBinaryInput)) { thing->setStateValue(ioModuleInput2StateTypeId, binaryInputCluster->presentValue()); } connect(binaryInputCluster, &ZigbeeClusterBinaryInput::presentValueChanged, thing, [thing](bool presentValue){ qCDebug(dcZigbeeDevelco()) << thing << "input 2 changed to" << presentValue; thing->setStateValue(ioModuleInput2StateTypeId, presentValue); }); } } // Input 3 ZigbeeNodeEndpoint *input3Endpoint = node->getEndpoint(IO_MODULE_EP_INPUT3); if (!input3Endpoint) { qCWarning(dcZigbeeDevelco()) << "Could not find endpoint for input 3 on" << thing << node; } else { ZigbeeClusterBinaryInput *binaryInputCluster = input3Endpoint->inputCluster(ZigbeeClusterLibrary::ClusterIdBinaryInput); if (!binaryInputCluster) { qCWarning(dcZigbeeDevelco()) << "Could not find BinaryInput cluster on" << thing << node << input3Endpoint; } else { if (binaryInputCluster->hasAttribute(ZigbeeClusterLibrary::ClusterIdBinaryInput)) { thing->setStateValue(ioModuleInput3StateTypeId, binaryInputCluster->presentValue()); } connect(binaryInputCluster, &ZigbeeClusterBinaryInput::presentValueChanged, thing, [thing](bool presentValue){ qCDebug(dcZigbeeDevelco()) << thing << "input 3 changed to" << presentValue; thing->setStateValue(ioModuleInput3StateTypeId, presentValue); }); } } // Input 4 ZigbeeNodeEndpoint *input4Endpoint = node->getEndpoint(IO_MODULE_EP_INPUT4); if (!input4Endpoint) { qCWarning(dcZigbeeDevelco()) << "Could not find endpoint for input 4 on" << thing << node; } else { ZigbeeClusterBinaryInput *binaryInputCluster = input4Endpoint->inputCluster(ZigbeeClusterLibrary::ClusterIdBinaryInput); if (!binaryInputCluster) { qCWarning(dcZigbeeDevelco()) << "Could not find BinaryInput cluster on" << thing << node << input4Endpoint; } else { if (binaryInputCluster->hasAttribute(ZigbeeClusterLibrary::ClusterIdBinaryInput)) { thing->setStateValue(ioModuleInput4StateTypeId, binaryInputCluster->presentValue()); } connect(binaryInputCluster, &ZigbeeClusterBinaryInput::presentValueChanged, thing, [thing](bool presentValue){ qCDebug(dcZigbeeDevelco()) << thing << "input 4 changed to" << presentValue; thing->setStateValue(ioModuleInput4StateTypeId, presentValue); }); } } } else if (thing->thingClassId() == airQualitySensorThingClassId) { ZigbeeNodeEndpoint *sensorEndpoint = node->getEndpoint(AIR_QUALITY_SENSOR_EP_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; } // Handle reachable state from node thing->setStateValue(m_connectedStateTypeIds.value(thing->thingClassId()), node->reachable()); connect(node, &ZigbeeNode::reachableChanged, thing, [=](bool reachable){ thing->setStateValue(m_connectedStateTypeIds.value(thing->thingClassId()), reachable); }); // Version state if (sensorEndpoint->hasInputCluster(ZigbeeClusterLibrary::ClusterIdBasic)) { ZigbeeCluster *basicCluster = sensorEndpoint->getInputCluster(ZigbeeClusterLibrary::ClusterIdBasic); if (basicCluster->hasAttribute(DEVELCO_BASIC_ATTRIBUTE_SW_VERSION)) { thing->setStateValue(airQualitySensorVersionStateTypeId, parseDevelcoVersionString(sensorEndpoint)); } connect(basicCluster, &ZigbeeCluster::attributeChanged, this, [=](const ZigbeeClusterAttribute &attribute){ if (attribute.id() == DEVELCO_BASIC_ATTRIBUTE_SW_VERSION) { thing->setStateValue(airQualitySensorVersionStateTypeId, parseDevelcoVersionString(sensorEndpoint)); } }); } // Temperature ZigbeeClusterTemperatureMeasurement *temperatureCluster = sensorEndpoint->inputCluster(ZigbeeClusterLibrary::ClusterIdTemperatureMeasurement); if (!temperatureCluster) { qCWarning(dcZigbeeDevelco()) << "Could not find the temperature measurement server cluster on" << thing << sensorEndpoint; } else { // Only set the state if the cluster actually has the attribute if (temperatureCluster->hasAttribute(ZigbeeClusterTemperatureMeasurement::AttributeMeasuredValue)) { thing->setStateValue(airQualitySensorTemperatureStateTypeId, temperatureCluster->temperature()); } connect(temperatureCluster, &ZigbeeClusterTemperatureMeasurement::temperatureChanged, thing, [thing](double temperature){ qCDebug(dcZigbeeDevelco()) << thing << "temperature changed" << temperature << "°C"; thing->setStateValue(airQualitySensorTemperatureStateTypeId, temperature); }); } // Humidity ZigbeeClusterRelativeHumidityMeasurement *humidityCluster = sensorEndpoint->inputCluster(ZigbeeClusterLibrary::ClusterIdRelativeHumidityMeasurement); if (!humidityCluster) { qCWarning(dcZigbeeDevelco()) << "Could not find the humidity measurement server cluster on" << thing << sensorEndpoint; } else { // Only set the state if the cluster actually has the attribute if (humidityCluster->hasAttribute(ZigbeeClusterRelativeHumidityMeasurement::AttributeMeasuredValue)) { thing->setStateValue(airQualitySensorHumidityStateTypeId, humidityCluster->humidity()); } connect(humidityCluster, &ZigbeeClusterRelativeHumidityMeasurement::humidityChanged, thing, [thing](double humidity){ qCDebug(dcZigbeeDevelco()) << thing << "humidity changed" << humidity << "%"; thing->setStateValue(airQualitySensorHumidityStateTypeId, humidity); }); } // Battery voltage ZigbeeClusterPowerConfiguration *powerConfigurationCluster = sensorEndpoint->inputCluster(ZigbeeClusterLibrary::ClusterIdPowerConfiguration); if (!powerConfigurationCluster) { qCWarning(dcZigbeeDevelco()) << "Could not find the power configuration server cluster on" << thing << sensorEndpoint; } else { // Only set the state if the cluster actually has the attribute if (powerConfigurationCluster->hasAttribute(ZigbeeClusterPowerConfiguration::AttributeBatteryVoltage)) { int batteryVoltage = powerConfigurationCluster->attribute(ZigbeeClusterPowerConfiguration::AttributeBatteryVoltage).dataType().toUInt8() * 100; qCDebug(dcZigbeeDevelco()) << thing << "battery voltage" << batteryVoltage << "mV"; thing->setStateValue(airQualitySensorBatteryCriticalStateTypeId, batteryVoltage < 2500); } connect(powerConfigurationCluster, &ZigbeeClusterPowerConfiguration::attributeChanged, thing, [=](const ZigbeeClusterAttribute &attribute){ if (attribute.id() == ZigbeeClusterPowerConfiguration::AttributeBatteryVoltage) { int batteryVoltage = attribute.dataType().toUInt8() * 100; qCDebug(dcZigbeeDevelco()) << thing << "battery voltage" << batteryVoltage << "mV"; thing->setStateValue(airQualitySensorBatteryCriticalStateTypeId, batteryVoltage < 2500); } }); } // VOC ZigbeeCluster *vocCluster = sensorEndpoint->getInputCluster(static_cast(AIR_QUALITY_SENSOR_VOC_MEASUREMENT_CLUSTER_ID)); if (!vocCluster) { qCWarning(dcZigbeeDevelco()) << "Could not find the VOC measurement server cluster on" << thing << sensorEndpoint; } else { // Only set the state if the cluster actually has the attribute if (vocCluster->hasAttribute(AIR_QUALITY_SENSOR_VOC_MEASUREMENT_ATTRIBUTE_MEASURED_VALUE)) { ZigbeeClusterAttribute measuredValueAttribute = vocCluster->attribute(AIR_QUALITY_SENSOR_VOC_MEASUREMENT_ATTRIBUTE_MEASURED_VALUE); bool valueOk = false; quint16 value = measuredValueAttribute.dataType().toUInt16(&valueOk); if (valueOk) { if (value == 0xFFFF) { qCWarning(dcZigbeeDevelco()) << "Received invalid VOC measurment. The sensor is not ready yet."; } else { qCDebug(dcZigbeeDevelco()) << thing << "VOC changed" << value << "ppm"; thing->setStateValue(airQualitySensorVocStateTypeId, value); updateIndoorAirQuality(thing, value); } } else { qCWarning(dcZigbeeDevelco()) << "Failed to convert VOC measurment value" << measuredValueAttribute; } } connect(vocCluster, &ZigbeeCluster::attributeChanged, thing, [=](const ZigbeeClusterAttribute &attribute){ if (attribute.id() == AIR_QUALITY_SENSOR_VOC_MEASUREMENT_ATTRIBUTE_MEASURED_VALUE) { bool valueOk = false; quint16 value = attribute.dataType().toUInt16(&valueOk); if (valueOk) { if (value == 0xFFFF) { qCWarning(dcZigbeeDevelco()) << "Received invalid VOC measurment. The sensor is not ready yet."; } else { qCDebug(dcZigbeeDevelco()) << thing << "VOC changed" << value << "ppm"; thing->setStateValue(airQualitySensorVocStateTypeId, value); updateIndoorAirQuality(thing, value); } } else { qCWarning(dcZigbeeDevelco()) << "Failed to convert VOC measurment value" << attribute; } } }); } } info->finish(Thing::ThingErrorNoError); } void IntegrationPluginZigbeeDevelco::postSetupThing(Thing *thing) { if (thing->thingClassId() == ioModuleThingClassId) { if (m_thingNodes.value(thing)->reachable()) { readIoModuleOutputPowerStates(thing); readIoModuleInputPowerStates(thing); } } } void IntegrationPluginZigbeeDevelco::executeAction(ThingActionInfo *info) { if (!hardwareManager()->zigbeeResource()->available()) { info->finish(Thing::ThingErrorHardwareNotAvailable); return; } // Get the node Thing *thing = info->thing(); ZigbeeNode *node = m_thingNodes.value(thing); if (!node->reachable()) { info->finish(Thing::ThingErrorHardwareNotAvailable); return; } if (thing->thingClassId() == ioModuleThingClassId) { // Identify if (info->action().actionTypeId() == ioModuleAlertActionTypeId) { ZigbeeNodeEndpoint *primaryEndpoint = node->getEndpoint(IO_MODULE_EP_INPUT1); if (!primaryEndpoint) { qCWarning(dcZigbeeDevelco()) << "Could not find endpoint for execute action on" << thing << node; info->finish(Thing::ThingErrorHardwareFailure); return; } ZigbeeClusterIdentify *identifyCluster = primaryEndpoint->inputCluster(ZigbeeClusterLibrary::ClusterIdIdentify); if (!identifyCluster) { qCWarning(dcZigbeeDevelco()) << "Could not find identify cluster for" << thing << "in" << node; info->finish(Thing::ThingErrorHardwareFailure); return; } // Send the command trough the network ZigbeeClusterReply *reply = identifyCluster->identify(2); connect(reply, &ZigbeeClusterReply::finished, this, [reply, info](){ // Note: reply will be deleted automatically if (reply->error() != ZigbeeClusterReply::ErrorNoError) { info->finish(Thing::ThingErrorHardwareFailure); } else { info->finish(Thing::ThingErrorNoError); } }); return; } // Output 1 if (info->action().actionTypeId() == ioModuleOutput1ActionTypeId) { bool power = info->action().paramValue(ioModuleOutput1ActionOutput1ParamTypeId).toBool(); qCDebug(dcZigbeeDevelco()) << "Set output 1 power of" << thing << "to" << power; ZigbeeNodeEndpoint *output1Endpoint = node->getEndpoint(IO_MODULE_EP_OUTPUT1); if (!output1Endpoint) { qCWarning(dcZigbeeDevelco()) << "Could not find endpoint for output 1 on" << thing << node; info->finish(Thing::ThingErrorHardwareFailure); return; } ZigbeeClusterOnOff *onOffCluster = output1Endpoint->inputCluster(ZigbeeClusterLibrary::ClusterIdOnOff); if (!onOffCluster) { qCWarning(dcZigbeeDevelco()) << "Could not find On/Off cluster on" << thing << node << output1Endpoint; info->finish(Thing::ThingErrorHardwareFailure); return; } ZigbeeClusterReply *reply = (power ? onOffCluster->commandOn() : onOffCluster->commandOff()); connect(reply, &ZigbeeClusterReply::finished, info, [=](){ // Note: reply will be deleted automatically if (reply->error() != ZigbeeClusterReply::ErrorNoError) { qCWarning(dcZigbeeDevelco()) << "Failed to set power for output 1 on" << thing << reply->error(); info->finish(Thing::ThingErrorHardwareFailure); } else { info->finish(Thing::ThingErrorNoError); qCDebug(dcZigbeeDevelco()) << "Set power on output 1 finished successfully for" << thing; thing->setStateValue(ioModuleOutput1StateTypeId, power); } }); return; } // Output 2 if (info->action().actionTypeId() == ioModuleOutput2ActionTypeId) { bool power = info->action().paramValue(ioModuleOutput2ActionOutput2ParamTypeId).toBool(); qCDebug(dcZigbeeDevelco()) << "Set output 2 power of" << thing << "to" << power; ZigbeeNodeEndpoint *output2Endpoint = node->getEndpoint(IO_MODULE_EP_OUTPUT2); if (!output2Endpoint) { qCWarning(dcZigbeeDevelco()) << "Could not find endpoint for output 2 on" << thing << node; info->finish(Thing::ThingErrorHardwareFailure); return; } ZigbeeClusterOnOff *onOffCluster = output2Endpoint->inputCluster(ZigbeeClusterLibrary::ClusterIdOnOff); if (!onOffCluster) { qCWarning(dcZigbeeDevelco()) << "Could not find On/Off cluster on" << thing << node << output2Endpoint; info->finish(Thing::ThingErrorHardwareFailure); return; } ZigbeeClusterReply *reply = (power ? onOffCluster->commandOn() : onOffCluster->commandOff()); connect(reply, &ZigbeeClusterReply::finished, info, [=](){ // Note: reply will be deleted automatically if (reply->error() != ZigbeeClusterReply::ErrorNoError) { qCWarning(dcZigbeeDevelco()) << "Failed to set power for output 2 on" << thing << reply->error(); info->finish(Thing::ThingErrorHardwareFailure); } else { info->finish(Thing::ThingErrorNoError); qCDebug(dcZigbeeDevelco()) << "Set power on output 2 finished successfully for" << thing; thing->setStateValue(ioModuleOutput2StateTypeId, power); } }); return; } // Impulse action if (info->action().actionTypeId() == ioModuleImpulseOutput1ActionTypeId || info->action().actionTypeId() == ioModuleImpulseOutput2ActionTypeId) { // Uint for time is 1/10 s uint impulseDuration = thing->settings().paramValue(ioModuleSettingsImpulseDurationParamTypeId).toUInt(); quint16 impulseDurationScaled = static_cast(qRound(impulseDuration / 100.0)); ZigbeeNodeEndpoint *endpoint = nullptr; if (info->action().actionTypeId() == ioModuleImpulseOutput1ActionTypeId) { endpoint = node->getEndpoint(IO_MODULE_EP_OUTPUT1); qCDebug(dcZigbeeDevelco()) << "Execute output 1 impulse with" << impulseDurationScaled * 100 << "ms"; } else if (info->action().actionTypeId() == ioModuleImpulseOutput2ActionTypeId) { endpoint = node->getEndpoint(IO_MODULE_EP_OUTPUT2); qCDebug(dcZigbeeDevelco()) << "Execute output 2 impulse with" << impulseDurationScaled * 100 << "ms"; } if (!endpoint) { qCWarning(dcZigbeeDevelco()) << "Could not find endpoint for impulse action on" << thing << node; info->finish(Thing::ThingErrorHardwareFailure); return; } ZigbeeClusterOnOff *onOffCluster = endpoint->inputCluster(ZigbeeClusterLibrary::ClusterIdOnOff); if (!onOffCluster) { qCWarning(dcZigbeeDevelco()) << "Could not find On/Off cluster on" << thing << node << endpoint; info->finish(Thing::ThingErrorHardwareFailure); return; } ZigbeeClusterReply *reply = onOffCluster->commandOnWithTimedOff(false, impulseDurationScaled, 0); connect(reply, &ZigbeeClusterReply::finished, info, [=](){ // Note: reply will be deleted automatically if (reply->error() != ZigbeeClusterReply::ErrorNoError) { qCWarning(dcZigbeeDevelco()) << "Failed to set on with timed off on" << thing << endpoint << reply->error(); info->finish(Thing::ThingErrorHardwareFailure); } else { info->finish(Thing::ThingErrorNoError); qCDebug(dcZigbeeDevelco()) << "Set on with timed off on finished successfully for" << thing << endpoint; } }); return; } } info->finish(Thing::ThingErrorUnsupportedFeature); } void IntegrationPluginZigbeeDevelco::thingRemoved(Thing *thing) { ZigbeeNode *node = m_thingNodes.take(thing); if (node) { QUuid networkUuid = thing->paramValue(m_networkUuidParamTypeIds.value(thing->thingClassId())).toUuid(); hardwareManager()->zigbeeResource()->removeNodeFromNetwork(networkUuid, node); } } void IntegrationPluginZigbeeDevelco::createThing(const ThingClassId &thingClassId, const QUuid &networkUuid, ZigbeeNode *node) { ThingDescriptor descriptor(thingClassId); QString deviceClassName = supportedThings().findById(thingClassId).displayName(); descriptor.setTitle(QString("%1 (%2 - %3)").arg(deviceClassName, node->manufacturerName(), node->modelName())); ParamList params; params.append(Param(m_networkUuidParamTypeIds[thingClassId], networkUuid.toString())); params.append(Param(m_ieeeAddressParamTypeIds[thingClassId], node->extendedAddress().toString())); descriptor.setParams(params); emit autoThingsAppeared({descriptor}); } QString IntegrationPluginZigbeeDevelco::parseDevelcoVersionString(ZigbeeNodeEndpoint *endpoint) { QString versionString; ZigbeeCluster *basicCluster = endpoint->getInputCluster(ZigbeeClusterLibrary::ClusterIdBasic); if (!basicCluster) { qCWarning(dcZigbeeDevelco()) << "Could not find basic cluster on" << endpoint << "for version parsing"; return versionString; } if (!basicCluster->hasAttribute(DEVELCO_BASIC_ATTRIBUTE_SW_VERSION)) { qCWarning(dcZigbeeDevelco()) << "Could not find manufacturer specific develco software version attribute in basic cluster on" << endpoint; return versionString; } ZigbeeClusterAttribute versionAttribute = basicCluster->attribute(DEVELCO_BASIC_ATTRIBUTE_SW_VERSION); // 1 Byte octet string length, 3 byte version infromation if (versionAttribute.dataType().data().length() < 4 || versionAttribute.dataType().data().at(0) != 3) { qCWarning(dcZigbeeDevelco()) << "Failed to parse version string from manufacturer specific develco software version attribute" << versionAttribute; return versionString; } int majorVersion = static_cast(versionAttribute.dataType().data().at(1)); int minorVersion = static_cast(versionAttribute.dataType().data().at(2)); int patchVersion = static_cast(versionAttribute.dataType().data().at(3)); versionString = QString("%1.%2.%3").arg(majorVersion).arg(minorVersion).arg(patchVersion); //qCDebug(dcZigbeeDevelco()) << versionAttribute << ZigbeeUtils::convertByteArrayToHexString(versionAttribute.dataType().data()) << versionString; return versionString; } void IntegrationPluginZigbeeDevelco::initIoModule(ZigbeeNode *node) { qCDebug(dcZigbeeDevelco()) << "Start initializing IO Module" << node; readDevelcoFirmwareVersion(node, node->getEndpoint(IO_MODULE_EP_INPUT1)); // Binding and reporting outputs configureOnOffPowerReporting(node, node->getEndpoint(IO_MODULE_EP_OUTPUT1)); configureOnOffPowerReporting(node, node->getEndpoint(IO_MODULE_EP_OUTPUT2)); // Binding and reporting inputs configureBinaryInputReporting(node, node->getEndpoint(IO_MODULE_EP_INPUT1)); configureBinaryInputReporting(node, node->getEndpoint(IO_MODULE_EP_INPUT2)); configureBinaryInputReporting(node, node->getEndpoint(IO_MODULE_EP_INPUT3)); configureBinaryInputReporting(node, node->getEndpoint(IO_MODULE_EP_INPUT4)); } void IntegrationPluginZigbeeDevelco::initAirQualitySensor(ZigbeeNode *node) { qCDebug(dcZigbeeDevelco()) << "Start initializing air quality sensor" << node; readDevelcoFirmwareVersion(node, node->getEndpoint(AIR_QUALITY_SENSOR_EP_SENSOR)); configureTemperatureReporting(node, node->getEndpoint(AIR_QUALITY_SENSOR_EP_SENSOR)); configureHumidityReporting(node, node->getEndpoint(AIR_QUALITY_SENSOR_EP_SENSOR)); configureBattryVoltageReporting(node, node->getEndpoint(AIR_QUALITY_SENSOR_EP_SENSOR)); configureVocReporting(node, node->getEndpoint(AIR_QUALITY_SENSOR_EP_SENSOR)); } void IntegrationPluginZigbeeDevelco::configureOnOffPowerReporting(ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint) { qCDebug(dcZigbeeDevelco()) << "Bind on/off cluster to coordinator IEEE address" << node << endpoint; ZigbeeDeviceObjectReply * zdoReply = node->deviceObject()->requestBindIeeeAddress(endpoint->endpointId(), ZigbeeClusterLibrary::ClusterIdOnOff, hardwareManager()->zigbeeResource()->coordinatorAddress(node->networkUuid()), 0x01); connect(zdoReply, &ZigbeeDeviceObjectReply::finished, node, [=](){ if (zdoReply->error() != ZigbeeDeviceObjectReply::ErrorNoError) { qCWarning(dcZigbeeDevelco()) << "Failed to bind on/off cluster to coordinator" << zdoReply->error(); } else { qCDebug(dcZigbeeDevelco()) << "Bind on/off cluster to coordinator finished successfully"; } // Configure attribute reporting ZigbeeClusterLibrary::AttributeReportingConfiguration reportingConfig; reportingConfig.attributeId = ZigbeeClusterOnOff::AttributeOnOff; reportingConfig.minReportingInterval = 0; reportingConfig.maxReportingInterval = 600; reportingConfig.dataType = Zigbee::Bool; qCDebug(dcZigbeeDevelco()) << "Configure attribute reporting for on/off cluster" << node << endpoint; ZigbeeClusterReply *reportingReply = endpoint->getInputCluster(ZigbeeClusterLibrary::ClusterIdOnOff)->configureReporting({reportingConfig}); connect(reportingReply, &ZigbeeClusterReply::finished, this, [=](){ if (reportingReply->error() != ZigbeeClusterReply::ErrorNoError) { qCWarning(dcZigbeeDevelco()) << "Failed configure attribute reporting on on/off cluster" << reportingReply->error(); } else { qCDebug(dcZigbeeDevelco()) << "Attribute reporting configuration finished for on/off cluster" << ZigbeeClusterLibrary::parseAttributeReportingStatusRecords(reportingReply->responseFrame().payload); } }); }); } void IntegrationPluginZigbeeDevelco::configureBinaryInputReporting(ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint) { qCDebug(dcZigbeeDevelco()) << "Bind binary input cluster to coordinator IEEE address" << node << endpoint; ZigbeeDeviceObjectReply * zdoReply = node->deviceObject()->requestBindIeeeAddress(endpoint->endpointId(), ZigbeeClusterLibrary::ClusterIdBinaryInput, hardwareManager()->zigbeeResource()->coordinatorAddress(node->networkUuid()), 0x01); connect(zdoReply, &ZigbeeDeviceObjectReply::finished, node, [=](){ if (zdoReply->error() != ZigbeeDeviceObjectReply::ErrorNoError) { qCWarning(dcZigbeeDevelco()) << "Failed to bind binary input cluster to coordinator" << zdoReply->error(); } else { qCDebug(dcZigbeeDevelco()) << "Bind binary input cluster to coordinator finished successfully"; } // Configure attribute reporting ZigbeeClusterLibrary::AttributeReportingConfiguration reportingConfig; reportingConfig.attributeId = ZigbeeClusterBinaryInput::AttributePresentValue; reportingConfig.minReportingInterval = 0; reportingConfig.maxReportingInterval = 600; reportingConfig.dataType = Zigbee::Bool; qCDebug(dcZigbeeDevelco()) << "Configure attribute reporting for binary input cluster" << node << endpoint; ZigbeeClusterReply *reportingReply = endpoint->getInputCluster(ZigbeeClusterLibrary::ClusterIdBinaryInput)->configureReporting({reportingConfig}); connect(reportingReply, &ZigbeeClusterReply::finished, this, [=](){ if (reportingReply->error() != ZigbeeClusterReply::ErrorNoError) { qCWarning(dcZigbeeDevelco()) << "Failed configure attribute reporting on binary input cluster" << reportingReply->error(); } else { qCDebug(dcZigbeeDevelco()) << "Attribute reporting configuration finished for on binary input cluster" << ZigbeeClusterLibrary::parseAttributeReportingStatusRecords(reportingReply->responseFrame().payload); } }); }); } void IntegrationPluginZigbeeDevelco::configureTemperatureReporting(ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint) { // Note: don not read initial value since it returns an invalid masurement. The device needs some time for initial measuring qCDebug(dcZigbeeDevelco()) << "Bind temperature measurement cluster to coordinator IEEE address" << node << endpoint; ZigbeeDeviceObjectReply * zdoReply = node->deviceObject()->requestBindIeeeAddress(endpoint->endpointId(), ZigbeeClusterLibrary::ClusterIdTemperatureMeasurement, hardwareManager()->zigbeeResource()->coordinatorAddress(node->networkUuid()), 0x01); connect(zdoReply, &ZigbeeDeviceObjectReply::finished, node, [=](){ if (zdoReply->error() != ZigbeeDeviceObjectReply::ErrorNoError) { qCWarning(dcZigbeeDevelco()) << "Failed to bind temperature measurement cluster to coordinator" << zdoReply->error(); } else { qCDebug(dcZigbeeDevelco()) << "Bind temperature measurement cluster to coordinator finished successfully"; } // Configure attribute reporting ZigbeeClusterLibrary::AttributeReportingConfiguration reportingConfig; reportingConfig.attributeId = ZigbeeClusterTemperatureMeasurement::AttributeMeasuredValue; reportingConfig.dataType = Zigbee::Int16; reportingConfig.minReportingInterval = 60; reportingConfig.maxReportingInterval = 300; reportingConfig.reportableChange = ZigbeeDataType(static_cast(10)).data(); qCDebug(dcZigbeeDevelco()) << "Configure attribute reporting for temperature measurement cluster" << node << endpoint; ZigbeeClusterReply *reportingReply = endpoint->getInputCluster(ZigbeeClusterLibrary::ClusterIdTemperatureMeasurement)->configureReporting({reportingConfig}); connect(reportingReply, &ZigbeeClusterReply::finished, this, [=](){ if (reportingReply->error() != ZigbeeClusterReply::ErrorNoError) { qCWarning(dcZigbeeDevelco()) << "Failed configure attribute reporting on temperature measurement cluster" << reportingReply->error(); } else { qCDebug(dcZigbeeDevelco()) << "Attribute reporting configuration finished for on temperature measurement cluster" << ZigbeeClusterLibrary::parseAttributeReportingStatusRecords(reportingReply->responseFrame().payload); } }); }); } void IntegrationPluginZigbeeDevelco::configureHumidityReporting(ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint) { // Note: don not read initial value since it returns an invalid masurement. The device needs some time for initial measuring qCDebug(dcZigbeeDevelco()) << "Bind humidity measurement cluster to coordinator IEEE address" << node << endpoint; ZigbeeDeviceObjectReply * zdoReply = node->deviceObject()->requestBindIeeeAddress(endpoint->endpointId(), ZigbeeClusterLibrary::ClusterIdRelativeHumidityMeasurement, hardwareManager()->zigbeeResource()->coordinatorAddress(node->networkUuid()), 0x01); connect(zdoReply, &ZigbeeDeviceObjectReply::finished, node, [=](){ if (zdoReply->error() != ZigbeeDeviceObjectReply::ErrorNoError) { qCWarning(dcZigbeeDevelco()) << "Failed to bind humidity measurement cluster to coordinator" << zdoReply->error(); } else { qCDebug(dcZigbeeDevelco()) << "Bind humidity measurement cluster to coordinator finished successfully"; } // Configure attribute reporting ZigbeeClusterLibrary::AttributeReportingConfiguration reportingConfig; reportingConfig.attributeId = ZigbeeClusterRelativeHumidityMeasurement::AttributeMeasuredValue; reportingConfig.dataType = Zigbee::Uint16; reportingConfig.minReportingInterval = 60; reportingConfig.maxReportingInterval = 300; reportingConfig.reportableChange = ZigbeeDataType(static_cast(10)).data(); qCDebug(dcZigbeeDevelco()) << "Configure attribute reporting for humidity measurement cluster" << node << endpoint; ZigbeeClusterReply *reportingReply = endpoint->getInputCluster(ZigbeeClusterLibrary::ClusterIdRelativeHumidityMeasurement)->configureReporting({reportingConfig}); connect(reportingReply, &ZigbeeClusterReply::finished, this, [=](){ if (reportingReply->error() != ZigbeeClusterReply::ErrorNoError) { qCWarning(dcZigbeeDevelco()) << "Failed configure attribute reporting on humidity measurement cluster" << reportingReply->error(); } else { qCDebug(dcZigbeeDevelco()) << "Attribute reporting configuration finished for on humidity measurement cluster" << ZigbeeClusterLibrary::parseAttributeReportingStatusRecords(reportingReply->responseFrame().payload); } }); }); } void IntegrationPluginZigbeeDevelco::configureBattryVoltageReporting(ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint) { // Note: don not read initial value since it returns an invalid masurement. The device needs some time for initial measuring qCDebug(dcZigbeeDevelco()) << "Bind power configuration cluster to coordinator IEEE address" << node << endpoint; ZigbeeDeviceObjectReply * zdoReply = node->deviceObject()->requestBindIeeeAddress(endpoint->endpointId(), ZigbeeClusterLibrary::ClusterIdPowerConfiguration, hardwareManager()->zigbeeResource()->coordinatorAddress(node->networkUuid()), 0x01); connect(zdoReply, &ZigbeeDeviceObjectReply::finished, node, [=](){ if (zdoReply->error() != ZigbeeDeviceObjectReply::ErrorNoError) { qCWarning(dcZigbeeDevelco()) << "Failed to bind power configuration cluster to coordinator" << zdoReply->error(); } else { qCDebug(dcZigbeeDevelco()) << "Bind power configuration cluster to coordinator finished successfully"; } // Configure attribute reporting ZigbeeClusterLibrary::AttributeReportingConfiguration reportingConfig; reportingConfig.attributeId = ZigbeeClusterPowerConfiguration::AttributeBatteryVoltage; reportingConfig.dataType = Zigbee::Uint8; reportingConfig.minReportingInterval = 60; reportingConfig.maxReportingInterval = 300; reportingConfig.reportableChange = ZigbeeDataType(static_cast(1)).data(); qCDebug(dcZigbeeDevelco()) << "Configure attribute reporting for power configuration cluster" << node << endpoint; ZigbeeClusterReply *reportingReply = endpoint->getInputCluster(ZigbeeClusterLibrary::ClusterIdPowerConfiguration)->configureReporting({reportingConfig}); connect(reportingReply, &ZigbeeClusterReply::finished, this, [=](){ if (reportingReply->error() != ZigbeeClusterReply::ErrorNoError) { qCWarning(dcZigbeeDevelco()) << "Failed configure attribute reporting on power configuration cluster" << reportingReply->error(); } else { qCDebug(dcZigbeeDevelco()) << "Attribute reporting configuration finished for on power configuration cluster" << ZigbeeClusterLibrary::parseAttributeReportingStatusRecords(reportingReply->responseFrame().payload); } }); }); } void IntegrationPluginZigbeeDevelco::configureVocReporting(ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint) { // Note: don not read initial value since it returns an invalid masurement. The device needs some time for initial measuring qCDebug(dcZigbeeDevelco()) << "Bind VOC measurement cluster to coordinator IEEE address" << node << endpoint; ZigbeeDeviceObjectReply * zdoReply = node->deviceObject()->requestBindIeeeAddress(endpoint->endpointId(), AIR_QUALITY_SENSOR_VOC_MEASUREMENT_CLUSTER_ID, hardwareManager()->zigbeeResource()->coordinatorAddress(node->networkUuid()), 0x01); connect(zdoReply, &ZigbeeDeviceObjectReply::finished, node, [=](){ if (zdoReply->error() != ZigbeeDeviceObjectReply::ErrorNoError) { qCWarning(dcZigbeeDevelco()) << "Failed to bind VOC measurement cluster to coordinator" << zdoReply->error(); } else { qCDebug(dcZigbeeDevelco()) << "Bind VOC measurement cluster to coordinator finished successfully"; } // Configure attribute reporting ZigbeeClusterLibrary::AttributeReportingConfiguration reportingConfig; reportingConfig.attributeId = AIR_QUALITY_SENSOR_VOC_MEASUREMENT_ATTRIBUTE_MEASURED_VALUE; reportingConfig.dataType = Zigbee::Uint16; reportingConfig.minReportingInterval = 60; reportingConfig.maxReportingInterval = 300; reportingConfig.reportableChange = ZigbeeDataType(static_cast(10)).data(); qCDebug(dcZigbeeDevelco()) << "Configure attribute reporting for VOC measurement cluster" << node << endpoint; ZigbeeClusterReply *reportingReply = endpoint->getInputCluster(static_cast(AIR_QUALITY_SENSOR_VOC_MEASUREMENT_CLUSTER_ID))->configureReporting({reportingConfig}, Zigbee::Develco); connect(reportingReply, &ZigbeeClusterReply::finished, this, [=](){ if (reportingReply->error() != ZigbeeClusterReply::ErrorNoError) { qCWarning(dcZigbeeDevelco()) << "Failed configure attribute reporting on VOC measurement cluster" << reportingReply->error(); } else { qCDebug(dcZigbeeDevelco()) << "Attribute reporting configuration finished for on VOC measurement cluster" << ZigbeeClusterLibrary::parseAttributeReportingStatusRecords(reportingReply->responseFrame().payload); } }); }); } void IntegrationPluginZigbeeDevelco::updateIndoorAirQuality(Thing *thing, uint voc) { if (voc <= 65) { thing->setStateValue(airQualitySensorIndoorAirQualityStateTypeId, "Excellent"); } else if (voc <= 220) { thing->setStateValue(airQualitySensorIndoorAirQualityStateTypeId, "Good"); } else if (voc <= 660) { thing->setStateValue(airQualitySensorIndoorAirQualityStateTypeId, "Moderate"); } else if (voc <= 2200) { thing->setStateValue(airQualitySensorIndoorAirQualityStateTypeId, "Poor"); } else { thing->setStateValue(airQualitySensorIndoorAirQualityStateTypeId, "Unhealthy"); } } void IntegrationPluginZigbeeDevelco::readDevelcoFirmwareVersion(ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint) { // Read manufacturer specific basic cluster attribute 0x8000 ZigbeeClusterBasic *basicCluster = endpoint->inputCluster(ZigbeeClusterLibrary::ClusterIdBasic); if (!basicCluster) { qCWarning(dcZigbeeDevelco()) << "Could not find basic cluster for manufacturer specific attribute reading on" << node << endpoint; return; } // We have to read the color capabilities ZigbeeClusterReply *reply = basicCluster->readAttributes({DEVELCO_BASIC_ATTRIBUTE_SW_VERSION}, Zigbee::Develco); connect(reply, &ZigbeeClusterReply::finished, node, [=](){ if (reply->error() != ZigbeeClusterReply::ErrorNoError) { qCWarning(dcZigbeeDevelco()) << "Failed to read manufacturer specific version attribute on" << node << endpoint << basicCluster; return; } qCDebug(dcZigbeeDevelco()) << "Reading develco manufacturer specific version attributes finished successfully"; }); } void IntegrationPluginZigbeeDevelco::readOnOffPowerAttribute(ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint) { qCDebug(dcZigbeeDevelco()) << "Reading power states of" << node << "on" << endpoint; ZigbeeClusterOnOff *onOffCluster = endpoint->inputCluster(ZigbeeClusterLibrary::ClusterIdOnOff); if (!onOffCluster) { qCWarning(dcZigbeeDevelco()) << "Could not find On/Off cluster on" << node << endpoint; } else { ZigbeeClusterReply *reply = onOffCluster->readAttributes({ZigbeeClusterOnOff::AttributeOnOff}); connect(reply, &ZigbeeClusterReply::finished, node, [=](){ if (reply->error() != ZigbeeClusterReply::ErrorNoError) { qCWarning(dcZigbeeDevelco()) << "Failed to read On/Off power attribute from" << node << endpoint << onOffCluster; } // Will be updated trough the attribute changed signal }); } } void IntegrationPluginZigbeeDevelco::readBinaryInputPresentValueAttribute(ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint) { ZigbeeClusterBinaryInput *binaryInputCluster = endpoint->inputCluster(ZigbeeClusterLibrary::ClusterIdBinaryInput); if (!binaryInputCluster) { qCWarning(dcZigbeeDevelco()) << "Could not find BinaryInput cluster on" << node << endpoint; } else { ZigbeeClusterReply *reply = binaryInputCluster->readAttributes({ZigbeeClusterBinaryInput::AttributePresentValue}); connect(reply, &ZigbeeClusterReply::finished, node, [=](){ if (reply->error() != ZigbeeClusterReply::ErrorNoError) { qCWarning(dcZigbeeDevelco()) << "Failed to read binary input value attribute from" << node << endpoint << binaryInputCluster; } // Will be updated trough the attribute changed signal }); } } void IntegrationPluginZigbeeDevelco::readIoModuleOutputPowerStates(Thing *thing) { ZigbeeNode *node = m_thingNodes.value(thing); if (!node) { qCWarning(dcZigbeeDevelco()) << "Could not find zigbee node for" << thing; return; } qCDebug(dcZigbeeDevelco()) << "Start reading power states of" << thing << node; // Read output 1 power state ZigbeeNodeEndpoint *output1Endpoint = node->getEndpoint(IO_MODULE_EP_OUTPUT1); if (!output1Endpoint) { qCWarning(dcZigbeeDevelco()) << "Could not find endpoint for output 1 on" << thing << node; } else { readOnOffPowerAttribute(node, output1Endpoint); } // Read output 2 power state ZigbeeNodeEndpoint *output2Endpoint = node->getEndpoint(IO_MODULE_EP_OUTPUT2); if (!output2Endpoint) { qCWarning(dcZigbeeDevelco()) << "Could not find endpoint for output 2 on" << thing << node; } else { readOnOffPowerAttribute(node, output2Endpoint); } } void IntegrationPluginZigbeeDevelco::readIoModuleInputPowerStates(Thing *thing) { ZigbeeNode *node = m_thingNodes.value(thing); if (!node) { qCWarning(dcZigbeeDevelco()) << "Could not find zigbee node for" << thing; return; } // Read input 1 state ZigbeeNodeEndpoint *input1Endpoint = node->getEndpoint(IO_MODULE_EP_INPUT1); if (!input1Endpoint) { qCWarning(dcZigbeeDevelco()) << "Could not find endpoint for input 1 on" << thing << node; } else { readBinaryInputPresentValueAttribute(node, input1Endpoint); } // Read input 2 state ZigbeeNodeEndpoint *input2Endpoint = node->getEndpoint(IO_MODULE_EP_INPUT2); if (!input2Endpoint) { qCWarning(dcZigbeeDevelco()) << "Could not find endpoint for input 2 on" << thing << node; } else { readBinaryInputPresentValueAttribute(node, input2Endpoint); } // Read input 3 state ZigbeeNodeEndpoint *input3Endpoint = node->getEndpoint(IO_MODULE_EP_INPUT3); if (!input3Endpoint) { qCWarning(dcZigbeeDevelco()) << "Could not find endpoint for input 3 on" << thing << node; } else { readBinaryInputPresentValueAttribute(node, input3Endpoint); } // Read input 4 state ZigbeeNodeEndpoint *input4Endpoint = node->getEndpoint(IO_MODULE_EP_INPUT4); if (!input4Endpoint) { qCWarning(dcZigbeeDevelco()) << "Could not find endpoint for input 4 on" << thing << node; } else { readBinaryInputPresentValueAttribute(node, input4Endpoint); } }