diff --git a/debian/control b/debian/control index e1975c7a..770c31f1 100644 --- a/debian/control +++ b/debian/control @@ -6,6 +6,7 @@ Build-depends: libboblight-dev, debhelper (>= 0.0.0), libnymea1-dev (>= 0.17), libnymea-mqtt-dev, + libnymea-zigbee-dev, libqt5serialport5-dev, libqt5websockets5-dev, nymea-dev-tools:native, @@ -1013,6 +1014,54 @@ Description: nymea.io plugin to connect to your Tado account This package will install the nymea.io plugin for Tado +Package: nymea-plugin-zigbee-lumi +Architecture: any +Depends: ${shlibs:Depends}, + ${misc:Depends}, + libnymea-zigbee1, + nymea-plugins-translations, +Description: nymea.io zigbee plugin for lumi/aquara/xiaomi 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 Lumi + + +Package: nymea-plugin-zigbee-generic +Architecture: any +Depends: ${shlibs:Depends}, + ${misc:Depends}, + libnymea-zigbee1, + nymea-plugins-translations, +Description: nymea.io zigbee plugin for different generic recognizable devices + 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 generic recognizable zigbee devices + + +Package: nymea-plugin-zigbee-generic-lights +Architecture: any +Depends: ${shlibs:Depends}, + ${misc:Depends}, + libnymea-zigbee1, + nymea-plugins-translations, +Description: nymea.io zigbee plugin for different generic recognizable lights + 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 generic recognizable lights + + Package: nymea-plugins-translations Section: misc Architecture: all @@ -1079,6 +1128,21 @@ Description: Plugins for nymea IoT server - the default plugin collection . This package will install the default nymea plugins. +Package: nymea-plugins-zigbee +Section: libs +Architecture: all +Depends: nymea-plugin-zigbee-lumi, + nymea-plugin-zigbee-generic, + nymea-plugin-zigbee-generic-lights +Description: Zigbee plugins for nymea IoT server - meta package for all zigbee replated plugins + 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 all nymea zigbee related plugins. + Package: nymea-plugins-maker Section: libs @@ -1125,6 +1189,7 @@ Package: nymea-plugins-all Section: libs Architecture: all Depends: nymea-plugins, + nymea-plugins-zigbee, nymea-plugin-simulation, nymea-plugin-snapd, nymea-plugins-maker, diff --git a/debian/nymea-plugin-zigbee-generic-lights.install.in b/debian/nymea-plugin-zigbee-generic-lights.install.in new file mode 100644 index 00000000..c02aec29 --- /dev/null +++ b/debian/nymea-plugin-zigbee-generic-lights.install.in @@ -0,0 +1 @@ +usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_integrationpluginzigbee-generic-lights.so diff --git a/debian/nymea-plugin-zigbee-generic.install.in b/debian/nymea-plugin-zigbee-generic.install.in new file mode 100644 index 00000000..c01e09e0 --- /dev/null +++ b/debian/nymea-plugin-zigbee-generic.install.in @@ -0,0 +1 @@ +usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_integrationpluginzigbee-generic.so diff --git a/debian/nymea-plugin-zigbee-lumi.install.in b/debian/nymea-plugin-zigbee-lumi.install.in new file mode 100644 index 00000000..357171e5 --- /dev/null +++ b/debian/nymea-plugin-zigbee-lumi.install.in @@ -0,0 +1 @@ +usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_integrationpluginzigbee-lumi.so diff --git a/nymea-plugins.pro b/nymea-plugins.pro index 5c1f18c6..13b45998 100644 --- a/nymea-plugins.pro +++ b/nymea-plugins.pro @@ -65,6 +65,7 @@ PLUGIN_DIRS = \ wemo \ ws2812fx \ zigbee-generic \ + zigbee-generic-lights \ zigbee-lumi \ diff --git a/zigbee-generic-lights/integrationpluginzigbee-generic-lights.json b/zigbee-generic-lights/integrationpluginzigbee-generic-lights.json new file mode 100644 index 00000000..b1185b55 --- /dev/null +++ b/zigbee-generic-lights/integrationpluginzigbee-generic-lights.json @@ -0,0 +1,110 @@ +{ + "name": "ZigbeeGenericLights", + "displayName": "Zigbee Generic Lights", + "id": "ee67f87d-d4cc-4223-a9f3-79e47c0267e0", + "vendors": [ + { + "name": "nymea", + "displayName": "nymea", + "id": "2062d64d-3232-433c-88bc-0d33c0ba2ba6", + "thingClasses": [ + { + "name": "onOffLight", + "displayName": "On/Off Light", + "id": "7fbc3165-e618-4dc0-8255-8dac2cc08dfa", + "setupMethod": "JustAdd", + "createMethods": [ "Auto" ], + "interfaces": [ "light", "alert", "wirelessconnectable" ], + "paramTypes": [ + { + "id": "1b434d5d-4fe4-439c-b842-3483e8e24820", + "name": "ieeeAddress", + "displayName": "IEEE address", + "type": "QString", + "defaultValue": "00:00:00:00:00:00:00:00" + }, + { + "id": "b3e298de-140c-4d3d-be81-0a9792359275", + "name": "networkUuid", + "displayName": "Zigbee network UUID", + "type": "QString", + "defaultValue": "" + }, + { + "id": "fd8a6ae6-202b-4634-824f-8d4b187430fa", + "name": "endpointId", + "displayName": "Endpoint ID", + "type": "uint", + "defaultValue": 1 + }, + { + "id": "acb5e910-ed8b-4968-972e-5fc57f466b8a", + "name": "manufacturer", + "displayName": "Manufacturer", + "type": "QString", + "defaultValue": "" + }, + { + "id": "3d942b2d-302a-4c42-8cf5-be2865736736", + "name": "model", + "displayName": "Model", + "type": "QString", + "defaultValue": "" + } + ], + "stateTypes": [ + { + "id": "7548ac08-4ea1-4dcd-98b9-9d58bbc06ab7", + "name": "connected", + "displayName": "Available", + "displayNameEvent": "Available changed", + "type": "bool", + "cached": false, + "defaultValue": false + }, + { + "id": "5857943e-ed90-4321-966e-b56eddaf2441", + "name": "signalStrength", + "displayName": "Signal strength", + "displayNameEvent": "Signal strength changed", + "defaultValue": 0, + "maxValue": 100, + "minValue": 0, + "type": "uint", + "unit": "Percentage" + }, + { + "id": "f5b5e0b3-2c34-4ccc-baef-8e29b84e7a2b", + "name": "version", + "displayName": "Version", + "displayNameEvent": "Version changed", + "type": "QString", + "cached": true, + "defaultValue": "" + }, + { + "id": "ae03155c-f17f-4d9f-bb40-157a8cb1f83c", + "name": "power", + "displayName": "Power", + "displayNameEvent": "Power changed", + "displayNameAction": "Set power", + "type": "bool", + "defaultValue": false, + "writable": true + } + ], + "actionTypes": [ + { + "id": "9388290d-5db2-484a-a702-53a0d842d412", + "name": "alert", + "displayName": "Identify" + } + ], + "eventTypes": [ + + ] + } + ] + } + ] +} diff --git a/zigbee-generic-lights/integrationpluginzigbeegenericlights.cpp b/zigbee-generic-lights/integrationpluginzigbeegenericlights.cpp new file mode 100644 index 00000000..7b7209ab --- /dev/null +++ b/zigbee-generic-lights/integrationpluginzigbeegenericlights.cpp @@ -0,0 +1,261 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* +* Copyright 2013 - 2020, nymea GmbH +* Contact: contact@nymea.io + +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU Lesser General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU Lesser General Public License as published by the Free +* Software Foundation; version 3. This project is distributed in the hope that +* it will be useful, but WITHOUT ANY WARRANTY; without even the implied +* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this project. If not, see . +* +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "integrationpluginzigbeegenericlights.h" +#include "plugininfo.h" +#include "hardware/zigbee/zigbeehardwareresource.h" + +#include + +IntegrationPluginZigbeeGenericLights::IntegrationPluginZigbeeGenericLights() +{ + m_ieeeAddressParamTypeIds[onOffLightThingClassId] = onOffLightThingIeeeAddressParamTypeId; + + m_networkUuidParamTypeIds[onOffLightThingClassId] = onOffLightThingNetworkUuidParamTypeId; + + m_endpointIdParamTypeIds[onOffLightThingClassId] = onOffLightThingEndpointIdParamTypeId; + + m_connectedStateTypeIds[onOffLightThingClassId] = onOffLightConnectedStateTypeId; + + m_signalStrengthStateTypeIds[onOffLightThingClassId] = onOffLightSignalStrengthStateTypeId; + + m_versionStateTypeIds[onOffLightThingClassId] = onOffLightVersionStateTypeId; +} + +QString IntegrationPluginZigbeeGenericLights::name() const +{ + return "Generic lights"; +} + +bool IntegrationPluginZigbeeGenericLights::handleNode(ZigbeeNode *node, const QUuid &networkUuid) +{ + foreach (ZigbeeNodeEndpoint *endpoint, node->endpoints()) { + if ((endpoint->profile() == Zigbee::ZigbeeProfile::ZigbeeProfileLightLink && + endpoint->deviceId() == Zigbee::LightLinkDevice::LightLinkDeviceOnOffLight) || + (endpoint->profile() == Zigbee::ZigbeeProfile::ZigbeeProfileHomeAutomation && + endpoint->deviceId() == Zigbee::HomeAutomationDeviceOnOffLight)) { + + ThingDescriptor descriptor(onOffLightThingClassId); + QString deviceClassName = supportedThings().findById(onOffLightThingClassId).displayName(); + descriptor.setTitle(QString("%1 (%2 - %3)").arg(deviceClassName).arg(endpoint->manufacturerName()).arg(endpoint->modelIdentifier())); + qCDebug(dcZigbeeGenericLights()) << "Handeling generic on/off light" << descriptor.title(); + + ParamList params; + params.append(Param(onOffLightThingNetworkUuidParamTypeId, networkUuid.toString())); + params.append(Param(onOffLightThingIeeeAddressParamTypeId, node->extendedAddress().toString())); + params.append(Param(onOffLightThingEndpointIdParamTypeId, endpoint->endpointId())); + params.append(Param(onOffLightThingModelParamTypeId, endpoint->modelIdentifier())); + params.append(Param(onOffLightThingManufacturerParamTypeId, endpoint->manufacturerName())); + descriptor.setParams(params); + emit autoThingsAppeared({descriptor}); + + return true; + } + } + + return false; +} + +void IntegrationPluginZigbeeGenericLights::handleRemoveNode(ZigbeeNode *node, const QUuid &networkUuid) +{ + Q_UNUSED(networkUuid) + + if (m_thingNodes.values().contains(node)) { + Thing *thing = m_thingNodes.key(node); + qCDebug(dcZigbeeGenericLights()) << node << "for" << thing << "has left the network."; + m_thingNodes.remove(thing); + emit autoThingDisappeared(thing->id()); + } +} + +void IntegrationPluginZigbeeGenericLights::init() +{ + hardwareManager()->zigbeeResource()->registerHandler(this, ZigbeeHardwareResource::HandlerTypeCatchAll); +} + +void IntegrationPluginZigbeeGenericLights::setupThing(ThingSetupInfo *info) +{ + Thing *thing = info->thing(); + + // Get the node for this thing + QUuid networkUuid = thing->paramValue(m_networkUuidParamTypeIds.value(thing->thingClassId())).toUuid(); + ZigbeeAddress zigbeeAddress = ZigbeeAddress(thing->paramValue(m_ieeeAddressParamTypeIds.value(thing->thingClassId())).toString()); + ZigbeeNode *node = hardwareManager()->zigbeeResource()->getNode(networkUuid, zigbeeAddress); + if (!node) { + qCWarning(dcZigbeeGenericLights()) << "Zigbee node for" << info->thing()->name() << "not found.´"; + info->finish(Thing::ThingErrorHardwareNotAvailable); + return; + } + + // Store the node thing association for removing + m_thingNodes.insert(thing, node); + + // Get the endpoint for this thing + ZigbeeNodeEndpoint *endpoint = findEndpoint(thing); + if (!endpoint) { + qCWarning(dcZigbeeGenericLights()) << "Unable to get the endpoint from node" << node << "for" << thing; + info->finish(Thing::ThingErrorSetupFailed); + return; + } + + // General thing setup + + // Update connected state + thing->setStateValue(m_connectedStateTypeIds.value(thing->thingClassId()), node->reachable()); + connect(node, &ZigbeeNode::reachableChanged, thing, [thing, this](bool reachable){ + thing->setStateValue(m_connectedStateTypeIds.value(thing->thingClassId()), reachable); + }); + + // 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(dcZigbeeGenericLights()) << thing << "signal strength changed" << signalStrength << "%"; + thing->setStateValue(m_signalStrengthStateTypeIds.value(thing->thingClassId()), signalStrength); + }); + + // Set the version + thing->setStateValue(m_versionStateTypeIds.value(thing->thingClassId()), endpoint->softwareBuildId()); + + // Thing specific setup + if (thing->thingClassId() == onOffLightThingClassId) { + + ZigbeeClusterOnOff *onOffCluster = endpoint->inputCluster(ZigbeeClusterLibrary::ClusterIdOnOff); + if (!onOffCluster) { + qCWarning(dcZigbeeGenericLights()) << "Could not find on/off cluster for" << thing << "in" << node; + info->finish(Thing::ThingErrorHardwareFailure); + return; + } + + connect(onOffCluster, &ZigbeeClusterOnOff::powerChanged, thing, [thing](bool power){ + qCDebug(dcZigbeeGenericLights()) << thing << "power state changed" << power; + thing->setStateValue(onOffLightPowerStateTypeId, power); + }); + + } + + info->finish(Thing::ThingErrorNoError); +} + +void IntegrationPluginZigbeeGenericLights::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; + } + + // Get the endpoint + ZigbeeNodeEndpoint *endpoint = findEndpoint(thing); + if (!endpoint) { + info->finish(Thing::ThingErrorHardwareNotAvailable); + return; + } + + if (thing->thingClassId() == onOffLightThingClassId) { + + if (info->action().actionTypeId() == onOffLightAlertActionTypeId) { + // Get the identify server cluster from the endpoint + ZigbeeClusterIdentify *identifyCluster = endpoint->inputCluster(ZigbeeClusterLibrary::ClusterIdIdentify); + if (!identifyCluster) { + qCWarning(dcZigbeeGenericLights()) << "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; + } + + if (info->action().actionTypeId() == onOffLightPowerActionTypeId) { + // Get the identify server cluster from the endpoint + ZigbeeClusterOnOff *onOffCluster = endpoint->inputCluster(ZigbeeClusterLibrary::ClusterIdOnOff); + if (!onOffCluster) { + qCWarning(dcZigbeeGenericLights()) << "Could not find on/off cluster for" << thing << "in" << node; + info->finish(Thing::ThingErrorHardwareFailure); + return; + } + + // Send the command trough the network + bool power = info->action().param(onOffLightPowerActionPowerParamTypeId).value().toBool(); + ZigbeeClusterReply *reply = (power ? onOffCluster->commandOn() : onOffCluster->commandOff()); + connect(reply, &ZigbeeClusterReply::finished, thing, [reply, info, power, thing](){ + // Note: reply will be deleted automatically + if (reply->error() != ZigbeeClusterReply::ErrorNoError) { + info->finish(Thing::ThingErrorHardwareFailure); + } else { + info->finish(Thing::ThingErrorNoError); + thing->setStateValue(onOffLightPowerStateTypeId, power); + } + }); + return; + } + } + + info->finish(Thing::ThingErrorUnsupportedFeature); +} + +void IntegrationPluginZigbeeGenericLights::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); + } +} + +ZigbeeNodeEndpoint *IntegrationPluginZigbeeGenericLights::findEndpoint(Thing *thing) +{ + ZigbeeNode *node = m_thingNodes.value(thing); + if (!node) { + qCWarning(dcZigbeeGenericLights()) << "Could not find the node for" << thing; + return nullptr; + } + + quint8 endpointId = thing->paramValue(m_endpointIdParamTypeIds.value(thing->thingClassId())).toUInt(); + return node->getEndpoint(endpointId); +} diff --git a/zigbee-generic-lights/integrationpluginzigbeegenericlights.h b/zigbee-generic-lights/integrationpluginzigbeegenericlights.h new file mode 100644 index 00000000..6854b65f --- /dev/null +++ b/zigbee-generic-lights/integrationpluginzigbeegenericlights.h @@ -0,0 +1,71 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2020, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU Lesser General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU Lesser General Public License as published by the Free +* Software Foundation; version 3. This project is distributed in the hope that +* it will be useful, but WITHOUT ANY WARRANTY; without even the implied +* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this project. If not, see . +* +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef INTEGRATIONPLUGINZIGBEEGENERICLIGHTS_H +#define INTEGRATIONPLUGINZIGBEEGENERICLIGHTS_H + +#include "integrations/integrationplugin.h" +#include "hardware/zigbee/zigbeehandler.h" + +class IntegrationPluginZigbeeGenericLights: public IntegrationPlugin, public ZigbeeHandler +{ + Q_OBJECT + + Q_PLUGIN_METADATA(IID "io.nymea.IntegrationPlugin" FILE "integrationpluginzigbee-generic-lights.json") + Q_INTERFACES(IntegrationPlugin) + +public: + explicit IntegrationPluginZigbeeGenericLights(); + + 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 executeAction(ThingActionInfo *info) override; + void thingRemoved(Thing *thing) override; + +private: + QHash m_ieeeAddressParamTypeIds; + QHash m_networkUuidParamTypeIds; + QHash m_endpointIdParamTypeIds; + + QHash m_connectedStateTypeIds; + QHash m_signalStrengthStateTypeIds; + QHash m_versionStateTypeIds; + + QHash m_thingNodes; + + ZigbeeNodeEndpoint *findEndpoint(Thing *thing); + +}; + +#endif // INTEGRATIONPLUGINZIGBEEGENERICLIGHTS_H diff --git a/zigbee-generic-lights/zigbee-generic-lights.pro b/zigbee-generic-lights/zigbee-generic-lights.pro new file mode 100644 index 00000000..5561cbf7 --- /dev/null +++ b/zigbee-generic-lights/zigbee-generic-lights.pro @@ -0,0 +1,9 @@ +include(../plugins.pri) + +PKGCONFIG += nymea-zigbee + +SOURCES += \ + integrationpluginzigbeegenericlights.cpp + +HEADERS += \ + integrationpluginzigbeegenericlights.h diff --git a/zigbee-lumi/integrationpluginzigbeelumi.cpp b/zigbee-lumi/integrationpluginzigbeelumi.cpp index 90ae8a13..ed1e33be 100644 --- a/zigbee-lumi/integrationpluginzigbeelumi.cpp +++ b/zigbee-lumi/integrationpluginzigbeelumi.cpp @@ -141,10 +141,10 @@ void IntegrationPluginZigbeeLumi::handleRemoveNode(ZigbeeNode *node, const QUuid { Q_UNUSED(networkUuid) - if (m_nodeThings.values().contains(node)) { - Thing *thing = m_nodeThings.key(node); - qCDebug(dcZigbeeLumi()) << "Node for" << thing << node << "has left the network. Removing thing"; - m_nodeThings.remove(thing); + if (m_thingNodes.values().contains(node)) { + Thing *thing = m_thingNodes.key(node); + qCDebug(dcZigbeeLumi()) << node << "for" << thing << "has left the network."; + m_thingNodes.remove(thing); emit autoThingDisappeared(thing->id()); } } @@ -157,10 +157,10 @@ void IntegrationPluginZigbeeLumi::init() void IntegrationPluginZigbeeLumi::setupThing(ThingSetupInfo *info) { Thing *thing = info->thing(); - QUuid networkUuid = thing->paramValue(m_networkUuidParamTypeIds.value(thing->thingClassId())).toUuid(); - qCDebug(dcZigbeeLumi()) << "Nework uuid:" << networkUuid; - ZigbeeAddress zigbeeAddress = ZigbeeAddress(thing->paramValue(m_zigbeeAddressParamTypeIds.value(thing->thingClassId())).toString()); + // Get the node for this thing + QUuid networkUuid = thing->paramValue(m_networkUuidParamTypeIds.value(thing->thingClassId())).toUuid(); + ZigbeeAddress zigbeeAddress = ZigbeeAddress(thing->paramValue(m_zigbeeAddressParamTypeIds.value(thing->thingClassId())).toString()); ZigbeeNode *node = hardwareManager()->zigbeeResource()->getNode(networkUuid, zigbeeAddress); if (!node) { qCWarning(dcZigbeeLumi()) << "Zigbee node for" << info->thing()->name() << "not found.´"; @@ -175,16 +175,14 @@ void IntegrationPluginZigbeeLumi::setupThing(ThingSetupInfo *info) info->finish(Thing::ThingErrorSetupFailed); return; } - - m_nodeThings.insert(thing, node); - // General signals and states + // Store the node thing association for removing + m_thingNodes.insert(thing, node); + // Update connected state - thing->setStateValue(m_connectedStateTypeIds.value(thing->thingClassId()), hardwareManager()->zigbeeResource()->networkState(networkUuid) == ZigbeeNetwork::StateRunning); - connect(hardwareManager()->zigbeeResource(), &ZigbeeHardwareResource::networkStateChanged, thing, [thing, this](const QUuid &networkUuid, ZigbeeNetwork::State state){ - if (thing->paramValue(m_networkUuidParamTypeIds.value(thing->thingClassId())).toUuid() == networkUuid) { - thing->setStateValue(m_connectedStateTypeIds.value(thing->thingClassId()), state == ZigbeeNetwork::StateRunning); - } + thing->setStateValue(m_connectedStateTypeIds.value(thing->thingClassId()), node->reachable()); + connect(node, &ZigbeeNode::reachableChanged, thing, [thing, this](bool reachable){ + thing->setStateValue(m_connectedStateTypeIds.value(thing->thingClassId()), reachable); }); // Update signal strength @@ -198,7 +196,7 @@ void IntegrationPluginZigbeeLumi::setupThing(ThingSetupInfo *info) // Set the version thing->setStateValue(m_versionStateTypeIds.value(thing->thingClassId()), endpoint->softwareBuildId()); - + // Thing specific setup if (thing->thingClassId() == lumiMagnetSensorThingClassId) { ZigbeeClusterOnOff *onOffCluster = endpoint->inputCluster(ZigbeeClusterLibrary::ClusterIdOnOff); if (onOffCluster) { @@ -461,7 +459,7 @@ void IntegrationPluginZigbeeLumi::executeAction(ThingActionInfo *info) void IntegrationPluginZigbeeLumi::thingRemoved(Thing *thing) { - ZigbeeNode *node = m_nodeThings.take(thing); + ZigbeeNode *node = m_thingNodes.take(thing); if (node) { QUuid networkUuid = thing->paramValue(m_networkUuidParamTypeIds.value(thing->thingClassId())).toUuid(); hardwareManager()->zigbeeResource()->removeNodeFromNetwork(networkUuid, node); diff --git a/zigbee-lumi/integrationpluginzigbeelumi.h b/zigbee-lumi/integrationpluginzigbeelumi.h index 17670aac..03ebce89 100644 --- a/zigbee-lumi/integrationpluginzigbeelumi.h +++ b/zigbee-lumi/integrationpluginzigbeelumi.h @@ -66,7 +66,7 @@ private: QHash m_knownLumiDevices; - QHash m_nodeThings; + QHash m_thingNodes; PluginTimer *m_presenceTimer = nullptr; };