From a58ee272e14807d1ef10f8ba6c1ca8cd68496acf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Thu, 19 Nov 2020 08:46:26 +0100 Subject: [PATCH] Add lumi relay and add ioType to lumi things --- zigbee-lumi/integrationpluginzigbee-lumi.json | 95 ++++++++++++++- zigbee-lumi/integrationpluginzigbeelumi.cpp | 113 +++++++++++++++++- 2 files changed, 202 insertions(+), 6 deletions(-) diff --git a/zigbee-lumi/integrationpluginzigbee-lumi.json b/zigbee-lumi/integrationpluginzigbee-lumi.json index 8e1fceca..06775ce5 100644 --- a/zigbee-lumi/integrationpluginzigbee-lumi.json +++ b/zigbee-lumi/integrationpluginzigbee-lumi.json @@ -238,7 +238,8 @@ "displayName": "Closed", "displayNameEvent": "Closed changed", "type": "bool", - "defaultValue": true + "defaultValue": true, + "ioType": "digitalInput" } ], "actionTypes": [ @@ -387,7 +388,8 @@ "displayName": "Present", "displayNameEvent": "Present changed", "type": "bool", - "defaultValue": true + "defaultValue": true, + "ioType": "digitalInput" }, { "id": "021ce190-a775-40e0-b19b-c6307c73e343", @@ -474,7 +476,8 @@ "displayName": "Water detected", "displayNameEvent": "Water detected changed", "type": "bool", - "defaultValue": true + "defaultValue": true, + "ioType": "digitalInput" }, { "id": "72aa617c-88cc-4992-a1a2-400b5e19106e", @@ -618,7 +621,8 @@ "displayNameAction": "Set power", "type": "bool", "defaultValue": false, - "writable": true + "writable": true, + "ioType": "digitalOutput" } ], "actionTypes": [ @@ -630,6 +634,89 @@ ], "eventTypes": [ + ] + }, + { + "name": "lumiRelay", + "displayName": "Relay", + "id": "8e381cb1-2caa-4261-be77-e76a538bf67a", + "setupMethod": "JustAdd", + "createMethods": [ "Auto" ], + "interfaces": [ "wirelessconnectable" ], + "paramTypes": [ + { + "id": "f4a51b83-9cfa-402f-9d2a-e42933e33660", + "name": "ieeeAddress", + "displayName": "IEEE adress", + "type": "QString", + "defaultValue": "00:00:00:00:00:00:00:00" + }, + { + "id": "b5783bb8-ae1d-4e93-84f9-3f1e8c59b955", + "name": "networkUuid", + "displayName": "Zigbee network UUID", + "type": "QString", + "defaultValue": "" + } + ], + "stateTypes": [ + { + "id": "b12d1765-0b2e-4e95-b5ef-780d27fdda44", + "name": "connected", + "displayName": "Connected", + "displayNameEvent": "Connected changed", + "type": "bool", + "cached": false, + "defaultValue": false + }, + { + "id": "bc22b220-003f-4f19-9aef-21c4049c5a0e", + "name": "signalStrength", + "displayName": "Signal strength", + "displayNameEvent": "Signal strength changed", + "defaultValue": 0, + "maxValue": 100, + "minValue": 0, + "type": "uint", + "unit": "Percentage" + }, + { + "id": "ca1a8d08-4423-4401-bca7-d52b11e54ed9", + "name": "version", + "displayName": "Version", + "displayNameEvent": "Version changed", + "type": "QString", + "cached": true, + "defaultValue": "" + }, + { + "id": "486e0df0-cbda-4b67-b844-6f70e5af6891", + "name": "relay1", + "displayName": "Relay 1", + "displayNameEvent": "Relay 1 changed", + "displayNameAction": "Set relay 1", + "type": "bool", + "writable": true, + "defaultValue": false, + "ioType": "digitalOutput" + }, + { + "id": "2caa4f0f-661e-4b59-83e5-8417058e48fd", + "name": "relay2", + "displayName": "Relay 2", + "displayNameEvent": "Relay 2 changed", + "displayNameAction": "Set relay 2", + "type": "bool", + "writable": true, + "defaultValue": false, + "ioType": "digitalOutput" + } + ], + "actionTypes": [ + + ], + "eventTypes": [ + ] } ] diff --git a/zigbee-lumi/integrationpluginzigbeelumi.cpp b/zigbee-lumi/integrationpluginzigbeelumi.cpp index 28c436a1..fc261f86 100644 --- a/zigbee-lumi/integrationpluginzigbeelumi.cpp +++ b/zigbee-lumi/integrationpluginzigbeelumi.cpp @@ -47,6 +47,7 @@ IntegrationPluginZigbeeLumi::IntegrationPluginZigbeeLumi() m_networkUuidParamTypeIds[lumiWeatherSensorThingClassId] = lumiWeatherSensorThingNetworkUuidParamTypeId; m_networkUuidParamTypeIds[lumiVibrationSensorThingClassId] = lumiVibrationSensorThingNetworkUuidParamTypeId; m_networkUuidParamTypeIds[lumiPowerSocketThingClassId] = lumiPowerSocketThingNetworkUuidParamTypeId; + m_networkUuidParamTypeIds[lumiRelayThingClassId] = lumiRelayThingNetworkUuidParamTypeId; m_zigbeeAddressParamTypeIds[lumiHTSensorThingClassId] = lumiHTSensorThingIeeeAddressParamTypeId; m_zigbeeAddressParamTypeIds[lumiButtonSensorThingClassId] = lumiButtonSensorThingIeeeAddressParamTypeId; @@ -56,6 +57,7 @@ IntegrationPluginZigbeeLumi::IntegrationPluginZigbeeLumi() m_zigbeeAddressParamTypeIds[lumiWeatherSensorThingClassId] = lumiWeatherSensorThingIeeeAddressParamTypeId; m_zigbeeAddressParamTypeIds[lumiVibrationSensorThingClassId] = lumiVibrationSensorThingIeeeAddressParamTypeId; m_zigbeeAddressParamTypeIds[lumiPowerSocketThingClassId] = lumiPowerSocketThingIeeeAddressParamTypeId; + m_zigbeeAddressParamTypeIds[lumiRelayThingClassId] = lumiRelayThingIeeeAddressParamTypeId; m_connectedStateTypeIds[lumiHTSensorThingClassId] = lumiHTSensorConnectedStateTypeId; m_connectedStateTypeIds[lumiButtonSensorThingClassId] = lumiButtonSensorConnectedStateTypeId; @@ -65,6 +67,7 @@ IntegrationPluginZigbeeLumi::IntegrationPluginZigbeeLumi() m_connectedStateTypeIds[lumiWeatherSensorThingClassId] = lumiWeatherSensorConnectedStateTypeId; m_connectedStateTypeIds[lumiVibrationSensorThingClassId] = lumiVibrationSensorConnectedStateTypeId; m_connectedStateTypeIds[lumiPowerSocketThingClassId] = lumiPowerSocketConnectedStateTypeId; + m_connectedStateTypeIds[lumiRelayThingClassId] = lumiRelayConnectedStateTypeId; m_versionStateTypeIds[lumiHTSensorThingClassId] = lumiHTSensorVersionStateTypeId; m_versionStateTypeIds[lumiButtonSensorThingClassId] = lumiButtonSensorVersionStateTypeId; @@ -74,6 +77,7 @@ IntegrationPluginZigbeeLumi::IntegrationPluginZigbeeLumi() m_versionStateTypeIds[lumiWeatherSensorThingClassId] = lumiWeatherSensorVersionStateTypeId; m_versionStateTypeIds[lumiVibrationSensorThingClassId] = lumiVibrationSensorVersionStateTypeId; m_versionStateTypeIds[lumiPowerSocketThingClassId] = lumiPowerSocketVersionStateTypeId; + m_versionStateTypeIds[lumiRelayThingClassId] = lumiRelayVersionStateTypeId; m_signalStrengthStateTypeIds[lumiHTSensorThingClassId] = lumiHTSensorSignalStrengthStateTypeId; m_signalStrengthStateTypeIds[lumiButtonSensorThingClassId] = lumiButtonSensorSignalStrengthStateTypeId; @@ -83,6 +87,7 @@ IntegrationPluginZigbeeLumi::IntegrationPluginZigbeeLumi() m_signalStrengthStateTypeIds[lumiWeatherSensorThingClassId] = lumiWeatherSensorSignalStrengthStateTypeId; m_signalStrengthStateTypeIds[lumiVibrationSensorThingClassId] = lumiVibrationSensorSignalStrengthStateTypeId; m_signalStrengthStateTypeIds[lumiPowerSocketThingClassId] = lumiPowerSocketSignalStrengthStateTypeId; + m_signalStrengthStateTypeIds[lumiRelayThingClassId] = lumiRelaySignalStrengthStateTypeId; // Known model identifier m_knownLumiDevices.insert("lumi.sensor_ht", lumiHTSensorThingClassId); @@ -93,6 +98,7 @@ IntegrationPluginZigbeeLumi::IntegrationPluginZigbeeLumi() m_knownLumiDevices.insert("lumi.weather", lumiWeatherSensorThingClassId); m_knownLumiDevices.insert("lumi.vibration", lumiVibrationSensorThingClassId); m_knownLumiDevices.insert("lumi.plug", lumiPowerSocketThingClassId); + m_knownLumiDevices.insert("lumi.relay", lumiRelayThingClassId); } QString IntegrationPluginZigbeeLumi::name() const @@ -136,7 +142,6 @@ bool IntegrationPluginZigbeeLumi::handleNode(ZigbeeNode *node, const QUuid &netw params << Param(m_zigbeeAddressParamTypeIds.value(thingClassId), node->extendedAddress().toString()); descriptor.setParams(params); emit autoThingsAppeared({descriptor}); - return true; } @@ -437,6 +442,47 @@ void IntegrationPluginZigbeeLumi::setupThing(ThingSetupInfo *info) } } + if (thing->thingClassId() == lumiRelayThingClassId) { + // Get the 2 endpoints + ZigbeeNodeEndpoint *endpoint1 = node->getEndpoint(0x01); + if (!endpoint1) { + ZigbeeClusterOnOff *onOffCluster = endpoint1->inputCluster(ZigbeeClusterLibrary::ClusterIdOnOff); + if (onOffCluster) { + if (onOffCluster->hasAttribute(ZigbeeClusterOnOff::AttributeOnOff)) { + thing->setStateValue(lumiRelayRelay1StateTypeId, onOffCluster->power()); + } + + connect(onOffCluster, &ZigbeeClusterOnOff::powerChanged, thing, [thing](bool power){ + qCDebug(dcZigbeeLumi()) << thing << "power changed" << power; + thing->setStateValue(lumiRelayRelay1StateTypeId, power); + }); + } else { + qCWarning(dcZigbeeLumi()) << "Could not find the OnOff input cluster on" << thing << endpoint1; + } + } else { + qCWarning(dcZigbeeLumi()) << "Could not find endpoint 1 on" << thing << node; + } + + ZigbeeNodeEndpoint *endpoint2 = node->getEndpoint(0x02); + if (!endpoint2) { + ZigbeeClusterOnOff *onOffCluster = endpoint2->inputCluster(ZigbeeClusterLibrary::ClusterIdOnOff); + if (onOffCluster) { + if (onOffCluster->hasAttribute(ZigbeeClusterOnOff::AttributeOnOff)) { + thing->setStateValue(lumiRelayRelay2StateTypeId, onOffCluster->power()); + } + + connect(onOffCluster, &ZigbeeClusterOnOff::powerChanged, thing, [thing](bool power){ + qCDebug(dcZigbeeLumi()) << thing << "power changed" << power; + thing->setStateValue(lumiRelayRelay2StateTypeId, power); + }); + } else { + qCWarning(dcZigbeeLumi()) << "Could not find the OnOff input cluster on" << thing << endpoint1; + } + } else { + qCWarning(dcZigbeeLumi()) << "Could not find endpoint 2 on" << thing << node; + } + } + info->finish(Thing::ThingErrorNoError); } @@ -447,7 +493,7 @@ void IntegrationPluginZigbeeLumi::executeAction(ThingActionInfo *info) if (thing->thingClassId() == lumiPowerSocketThingClassId) { ZigbeeNode *node = m_thingNodes.value(thing); if (!node) { - qCWarning(dcZigbeeLumi()) << "Zigbee node for" << thing->name() << "not found."; + qCWarning(dcZigbeeLumi()) << "Zigbee node for" << thing << "not found."; info->finish(Thing::ThingErrorHardwareFailure); return; } @@ -483,6 +529,69 @@ void IntegrationPluginZigbeeLumi::executeAction(ThingActionInfo *info) return; } + if (thing->thingClassId() == lumiRelayThingClassId) { + ZigbeeNode *node = m_thingNodes.value(thing); + if (!node) { + qCWarning(dcZigbeeLumi()) << "Zigbee node for" << thing << "not found."; + info->finish(Thing::ThingErrorHardwareFailure); + return; + } + + if (info->action().actionTypeId() == lumiRelayRelay1ActionTypeId) { + ZigbeeNodeEndpoint *endpoint = node->getEndpoint(0x01); + if (!endpoint) { + qCWarning(dcZigbeeLumi()) << "Unable to get the endpoint from node" << node << "for" << thing; + info->finish(Thing::ThingErrorSetupFailed); + return; + } + + ZigbeeClusterOnOff *onOffCluster = endpoint->inputCluster(ZigbeeClusterLibrary::ClusterIdOnOff); + if (!onOffCluster) { + qCWarning(dcZigbeeLumi()) << "Unable to get the OnOff cluster from endpoint" << endpoint << "on" << node << "for" << thing; + info->finish(Thing::ThingErrorSetupFailed); + return; + } + bool power = info->action().param(lumiRelayRelay1ActionRelay1ParamTypeId).value().toBool(); + ZigbeeClusterReply *reply = (power ? onOffCluster->commandOn() : onOffCluster->commandOff()); + connect(reply, &ZigbeeClusterReply::finished, this, [=](){ + // Note: reply will be deleted automatically + if (reply->error() != ZigbeeClusterReply::ErrorNoError) { + info->finish(Thing::ThingErrorHardwareFailure); + } else { + info->finish(Thing::ThingErrorNoError); + thing->setStateValue(lumiRelayRelay1StateTypeId, power); + } + }); + } + + if (info->action().actionTypeId() == lumiRelayRelay2ActionTypeId) { + ZigbeeNodeEndpoint *endpoint = node->getEndpoint(0x02); + if (!endpoint) { + qCWarning(dcZigbeeLumi()) << "Unable to get the endpoint from node" << node << "for" << thing; + info->finish(Thing::ThingErrorSetupFailed); + return; + } + + ZigbeeClusterOnOff *onOffCluster = endpoint->inputCluster(ZigbeeClusterLibrary::ClusterIdOnOff); + if (!onOffCluster) { + qCWarning(dcZigbeeLumi()) << "Unable to get the OnOff cluster from endpoint" << endpoint << "on" << node << "for" << thing; + info->finish(Thing::ThingErrorSetupFailed); + return; + } + bool power = info->action().param(lumiRelayRelay2ActionRelay2ParamTypeId).value().toBool(); + ZigbeeClusterReply *reply = (power ? onOffCluster->commandOn() : onOffCluster->commandOff()); + connect(reply, &ZigbeeClusterReply::finished, this, [=](){ + // Note: reply will be deleted automatically + if (reply->error() != ZigbeeClusterReply::ErrorNoError) { + info->finish(Thing::ThingErrorHardwareFailure); + } else { + info->finish(Thing::ThingErrorNoError); + thing->setStateValue(lumiRelayRelay2StateTypeId, power); + } + }); + } + } + info->finish(Thing::ThingErrorUnsupportedFeature); }