From 4c367bbce6894c6331c7a7414249d8c66409a0b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Mon, 16 Nov 2020 16:41:39 +0100 Subject: [PATCH] Add lumi smart power socket basics --- .../integrationpluginzigbeegenericlights.cpp | 2 +- zigbee-lumi/integrationpluginzigbee-lumi.json | 75 +++++++++++++++ zigbee-lumi/integrationpluginzigbeelumi.cpp | 95 ++++++++++++------- 3 files changed, 138 insertions(+), 34 deletions(-) diff --git a/zigbee-generic-lights/integrationpluginzigbeegenericlights.cpp b/zigbee-generic-lights/integrationpluginzigbeegenericlights.cpp index d928a55d..6105b550 100644 --- a/zigbee-generic-lights/integrationpluginzigbeegenericlights.cpp +++ b/zigbee-generic-lights/integrationpluginzigbeegenericlights.cpp @@ -165,7 +165,7 @@ void IntegrationPluginZigbeeGenericLights::setupThing(ThingSetupInfo *info) ZigbeeAddress zigbeeAddress = ZigbeeAddress(thing->paramValue(m_ieeeAddressParamTypeIds.value(thing->thingClassId())).toString()); ZigbeeNode *node = hardwareManager()->zigbeeResource()->claimNode(this, networkUuid, zigbeeAddress); if (!node) { - qCWarning(dcZigbeeGenericLights()) << "Zigbee node for" << info->thing()->name() << "not found.ยด"; + qCWarning(dcZigbeeGenericLights()) << "Zigbee node for" << info->thing()->name() << "not found."; info->finish(Thing::ThingErrorHardwareNotAvailable); return; } diff --git a/zigbee-lumi/integrationpluginzigbee-lumi.json b/zigbee-lumi/integrationpluginzigbee-lumi.json index 3c8f0e55..8e1fceca 100644 --- a/zigbee-lumi/integrationpluginzigbee-lumi.json +++ b/zigbee-lumi/integrationpluginzigbee-lumi.json @@ -556,6 +556,81 @@ "displayName": "Vibration detected" } ] + }, + { + "name": "lumiPowerSocket", + "displayName": "Power socket", + "id": "76afde12-e781-4723-94ca-5704e800e821", + "setupMethod": "JustAdd", + "createMethods": [ "Auto" ], + "interfaces": [ "powersocket", "alert", "wirelessconnectable" ], + "paramTypes": [ + { + "id": "0ebea7b9-488d-490e-9d70-e04ec92efd67", + "name": "ieeeAddress", + "displayName": "IEEE adress", + "type": "QString", + "defaultValue": "00:00:00:00:00:00:00:00" + }, + { + "id": "f92b9d8b-6dbd-45dd-91f7-cd5f4d496877", + "name": "networkUuid", + "displayName": "Zigbee network UUID", + "type": "QString", + "defaultValue": "" + } + ], + "stateTypes": [ + { + "id": "58e01e24-fa6e-4518-ad05-f3b118901f69", + "name": "connected", + "displayName": "Connected", + "displayNameEvent": "Connected changed", + "type": "bool", + "cached": false, + "defaultValue": false + }, + { + "id": "81926f63-4dc8-4701-a62d-07b20a849b37", + "name": "signalStrength", + "displayName": "Signal strength", + "displayNameEvent": "Signal strength changed", + "defaultValue": 0, + "maxValue": 100, + "minValue": 0, + "type": "uint", + "unit": "Percentage" + }, + { + "id": "e4a8e90c-4e38-4b3b-b5e6-fd336e3fa2a8", + "name": "version", + "displayName": "Version", + "displayNameEvent": "Version changed", + "type": "QString", + "cached": true, + "defaultValue": "" + }, + { + "id": "4c81aa6b-3a8a-4d2c-be87-29486ca242af", + "name": "power", + "displayName": "Power", + "displayNameEvent": "Power changed", + "displayNameAction": "Set power", + "type": "bool", + "defaultValue": false, + "writable": true + } + ], + "actionTypes": [ + { + "id": "67052e4d-350a-406f-91a9-3ce3f8eaaa7f", + "name": "alert", + "displayName": "Identify" + } + ], + "eventTypes": [ + + ] } ] } diff --git a/zigbee-lumi/integrationpluginzigbeelumi.cpp b/zigbee-lumi/integrationpluginzigbeelumi.cpp index fb099283..28c436a1 100644 --- a/zigbee-lumi/integrationpluginzigbeelumi.cpp +++ b/zigbee-lumi/integrationpluginzigbeelumi.cpp @@ -46,6 +46,7 @@ IntegrationPluginZigbeeLumi::IntegrationPluginZigbeeLumi() m_networkUuidParamTypeIds[lumiWaterSensorThingClassId] = lumiWaterSensorThingNetworkUuidParamTypeId; m_networkUuidParamTypeIds[lumiWeatherSensorThingClassId] = lumiWeatherSensorThingNetworkUuidParamTypeId; m_networkUuidParamTypeIds[lumiVibrationSensorThingClassId] = lumiVibrationSensorThingNetworkUuidParamTypeId; + m_networkUuidParamTypeIds[lumiPowerSocketThingClassId] = lumiPowerSocketThingNetworkUuidParamTypeId; m_zigbeeAddressParamTypeIds[lumiHTSensorThingClassId] = lumiHTSensorThingIeeeAddressParamTypeId; m_zigbeeAddressParamTypeIds[lumiButtonSensorThingClassId] = lumiButtonSensorThingIeeeAddressParamTypeId; @@ -54,6 +55,7 @@ IntegrationPluginZigbeeLumi::IntegrationPluginZigbeeLumi() m_zigbeeAddressParamTypeIds[lumiWaterSensorThingClassId] = lumiWaterSensorThingIeeeAddressParamTypeId; m_zigbeeAddressParamTypeIds[lumiWeatherSensorThingClassId] = lumiWeatherSensorThingIeeeAddressParamTypeId; m_zigbeeAddressParamTypeIds[lumiVibrationSensorThingClassId] = lumiVibrationSensorThingIeeeAddressParamTypeId; + m_zigbeeAddressParamTypeIds[lumiPowerSocketThingClassId] = lumiPowerSocketThingIeeeAddressParamTypeId; m_connectedStateTypeIds[lumiHTSensorThingClassId] = lumiHTSensorConnectedStateTypeId; m_connectedStateTypeIds[lumiButtonSensorThingClassId] = lumiButtonSensorConnectedStateTypeId; @@ -62,6 +64,7 @@ IntegrationPluginZigbeeLumi::IntegrationPluginZigbeeLumi() m_connectedStateTypeIds[lumiWaterSensorThingClassId] = lumiWaterSensorConnectedStateTypeId; m_connectedStateTypeIds[lumiWeatherSensorThingClassId] = lumiWeatherSensorConnectedStateTypeId; m_connectedStateTypeIds[lumiVibrationSensorThingClassId] = lumiVibrationSensorConnectedStateTypeId; + m_connectedStateTypeIds[lumiPowerSocketThingClassId] = lumiPowerSocketConnectedStateTypeId; m_versionStateTypeIds[lumiHTSensorThingClassId] = lumiHTSensorVersionStateTypeId; m_versionStateTypeIds[lumiButtonSensorThingClassId] = lumiButtonSensorVersionStateTypeId; @@ -70,6 +73,7 @@ IntegrationPluginZigbeeLumi::IntegrationPluginZigbeeLumi() m_versionStateTypeIds[lumiWaterSensorThingClassId] = lumiWaterSensorVersionStateTypeId; m_versionStateTypeIds[lumiWeatherSensorThingClassId] = lumiWeatherSensorVersionStateTypeId; m_versionStateTypeIds[lumiVibrationSensorThingClassId] = lumiVibrationSensorVersionStateTypeId; + m_versionStateTypeIds[lumiPowerSocketThingClassId] = lumiPowerSocketVersionStateTypeId; m_signalStrengthStateTypeIds[lumiHTSensorThingClassId] = lumiHTSensorSignalStrengthStateTypeId; m_signalStrengthStateTypeIds[lumiButtonSensorThingClassId] = lumiButtonSensorSignalStrengthStateTypeId; @@ -78,6 +82,7 @@ IntegrationPluginZigbeeLumi::IntegrationPluginZigbeeLumi() m_signalStrengthStateTypeIds[lumiWaterSensorThingClassId] = lumiWaterSensorSignalStrengthStateTypeId; m_signalStrengthStateTypeIds[lumiWeatherSensorThingClassId] = lumiWeatherSensorSignalStrengthStateTypeId; m_signalStrengthStateTypeIds[lumiVibrationSensorThingClassId] = lumiVibrationSensorSignalStrengthStateTypeId; + m_signalStrengthStateTypeIds[lumiPowerSocketThingClassId] = lumiPowerSocketSignalStrengthStateTypeId; // Known model identifier m_knownLumiDevices.insert("lumi.sensor_ht", lumiHTSensorThingClassId); @@ -87,6 +92,7 @@ IntegrationPluginZigbeeLumi::IntegrationPluginZigbeeLumi() m_knownLumiDevices.insert("lumi.sensor_wleak", lumiWaterSensorThingClassId); m_knownLumiDevices.insert("lumi.weather", lumiWeatherSensorThingClassId); m_knownLumiDevices.insert("lumi.vibration", lumiVibrationSensorThingClassId); + m_knownLumiDevices.insert("lumi.plug", lumiPowerSocketThingClassId); } QString IntegrationPluginZigbeeLumi::name() const @@ -415,45 +421,68 @@ void IntegrationPluginZigbeeLumi::setupThing(ThingSetupInfo *info) } + if (thing->thingClassId() == lumiPowerSocketThingClassId) { + ZigbeeClusterOnOff *onOffCluster = endpoint->inputCluster(ZigbeeClusterLibrary::ClusterIdOnOff); + if (onOffCluster) { + if (onOffCluster->hasAttribute(ZigbeeClusterOnOff::AttributeOnOff)) { + thing->setStateValue(lumiPowerSocketPowerStateTypeId, onOffCluster->power()); + } + + connect(onOffCluster, &ZigbeeClusterOnOff::powerChanged, thing, [thing](bool power){ + qCDebug(dcZigbeeLumi()) << thing << "power changed" << power; + thing->setStateValue(lumiPowerSocketPowerStateTypeId, power); + }); + } else { + qCWarning(dcZigbeeLumi()) << "Could not find the OnOff input 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) { + Thing *thing = info->thing(); + + if (thing->thingClassId() == lumiPowerSocketThingClassId) { + ZigbeeNode *node = m_thingNodes.value(thing); + if (!node) { + qCWarning(dcZigbeeLumi()) << "Zigbee node for" << thing->name() << "not found."; + info->finish(Thing::ThingErrorHardwareFailure); + return; + } + + 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()) << "Could not find on/off cluster for" << thing << "in" << node; + info->finish(Thing::ThingErrorHardwareFailure); + return; + } + + bool power = info->action().param(lumiPowerSocketPowerActionPowerParamTypeId).value().toBool(); + qCDebug(dcZigbeeLumi()) << "Set power for" << info->thing() << "to" << power; + ZigbeeClusterReply *reply = (power ? onOffCluster->commandOn() : onOffCluster->commandOff()); + connect(reply, &ZigbeeClusterReply::finished, info, [=](){ + // Note: reply will be deleted automatically + if (reply->error() != ZigbeeClusterReply::ErrorNoError) { + qCWarning(dcZigbeeLumi()) << "Failed to set power on" << thing << reply->error(); + info->finish(Thing::ThingErrorHardwareFailure); + } else { + info->finish(Thing::ThingErrorNoError); + qCDebug(dcZigbeeLumi()) << "Set power finished successfully for" << thing; + thing->setStateValue(lumiPowerSocketPowerStateTypeId, power); + } + }); + return; + } + info->finish(Thing::ThingErrorUnsupportedFeature); }