diff --git a/zigbeephilipshue/integrationpluginzigbeephilipshue.cpp b/zigbeephilipshue/integrationpluginzigbeephilipshue.cpp index ceac8180..c826023e 100644 --- a/zigbeephilipshue/integrationpluginzigbeephilipshue.cpp +++ b/zigbeephilipshue/integrationpluginzigbeephilipshue.cpp @@ -165,46 +165,36 @@ void IntegrationPluginZigbeePhilipsHue::setupThing(ThingSetupInfo *info) // Set the version thing->setStateValue(m_versionStateTypeIds.value(thing->thingClassId()), endpointZll->softwareBuildId()); - // Receive on/off commands - ZigbeeClusterOnOff *onOffCluster = endpointZll->outputCluster(ZigbeeClusterLibrary::ClusterIdOnOff); - if (!onOffCluster) { - qCWarning(dcZigbeePhilipsHue()) << "Could not find on/off client cluster on" << thing << endpointZll; + ZigbeeClusterManufacturerSpecificPhilips *philipsCluster = endpointHa->inputCluster(ZigbeeClusterLibrary::ClusterIdManufacturerSpecificPhilips); + if (!philipsCluster) { + qCWarning(dcZigbeePhilipsHue()) << "Could not find Manufacturer Specific (Philips) cluster on" << thing << endpointHa; } else { - connect(onOffCluster, &ZigbeeClusterOnOff::commandSent, thing, [=](ZigbeeClusterOnOff::Command command){ - if (command == ZigbeeClusterOnOff::CommandOn) { - qCDebug(dcZigbeePhilipsHue()) << thing << "pressed ON"; - emit emitEvent(Event(dimmerSwitchPressedEventTypeId, thing->id(), ParamList() << Param(dimmerSwitchPressedEventButtonNameParamTypeId, "ON"))); - } else { - qCWarning(dcZigbeePhilipsHue()) << thing << "unhandled command received" << command; - } - }); - - connect(onOffCluster, &ZigbeeClusterOnOff::commandOffWithEffectSent, thing, [=](ZigbeeClusterOnOff::Effect effect, quint8 effectVariant){ - qCDebug(dcZigbeePhilipsHue()) << thing << "OFF button pressed" << effect << effectVariant; - emit emitEvent(Event(dimmerSwitchPressedEventTypeId, thing->id(), ParamList() << Param(dimmerSwitchPressedEventButtonNameParamTypeId, "OFF"))); - }); - } - - // Receive level control commands - ZigbeeClusterLevelControl *levelCluster = endpointZll->outputCluster(ZigbeeClusterLibrary::ClusterIdLevelControl); - if (!levelCluster) { - qCWarning(dcZigbeePhilipsHue()) << "Could not find level client cluster on" << thing << endpointZll; - } else { - connect(levelCluster, &ZigbeeClusterLevelControl::commandStepSent, thing, [=](ZigbeeClusterLevelControl::FadeMode fadeMode, quint8 stepSize, quint16 transitionTime){ - qCDebug(dcZigbeePhilipsHue()) << thing << "level button pressed" << fadeMode << stepSize << transitionTime; - switch (fadeMode) { - case ZigbeeClusterLevelControl::FadeModeUp: - qCDebug(dcZigbeePhilipsHue()) << thing << "DIM UP pressed"; - emit emitEvent(Event(dimmerSwitchPressedEventTypeId, thing->id(), ParamList() << Param(dimmerSwitchPressedEventButtonNameParamTypeId, "DIM UP"))); + connect(philipsCluster, &ZigbeeClusterManufacturerSpecificPhilips::buttonPressed, thing, [=](quint8 button, ZigbeeClusterManufacturerSpecificPhilips::Operation operation) { + qCDebug(dcZigbeePhilipsHue()) << "Button" << button << operation; + QHash buttonMap = { + {1, "ON"}, + {2, "DIM UP"}, + {3, "DIM DOWN"}, + {4, "OFF"} + }; + switch (operation) { + case ZigbeeClusterManufacturerSpecificPhilips::OperationButtonPress: + // This doesn't appear on very quick press/release. But we always get the short release, so let's use that instead break; - case ZigbeeClusterLevelControl::FadeModeDown: - qCDebug(dcZigbeePhilipsHue()) << thing << "DIM DOWN pressed"; - emit emitEvent(Event(dimmerSwitchPressedEventTypeId, thing->id(), ParamList() << Param(dimmerSwitchPressedEventButtonNameParamTypeId, "DIM DOWN"))); + case ZigbeeClusterManufacturerSpecificPhilips::OperationButtonShortRelease: + thing->emitEvent(dimmerSwitchPressedEventTypeId, ParamList() << Param(dimmerSwitchPressedEventButtonNameParamTypeId, buttonMap.value(button))); + break; + case ZigbeeClusterManufacturerSpecificPhilips::OperationButtonHold: + thing->emitEvent(dimmerSwitchLongPressedEventTypeId, ParamList() << Param(dimmerSwitchLongPressedEventButtonNameParamTypeId, buttonMap.value(button))); + break; + case ZigbeeClusterManufacturerSpecificPhilips::OperationButtonLongRelease: + // Release after a longpress. But for longpresses we always seem to get the Hold before, so we'll use that. break; } }); } + // Get battery level changes ZigbeeClusterPowerConfiguration *powerCluster = endpointHa->inputCluster(ZigbeeClusterLibrary::ClusterIdPowerConfiguration); if (!powerCluster) { @@ -296,7 +286,7 @@ void IntegrationPluginZigbeePhilipsHue::setupThing(ThingSetupInfo *info) thing->setStateValue(m_versionStateTypeIds.value(thing->thingClassId()), endpointHa->softwareBuildId()); // Get battery level changes - ZigbeeClusterPowerConfiguration *powerCluster = endpointHa->inputCluster(ZigbeeClusterLibrary::ClusterIdPowerConfiguration); + ZigbeeClusterPowerConfiguration *powerCluster = endpointHa->outputCluster(ZigbeeClusterLibrary::ClusterIdPowerConfiguration); if (!powerCluster) { qCWarning(dcZigbeePhilipsHue()) << "Could not find power configuration cluster on" << thing << endpointHa; } else { @@ -411,7 +401,6 @@ void IntegrationPluginZigbeePhilipsHue::createThing(const ThingClassId &thingCla void IntegrationPluginZigbeePhilipsHue::initDimmerSwitch(ZigbeeNode *node) { - ZigbeeNodeEndpoint *endpointZll = node->getEndpoint(0x01); ZigbeeNodeEndpoint *endpointHa = node->getEndpoint(0x02); // Get the current configured bindings for this node @@ -429,76 +418,68 @@ void IntegrationPluginZigbeePhilipsHue::initDimmerSwitch(ZigbeeNode *node) return; } - qCDebug(dcZigbeePhilipsHue()) << "Read power configuration cluster attributes" << node; + qCDebug(dcZigbeePhilipsHue()) << "Reading power configuration cluster attributes" << node; ZigbeeClusterReply *readAttributeReply = endpointHa->getInputCluster(ZigbeeClusterLibrary::ClusterIdPowerConfiguration)->readAttributes({ZigbeeClusterPowerConfiguration::AttributeBatteryPercentageRemaining}); connect(readAttributeReply, &ZigbeeClusterReply::finished, node, [=](){ if (readAttributeReply->error() != ZigbeeClusterReply::ErrorNoError) { qCWarning(dcZigbeePhilipsHue()) << "Failed to read power cluster attributes" << readAttributeReply->error(); } else { - qCDebug(dcZigbeePhilipsHue()) << "Read power configuration cluster attributes finished successfully"; + qCDebug(dcZigbeePhilipsHue()) << "Reading power configuration cluster attributes finished successfully"; + } + }); + + + // Bind the cluster to the coordinator + qCDebug(dcZigbeePhilipsHue()) << "Binding power configuration cluster to coordinator IEEE address"; + ZigbeeDeviceObjectReply * zdoReply = node->deviceObject()->requestBindIeeeAddress(endpointHa->endpointId(), ZigbeeClusterLibrary::ClusterIdPowerConfiguration, + hardwareManager()->zigbeeResource()->coordinatorAddress(node->networkUuid()), 0x01); + connect(zdoReply, &ZigbeeDeviceObjectReply::finished, node, [=](){ + if (zdoReply->error() != ZigbeeDeviceObjectReply::ErrorNoError) { + qCWarning(dcZigbeePhilipsHue()) << "Failed to bind power cluster to coordinator" << zdoReply->error(); + } else { + qCDebug(dcZigbeePhilipsHue()) << "Binding power configuration cluster to coordinator finished successfully"; } + // Configure attribute rporting for battery remaining (0.5 % changes = 1) + ZigbeeClusterLibrary::AttributeReportingConfiguration reportingConfig; + reportingConfig.attributeId = ZigbeeClusterPowerConfiguration::AttributeBatteryPercentageRemaining; + reportingConfig.dataType = Zigbee::Uint8; + reportingConfig.minReportingInterval = 300; + reportingConfig.maxReportingInterval = 2700; + reportingConfig.reportableChange = ZigbeeDataType(static_cast(1)).data(); - // Bind the cluster to the coordinator - qCDebug(dcZigbeePhilipsHue()) << "Bind power configuration cluster to coordinator IEEE address"; - ZigbeeDeviceObjectReply * zdoReply = node->deviceObject()->requestBindIeeeAddress(endpointHa->endpointId(), ZigbeeClusterLibrary::ClusterIdPowerConfiguration, - hardwareManager()->zigbeeResource()->coordinatorAddress(node->networkUuid()), 0x01); - connect(zdoReply, &ZigbeeDeviceObjectReply::finished, node, [=](){ - if (zdoReply->error() != ZigbeeDeviceObjectReply::ErrorNoError) { - qCWarning(dcZigbeePhilipsHue()) << "Failed to bind power cluster to coordinator" << zdoReply->error(); + qCDebug(dcZigbeePhilipsHue()) << "Configure attribute reporting for power configuration cluster to coordinator"; + ZigbeeClusterReply *reportingReply = endpointHa->getInputCluster(ZigbeeClusterLibrary::ClusterIdPowerConfiguration)->configureReporting({reportingConfig}); + connect(reportingReply, &ZigbeeClusterReply::finished, this, [=](){ + if (reportingReply->error() != ZigbeeClusterReply::ErrorNoError) { + qCWarning(dcZigbeePhilipsHue()) << "Failed to configure power cluster attribute reporting" << reportingReply->error(); } else { - qCDebug(dcZigbeePhilipsHue()) << "Bind power configuration cluster to coordinator finished successfully"; + qCDebug(dcZigbeePhilipsHue()) << "Attribute reporting configuration finished for power cluster" << ZigbeeClusterLibrary::parseAttributeReportingStatusRecords(reportingReply->responseFrame().payload); } + }); + }); - // Configure attribute rporting for battery remaining (0.5 % changes = 1) - ZigbeeClusterLibrary::AttributeReportingConfiguration reportingConfig; - reportingConfig.attributeId = ZigbeeClusterPowerConfiguration::AttributeBatteryPercentageRemaining; - reportingConfig.dataType = Zigbee::Uint8; - reportingConfig.minReportingInterval = 300; - reportingConfig.maxReportingInterval = 2700; - reportingConfig.reportableChange = ZigbeeDataType(static_cast(1)).data(); - qCDebug(dcZigbeePhilipsHue()) << "Configure attribute reporting for power configuration cluster to coordinator"; - ZigbeeClusterReply *reportingReply = endpointHa->getInputCluster(ZigbeeClusterLibrary::ClusterIdPowerConfiguration)->configureReporting({reportingConfig}); - connect(reportingReply, &ZigbeeClusterReply::finished, this, [=](){ - if (reportingReply->error() != ZigbeeClusterReply::ErrorNoError) { - qCWarning(dcZigbeePhilipsHue()) << "Failed to configure power cluster attribute reporting" << reportingReply->error(); - } else { - qCDebug(dcZigbeePhilipsHue()) << "Attribute reporting configuration finished for power cluster" << ZigbeeClusterLibrary::parseAttributeReportingStatusRecords(reportingReply->responseFrame().payload); + + qCDebug(dcZigbeePhilipsHue()) << "Binding Manufacturer specific cluster to coordinator"; + zdoReply = node->deviceObject()->requestBindGroupAddress(endpointHa->endpointId(), ZigbeeClusterLibrary::ClusterIdManufacturerSpecificPhilips, 0x0000); + connect(zdoReply, &ZigbeeDeviceObjectReply::finished, node, [=](){ + if (zdoReply->error() != ZigbeeDeviceObjectReply::ErrorNoError) { + qCWarning(dcZigbeePhilipsHue()) << "Failed to bind manufacturer specific cluster to coordinator" << zdoReply->error(); + } else { + qCDebug(dcZigbeePhilipsHue()) << "Binding manufacturer specific cluster to coordinator finished successfully"; + } + + qCDebug(dcZigbeePhilipsHue()) << "Reading binding table from node" << node; + ZigbeeReply *reply = node->readBindingTableEntries(); + connect(reply, &ZigbeeReply::finished, node, [=](){ + if (reply->error() != ZigbeeReply::ErrorNoError) { + qCWarning(dcZigbeePhilipsHue()) << "Failed to read binding table from" << node; + } else { + foreach (const ZigbeeDeviceProfile::BindingTableListRecord &binding, node->bindingTableRecords()) { + qCDebug(dcZigbeePhilipsHue()) << node << binding; } - - qCDebug(dcZigbeePhilipsHue()) << "Bind on/off cluster to coordinator"; - ZigbeeDeviceObjectReply * zdoReply = node->deviceObject()->requestBindGroupAddress(endpointZll->endpointId(), ZigbeeClusterLibrary::ClusterIdOnOff, 0x0000); - connect(zdoReply, &ZigbeeDeviceObjectReply::finished, node, [=](){ - if (zdoReply->error() != ZigbeeDeviceObjectReply::ErrorNoError) { - qCWarning(dcZigbeePhilipsHue()) << "Failed to bind on/off cluster to coordinator" << zdoReply->error(); - } else { - qCDebug(dcZigbeePhilipsHue()) << "Bind on/off cluster to coordinator finished successfully"; - } - - qCDebug(dcZigbeePhilipsHue()) << "Bind power level cluster to coordinator"; - ZigbeeDeviceObjectReply * zdoReply = node->deviceObject()->requestBindGroupAddress(endpointZll->endpointId(), ZigbeeClusterLibrary::ClusterIdLevelControl, 0x0000); - connect(zdoReply, &ZigbeeDeviceObjectReply::finished, node, [=](){ - if (zdoReply->error() != ZigbeeDeviceObjectReply::ErrorNoError) { - qCWarning(dcZigbeePhilipsHue()) << "Failed to bind level cluster to coordinator" << zdoReply->error(); - } else { - qCDebug(dcZigbeePhilipsHue()) << "Bind level cluster to coordinator finished successfully"; - } - - qCDebug(dcZigbeePhilipsHue()) << "Read binding table from node" << node; - ZigbeeReply *reply = node->readBindingTableEntries(); - connect(reply, &ZigbeeReply::finished, node, [=](){ - if (reply->error() != ZigbeeReply::ErrorNoError) { - qCWarning(dcZigbeePhilipsHue()) << "Failed to read binding table from" << node; - } else { - foreach (const ZigbeeDeviceProfile::BindingTableListRecord &binding, node->bindingTableRecords()) { - qCDebug(dcZigbeePhilipsHue()) << node << binding; - } - } - }); - }); - }); - }); + } }); }); }); diff --git a/zigbeephilipshue/integrationpluginzigbeephilipshue.h b/zigbeephilipshue/integrationpluginzigbeephilipshue.h index 5e25cd26..38ee2fe2 100644 --- a/zigbeephilipshue/integrationpluginzigbeephilipshue.h +++ b/zigbeephilipshue/integrationpluginzigbeephilipshue.h @@ -34,7 +34,7 @@ #include "integrations/integrationplugin.h" #include "hardware/zigbee/zigbeehandler.h" #include "plugintimer.h" - +#include "extern-plugininfo.h" #include class IntegrationPluginZigbeePhilipsHue: public IntegrationPlugin, public ZigbeeHandler diff --git a/zigbeephilipshue/integrationpluginzigbeephilipshue.json b/zigbeephilipshue/integrationpluginzigbeephilipshue.json index b52598a5..cd2e5088 100644 --- a/zigbeephilipshue/integrationpluginzigbeephilipshue.json +++ b/zigbeephilipshue/integrationpluginzigbeephilipshue.json @@ -14,7 +14,7 @@ "id": "b2711164-a848-4715-8ddf-33ca86f9f4cf", "setupMethod": "JustAdd", "createMethods": [ "Auto" ], - "interfaces": [ "multibutton", "battery", "wirelessconnectable" ], + "interfaces": [ "longpressmultibutton", "battery", "wirelessconnectable" ], "paramTypes": [ { "id": "b221cad1-ef2e-4192-8168-11d0205a43da", @@ -88,7 +88,7 @@ { "id": "33bb5816-8479-4995-99e2-cb0443886003", "name": "pressed", - "displayName": "Button pressed", + "displayName": "Pressed", "paramTypes": [ { "id": "c086a247-838f-49c0-b1e4-2ae1ed181b55", @@ -98,6 +98,20 @@ "allowedValues": ["ON", "OFF", "DIM UP", "DIM DOWN"] } ] + }, + { + "id": "1a0be168-c9cb-44be-bca3-14c389f18e9f", + "name": "longPressed", + "displayName": "Long pressed", + "paramTypes": [ + { + "id": "046877e7-8e1c-489b-ac5f-8f0242c27b68", + "name": "buttonName", + "displayName": "Button name", + "type": "QString", + "allowedValues": ["ON", "OFF", "DIM UP", "DIM DOWN"] + } + ] } ] },