powersync-plugins/zigbeedevelco/integrationpluginzigbeedeve...

982 lines
53 KiB
C++

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
*
* 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 <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under
* contact@nymea.io or see our FAQ/Licensing Information on
* https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "integrationpluginzigbeedevelco.h"
#include "plugininfo.h"
#include "hardware/zigbee/zigbeehardwareresource.h"
#include <QDebug>
#include <zigbeeutils.h>
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<ZigbeeClusterOnOff>(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<ZigbeeClusterOnOff>(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<ZigbeeClusterBinaryInput>(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<ZigbeeClusterBinaryInput>(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<ZigbeeClusterBinaryInput>(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<ZigbeeClusterBinaryInput>(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<ZigbeeClusterTemperatureMeasurement>(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<ZigbeeClusterRelativeHumidityMeasurement>(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<ZigbeeClusterPowerConfiguration>(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<ZigbeeClusterLibrary::ClusterId>(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<ZigbeeClusterIdentify>(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<ZigbeeClusterOnOff>(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<ZigbeeClusterOnOff>(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<quint16>(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<ZigbeeClusterOnOff>(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<int>(versionAttribute.dataType().data().at(1));
int minorVersion = static_cast<int>(versionAttribute.dataType().data().at(2));
int patchVersion = static_cast<int>(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<qint16>(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<quint16>(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<quint8>(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<quint16>(10)).data();
qCDebug(dcZigbeeDevelco()) << "Configure attribute reporting for VOC measurement cluster" << node << endpoint;
ZigbeeClusterReply *reportingReply = endpoint->getInputCluster(static_cast<ZigbeeClusterLibrary::ClusterId>(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<ZigbeeClusterBasic>(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<ZigbeeClusterOnOff>(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<ZigbeeClusterBinaryInput>(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);
}
}