From 80d1a3798b0d7c452d0b2d19403aa04381489f3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Wed, 28 Apr 2021 13:05:06 +0200 Subject: [PATCH 1/5] Add zigbee develco plugin debian package configuration --- debian/control | 19 ++++++++++++++++++- nymea-plugins.pro | 1 + 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/debian/control b/debian/control index db1cc724..d7c9d12a 100644 --- a/debian/control +++ b/debian/control @@ -1037,6 +1037,22 @@ Description: nymea.io plugin to monitor the ISS position This package will install the nymea.io plugin for wheretheiss.at +Package: nymea-plugin-zigbee-develco +Architecture: any +Depends: ${shlibs:Depends}, + ${misc:Depends}, + libnymea-zigbee1, + nymea-plugins-translations, +Description: nymea.io zigbee plugin for develco things + The nymea daemon is a plugin based IoT (Internet of Things) server. The + server works like a translator for devices, things and services and + allows them to interact. + With the powerful rule engine you are able to connect any device available + in the system and create individual scenes and behaviors for your environment. + . + This package will install the nymea.io plugin for Develco + + Package: nymea-plugin-zigbee-lumi Architecture: any Depends: ${shlibs:Depends}, @@ -1189,7 +1205,8 @@ Description: Plugins for nymea IoT server - the default plugin collection Package: nymea-plugins-zigbee Section: libs Architecture: all -Depends: nymea-plugin-zigbee-lumi, +Depends: nymea-plugin-zigbee-develco, + nymea-plugin-zigbee-lumi, nymea-plugin-zigbee-generic, nymea-plugin-zigbee-generic-lights, nymea-plugin-zigbee-philipshue, diff --git a/nymea-plugins.pro b/nymea-plugins.pro index eb037d01..e1279247 100644 --- a/nymea-plugins.pro +++ b/nymea-plugins.pro @@ -67,6 +67,7 @@ PLUGIN_DIRS = \ wakeonlan \ wemo \ ws2812fx \ + zigbeedevelco \ zigbeegeneric \ zigbeegenericlights \ zigbeelumi \ From e733644d6a9d3eba858ffd46cb5f6697c10d1375 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Wed, 28 Apr 2021 13:05:47 +0200 Subject: [PATCH 2/5] Add zigbee develco plugin containing IO Module --- debian/nymea-plugin-zigbee-develco.install.in | 1 + zigbeedevelco/README.md | 18 + .../integrationpluginzigbeedevelco.cpp | 643 ++++++++++++++++++ .../integrationpluginzigbeedevelco.h | 123 ++++ .../integrationpluginzigbeedevelco.json | 144 ++++ zigbeedevelco/meta.json | 12 + zigbeedevelco/zigbee.svg | 179 +++++ zigbeedevelco/zigbeedevelco.pro | 12 + 8 files changed, 1132 insertions(+) create mode 100644 debian/nymea-plugin-zigbee-develco.install.in create mode 100644 zigbeedevelco/README.md create mode 100644 zigbeedevelco/integrationpluginzigbeedevelco.cpp create mode 100644 zigbeedevelco/integrationpluginzigbeedevelco.h create mode 100644 zigbeedevelco/integrationpluginzigbeedevelco.json create mode 100644 zigbeedevelco/meta.json create mode 100644 zigbeedevelco/zigbee.svg create mode 100644 zigbeedevelco/zigbeedevelco.pro diff --git a/debian/nymea-plugin-zigbee-develco.install.in b/debian/nymea-plugin-zigbee-develco.install.in new file mode 100644 index 00000000..5d94b1e4 --- /dev/null +++ b/debian/nymea-plugin-zigbee-develco.install.in @@ -0,0 +1 @@ +usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_integrationpluginzigbeedevelco.so diff --git a/zigbeedevelco/README.md b/zigbeedevelco/README.md new file mode 100644 index 00000000..26ba5bf2 --- /dev/null +++ b/zigbeedevelco/README.md @@ -0,0 +1,18 @@ +# Develco ZigBee + +This plugin allows to interact with ZigBee devices from [Develco](https://www.develcoproducts.com/). + +## Supported Things + +In order to bring a ZigBee device into the nymea ZigBee network, the network needs to be opened for joining before you perform the device pairing instructions. The joining process can take up to 30 seconds. If the device does not show up, please restart the pairing process. + +### IO Module + +The [Develco IO Module](https://www.develcoproducts.com/products/smart-relays/io-module/) is fully supported. + +**Pairing instructions**: Open the ZigBee network for joining. Press and hold the reset button of the IO module until the LED blinks constanly. + +## Requirements + +* A compatible ZigBee controller and a running ZigBee network in nymea. You can find more information about supported controllers and ZigBee network configurations [here](https://nymea.io/documentation/users/usage/configuration#zigbee). + diff --git a/zigbeedevelco/integrationpluginzigbeedevelco.cpp b/zigbeedevelco/integrationpluginzigbeedevelco.cpp new file mode 100644 index 00000000..c0c256c5 --- /dev/null +++ b/zigbeedevelco/integrationpluginzigbeedevelco.cpp @@ -0,0 +1,643 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* +* 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_networkUuidParamTypeIds[ioModuleThingClassId] = ioModuleThingNetworkUuidParamTypeId; + + m_connectedStateTypeIds[ioModuleThingClassId] = ioModuleConnectedStateTypeId; + + m_signalStrengthStateTypeIds[ioModuleThingClassId] = ioModuleSignalStrengthStateTypeId; +} + +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; + } + } + + 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_ATTRIBUTE_SW_VERSION)) { + thing->setStateValue(ioModuleVersionStateTypeId, parseDevelcoVersionString(primaryEndpoint)); + } else { + readDevelcoFirmwareVersion(node, primaryEndpoint); + } + + connect(basicCluster, &ZigbeeCluster::attributeChanged, this, [=](const ZigbeeClusterAttribute &attribute){ + if (attribute.id() == DEVELCO_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); + }); + } + } + } + + 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; + } else { + 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; + } else { + 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; + } else { + 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; + } else { + 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; + } + } + } + } + + 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_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_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)); + configureOnOffPowerReporting(node, node->getEndpoint(IO_MODULE_EP_OUTPUT1)); + configureOnOffPowerReporting(node, node->getEndpoint(IO_MODULE_EP_OUTPUT1)); +} + +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 for lock state + ZigbeeClusterLibrary::AttributeReportingConfiguration reportingConfig; + reportingConfig.attributeId = ZigbeeClusterOnOff::AttributeOnOff; + reportingConfig.dataType = Zigbee::Bool; + reportingConfig.reportableChange = ZigbeeDataType(static_cast(1)).data(); + + 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 for lock state + ZigbeeClusterLibrary::AttributeReportingConfiguration reportingConfig; + reportingConfig.attributeId = ZigbeeClusterBinaryInput::AttributePresentValue; + reportingConfig.dataType = Zigbee::Bool; + reportingConfig.reportableChange = ZigbeeDataType(static_cast(1)).data(); + + 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::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_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); + } +} + diff --git a/zigbeedevelco/integrationpluginzigbeedevelco.h b/zigbeedevelco/integrationpluginzigbeedevelco.h new file mode 100644 index 00000000..b7b0093a --- /dev/null +++ b/zigbeedevelco/integrationpluginzigbeedevelco.h @@ -0,0 +1,123 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* 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 +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef INTEGRATIONPLUGINZIGBEEDEVELCO_H +#define INTEGRATIONPLUGINZIGBEEDEVELCO_H + +#include "integrations/integrationplugin.h" +#include "hardware/zigbee/zigbeehandler.h" +#include "plugintimer.h" + +#include + +#include + +/* IO Module + * + * https://www.develcoproducts.com/media/1967/iomzb-110-technical-manual-io-module.pdf + * + * Profile 0xC0C9 (Develco Products private profile) + * Endpoints: + * - 0x70 Simple sensor: input 1 + * - 0x71 Simple sensor: input 2 + * - 0x72 Simple sensor: input 3 + * - 0x74 Simple sensor: input 4 + * - 0x75 On/Off Output : relay 1 + * - 0x76 On/Off Output : relay 2 + */ +#define IO_MODULE_EP_INPUT1 0x70 +#define IO_MODULE_EP_INPUT2 0x71 +#define IO_MODULE_EP_INPUT3 0x72 +#define IO_MODULE_EP_INPUT4 0x73 +#define IO_MODULE_EP_OUTPUT1 0x74 +#define IO_MODULE_EP_OUTPUT2 0x75 + + +/* Develco manufacturer specific Basic cluster attributes + * 0x8000 Primary SW version + * 0x8010 Primary Bootloader SW version + * 0x8020 Primary HW version + * 0x8030 Primary HW name + * 0x8050 Primary SW version 3rd party + */ +#define DEVELCO_ATTRIBUTE_SW_VERSION 0x8000 +#define DEVELCO_ATTRIBUTE_BOOTLOADER_VERSION 0x8010 +#define DEVELCO_ATTRIBUTE_HARDWARE_VERSION 0x8020 +#define DEVELCO_ATTRIBUTE_HARDWARE_NAME 0x8030 +#define DEVELCO_ATTRIBUTE_3RD_PARTY_SW_VERSION 0x8050 + + +class IntegrationPluginZigbeeDevelco: public IntegrationPlugin, public ZigbeeHandler +{ + Q_OBJECT + + Q_PLUGIN_METADATA(IID "io.nymea.IntegrationPlugin" FILE "integrationpluginzigbeedevelco.json") + Q_INTERFACES(IntegrationPlugin) + +public: + explicit IntegrationPluginZigbeeDevelco(); + + QString name() const override; + bool handleNode(ZigbeeNode *node, const QUuid &networkUuid) override; + void handleRemoveNode(ZigbeeNode *node, const QUuid &networkUuid) override; + + void init() override; + void setupThing(ThingSetupInfo *info) override; + void postSetupThing(Thing *thing) override; + void executeAction(ThingActionInfo *info) override; + void thingRemoved(Thing *thing) override; + +private: + QHash m_thingNodes; + + QHash m_ieeeAddressParamTypeIds; + QHash m_networkUuidParamTypeIds; + QHash m_connectedStateTypeIds; + QHash m_signalStrengthStateTypeIds; + + // Get the endpoint for the given thing + void createThing(const ThingClassId &thingClassId, const QUuid &networkUuid, ZigbeeNode *node); + + QString parseDevelcoVersionString(ZigbeeNodeEndpoint *endpoint); + + void initIoModule(ZigbeeNode *node); + void configureOnOffPowerReporting(ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint); + void configureBinaryInputReporting(ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint); + + void readDevelcoFirmwareVersion(ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint); + void readOnOffPowerAttribute(ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint); + void readBinaryInputPresentValueAttribute(ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint); + + void readIoModuleOutputPowerStates(Thing *thing); + void readIoModuleInputPowerStates(Thing *thing); + +}; + +#endif // INTEGRATIONPLUGINZIGBEEDEVELCO_H diff --git a/zigbeedevelco/integrationpluginzigbeedevelco.json b/zigbeedevelco/integrationpluginzigbeedevelco.json new file mode 100644 index 00000000..5517d3b8 --- /dev/null +++ b/zigbeedevelco/integrationpluginzigbeedevelco.json @@ -0,0 +1,144 @@ +{ + "name": "ZigbeeDevelco", + "displayName": "Zigbee Develco", + "id": "1b9e976d-e842-40e1-9fad-87d71a69c721", + "vendors": [ + { + "name": "develco", + "displayName": "Develco", + "id": "e4b36f3e-ccdc-4a88-8968-39025c3ec742", + "thingClasses": [ + { + "id": "3fb419ef-795d-4f4d-b801-e7eaff16cdb0", + "name": "ioModule", + "displayName": "IO Module", + "createMethods": ["auto"], + "interfaces": ["wirelessconnectable", "alert"], + "paramTypes": [ + { + "id": "d9a3afa7-c460-43e7-bc84-c8b5ff1adf44", + "name": "ieeeAddress", + "displayName": "IEEE adress", + "type": "QString", + "defaultValue": "00:00:00:00:00:00:00:00" + }, + { + "id": "f4c83d2b-06e1-4206-98b7-e3ace79ff447", + "name": "networkUuid", + "displayName": "Zigbee network UUID", + "type": "QString", + "defaultValue": "" + } + ], + "settingsTypes": [ + { + "id": "c2806b97-bf94-4ad2-ae22-5b5d7d1eaf5a", + "name": "impulseDuration", + "displayName": "Impulse duration", + "type": "uint", + "unit": "MilliSeconds", + "defaultValue": 100, + "minValue": 10 + } + ], + "stateTypes": [ + { + "id": "16d5ebb7-21d1-4294-98a6-ca06eea8e3a3", + "name": "connected", + "displayName": "Connected", + "displayNameEvent": "Connected changed", + "type": "bool", + "cached": false, + "defaultValue": false + }, + { + "id": "0e09e6a6-8b3f-4b63-acb1-ac04ab31957a", + "name": "signalStrength", + "displayName": "Signal strength", + "displayNameEvent": "Signal strength changed", + "defaultValue": 0, + "maxValue": 100, + "minValue": 0, + "type": "uint", + "unit": "Percentage" + }, + { + "id": "f28c9a61-b8ab-419c-bd85-58692df26ac7", + "name": "version", + "displayName": "Version", + "displayNameEvent": "Version changed", + "type": "QString", + "cached": true, + "defaultValue": "" + }, + { + "id": "aaeda2c6-439a-452a-b829-45d6249bdee6", + "name": "output1", + "displayName": "Output 1", + "displayNameEvent": "Output 1 changed", + "displayNameAction": "Set output 1", + "type": "bool", + "writable": true, + "defaultValue": false, + "ioType": "digitalOutput" + }, + { + "id": "105cf314-35b5-4a8b-8e6d-d011009f97ff", + "name": "output2", + "displayName": "Output 2", + "displayNameEvent": "Output 2 changed", + "displayNameAction": "Set output 2", + "type": "bool", + "writable": true, + "defaultValue": false, + "ioType": "digitalOutput" + }, + { + "id": "bc23c7e8-f4ad-47c4-b938-f9af2dbd3393", + "name": "input1", + "displayName": "Input 1", + "displayNameEvent": "Input 1 changed", + "type": "bool", + "defaultValue": false, + "ioType": "digitalInput" + }, + { + "id": "065b0dc0-6d31-40ec-b356-02fea57e2fe1", + "name": "input2", + "displayName": "Input 2", + "displayNameEvent": "Input 2 changed", + "type": "bool", + "defaultValue": false, + "ioType": "digitalInput" + }, + { + "id": "8f2b052a-30b7-49aa-a8d4-503cb0b9b66b", + "name": "input3", + "displayName": "Input 3", + "displayNameEvent": "Input 3 changed", + "type": "bool", + "defaultValue": false, + "ioType": "digitalInput" + }, + { + "id": "caef986c-da13-4ef3-ab81-316244c7be1e", + "name": "input4", + "displayName": "Input 4", + "displayNameEvent": "Input 4 changed", + "type": "bool", + "defaultValue": false, + "ioType": "digitalInput" + } + ], + "actionTypes": [ + { + "id": "62c5562a-8bdf-49b4-8e1d-f27442b2b360", + "name": "alert", + "displayName": "Identify" + } + ] + } + ] + } + ] +} diff --git a/zigbeedevelco/meta.json b/zigbeedevelco/meta.json new file mode 100644 index 00000000..e4133b6f --- /dev/null +++ b/zigbeedevelco/meta.json @@ -0,0 +1,12 @@ +{ + "title": "ZigBee Develco", + "tagline": "Connect and control native ZigBee devices from Develco.", + "icon": "zigbee.svg", + "stability": "consumer", + "offline": true, + "technologies": [ + "zigbee" + ], + "categories": [ + ] +} diff --git a/zigbeedevelco/zigbee.svg b/zigbeedevelco/zigbee.svg new file mode 100644 index 00000000..5366feb6 --- /dev/null +++ b/zigbeedevelco/zigbee.svg @@ -0,0 +1,179 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/zigbeedevelco/zigbeedevelco.pro b/zigbeedevelco/zigbeedevelco.pro new file mode 100644 index 00000000..3f30c9ce --- /dev/null +++ b/zigbeedevelco/zigbeedevelco.pro @@ -0,0 +1,12 @@ +include(../plugins.pri) + +PKGCONFIG += nymea-zigbee + +SOURCES += \ + integrationpluginzigbeedevelco.cpp + +HEADERS += \ + integrationpluginzigbeedevelco.h + + + From 948b53eddd80a448f49aaea19d516a931c617432 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Wed, 28 Apr 2021 13:19:10 +0200 Subject: [PATCH 3/5] Add translations for zigbee develco plugin --- ...e976d-e842-40e1-9fad-87d71a69c721-en_US.ts | 202 ++++++++++++++++++ 1 file changed, 202 insertions(+) create mode 100644 zigbeedevelco/translations/1b9e976d-e842-40e1-9fad-87d71a69c721-en_US.ts diff --git a/zigbeedevelco/translations/1b9e976d-e842-40e1-9fad-87d71a69c721-en_US.ts b/zigbeedevelco/translations/1b9e976d-e842-40e1-9fad-87d71a69c721-en_US.ts new file mode 100644 index 00000000..a0b47a28 --- /dev/null +++ b/zigbeedevelco/translations/1b9e976d-e842-40e1-9fad-87d71a69c721-en_US.ts @@ -0,0 +1,202 @@ + + + + + ZigbeeDevelco + + + + Connected + The name of the ParamType (ThingClass: ioModule, EventType: connected, ID: {16d5ebb7-21d1-4294-98a6-ca06eea8e3a3}) +---------- +The name of the StateType ({16d5ebb7-21d1-4294-98a6-ca06eea8e3a3}) of ThingClass ioModule + + + + + Connected changed + The name of the EventType ({16d5ebb7-21d1-4294-98a6-ca06eea8e3a3}) of ThingClass ioModule + + + + + Develco + The name of the vendor ({e4b36f3e-ccdc-4a88-8968-39025c3ec742}) + + + + + IEEE adress + The name of the ParamType (ThingClass: ioModule, Type: thing, ID: {d9a3afa7-c460-43e7-bc84-c8b5ff1adf44}) + + + + + IO Module + The name of the ThingClass ({3fb419ef-795d-4f4d-b801-e7eaff16cdb0}) + + + + + Identify + The name of the ActionType ({62c5562a-8bdf-49b4-8e1d-f27442b2b360}) of ThingClass ioModule + + + + + Impulse duration + The name of the ParamType (ThingClass: ioModule, Type: settings, ID: {c2806b97-bf94-4ad2-ae22-5b5d7d1eaf5a}) + + + + + + Input 1 + The name of the ParamType (ThingClass: ioModule, EventType: input1, ID: {bc23c7e8-f4ad-47c4-b938-f9af2dbd3393}) +---------- +The name of the StateType ({bc23c7e8-f4ad-47c4-b938-f9af2dbd3393}) of ThingClass ioModule + + + + + Input 1 changed + The name of the EventType ({bc23c7e8-f4ad-47c4-b938-f9af2dbd3393}) of ThingClass ioModule + + + + + + Input 2 + The name of the ParamType (ThingClass: ioModule, EventType: input2, ID: {065b0dc0-6d31-40ec-b356-02fea57e2fe1}) +---------- +The name of the StateType ({065b0dc0-6d31-40ec-b356-02fea57e2fe1}) of ThingClass ioModule + + + + + Input 2 changed + The name of the EventType ({065b0dc0-6d31-40ec-b356-02fea57e2fe1}) of ThingClass ioModule + + + + + + Input 3 + The name of the ParamType (ThingClass: ioModule, EventType: input3, ID: {8f2b052a-30b7-49aa-a8d4-503cb0b9b66b}) +---------- +The name of the StateType ({8f2b052a-30b7-49aa-a8d4-503cb0b9b66b}) of ThingClass ioModule + + + + + Input 3 changed + The name of the EventType ({8f2b052a-30b7-49aa-a8d4-503cb0b9b66b}) of ThingClass ioModule + + + + + + Input 4 + The name of the ParamType (ThingClass: ioModule, EventType: input4, ID: {caef986c-da13-4ef3-ab81-316244c7be1e}) +---------- +The name of the StateType ({caef986c-da13-4ef3-ab81-316244c7be1e}) of ThingClass ioModule + + + + + Input 4 changed + The name of the EventType ({caef986c-da13-4ef3-ab81-316244c7be1e}) of ThingClass ioModule + + + + + + + Output 1 + The name of the ParamType (ThingClass: ioModule, ActionType: output1, ID: {aaeda2c6-439a-452a-b829-45d6249bdee6}) +---------- +The name of the ParamType (ThingClass: ioModule, EventType: output1, ID: {aaeda2c6-439a-452a-b829-45d6249bdee6}) +---------- +The name of the StateType ({aaeda2c6-439a-452a-b829-45d6249bdee6}) of ThingClass ioModule + + + + + Output 1 changed + The name of the EventType ({aaeda2c6-439a-452a-b829-45d6249bdee6}) of ThingClass ioModule + + + + + + + Output 2 + The name of the ParamType (ThingClass: ioModule, ActionType: output2, ID: {105cf314-35b5-4a8b-8e6d-d011009f97ff}) +---------- +The name of the ParamType (ThingClass: ioModule, EventType: output2, ID: {105cf314-35b5-4a8b-8e6d-d011009f97ff}) +---------- +The name of the StateType ({105cf314-35b5-4a8b-8e6d-d011009f97ff}) of ThingClass ioModule + + + + + Output 2 changed + The name of the EventType ({105cf314-35b5-4a8b-8e6d-d011009f97ff}) of ThingClass ioModule + + + + + Set output 1 + The name of the ActionType ({aaeda2c6-439a-452a-b829-45d6249bdee6}) of ThingClass ioModule + + + + + Set output 2 + The name of the ActionType ({105cf314-35b5-4a8b-8e6d-d011009f97ff}) of ThingClass ioModule + + + + + + Signal strength + The name of the ParamType (ThingClass: ioModule, EventType: signalStrength, ID: {0e09e6a6-8b3f-4b63-acb1-ac04ab31957a}) +---------- +The name of the StateType ({0e09e6a6-8b3f-4b63-acb1-ac04ab31957a}) of ThingClass ioModule + + + + + Signal strength changed + The name of the EventType ({0e09e6a6-8b3f-4b63-acb1-ac04ab31957a}) of ThingClass ioModule + + + + + + Version + The name of the ParamType (ThingClass: ioModule, EventType: version, ID: {f28c9a61-b8ab-419c-bd85-58692df26ac7}) +---------- +The name of the StateType ({f28c9a61-b8ab-419c-bd85-58692df26ac7}) of ThingClass ioModule + + + + + Version changed + The name of the EventType ({f28c9a61-b8ab-419c-bd85-58692df26ac7}) of ThingClass ioModule + + + + + Zigbee Develco + The name of the plugin ZigbeeDevelco ({1b9e976d-e842-40e1-9fad-87d71a69c721}) + + + + + Zigbee network UUID + The name of the ParamType (ThingClass: ioModule, Type: thing, ID: {f4c83d2b-06e1-4206-98b7-e3ace79ff447}) + + + + From 263d75b6ef0f72b147d64560717edc3d9f6fd943 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Tue, 11 May 2021 11:41:38 +0200 Subject: [PATCH 4/5] Fix input states and implement impulse actions using the settings impulse duration value on zigbee layer --- .../integrationpluginzigbeedevelco.cpp | 148 ++++++++++++------ .../integrationpluginzigbeedevelco.json | 12 +- ...e976d-e842-40e1-9fad-87d71a69c721-en_US.ts | 88 ++++++----- 3 files changed, 161 insertions(+), 87 deletions(-) diff --git a/zigbeedevelco/integrationpluginzigbeedevelco.cpp b/zigbeedevelco/integrationpluginzigbeedevelco.cpp index c0c256c5..2e91b338 100644 --- a/zigbeedevelco/integrationpluginzigbeedevelco.cpp +++ b/zigbeedevelco/integrationpluginzigbeedevelco.cpp @@ -137,9 +137,9 @@ void IntegrationPluginZigbeeDevelco::setupThing(ThingSetupInfo *info) } connect(basicCluster, &ZigbeeCluster::attributeChanged, this, [=](const ZigbeeClusterAttribute &attribute){ - if (attribute.id() == DEVELCO_ATTRIBUTE_SW_VERSION) { - thing->setStateValue(ioModuleVersionStateTypeId, parseDevelcoVersionString(primaryEndpoint)); - } + if (attribute.id() == DEVELCO_ATTRIBUTE_SW_VERSION) { + thing->setStateValue(ioModuleVersionStateTypeId, parseDevelcoVersionString(primaryEndpoint)); + } }); } @@ -338,28 +338,28 @@ void IntegrationPluginZigbeeDevelco::executeAction(ThingActionInfo *info) qCWarning(dcZigbeeDevelco()) << "Could not find endpoint for output 1 on" << thing << node; info->finish(Thing::ThingErrorHardwareFailure); return; - } else { - 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; - } else { - 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; - } } + + 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 @@ -372,28 +372,70 @@ void IntegrationPluginZigbeeDevelco::executeAction(ThingActionInfo *info) qCWarning(dcZigbeeDevelco()) << "Could not find endpoint for output 2 on" << thing << node; info->finish(Thing::ThingErrorHardwareFailure); return; - } else { - 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; - } else { - 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; - } } + + 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; } } @@ -456,8 +498,16 @@ 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_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::configureOnOffPowerReporting(ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint) @@ -474,8 +524,9 @@ void IntegrationPluginZigbeeDevelco::configureOnOffPowerReporting(ZigbeeNode *no // Configure attribute reporting for lock state ZigbeeClusterLibrary::AttributeReportingConfiguration reportingConfig; reportingConfig.attributeId = ZigbeeClusterOnOff::AttributeOnOff; + reportingConfig.minReportingInterval = 0; + reportingConfig.maxReportingInterval = 600; reportingConfig.dataType = Zigbee::Bool; - reportingConfig.reportableChange = ZigbeeDataType(static_cast(1)).data(); qCDebug(dcZigbeeDevelco()) << "Configure attribute reporting for on/off cluster" << node << endpoint; ZigbeeClusterReply *reportingReply = endpoint->getInputCluster(ZigbeeClusterLibrary::ClusterIdOnOff)->configureReporting({reportingConfig}); @@ -503,8 +554,9 @@ void IntegrationPluginZigbeeDevelco::configureBinaryInputReporting(ZigbeeNode *n // Configure attribute reporting for lock state ZigbeeClusterLibrary::AttributeReportingConfiguration reportingConfig; reportingConfig.attributeId = ZigbeeClusterBinaryInput::AttributePresentValue; + reportingConfig.minReportingInterval = 0; + reportingConfig.maxReportingInterval = 600; reportingConfig.dataType = Zigbee::Bool; - reportingConfig.reportableChange = ZigbeeDataType(static_cast(1)).data(); qCDebug(dcZigbeeDevelco()) << "Configure attribute reporting for binary input cluster" << node << endpoint; ZigbeeClusterReply *reportingReply = endpoint->getInputCluster(ZigbeeClusterLibrary::ClusterIdBinaryInput)->configureReporting({reportingConfig}); diff --git a/zigbeedevelco/integrationpluginzigbeedevelco.json b/zigbeedevelco/integrationpluginzigbeedevelco.json index 5517d3b8..bbf58f73 100644 --- a/zigbeedevelco/integrationpluginzigbeedevelco.json +++ b/zigbeedevelco/integrationpluginzigbeedevelco.json @@ -38,7 +38,7 @@ "type": "uint", "unit": "MilliSeconds", "defaultValue": 100, - "minValue": 10 + "minValue": 100 } ], "stateTypes": [ @@ -135,6 +135,16 @@ "id": "62c5562a-8bdf-49b4-8e1d-f27442b2b360", "name": "alert", "displayName": "Identify" + }, + { + "id": "45ec5c65-0719-4148-82ea-3e69b34be939", + "name": "impulseOutput1", + "displayName": "Impulse output 1" + }, + { + "id": "d780946c-4ddf-4b59-a669-dbf6ecfda5d6", + "name": "impulseOutput2", + "displayName": "Impulse output 2" } ] } diff --git a/zigbeedevelco/translations/1b9e976d-e842-40e1-9fad-87d71a69c721-en_US.ts b/zigbeedevelco/translations/1b9e976d-e842-40e1-9fad-87d71a69c721-en_US.ts index a0b47a28..e5e1e220 100644 --- a/zigbeedevelco/translations/1b9e976d-e842-40e1-9fad-87d71a69c721-en_US.ts +++ b/zigbeedevelco/translations/1b9e976d-e842-40e1-9fad-87d71a69c721-en_US.ts @@ -4,8 +4,8 @@ ZigbeeDevelco - - + + Connected The name of the ParamType (ThingClass: ioModule, EventType: connected, ID: {16d5ebb7-21d1-4294-98a6-ca06eea8e3a3}) ---------- @@ -13,44 +13,56 @@ The name of the StateType ({16d5ebb7-21d1-4294-98a6-ca06eea8e3a3}) of ThingClass - + Connected changed The name of the EventType ({16d5ebb7-21d1-4294-98a6-ca06eea8e3a3}) of ThingClass ioModule - + Develco The name of the vendor ({e4b36f3e-ccdc-4a88-8968-39025c3ec742}) - + IEEE adress The name of the ParamType (ThingClass: ioModule, Type: thing, ID: {d9a3afa7-c460-43e7-bc84-c8b5ff1adf44}) - + IO Module The name of the ThingClass ({3fb419ef-795d-4f4d-b801-e7eaff16cdb0}) - + Identify The name of the ActionType ({62c5562a-8bdf-49b4-8e1d-f27442b2b360}) of ThingClass ioModule - + Impulse duration The name of the ParamType (ThingClass: ioModule, Type: settings, ID: {c2806b97-bf94-4ad2-ae22-5b5d7d1eaf5a}) - - + + Impulse output 1 + The name of the ActionType ({45ec5c65-0719-4148-82ea-3e69b34be939}) of ThingClass ioModule + + + + + Impulse output 2 + The name of the ActionType ({d780946c-4ddf-4b59-a669-dbf6ecfda5d6}) of ThingClass ioModule + + + + + Input 1 The name of the ParamType (ThingClass: ioModule, EventType: input1, ID: {bc23c7e8-f4ad-47c4-b938-f9af2dbd3393}) ---------- @@ -58,14 +70,14 @@ The name of the StateType ({bc23c7e8-f4ad-47c4-b938-f9af2dbd3393}) of ThingClass - + Input 1 changed The name of the EventType ({bc23c7e8-f4ad-47c4-b938-f9af2dbd3393}) of ThingClass ioModule - - + + Input 2 The name of the ParamType (ThingClass: ioModule, EventType: input2, ID: {065b0dc0-6d31-40ec-b356-02fea57e2fe1}) ---------- @@ -73,14 +85,14 @@ The name of the StateType ({065b0dc0-6d31-40ec-b356-02fea57e2fe1}) of ThingClass - + Input 2 changed The name of the EventType ({065b0dc0-6d31-40ec-b356-02fea57e2fe1}) of ThingClass ioModule - - + + Input 3 The name of the ParamType (ThingClass: ioModule, EventType: input3, ID: {8f2b052a-30b7-49aa-a8d4-503cb0b9b66b}) ---------- @@ -88,14 +100,14 @@ The name of the StateType ({8f2b052a-30b7-49aa-a8d4-503cb0b9b66b}) of ThingClass - + Input 3 changed The name of the EventType ({8f2b052a-30b7-49aa-a8d4-503cb0b9b66b}) of ThingClass ioModule - - + + Input 4 The name of the ParamType (ThingClass: ioModule, EventType: input4, ID: {caef986c-da13-4ef3-ab81-316244c7be1e}) ---------- @@ -103,15 +115,15 @@ The name of the StateType ({caef986c-da13-4ef3-ab81-316244c7be1e}) of ThingClass - + Input 4 changed The name of the EventType ({caef986c-da13-4ef3-ab81-316244c7be1e}) of ThingClass ioModule - - - + + + Output 1 The name of the ParamType (ThingClass: ioModule, ActionType: output1, ID: {aaeda2c6-439a-452a-b829-45d6249bdee6}) ---------- @@ -121,15 +133,15 @@ The name of the StateType ({aaeda2c6-439a-452a-b829-45d6249bdee6}) of ThingClass - + Output 1 changed The name of the EventType ({aaeda2c6-439a-452a-b829-45d6249bdee6}) of ThingClass ioModule - - - + + + Output 2 The name of the ParamType (ThingClass: ioModule, ActionType: output2, ID: {105cf314-35b5-4a8b-8e6d-d011009f97ff}) ---------- @@ -139,26 +151,26 @@ The name of the StateType ({105cf314-35b5-4a8b-8e6d-d011009f97ff}) of ThingClass - + Output 2 changed The name of the EventType ({105cf314-35b5-4a8b-8e6d-d011009f97ff}) of ThingClass ioModule - + Set output 1 The name of the ActionType ({aaeda2c6-439a-452a-b829-45d6249bdee6}) of ThingClass ioModule - + Set output 2 The name of the ActionType ({105cf314-35b5-4a8b-8e6d-d011009f97ff}) of ThingClass ioModule - - + + Signal strength The name of the ParamType (ThingClass: ioModule, EventType: signalStrength, ID: {0e09e6a6-8b3f-4b63-acb1-ac04ab31957a}) ---------- @@ -166,14 +178,14 @@ The name of the StateType ({0e09e6a6-8b3f-4b63-acb1-ac04ab31957a}) of ThingClass - + Signal strength changed The name of the EventType ({0e09e6a6-8b3f-4b63-acb1-ac04ab31957a}) of ThingClass ioModule - - + + Version The name of the ParamType (ThingClass: ioModule, EventType: version, ID: {f28c9a61-b8ab-419c-bd85-58692df26ac7}) ---------- @@ -181,19 +193,19 @@ The name of the StateType ({f28c9a61-b8ab-419c-bd85-58692df26ac7}) of ThingClass - + Version changed The name of the EventType ({f28c9a61-b8ab-419c-bd85-58692df26ac7}) of ThingClass ioModule - + Zigbee Develco The name of the plugin ZigbeeDevelco ({1b9e976d-e842-40e1-9fad-87d71a69c721}) - + Zigbee network UUID The name of the ParamType (ThingClass: ioModule, Type: thing, ID: {f4c83d2b-06e1-4206-98b7-e3ace79ff447}) From 2b63f200775ca7920427f6d684fd85ac15135891 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Mon, 28 Jun 2021 09:30:59 +0200 Subject: [PATCH 5/5] Update Develco plugin information and readme file --- zigbeedevelco/README.md | 6 ++ zigbeedevelco/develco.png | Bin 0 -> 10362 bytes zigbeedevelco/meta.json | 2 +- zigbeedevelco/zigbee.svg | 179 -------------------------------------- 4 files changed, 7 insertions(+), 180 deletions(-) create mode 100644 zigbeedevelco/develco.png delete mode 100644 zigbeedevelco/zigbee.svg diff --git a/zigbeedevelco/README.md b/zigbeedevelco/README.md index 26ba5bf2..b93dd8e4 100644 --- a/zigbeedevelco/README.md +++ b/zigbeedevelco/README.md @@ -12,6 +12,12 @@ The [Develco IO Module](https://www.develcoproducts.com/products/smart-relays/io **Pairing instructions**: Open the ZigBee network for joining. Press and hold the reset button of the IO module until the LED blinks constanly. +### Air Quality sensor + +The [Develco Air Quality sensor](https://www.develcoproducts.com/products/sensors-and-alarms/air-quality-sensor/) is fully supported. + +**Pairing instructions**: Open the ZigBee network for joining. Press and hold the reset button in the sensor casing until the LED blinks constanly. + ## Requirements * A compatible ZigBee controller and a running ZigBee network in nymea. You can find more information about supported controllers and ZigBee network configurations [here](https://nymea.io/documentation/users/usage/configuration#zigbee). diff --git a/zigbeedevelco/develco.png b/zigbeedevelco/develco.png new file mode 100644 index 0000000000000000000000000000000000000000..0e50ef133b936531651abda6afe5c1aeb57b8862 GIT binary patch literal 10362 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 I75j+)0w(4Cu>b%7 literal 0 HcmV?d00001 diff --git a/zigbeedevelco/meta.json b/zigbeedevelco/meta.json index e4133b6f..0e90b9f4 100644 --- a/zigbeedevelco/meta.json +++ b/zigbeedevelco/meta.json @@ -1,7 +1,7 @@ { "title": "ZigBee Develco", "tagline": "Connect and control native ZigBee devices from Develco.", - "icon": "zigbee.svg", + "icon": "develcologo.png", "stability": "consumer", "offline": true, "technologies": [ diff --git a/zigbeedevelco/zigbee.svg b/zigbeedevelco/zigbee.svg deleted file mode 100644 index 5366feb6..00000000 --- a/zigbeedevelco/zigbee.svg +++ /dev/null @@ -1,179 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - -