diff --git a/nymea-plugins.pro b/nymea-plugins.pro index 1194d487..5c1f18c6 100644 --- a/nymea-plugins.pro +++ b/nymea-plugins.pro @@ -64,6 +64,8 @@ PLUGIN_DIRS = \ wakeonlan \ wemo \ ws2812fx \ + zigbee-generic \ + zigbee-lumi \ message(============================================) diff --git a/zigbee-generic/integrationpluginzigbee-generic.json b/zigbee-generic/integrationpluginzigbee-generic.json new file mode 100644 index 00000000..b5a16145 --- /dev/null +++ b/zigbee-generic/integrationpluginzigbee-generic.json @@ -0,0 +1,14 @@ +{ + "name": "ZigbeeGeneric", + "displayName": "Zigbee Generic", + "id": "6a4343be-9fd6-4015-9ff5-38542651c534", + "vendors": [ + { + "name": "nymea", + "displayName": "nymea", + "id": "2062d64d-3232-433c-88bc-0d33c0ba2ba6", + "thingClasses": [ + ] + } + ] +} diff --git a/zigbee-generic/integrationpluginzigbeegeneric.cpp b/zigbee-generic/integrationpluginzigbeegeneric.cpp new file mode 100644 index 00000000..08921540 --- /dev/null +++ b/zigbee-generic/integrationpluginzigbeegeneric.cpp @@ -0,0 +1,100 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* +* 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 "integrationpluginzigbeegeneric.h" +#include "plugininfo.h" +#include "hardware/zigbee/zigbeehardwareresource.h" + +#include + +IntegrationPluginZigbeeGeneric::IntegrationPluginZigbeeGeneric() +{ + +} + +QString IntegrationPluginZigbeeGeneric::name() const +{ + return "Generic"; +} + +bool IntegrationPluginZigbeeGeneric::handleNode(ZigbeeNode *node, const QUuid &networkUuid) +{ + foreach (ZigbeeNodeEndpoint *endpoint, node->endpoints()) { + qCDebug(dcZigbeeGeneric) << "Endpoint profile:" << endpoint->profile() << endpoint->deviceId() << networkUuid; +// if ((endpoint->profile() == Zigbee::ZigbeeProfile::ZigbeeProfileLightLink && endpoint->deviceId() == Zigbee::LightLinkDevice::LightLinkDeviceOnOff +// (endpoint->profile() == Zigbee::ZigbeeProfile::ZigbeeProfileHomeAutomation && endpoint->deviceId() == Zigbee::HomeAutomationDeviceOnOf> + +// // Create generic power socket +// qCDebug(dcZigbee()) << "This device is an power socket"; +// if (myThings().filterByThingClassId(genericPowerSocketThingClassId) +// .filterByParam(genericPowerSocketThingIeeeAddressParamTypeId, node->extendedAddress().toString()) +// .isEmpty()) { +// qCDebug(dcZigbee()) << "Adding new generic power socket"; +// ThingDescriptor descriptor(genericPowerSocketThingClassId); +// QString deviceClassName = supportedThings().findById(genericPowerSocketThingClassId).displayName(); +// descriptor.setTitle(QString("%1 (%2 - %3)").arg(deviceClassName).arg(endpoint->manufacturerName()).arg(endpoint->modelIdentifier())); +// ParamList params; +// params.append(Param(genericPowerSocketThingIeeeAddressParamTypeId, node->extendedAddress().toString())); +// params.append(Param(genericPowerSocketThingManufacturerParamTypeId, endpoint->manufacturerName())); +// params.append(Param(genericPowerSocketThingModelParamTypeId, endpoint->modelIdentifier())); +// descriptor.setParams(params); +// descriptor.setParentId(networkManagerDevice->id()); +// emit autoThingsAppeared({descriptor}); +// } else { +// qCDebug(dcZigbee()) << "The device for this node has already been created."; +// } +// return true; +// } + } + + + return false; +} + +void IntegrationPluginZigbeeGeneric::init() +{ + hardwareManager()->zigbeeResource()->registerHandler(this, ZigbeeHardwareResource::HandlerTypeCatchAll); +} + +void IntegrationPluginZigbeeGeneric::setupThing(ThingSetupInfo *info) +{ + info->finish(Thing::ThingErrorNoError); +} + +void IntegrationPluginZigbeeGeneric::executeAction(ThingActionInfo *info) +{ + info->finish(Thing::ThingErrorUnsupportedFeature); +} + +void IntegrationPluginZigbeeGeneric::thingRemoved(Thing *thing) +{ + Q_UNUSED(thing) +} diff --git a/zigbee-generic/integrationpluginzigbeegeneric.h b/zigbee-generic/integrationpluginzigbeegeneric.h new file mode 100644 index 00000000..a678b50a --- /dev/null +++ b/zigbee-generic/integrationpluginzigbeegeneric.h @@ -0,0 +1,61 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* 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 INTEGRATIONPLUGINZIGBEEGENERIC_H +#define INTEGRATIONPLUGINZIGBEEGENERIC_H + +#include "integrations/integrationplugin.h" +#include "hardware/zigbee/zigbeehandler.h" +#include "plugintimer.h" + +#include + +class IntegrationPluginZigbeeGeneric: public IntegrationPlugin, public ZigbeeHandler +{ + Q_OBJECT + + Q_PLUGIN_METADATA(IID "io.nymea.IntegrationPlugin" FILE "integrationpluginzigbee-generic.json") + Q_INTERFACES(IntegrationPlugin) + +public: + explicit IntegrationPluginZigbeeGeneric(); + + QString name() const override; + bool handleNode(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: +}; + +#endif // INTEGRATIONPLUGINZIGBEEGENERIC_H diff --git a/zigbee-generic/zigbee-generic.pro b/zigbee-generic/zigbee-generic.pro new file mode 100644 index 00000000..23192648 --- /dev/null +++ b/zigbee-generic/zigbee-generic.pro @@ -0,0 +1,12 @@ +include(../plugins.pri) + +PKGCONFIG += nymea-zigbee + +SOURCES += \ + integrationpluginzigbeegeneric.cpp + +HEADERS += \ + integrationpluginzigbeegeneric.h + + + diff --git a/zigbee-lumi/integrationpluginzigbee-lumi.json b/zigbee-lumi/integrationpluginzigbee-lumi.json new file mode 100644 index 00000000..927efef4 --- /dev/null +++ b/zigbee-lumi/integrationpluginzigbee-lumi.json @@ -0,0 +1,434 @@ +{ + "name": "ZigbeeLumi", + "displayName": "Zigbee Lumi", + "id": "4a6e97da-0e4b-4731-8bfe-c93d806a9383", + "vendors": [ + { + "name": "lumi", + "displayName": "Lumi", + "id": "f58dac98-0ba1-408f-946a-25d452080baa", + "thingClasses": [ + { + "name": "lumiHTSensor", + "displayName": "Temperature humidity sensor", + "id": "dfabab0e-d483-43f8-82c6-720899e70c86", + "setupMethod": "JustAdd", + "createMethods": [ "Auto" ], + "interfaces": [ "temperaturesensor", "humiditysensor", "wirelessconnectable" ], + "paramTypes": [ + { + "id": "bd0b2bf2-2ec3-497f-9679-a63850101257", + "name": "ieeeAddress", + "displayName": "IEEE adress", + "type": "QString", + "defaultValue": "00:00:00:00:00:00:00:00" + }, + { + "id": "2daebedb-d272-4553-a410-91feff69ff7a", + "name": "networkUuid", + "displayName": "Zigbee network UUID", + "type": "QString", + "defaultValue": "" + } + ], + "stateTypes": [ + { + "id": "eb09e3f4-44ea-4ca9-9eac-9a693595fe37", + "name": "connected", + "displayName": "Available", + "displayNameEvent": "Available changed", + "type": "bool", + "cached": false, + "defaultValue": false + }, + { + "id": "0cb72e84-01c4-4041-9aee-1dabf26a062e", + "name": "signalStrength", + "displayName": "Signal strength", + "displayNameEvent": "Signal strength changed", + "defaultValue": 0, + "maxValue": 100, + "minValue": 0, + "type": "uint", + "unit": "Percentage" + }, + { + "id": "968ff8bb-b852-48d8-bde2-28cfa810b20e", + "name": "version", + "displayName": "Version", + "displayNameEvent": "Version changed", + "type": "QString", + "cached": true, + "defaultValue": "" + }, + { + "id": "152b475e-6bd8-4e6f-8765-0636c0bc8e9d", + "name": "temperature", + "displayName": "Temperature", + "displayNameEvent": "Temperature changed", + "type": "double", + "unit": "DegreeCelsius", + "defaultValue": 0.0 + }, + { + "id": "e6559d9e-2c64-485d-bb2b-8a9aec0ac656", + "name": "humidity", + "displayName": "Humidity", + "displayNameEvent": "Humidity changed", + "maxValue": 100, + "minValue": 0, + "unit": "Percentage", + "type": "double", + "defaultValue": 0.0 + } + ], + "actionTypes": [ + { + "id": "ce853c00-d175-45bc-a3d9-3c8c93d88099", + "name": "removeFromNetwork", + "displayName": "Remove from network" + }, + { + "id": "4a9d9427-52cf-4c13-9b71-496145b18476", + "name": "test1", + "displayName": "Test 1" + } + ], + "eventTypes": [ + + ] + }, + { + "name": "lumiMagnetSensor", + "displayName": "Magnet sensor", + "id": "b3e30f37-7467-4c66-8694-9fe624aebd10", + "setupMethod": "JustAdd", + "createMethods": [ "Auto" ], + "interfaces": [ "closablesensor", "wirelessconnectable" ], + "paramTypes": [ + { + "id": "36d8a40a-7f37-4d59-a0d9-6d4977ea63f3", + "name": "ieeeAddress", + "displayName": "IEEE adress", + "type": "QString", + "defaultValue": "00:00:00:00:00:00:00:00" + }, + { + "id": "6a238726-ccd5-484b-ac5d-92649a604f9a", + "name": "networkUuid", + "displayName": "Zigbee network UUID", + "type": "QString", + "defaultValue": "" + } + ], + "stateTypes": [ + { + "id": "76f6c739-73e0-4a36-97c9-043af5854af5", + "name": "connected", + "displayName": "Available", + "displayNameEvent": "Available changed", + "type": "bool", + "cached": false, + "defaultValue": false + }, + { + "id": "7fef2f25-91ac-4818-bc7a-e427bdeff438", + "name": "signalStrength", + "displayName": "Signal strength", + "displayNameEvent": "Signal strength changed", + "defaultValue": 0, + "maxValue": 100, + "minValue": 0, + "type": "uint", + "unit": "Percentage" + }, + { + "id": "ac0e4cd1-553b-4e43-b08a-6dd8d617e765", + "name": "version", + "displayName": "Version", + "displayNameEvent": "Version changed", + "type": "QString", + "cached": true, + "defaultValue": "" + }, + { + "id": "6fad76ec-9d36-408d-bbf1-00e5d18bf6b0", + "name": "closed", + "displayName": "Closed", + "displayNameEvent": "Closed changed", + "type": "bool", + "defaultValue": true + } + ], + "actionTypes": [ + { + "id": "2bae0e2d-0c20-475d-a6cd-2e685e6b0626", + "name": "removeFromNetwork", + "displayName": "Remove from network" + } + ], + "eventTypes": [ + + ] + }, + { + "name": "lumiButtonSensor", + "displayName": "Button sensor", + "id": "42c1edba-cc5f-4eb9-84f8-1b0d47a6f95e", + "setupMethod": "JustAdd", + "createMethods": [ "Auto" ], + "interfaces": [ "longpressbutton", "wirelessconnectable" ], + "paramTypes": [ + { + "id": "929eb2be-6d8f-46b7-8cc9-896e7e2c494a", + "name": "ieeeAddress", + "displayName": "IEEE adress", + "type": "QString", + "defaultValue": "00:00:00:00:00:00:00:00" + }, + { + "id": "de153a5e-c7bd-4815-aa08-21d093b296d7", + "name": "networkUuid", + "displayName": "Zigbee network UUID", + "type": "QString", + "defaultValue": "" + } + ], + "stateTypes": [ + { + "id": "f0da4762-4778-4b3b-ba68-466ff504a3d3", + "name": "connected", + "displayName": "Available", + "displayNameEvent": "Available changed", + "type": "bool", + "cached": false, + "defaultValue": false + }, + { + "id": "6e9de8ee-cb28-4f7d-a0f6-fe9e64df0463", + "name": "signalStrength", + "displayName": "Signal strength", + "displayNameEvent": "Signal strength changed", + "defaultValue": 0, + "maxValue": 100, + "minValue": 0, + "type": "uint", + "unit": "Percentage" + }, + { + "id": "11d03ab4-7450-4caf-81ec-0b2b73e56b08", + "name": "version", + "displayName": "Version", + "displayNameEvent": "Version changed", + "type": "QString", + "cached": true, + "defaultValue": "" + } + ], + "actionTypes": [ + { + "id": "65b43aad-1784-4e85-ad5a-ce01cc5757bd", + "name": "removeFromNetwork", + "displayName": "Remove from network" + } + ], + "eventTypes": [ + { + "id": "2515e40a-8146-4f11-8d79-2be9e20224e5", + "name": "pressed", + "displayName": "Pressed" + }, + { + "id": "6e9dda9f-e51b-48c4-9839-01aa33085e2c", + "name": "longPressed", + "displayName": "Long pressed" + } + ] + }, + { + "name": "lumiMotionSensor", + "displayName": "Motion sensor", + "id": "b5530192-0891-4934-88a2-7338b069be24", + "setupMethod": "JustAdd", + "createMethods": [ "Auto" ], + "interfaces": [ "presencesensor", "lightsensor", "wirelessconnectable" ], + "paramTypes": [ + { + "id": "3a44ed47-5a70-4052-9a14-78f9033eab85", + "name": "ieeeAddress", + "displayName": "IEEE adress", + "type": "QString", + "defaultValue": "00:00:00:00:00:00:00:00" + }, + { + "id": "bc0c78f6-ed88-4ae3-8e1e-2190374391af", + "name": "networkUuid", + "displayName": "Zigbee network UUID", + "type": "QString", + "defaultValue": "" + } + ], + "settingsTypes": [ + { + "id": "4aeb84fa-e3ef-495e-8754-a5f6c3aa934a", + "name": "timeout", + "displayName": "Time period", + "type": "uint", + "unit": "Seconds", + "defaultValue": 10, + "minValue": 10 + } + ], + "stateTypes": [ + { + "id": "007e2fe5-80d5-4f98-941d-b458b555965a", + "name": "connected", + "displayName": "Available", + "displayNameEvent": "Available changed", + "type": "bool", + "cached": false, + "defaultValue": false + }, + { + "id": "c5913170-e377-484b-a330-c5d5b590fee2", + "name": "signalStrength", + "displayName": "Signal strength", + "displayNameEvent": "Signal strength changed", + "defaultValue": 0, + "maxValue": 100, + "minValue": 0, + "type": "uint", + "unit": "Percentage" + }, + { + "id": "d1337b3b-0dfb-4505-90f1-e5b2b45be9bd", + "name": "version", + "displayName": "Version", + "displayNameEvent": "Version changed", + "type": "QString", + "cached": true, + "defaultValue": "" + }, + { + "id": "fed7582b-c057-42ab-b7c0-d562a2e7917c", + "name": "isPresent", + "displayName": "Present", + "displayNameEvent": "Present changed", + "type": "bool", + "defaultValue": true + }, + { + "id": "021ce190-a775-40e0-b19b-c6307c73e343", + "name": "lastSeenTime", + "displayName": "Last seen time", + "displayNameEvent": "Last seen time changed", + "type": "int", + "unit": "UnixTime", + "defaultValue": 0 + }, + { + "id": "eb3fb43e-a9bb-4498-880b-464cbb752ff6", + "name": "lightIntensity", + "displayName": "Light intensity", + "displayNameEvent": "Light intensity changed", + "type": "double", + "unit": "Lux", + "defaultValue": 0 + } + ], + "actionTypes": [ + { + "id": "2797c499-ca7f-4cf6-a732-c8e3cd580e56", + "name": "removeFromNetwork", + "displayName": "Remove from network" + } + ], + "eventTypes": [ + + ] + }, + { + "name": "lumiWaterSensor", + "displayName": "Water sensor", + "id": "e8e18bbe-27bb-4fd7-98fc-f5cef3b5f213", + "setupMethod": "JustAdd", + "createMethods": [ "Auto" ], + "interfaces": [ "sensor", "battery", "wirelessconnectable" ], + "paramTypes": [ + { + "id": "e12453e7-5fc3-4549-b4fc-8b85f78607c6", + "name": "ieeeAddress", + "displayName": "IEEE adress", + "type": "QString", + "defaultValue": "00:00:00:00:00:00:00:00" + }, + { + "id": "4c67bab4-da84-407a-8d8d-84a5c373d585", + "name": "networkUuid", + "displayName": "Zigbee network UUID", + "type": "QString", + "defaultValue": "" + } + ], + "stateTypes": [ + { + "id": "03fc0e4d-dcd9-41dc-8aac-dbe735f11ef7", + "name": "connected", + "displayName": "Available", + "displayNameEvent": "Available changed", + "type": "bool", + "cached": false, + "defaultValue": false + }, + { + "id": "b3bba018-b9d0-47a8-a2cf-d8d2d6fcecce", + "name": "signalStrength", + "displayName": "Signal strength", + "displayNameEvent": "Signal strength changed", + "defaultValue": 0, + "maxValue": 100, + "minValue": 0, + "type": "uint", + "unit": "Percentage" + }, + { + "id": "bae1014f-67f5-4dcc-b36f-e15eb43afd6e", + "name": "version", + "displayName": "Version", + "displayNameEvent": "Version changed", + "type": "QString", + "cached": true, + "defaultValue": "" + }, + { + "id": "e43532a7-cbee-446e-9e4a-aff88026ad1a", + "name": "waterDetected", + "displayName": "Water detected", + "displayNameEvent": "Water detected changed", + "type": "bool", + "defaultValue": true + }, + { + "id": "72aa617c-88cc-4992-a1a2-400b5e19106e", + "name": "batteryCritical", + "displayName": "Battery critical", + "displayNameEvent": "Battery critical changed", + "type": "bool", + "defaultValue": false + } + ], + "actionTypes": [ + { + "id": "ed085bda-5608-419d-88e2-81ebcb9f26e1", + "name": "removeFromNetwork", + "displayName": "Remove from network" + } + ], + "eventTypes": [ + + ] + } + ] + } + ] +} diff --git a/zigbee-lumi/integrationpluginzigbeelumi.cpp b/zigbee-lumi/integrationpluginzigbeelumi.cpp new file mode 100644 index 00000000..4a019298 --- /dev/null +++ b/zigbee-lumi/integrationpluginzigbeelumi.cpp @@ -0,0 +1,286 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* +* 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 "integrationpluginzigbeelumi.h" +#include "plugininfo.h" +#include "hardware/zigbee/zigbeehardwareresource.h" + +#include + +#include + +IntegrationPluginZigbeeLumi::IntegrationPluginZigbeeLumi() +{ + m_networkUuidParamTypeIds[lumiHTSensorThingClassId] = lumiHTSensorThingNetworkUuidParamTypeId; + m_networkUuidParamTypeIds[lumiButtonSensorThingClassId] = lumiButtonSensorThingNetworkUuidParamTypeId; + m_networkUuidParamTypeIds[lumiMagnetSensorThingClassId] = lumiMagnetSensorThingNetworkUuidParamTypeId; + m_networkUuidParamTypeIds[lumiMotionSensorThingClassId] = lumiMotionSensorThingNetworkUuidParamTypeId; + m_networkUuidParamTypeIds[lumiWaterSensorThingClassId] = lumiWaterSensorThingNetworkUuidParamTypeId; + + m_zigbeeAddressParamTypeIds[lumiHTSensorThingClassId] = lumiHTSensorThingIeeeAddressParamTypeId; + m_zigbeeAddressParamTypeIds[lumiButtonSensorThingClassId] = lumiButtonSensorThingIeeeAddressParamTypeId; + m_zigbeeAddressParamTypeIds[lumiMagnetSensorThingClassId] = lumiMagnetSensorThingIeeeAddressParamTypeId; + m_zigbeeAddressParamTypeIds[lumiMotionSensorThingClassId] = lumiMotionSensorThingIeeeAddressParamTypeId; + m_zigbeeAddressParamTypeIds[lumiWaterSensorThingClassId] = lumiWaterSensorThingIeeeAddressParamTypeId; + + m_connectedStateTypeIds[lumiHTSensorThingClassId] = lumiHTSensorConnectedStateTypeId; + m_connectedStateTypeIds[lumiButtonSensorThingClassId] = lumiButtonSensorConnectedStateTypeId; + m_connectedStateTypeIds[lumiMagnetSensorThingClassId] = lumiMagnetSensorConnectedStateTypeId; + m_connectedStateTypeIds[lumiMotionSensorThingClassId] = lumiMotionSensorConnectedStateTypeId; + m_connectedStateTypeIds[lumiWaterSensorThingClassId] = lumiWaterSensorConnectedStateTypeId; + + m_signalStrengthStateTypeIds[lumiHTSensorThingClassId] = lumiHTSensorSignalStrengthStateTypeId; + m_signalStrengthStateTypeIds[lumiButtonSensorThingClassId] = lumiButtonSensorSignalStrengthStateTypeId; + m_signalStrengthStateTypeIds[lumiMagnetSensorThingClassId] = lumiMagnetSensorSignalStrengthStateTypeId; + m_signalStrengthStateTypeIds[lumiMotionSensorThingClassId] = lumiMotionSensorSignalStrengthStateTypeId; + m_signalStrengthStateTypeIds[lumiWaterSensorThingClassId] = lumiWaterSensorSignalStrengthStateTypeId; + +} + +QString IntegrationPluginZigbeeLumi::name() const +{ + return "Lumi"; +} + +bool IntegrationPluginZigbeeLumi::handleNode(ZigbeeNode *node, const QUuid &networkUuid) +{ + // Check if this is Lumi + // Note: Lumi / Xiaomi / Aquara devices are not in the specs, so no enum here + if (node->nodeDescriptor().manufacturerCode != 0x1037) { + return false; + } + + foreach (ZigbeeNodeEndpoint *endpoint, node->endpoints()) { + // Get the model identifier if present from the first endpoint. Also this is out of spec + if (!endpoint->hasInputCluster(ZigbeeClusterLibrary::ClusterIdBasic)) { + qCWarning(dcZigbeeLumi()) << "This lumi device does not have the basic input cluster yet."; + continue; + } + + QHash knownLumiDevices; + knownLumiDevices.insert("lumi.sensor_ht", lumiHTSensorThingClassId); + knownLumiDevices.insert("lumi.sensor_magnet", lumiMagnetSensorThingClassId); + knownLumiDevices.insert("lumi.sensor_switch", lumiButtonSensorThingClassId); + knownLumiDevices.insert("lumi.sensor_motion", lumiMotionSensorThingClassId); + knownLumiDevices.insert("lumi.sensor_water", lumiWaterSensorThingClassId); + + ThingClassId thingClassId; + foreach (const QString &knownLumi, knownLumiDevices.keys()) { + if (endpoint->modelIdentifier().startsWith(knownLumi)) { + thingClassId = knownLumiDevices.value(knownLumi); + break; + } + } + if (thingClassId.isNull()) { + qCWarning(dcZigbeeLumi()) << "Unhandled Lumi device:" << endpoint->modelIdentifier(); + return false; + } + + ThingDescriptor descriptor(thingClassId, supportedThings().findById(thingClassId).displayName()); + ParamList params; + params << Param(m_networkUuidParamTypeIds.value(thingClassId), networkUuid.toString()); + params << Param(m_zigbeeAddressParamTypeIds.value(thingClassId), node->extendedAddress().toString()); + descriptor.setParams(params); + emit autoThingsAppeared({descriptor}); + + return true; + } + + return false; +} + +void IntegrationPluginZigbeeLumi::init() +{ + hardwareManager()->zigbeeResource()->registerHandler(this); +} + +void IntegrationPluginZigbeeLumi::setupThing(ThingSetupInfo *info) +{ + if (!hardwareManager()->zigbeeResource()->available()) { + qCWarning(dcZigbeeLumi()) << "Zigbee is not available. Not setting up" << info->thing()->name(); + info->finish(Thing::ThingErrorHardwareNotAvailable); + return; + } + 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()); + ZigbeeNode *node = hardwareManager()->zigbeeResource()->getNode(networkUuid, zigbeeAddress); + if (!node) { + qCWarning(dcZigbeeLumi()) << "Zigbee node for" << info->thing()->name() << "not found.´"; + info->finish(Thing::ThingErrorHardwareNotAvailable); + return; + } + + ZigbeeNodeEndpoint *endpoint = node->getEndpoint(0x01); + if (!endpoint) { + qCWarning(dcZigbeeLumi()) << "Zigbee endpoint 1 not found on" << thing->name(); + info->finish(Thing::ThingErrorSetupFailed); + return; + } + + // 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); + } + }); + + // 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(dcZigbeeLumi()) << thing << "signal strength changed" << signalStrength << "%"; + thing->setStateValue(m_signalStrengthStateTypeIds.value(thing->thingClassId()), signalStrength); + }); + + + if (thing->thingClassId() == lumiMagnetSensorThingClassId) { + ZigbeeClusterOnOff *onOffCluster = endpoint->inputCluster(ZigbeeClusterLibrary::ClusterIdOnOff); + if (onOffCluster) { + thing->setStateValue(lumiMagnetSensorClosedStateTypeId, !onOffCluster->powered()); + connect(onOffCluster, &ZigbeeClusterOnOff::powerChanged, thing, [thing](bool power){ + qCDebug(dcZigbeeLumi()) << thing << "state changed" << (power ? "closed" : "open"); + thing->setStateValue(lumiMagnetSensorClosedStateTypeId, !power); + }); + } else { + qCWarning(dcZigbeeLumi()) << "Could not find the OnOff input cluster on" << thing << endpoint; + } + } + + if (thing->thingClassId() == lumiMotionSensorThingClassId) { + ZigbeeClusterOccupancySensing *occupancyCluster = endpoint->inputCluster(ZigbeeClusterLibrary::ClusterIdOccupancySensing); + if (occupancyCluster) { + thing->setStateValue(lumiMotionSensorIsPresentStateTypeId, occupancyCluster->occupancy()); + connect(occupancyCluster, &ZigbeeClusterOccupancySensing::occupancyChanged, thing, [thing](bool occupancy){ + qCDebug(dcZigbeeLumi()) << "occupancy changed" << occupancy; + thing->setStateValue(lumiMotionSensorIsPresentStateTypeId, occupancy); + thing->setStateValue(lumiMotionSensorLastSeenTimeStateTypeId, QDateTime::currentMSecsSinceEpoch() / 1000); + }); + + if (!m_presenceTimer) { + m_presenceTimer = hardwareManager()->pluginTimerManager()->registerTimer(1); + } + + connect(m_presenceTimer, &PluginTimer::timeout, thing, [thing](){ + if (thing->stateValue(lumiMotionSensorIsPresentStateTypeId).toBool()) { + int timeout = thing->setting(lumiMotionSensorSettingsTimeoutParamTypeId).toInt(); + QDateTime lastSeenTime = QDateTime::fromMSecsSinceEpoch(thing->stateValue(lumiMotionSensorLastSeenTimeStateTypeId).toULongLong() * 1000); + if (lastSeenTime.addSecs(timeout) < QDateTime::currentDateTime()) { + thing->setStateValue(lumiMotionSensorIsPresentStateTypeId, false); + } + } + }); + + } else { + qCWarning(dcZigbeeLumi()) << "Occupancy cluster not found on" << thing->name(); + } + + ZigbeeClusterIlluminanceMeasurment *illuminanceCluster = endpoint->inputCluster(ZigbeeClusterLibrary::ClusterIdIlluminanceMeasurement); + if (illuminanceCluster) { + thing->setStateValue(lumiHTSensorTemperatureStateTypeId, illuminanceCluster->illuminance()); + connect(illuminanceCluster, &ZigbeeClusterIlluminanceMeasurment::illuminanceChanged, thing, [thing](quint16 illuminance){ + thing->setStateValue(lumiMotionSensorLightIntensityStateTypeId, illuminance); + }); + } else { + qCWarning(dcZigbeeLumi()) << "Illuminance cluster not found on" << thing->name(); + } + } + + if (thing->thingClassId() == lumiHTSensorThingClassId) { + ZigbeeClusterTemperatureMeasurement *temperatureCluster = endpoint->inputCluster(ZigbeeClusterLibrary::ClusterIdTemperatureMeasurement); + if (temperatureCluster) { + thing->setStateValue(lumiHTSensorTemperatureStateTypeId, temperatureCluster->temperature()); + connect(temperatureCluster, &ZigbeeClusterTemperatureMeasurement::temperatureChanged, thing, [thing](double temperature){ + thing->setStateValue(lumiHTSensorTemperatureStateTypeId, temperature); + }); + } else { + qCWarning(dcZigbeeLumi()) << "Could not find the temperature measurement server cluster on" << thing << endpoint; + } + + ZigbeeClusterRelativeHumidityMeasurement *humidityCluster = endpoint->inputCluster(ZigbeeClusterLibrary::ClusterIdRelativeHumidityMeasurement); + if (humidityCluster) { + thing->setStateValue(lumiHTSensorHumidityStateTypeId, humidityCluster->humidity()); + connect(humidityCluster, &ZigbeeClusterRelativeHumidityMeasurement::humidityChanged, thing, [thing](double humidity){ + thing->setStateValue(lumiHTSensorHumidityStateTypeId, humidity); + }); + } else { + qCWarning(dcZigbeeLumi()) << "Could not find the relative humidity measurement server cluster on" << thing << endpoint; + } + } + + info->finish(Thing::ThingErrorNoError); + + + +// if (thing->thingClassId() == lumiButtonSensorThingClassId) { +// qCDebug(dcZigbee()) << "Lumi button sensor" << thing; +// ZigbeeAddress ieeeAddress(thing->paramValue(lumiButtonSensorThingIeeeAddressParamTypeId).toString()); +// ZigbeeNetwork *network = findParentNetwork(thing); +// LumiButtonSensor *sensor = new LumiButtonSensor(network, ieeeAddress, thing, this); +// connect(sensor, &LumiButtonSensor::buttonPressed, this, [this, thing](){ +// qCDebug(dcZigbee()) << thing << "clicked event"; +// emit emitEvent(Event(lumiButtonSensorPressedEventTypeId, thing->id())); +// }); +// connect(sensor, &LumiButtonSensor::buttonLongPressed, this, [this, thing](){ +// qCDebug(dcZigbee()) << thing << "long pressed event"; +// emit emitEvent(Event(lumiButtonSensorLongPressedEventTypeId, thing->id())); +// }); + +// m_zigbeeDevices.insert(thing, sensor); +// info->finish(Thing::ThingErrorNoError); +// return; +// } + + +// if (thing->thingClassId() == lumiWaterSensorThingClassId) { +// qCDebug(dcZigbee()) << "Lumi water sensor" << thing; +// ZigbeeAddress ieeeAddress(thing->paramValue(lumiWaterSensorThingIeeeAddressParamTypeId).toString()); +// ZigbeeNetwork *network = findParentNetwork(thing); +// LumiWaterSensor *sensor = new LumiWaterSensor(network, ieeeAddress, thing, this); +// m_zigbeeDevices.insert(thing, sensor); +// info->finish(Thing::ThingErrorNoError); +// return; +// } + +// info->finish(Thing::ThingErrorThingClassNotFound); +} + +void IntegrationPluginZigbeeLumi::executeAction(ThingActionInfo *info) +{ + info->finish(Thing::ThingErrorUnsupportedFeature); +} + +void IntegrationPluginZigbeeLumi::thingRemoved(Thing *thing) +{ + Q_UNUSED(thing) +} diff --git a/zigbee-lumi/integrationpluginzigbeelumi.h b/zigbee-lumi/integrationpluginzigbeelumi.h new file mode 100644 index 00000000..e2fed597 --- /dev/null +++ b/zigbee-lumi/integrationpluginzigbeelumi.h @@ -0,0 +1,68 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* 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 INTEGRATIONPLUGINZIGBEELUMI_H +#define INTEGRATIONPLUGINZIGBEELUMI_H + +#include "integrations/integrationplugin.h" +#include "hardware/zigbee/zigbeehandler.h" +#include "plugintimer.h" + +#include + +class IntegrationPluginZigbeeLumi: public IntegrationPlugin, public ZigbeeHandler +{ + Q_OBJECT + + Q_PLUGIN_METADATA(IID "io.nymea.IntegrationPlugin" FILE "integrationpluginzigbee-lumi.json") + Q_INTERFACES(IntegrationPlugin) + +public: + explicit IntegrationPluginZigbeeLumi(); + + QString name() const override; + bool handleNode(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_networkUuidParamTypeIds; + QHash m_zigbeeAddressParamTypeIds; + + QHash m_connectedStateTypeIds; + QHash m_signalStrengthStateTypeIds; + + PluginTimer *m_presenceTimer = nullptr; +}; + +#endif // INTEGRATIONPLUGINZIGBEELUMI_H diff --git a/zigbee-lumi/zigbee-lumi.pro b/zigbee-lumi/zigbee-lumi.pro new file mode 100644 index 00000000..e4e53c79 --- /dev/null +++ b/zigbee-lumi/zigbee-lumi.pro @@ -0,0 +1,12 @@ +include(../plugins.pri) + +PKGCONFIG += nymea-zigbee + +SOURCES += \ + integrationpluginzigbeelumi.cpp \ + +HEADERS += \ + integrationpluginzigbeelumi.h \ + + +