Add mre tradfri devices for testing

This commit is contained in:
Simon Stürz 2020-11-25 13:10:19 +01:00
parent b48e1245ab
commit 0946e04ebb
5 changed files with 673 additions and 18 deletions

View File

@ -0,0 +1 @@
usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_integrationpluginzigbee-tradfri.so

View File

@ -149,6 +149,10 @@ void IntegrationPluginZigbeeGenericLights::handleRemoveNode(ZigbeeNode *node, co
if (m_colorTemperatureRanges.contains(thing)) {
m_colorTemperatureRanges.remove(thing);
}
if (m_colorCapabilities.contains(thing)) {
m_colorCapabilities.remove(thing);
}
}
}
@ -609,6 +613,10 @@ void IntegrationPluginZigbeeGenericLights::thingRemoved(Thing *thing)
if (m_colorTemperatureRanges.contains(thing)) {
m_colorTemperatureRanges.remove(thing);
}
if (m_colorCapabilities.contains(thing)) {
m_colorCapabilities.remove(thing);
}
}
ZigbeeNodeEndpoint *IntegrationPluginZigbeeGenericLights::findEndpoint(Thing *thing)

View File

@ -1,7 +1,7 @@
{
"name": "ZigbeeTradfri",
"displayName": "Zigbee TRÅDFRI",
"id": "6a4343be-9fd6-4015-9ff5-38542651c534",
"id": "ad9480c8-05f0-4ba1-92b1-4dd8e2ae2830",
"vendors": [
{
"name": "ikeaTradfri",
@ -121,6 +121,236 @@
]
}
]
},
{
"name": "remote",
"displayName": "TRÅDFRI remote",
"id": "19ee3f56-f958-4b04-8ea3-747b72d7c7d6",
"setupMethod": "JustAdd",
"createMethods": [ "Auto" ],
"interfaces": [ "longpressmultibutton", "batterylevel", "wirelessconnectable" ],
"paramTypes": [
{
"id": "2ee18d3e-5fb8-4809-a52a-8e420b677f6e",
"name": "ieeeAddress",
"displayName": "IEEE adress",
"type": "QString",
"defaultValue": "00:00:00:00:00:00:00:00"
},
{
"id": "7bb2e588-5e05-4cf8-bedc-a54d6cdae30e",
"name": "networkUuid",
"displayName": "Zigbee network UUID",
"type": "QString",
"defaultValue": ""
},
{
"id": "09cbf843-0036-47b8-8c2b-a58939075203",
"name": "endpointId",
"displayName": "Endpoint ID",
"type": "uint",
"defaultValue": 1
}
],
"stateTypes": [
{
"id": "9d5d116c-f742-4766-baf9-e7662f5003a9",
"name": "connected",
"displayName": "Connected",
"displayNameEvent": "Connected changed",
"type": "bool",
"cached": false,
"defaultValue": false
},
{
"id": "b87d310a-28ba-4546-9f47-822e5182836c",
"name": "signalStrength",
"displayName": "Signal strength",
"displayNameEvent": "Signal strength changed",
"defaultValue": 0,
"maxValue": 100,
"minValue": 0,
"type": "uint",
"unit": "Percentage"
},
{
"id": "340444fa-b158-4d03-9967-7fa45a96bd45",
"name": "version",
"displayName": "Version",
"displayNameEvent": "Version changed",
"type": "QString",
"cached": true,
"defaultValue": ""
},
{
"id": "4a72d569-44be-4689-bda6-addc0c6351d4",
"name": "batteryLevel",
"displayName": "Battery",
"displayNameEvent": "Battery changed",
"type": "int",
"unit": "Percentage",
"defaultValue": 0,
"minValue": 0,
"maxValue": 100
},
{
"id": "87b60c83-d980-48f5-bc31-0daa241d727e",
"name": "batteryCritical",
"displayName": "Battery critical",
"displayNameEvent": "Battery critical changed",
"type": "bool",
"defaultValue": false
}
],
"actionTypes": [
],
"eventTypes": [
{
"id": "4d671edd-d57c-4f72-a43a-d8e4eaa741cf",
"name": "pressed",
"displayName": "Button pressed",
"paramTypes": [
{
"id": "2e6bc343-41a3-4634-b514-4512ae54afbc",
"name": "buttonName",
"displayName": "Button name",
"type": "QString",
"allowedValues": ["Power"]
}
]
},
{
"id": "3fb1bc3b-50ea-4a9f-a5b1-10b1fae2592e",
"name": "longPressed",
"displayName": "Button longpressed",
"paramTypes": [
{
"id": "4a151723-5798-4087-a4e5-5aa08ecbb9b8",
"name": "buttonName",
"displayName": "Button name",
"type": "QString",
"allowedValues": ["Power"]
}
]
}
]
},
{
"name": "motionSensor",
"displayName": "TRÅDFRI motion sensor",
"id": "30a1090e-8058-4a5b-aee3-4ba112ba3ba7",
"setupMethod": "JustAdd",
"createMethods": [ "Auto" ],
"interfaces": [ "presencesensor", "batterylevel", "wirelessconnectable" ],
"paramTypes": [
{
"id": "a03fabac-df59-4f4c-a691-237b1c37270f",
"name": "ieeeAddress",
"displayName": "IEEE adress",
"type": "QString",
"defaultValue": "00:00:00:00:00:00:00:00"
},
{
"id": "263a92bc-a993-4527-915c-1eccb3c2a7c5",
"name": "networkUuid",
"displayName": "Zigbee network UUID",
"type": "QString",
"defaultValue": ""
},
{
"id": "11e97521-0108-4157-8d85-26d64ecd7723",
"name": "endpointId",
"displayName": "Endpoint ID",
"type": "uint",
"defaultValue": 1
}
],
"settingsTypes": [
{
"id": "31cd2810-453b-4d86-a3ea-5556bcde4822",
"name": "timeout",
"displayName": "Time period",
"type": "uint",
"unit": "Seconds",
"defaultValue": 180,
"minValue": 180
}
],
"stateTypes": [
{
"id": "6bf470bb-4756-43fa-bddf-5e6ce002caa3",
"name": "connected",
"displayName": "Connected",
"displayNameEvent": "Connected changed",
"type": "bool",
"cached": false,
"defaultValue": false
},
{
"id": "0a54036e-ca25-40fd-8f24-3516c5141c0b",
"name": "signalStrength",
"displayName": "Signal strength",
"displayNameEvent": "Signal strength changed",
"defaultValue": 0,
"maxValue": 100,
"minValue": 0,
"type": "uint",
"unit": "Percentage"
},
{
"id": "709a215b-5664-4cea-b841-54dd01e387b8",
"name": "version",
"displayName": "Version",
"displayNameEvent": "Version changed",
"type": "QString",
"cached": true,
"defaultValue": ""
},
{
"id": "e2e414c1-5f21-4244-942a-ee17cb25f1c0",
"name": "batteryLevel",
"displayName": "Battery",
"displayNameEvent": "Battery changed",
"type": "int",
"unit": "Percentage",
"defaultValue": 0,
"minValue": 0,
"maxValue": 100
},
{
"id": "21e7c58e-595e-4b7d-bd21-c0a424c876dc",
"name": "batteryCritical",
"displayName": "Battery critical",
"displayNameEvent": "Battery critical changed",
"type": "bool",
"defaultValue": false
},
{
"id": "16ee7fe2-1a86-40e9-aae5-610f712ca155",
"name": "isPresent",
"displayName": "Present",
"displayNameEvent": "Present changed",
"type": "bool",
"defaultValue": true,
"ioType": "digitalInput"
},
{
"id": "12041993-c731-4cc1-8010-904b5655d920",
"name": "lastSeenTime",
"displayName": "Last seen time",
"displayNameEvent": "Last seen time changed",
"type": "int",
"unit": "UnixTime",
"defaultValue": 0
}
],
"actionTypes": [
],
"eventTypes": [
]
}
]
}

View File

@ -37,16 +37,28 @@
IntegrationPluginZigbeeTradfri::IntegrationPluginZigbeeTradfri()
{
m_ieeeAddressParamTypeIds[onOffSwitchThingClassId] = onOffSwitchThingIeeeAddressParamTypeId;
m_ieeeAddressParamTypeIds[remoteThingClassId] = remoteThingIeeeAddressParamTypeId;
m_ieeeAddressParamTypeIds[motionSensorThingClassId] = motionSensorThingIeeeAddressParamTypeId;
m_networkUuidParamTypeIds[onOffSwitchThingClassId] = onOffSwitchThingNetworkUuidParamTypeId;
m_networkUuidParamTypeIds[remoteThingClassId] = remoteThingNetworkUuidParamTypeId;
m_networkUuidParamTypeIds[motionSensorThingClassId] = motionSensorThingNetworkUuidParamTypeId;
m_endpointIdParamTypeIds[onOffSwitchThingClassId] = onOffSwitchThingEndpointIdParamTypeId;
m_endpointIdParamTypeIds[remoteThingClassId] = remoteThingEndpointIdParamTypeId;
m_endpointIdParamTypeIds[motionSensorThingClassId] = motionSensorThingEndpointIdParamTypeId;
m_connectedStateTypeIds[onOffSwitchThingClassId] = onOffSwitchConnectedStateTypeId;
m_connectedStateTypeIds[remoteThingClassId] = remoteConnectedStateTypeId;
m_connectedStateTypeIds[motionSensorThingClassId] = motionSensorConnectedStateTypeId;
m_signalStrengthStateTypeIds[onOffSwitchThingClassId] = onOffSwitchSignalStrengthStateTypeId;
m_signalStrengthStateTypeIds[remoteThingClassId] = remoteSignalStrengthStateTypeId;
m_signalStrengthStateTypeIds[motionSensorThingClassId] = motionSensorSignalStrengthStateTypeId;
m_versionStateTypeIds[onOffSwitchThingClassId] = onOffSwitchVersionStateTypeId;
m_versionStateTypeIds[remoteThingClassId] = remoteVersionStateTypeId;
m_versionStateTypeIds[motionSensorThingClassId] = motionSensorVersionStateTypeId;
}
QString IntegrationPluginZigbeeTradfri::name() const
@ -62,18 +74,38 @@ bool IntegrationPluginZigbeeTradfri::handleNode(ZigbeeNode *node, const QUuid &n
bool handled = false;
foreach (ZigbeeNodeEndpoint *endpoint, node->endpoints()) {
if (endpoint->profile() == Zigbee::ZigbeeProfileHomeAutomation &&
endpoint->deviceId() == Zigbee::HomeAutomationDeviceNonColourController) {
if (endpoint->profile() == Zigbee::ZigbeeProfileHomeAutomation && endpoint->deviceId() == Zigbee::HomeAutomationDeviceNonColourController) {
qCDebug(dcZigbeeTradfri()) << "Handeling TRADFRI on/off switch" << node << endpoint;
ThingDescriptor descriptor(onOffSwitchThingClassId);
QString deviceClassName = supportedThings().findById(onOffSwitchThingClassId).displayName();
descriptor.setTitle(deviceClassName);
createThing(onOffSwitchThingClassId, networkUuid, node, endpoint);
initOnOffSwitch(node, endpoint);
handled = true;
}
if (endpoint->profile() == Zigbee::ZigbeeProfileHomeAutomation && endpoint->deviceId() == Zigbee::HomeAutomationDeviceOnOffSensor) {
qCDebug(dcZigbeeTradfri()) << "Handeling TRADFRI motion sensor" << node << endpoint;
createThing(motionSensorThingClassId, networkUuid, node, endpoint);
handled = true;
}
if (endpoint->profile() == Zigbee::ZigbeeProfileLightLink && endpoint->deviceId() == Zigbee::LightLinkDeviceNonColourSceneController) {
qCDebug(dcZigbeeTradfri()) << "Handeling TRADFRI remote control" << node << endpoint;
createThing(remoteThingClassId, networkUuid, node, endpoint);
ZigbeeClusterBasic *basicCluster = endpoint->inputCluster<ZigbeeClusterBasic>(ZigbeeClusterLibrary::ClusterIdBasic);
if (!basicCluster) {
qCWarning(dcZigbeeTradfri()) << "Failed to find basic cluster for performing factory reset to defaults";
initRemote(node, endpoint);
} else {
ZigbeeClusterReply *zclReply = basicCluster->resetToFactoryDefaults();
connect(zclReply, &ZigbeeClusterReply::finished, node, [=](){
if (zclReply->error() != ZigbeeClusterReply::ErrorNoError) {
qCWarning(dcZigbeeTradfri()) << "Failed to perform factory reset on basic cluster on" << endpoint;
}
initRemote(node, endpoint);
});
}
ParamList params;
params.append(Param(onOffSwitchThingNetworkUuidParamTypeId, networkUuid.toString()));
params.append(Param(onOffSwitchThingIeeeAddressParamTypeId, node->extendedAddress().toString()));
descriptor.setParams(params);
emit autoThingsAppeared({descriptor});
handled = true;
}
}
@ -84,13 +116,12 @@ bool IntegrationPluginZigbeeTradfri::handleNode(ZigbeeNode *node, const QUuid &n
void IntegrationPluginZigbeeTradfri::handleRemoveNode(ZigbeeNode *node, const QUuid &networkUuid)
{
Q_UNUSED(networkUuid)
Thing *thing = m_thingNodes.key(node);
if (thing) {
qCDebug(dcZigbeeTradfri()) << node << "for" << thing << "has left the network.";
emit autoThingDisappeared(thing->id());
// Removing it from our map to prevent a loop that would ask the zigbee network to remove this node (see thingRemoved())
if (m_thingNodes.values().contains(node)) {
Thing *thing = m_thingNodes.key(node);
qCDebug(dcZigbeeTradfri()) << node << "for" << thing << "has left the network.";
m_thingNodes.remove(thing);
emit autoThingDisappeared(thing->id());
}
}
@ -136,8 +167,8 @@ void IntegrationPluginZigbeeTradfri::setupThing(ThingSetupInfo *info)
// Set the version
thing->setStateValue(m_versionStateTypeIds.value(thing->thingClassId()), endpoint->softwareBuildId());
// Thing specific setup
if (thing->thingClassId() == onOffSwitchThingClassId) {
// Receive on/off commands
ZigbeeClusterOnOff *onOffCluster = endpoint->outputCluster<ZigbeeClusterOnOff>(ZigbeeClusterLibrary::ClusterIdOnOff);
if (!onOffCluster) {
@ -155,7 +186,7 @@ void IntegrationPluginZigbeeTradfri::setupThing(ThingSetupInfo *info)
});
}
// Receive level cntrol commands
// Receive level control commands
ZigbeeClusterLevelControl *levelCluster = endpoint->outputCluster<ZigbeeClusterLevelControl>(ZigbeeClusterLibrary::ClusterIdLevelControl);
if (!levelCluster) {
qCWarning(dcZigbeeTradfri()) << "Could not find level client cluster on" << thing << endpoint;
@ -177,6 +208,7 @@ void IntegrationPluginZigbeeTradfri::setupThing(ThingSetupInfo *info)
});
}
// Get battery level changes
ZigbeeClusterPowerConfiguration *powerCluster = endpoint->inputCluster<ZigbeeClusterPowerConfiguration>(ZigbeeClusterLibrary::ClusterIdPowerConfiguration);
if (!powerCluster) {
qCWarning(dcZigbeeTradfri()) << "Could not find power configuration cluster on" << thing << endpoint;
@ -195,6 +227,87 @@ void IntegrationPluginZigbeeTradfri::setupThing(ThingSetupInfo *info)
}
}
if (thing->thingClassId() == motionSensorThingClassId) {
// Create plugintimer if required
if (!m_presenceTimer) {
m_presenceTimer = hardwareManager()->pluginTimerManager()->registerTimer(1);
}
connect(m_presenceTimer, &PluginTimer::timeout, thing, [thing](){
if (thing->stateValue(motionSensorIsPresentStateTypeId).toBool()) {
int timeout = thing->setting(motionSensorSettingsTimeoutParamTypeId).toInt();
QDateTime lastSeenTime = QDateTime::fromMSecsSinceEpoch(thing->stateValue(motionSensorLastSeenTimeStateTypeId).toULongLong() * 1000);
if (lastSeenTime.addSecs(timeout) < QDateTime::currentDateTime()) {
thing->setStateValue(motionSensorIsPresentStateTypeId, false);
}
}
});
// Receive on/off commands
ZigbeeClusterOnOff *onOffCluster = endpoint->outputCluster<ZigbeeClusterOnOff>(ZigbeeClusterLibrary::ClusterIdOnOff);
if (!onOffCluster) {
qCWarning(dcZigbeeTradfri()) << "Could not find on/off client cluster on" << thing << endpoint;
} else {
connect(onOffCluster, &ZigbeeClusterOnOff::commandOnWithTimedOffSent, thing, [=](bool acceptOnlyWhenOn, quint16 onTime, quint16 offTime){
qCDebug(dcZigbeeTradfri()) << thing << "command received: Accept only when on:" << acceptOnlyWhenOn << "On time:" << onTime / 10 << "s" << "Off time:" << offTime / 10 << "s";
thing->setStateValue(motionSensorLastSeenTimeStateTypeId, QDateTime::currentDateTime().toMSecsSinceEpoch() / 1000);
thing->setStateValue(motionSensorIsPresentStateTypeId, true);
m_presenceTimer->start();
});
}
// Get battery level changes
ZigbeeClusterPowerConfiguration *powerCluster = endpoint->inputCluster<ZigbeeClusterPowerConfiguration>(ZigbeeClusterLibrary::ClusterIdPowerConfiguration);
if (!powerCluster) {
qCWarning(dcZigbeeTradfri()) << "Could not find power configuration cluster on" << thing << endpoint;
} else {
// Only set the initial state if the attribute already exists
if (powerCluster->hasAttribute(ZigbeeClusterPowerConfiguration::AttributeBatteryPercentageRemaining)) {
thing->setStateValue(motionSensorBatteryLevelStateTypeId, powerCluster->batteryPercentage());
thing->setStateValue(motionSensorBatteryCriticalStateTypeId, (powerCluster->batteryPercentage() < 10.0));
}
connect(powerCluster, &ZigbeeClusterPowerConfiguration::batteryPercentageChanged, thing, [=](double percentage){
qCDebug(dcZigbeeTradfri()) << "Battery percentage changed" << percentage << "%" << thing;
thing->setStateValue(motionSensorBatteryLevelStateTypeId, percentage);
thing->setStateValue(motionSensorBatteryCriticalStateTypeId, (percentage < 10.0));
});
}
}
if (thing->thingClassId() == remoteThingClassId) {
// Receive on/off commands
ZigbeeClusterOnOff *onOffCluster = endpoint->outputCluster<ZigbeeClusterOnOff>(ZigbeeClusterLibrary::ClusterIdOnOff);
if (!onOffCluster) {
qCWarning(dcZigbeeTradfri()) << "Could not find on/off client cluster on" << thing << endpoint;
} else {
connect(onOffCluster, &ZigbeeClusterOnOff::commandSent, thing, [=](ZigbeeClusterOnOff::Command command){
qCDebug(dcZigbeeTradfri()) << thing << "button pressed" << command;
if (command == ZigbeeClusterOnOff::CommandToggle) {
qCDebug(dcZigbeeTradfri()) << thing << "pressed power";
emit emitEvent(Event(remotePressedEventTypeId, thing->id(), ParamList() << Param(remotePressedEventButtonNameParamTypeId, "Power")));
}
});
}
// Get battery level changes
ZigbeeClusterPowerConfiguration *powerCluster = endpoint->inputCluster<ZigbeeClusterPowerConfiguration>(ZigbeeClusterLibrary::ClusterIdPowerConfiguration);
if (!powerCluster) {
qCWarning(dcZigbeeTradfri()) << "Could not find power configuration cluster on" << thing << endpoint;
} else {
// Only set the initial state if the attribute already exists
if (powerCluster->hasAttribute(ZigbeeClusterPowerConfiguration::AttributeBatteryPercentageRemaining)) {
thing->setStateValue(remoteBatteryLevelStateTypeId, powerCluster->batteryPercentage());
thing->setStateValue(remoteBatteryCriticalStateTypeId, (powerCluster->batteryPercentage() < 10.0));
}
connect(powerCluster, &ZigbeeClusterPowerConfiguration::batteryPercentageChanged, thing, [=](double percentage){
qCDebug(dcZigbeeTradfri()) << "Battery percentage changed" << percentage << "%" << thing;
thing->setStateValue(remoteBatteryLevelStateTypeId, percentage);
thing->setStateValue(remoteBatteryCriticalStateTypeId, (percentage < 10.0));
});
}
}
info->finish(Thing::ThingErrorNoError);
}
@ -224,3 +337,294 @@ ZigbeeNodeEndpoint *IntegrationPluginZigbeeTradfri::findEndpoint(Thing *thing)
return node->getEndpoint(endpointId);
}
void IntegrationPluginZigbeeTradfri::createThing(const ThingClassId &thingClassId, const QUuid &networkUuid, ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint)
{
ThingDescriptor descriptor(thingClassId);
QString deviceClassName = supportedThings().findById(thingClassId).displayName();
descriptor.setTitle(deviceClassName);
ParamList params;
params.append(Param(m_networkUuidParamTypeIds[thingClassId], networkUuid.toString()));
params.append(Param(m_ieeeAddressParamTypeIds[thingClassId], node->extendedAddress().toString()));
params.append(Param(m_endpointIdParamTypeIds[thingClassId], endpoint->endpointId()));
descriptor.setParams(params);
emit autoThingsAppeared({descriptor});
}
void IntegrationPluginZigbeeTradfri::initOnOffSwitch(ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint)
{
// Get the current configured bindings for this node
ZigbeeReply *reply = node->removeAllBindings();
connect(reply, &ZigbeeReply::finished, node, [=](){
if (reply->error() != ZigbeeReply::ErrorNoError) {
qCWarning(dcZigbeeTradfri()) << "Failed to remove all bindings for initialization of" << node;
}
// Read battery, bind and configure attribute reporting for battery
if (!endpoint->hasInputCluster(ZigbeeClusterLibrary::ClusterIdPowerConfiguration)) {
qCWarning(dcZigbeeTradfri()) << "Failed to initialize the power configuration cluster because the cluster could not be found" << node << endpoint;
return;
}
qCDebug(dcZigbeeTradfri()) << "Read power configuration cluster attributes" << node;
ZigbeeClusterReply *readAttributeReply = endpoint->getInputCluster(ZigbeeClusterLibrary::ClusterIdPowerConfiguration)->readAttributes({ZigbeeClusterPowerConfiguration::AttributeBatteryPercentageRemaining});
connect(readAttributeReply, &ZigbeeClusterReply::finished, node, [=](){
if (readAttributeReply->error() != ZigbeeClusterReply::ErrorNoError) {
qCWarning(dcZigbeeTradfri()) << "Failed to read power cluster attributes" << readAttributeReply->error();
//emit nodeInitialized(node);
return;
}
qCDebug(dcZigbeeTradfri()) << "Read power configuration cluster attributes finished successfully";
// Bind the cluster to the coordinator
qCDebug(dcZigbeeTradfri()) << "Bind power configuration cluster to coordinator IEEE address";
ZigbeeDeviceObjectReply * zdoReply = node->deviceObject()->requestBindIeeeAddress(endpoint->endpointId(), ZigbeeClusterLibrary::ClusterIdPowerConfiguration,
hardwareManager()->zigbeeResource()->coordinatorAddress(node->networkUuid()), 0x01);
connect(zdoReply, &ZigbeeDeviceObjectReply::finished, node, [=](){
if (zdoReply->error() != ZigbeeDeviceObjectReply::ErrorNoError) {
qCWarning(dcZigbeeTradfri()) << "Failed to bind power cluster to coordinator" << zdoReply->error();
return;
}
qCDebug(dcZigbeeTradfri()) << "Bind 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 = 60;//300;
reportingConfig.maxReportingInterval = 120;//2700;
reportingConfig.reportableChange = ZigbeeDataType(static_cast<quint8>(1)).data();
qCDebug(dcZigbeeTradfri()) << "Configure attribute reporting for power configuration cluster to coordinator";
ZigbeeClusterReply *reportingReply = endpoint->getInputCluster(ZigbeeClusterLibrary::ClusterIdPowerConfiguration)->configureReporting({reportingConfig});
connect(reportingReply, &ZigbeeClusterReply::finished, this, [=](){
if (reportingReply->error() != ZigbeeClusterReply::ErrorNoError) {
qCWarning(dcZigbeeTradfri()) << "Failed to configure power cluster attribute reporting" << reportingReply->error();
return;
}
qCDebug(dcZigbeeTradfri()) << "Attribute reporting configuration finished for power cluster" << ZigbeeClusterLibrary::parseAttributeReportingStatusRecords(reportingReply->responseFrame().payload);
qCDebug(dcZigbeeTradfri()) << "Bind power configuration cluster to coordinator";
ZigbeeDeviceObjectReply * zdoReply = node->deviceObject()->requestBindShortAddress(endpoint->endpointId(), ZigbeeClusterLibrary::ClusterIdOnOff, 0x0000);
connect(zdoReply, &ZigbeeDeviceObjectReply::finished, node, [=](){
if (zdoReply->error() != ZigbeeDeviceObjectReply::ErrorNoError) {
qCWarning(dcZigbeeTradfri()) << "Failed to bind on/off cluster to coordinator" << zdoReply->error();
return;
}
qCDebug(dcZigbeeTradfri()) << "Bind on/off cluster to coordinator finished successfully";
qCDebug(dcZigbeeTradfri()) << "Bind power level cluster to coordinator";
ZigbeeDeviceObjectReply * zdoReply = node->deviceObject()->requestBindShortAddress(endpoint->endpointId(), ZigbeeClusterLibrary::ClusterIdLevelControl, 0x0000);
connect(zdoReply, &ZigbeeDeviceObjectReply::finished, node, [=](){
if (zdoReply->error() != ZigbeeDeviceObjectReply::ErrorNoError) {
qCWarning(dcZigbeeTradfri()) << "Failed to bind level cluster to coordinator" << zdoReply->error();
return;
}
qCDebug(dcZigbeeTradfri()) << "Bind level cluster to coordinator finished successfully";
qCDebug(dcZigbeeTradfri()) << "Read binding table from node" << node;
ZigbeeReply *reply = node->readBindingTableEntries();
connect(reply, &ZigbeeReply::finished, node, [=](){
if (reply->error() != ZigbeeReply::ErrorNoError) {
qCWarning(dcZigbeeTradfri()) << "Failed to read binding table from" << node;
} else {
foreach (const ZigbeeDeviceProfile::BindingTableListRecord &binding, node->bindingTableRecords()) {
qCDebug(dcZigbeeTradfri()) << node << binding;
}
}
});
});
});
});
});
});
});
}
void IntegrationPluginZigbeeTradfri::initRemote(ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint)
{
// Get the current configured bindings for this node
ZigbeeReply *reply = node->removeAllBindings();
connect(reply, &ZigbeeReply::finished, node, [=](){
if (reply->error() != ZigbeeReply::ErrorNoError) {
qCWarning(dcZigbeeTradfri()) << "Failed to remove all bindings for initialization of" << node;
}
// Read battery, bind and configure attribute reporting for battery
if (!endpoint->hasInputCluster(ZigbeeClusterLibrary::ClusterIdPowerConfiguration)) {
qCWarning(dcZigbeeTradfri()) << "Failed to initialize the power configuration cluster because the cluster could not be found" << node << endpoint;
return;
}
qCDebug(dcZigbeeTradfri()) << "Read power configuration cluster attributes" << node;
ZigbeeClusterReply *readAttributeReply = endpoint->getInputCluster(ZigbeeClusterLibrary::ClusterIdPowerConfiguration)->readAttributes({ZigbeeClusterPowerConfiguration::AttributeBatteryPercentageRemaining});
connect(readAttributeReply, &ZigbeeClusterReply::finished, node, [=](){
if (readAttributeReply->error() != ZigbeeClusterReply::ErrorNoError) {
qCWarning(dcZigbeeTradfri()) << "Failed to read power cluster attributes" << readAttributeReply->error();
//emit nodeInitialized(node);
return;
}
qCDebug(dcZigbeeTradfri()) << "Read power configuration cluster attributes finished successfully";
// Bind the cluster to the coordinator
qCDebug(dcZigbeeTradfri()) << "Bind power configuration cluster to coordinator IEEE address";
ZigbeeDeviceObjectReply * zdoReply = node->deviceObject()->requestBindIeeeAddress(endpoint->endpointId(), ZigbeeClusterLibrary::ClusterIdPowerConfiguration,
hardwareManager()->zigbeeResource()->coordinatorAddress(node->networkUuid()), 0x01);
connect(zdoReply, &ZigbeeDeviceObjectReply::finished, node, [=](){
if (zdoReply->error() != ZigbeeDeviceObjectReply::ErrorNoError) {
qCWarning(dcZigbeeTradfri()) << "Failed to bind power cluster to coordinator" << zdoReply->error();
return;
}
qCDebug(dcZigbeeTradfri()) << "Bind 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 = 60;//300;
reportingConfig.maxReportingInterval = 120;//2700;
reportingConfig.reportableChange = ZigbeeDataType(static_cast<quint8>(1)).data();
qCDebug(dcZigbeeTradfri()) << "Configure attribute reporting for power configuration cluster to coordinator";
ZigbeeClusterReply *reportingReply = endpoint->getInputCluster(ZigbeeClusterLibrary::ClusterIdPowerConfiguration)->configureReporting({reportingConfig});
connect(reportingReply, &ZigbeeClusterReply::finished, this, [=](){
if (reportingReply->error() != ZigbeeClusterReply::ErrorNoError) {
qCWarning(dcZigbeeTradfri()) << "Failed to configure power cluster attribute reporting" << reportingReply->error();
return;
}
qCDebug(dcZigbeeTradfri()) << "Attribute reporting configuration finished for power cluster" << ZigbeeClusterLibrary::parseAttributeReportingStatusRecords(reportingReply->responseFrame().payload);
qCDebug(dcZigbeeTradfri()) << "Bind power configuration cluster to coordinator";
ZigbeeDeviceObjectReply * zdoReply = node->deviceObject()->requestBindShortAddress(endpoint->endpointId(), ZigbeeClusterLibrary::ClusterIdOnOff, 0x0000);
connect(zdoReply, &ZigbeeDeviceObjectReply::finished, node, [=](){
if (zdoReply->error() != ZigbeeDeviceObjectReply::ErrorNoError) {
qCWarning(dcZigbeeTradfri()) << "Failed to bind on/off cluster to coordinator" << zdoReply->error();
return;
}
qCDebug(dcZigbeeTradfri()) << "Bind on/off cluster to coordinator finished successfully";
qCDebug(dcZigbeeTradfri()) << "Bind power level cluster to coordinator";
ZigbeeDeviceObjectReply * zdoReply = node->deviceObject()->requestBindShortAddress(endpoint->endpointId(), ZigbeeClusterLibrary::ClusterIdLevelControl, 0x0000);
connect(zdoReply, &ZigbeeDeviceObjectReply::finished, node, [=](){
if (zdoReply->error() != ZigbeeDeviceObjectReply::ErrorNoError) {
qCWarning(dcZigbeeTradfri()) << "Failed to bind level cluster to coordinator" << zdoReply->error();
return;
}
qCDebug(dcZigbeeTradfri()) << "Bind level cluster to coordinator finished successfully";
qCDebug(dcZigbeeTradfri()) << "Read binding table from node" << node;
ZigbeeReply *reply = node->readBindingTableEntries();
connect(reply, &ZigbeeReply::finished, node, [=](){
if (reply->error() != ZigbeeReply::ErrorNoError) {
qCWarning(dcZigbeeTradfri()) << "Failed to read binding table from" << node;
} else {
foreach (const ZigbeeDeviceProfile::BindingTableListRecord &binding, node->bindingTableRecords()) {
qCDebug(dcZigbeeTradfri()) << node << binding;
}
}
});
});
});
});
});
});
});
}
void IntegrationPluginZigbeeTradfri::readBindings(ZigbeeNode *node)
{
qCDebug(dcZigbeeTradfri()) << "Read binding table from node" << node;
ZigbeeReply *reply = node->readBindingTableEntries();
connect(reply, &ZigbeeReply::finished, node, [=](){
if (reply->error() != ZigbeeReply::ErrorNoError) {
qCWarning(dcZigbeeTradfri()) << "Failed to read binding table from" << node;
} else {
foreach (const ZigbeeDeviceProfile::BindingTableListRecord &binding, node->bindingTableRecords()) {
qCDebug(dcZigbeeTradfri()) << node << binding;
}
}
});
}
void IntegrationPluginZigbeeTradfri::initPowerConfigurationCluster(ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint)
{
if (!endpoint->hasInputCluster(ZigbeeClusterLibrary::ClusterIdPowerConfiguration)) {
qCWarning(dcZigbeeTradfri()) << "Failed to initialize the power configuration cluster because the cluster could not be found" << node << endpoint;
return;
}
qCDebug(dcZigbeeTradfri()) << "Read power configuration cluster attributes" << node;
ZigbeeClusterReply *readAttributeReply = endpoint->getInputCluster(ZigbeeClusterLibrary::ClusterIdPowerConfiguration)->readAttributes({ZigbeeClusterPowerConfiguration::AttributeBatteryPercentageRemaining});
connect(readAttributeReply, &ZigbeeClusterReply::finished, node, [=](){
if (readAttributeReply->error() != ZigbeeClusterReply::ErrorNoError) {
qCWarning(dcZigbeeTradfri()) << "Failed to read power cluster attributes" << readAttributeReply->error();
//emit nodeInitialized(node);
return;
}
qCDebug(dcZigbeeTradfri()) << "Read power configuration cluster attributes finished successfully";
// Bind the cluster to the coordinator
qCDebug(dcZigbeeTradfri()) << "Bind power configuration cluster to coordinator IEEE address";
ZigbeeDeviceObjectReply * zdoReply = node->deviceObject()->requestBindIeeeAddress(endpoint->endpointId(), ZigbeeClusterLibrary::ClusterIdPowerConfiguration,
hardwareManager()->zigbeeResource()->coordinatorAddress(node->networkUuid()), 0x01);
connect(zdoReply, &ZigbeeDeviceObjectReply::finished, node, [=](){
if (zdoReply->error() != ZigbeeDeviceObjectReply::ErrorNoError) {
qCWarning(dcZigbeeTradfri()) << "Failed to bind power cluster to coordinator" << zdoReply->error();
return;
}
qCDebug(dcZigbeeTradfri()) << "Bind 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 = 60;//300;
reportingConfig.maxReportingInterval = 120;//2700;
reportingConfig.reportableChange = ZigbeeDataType(static_cast<quint8>(1)).data();
qCDebug(dcZigbeeTradfri()) << "Configure attribute reporting for power configuration cluster to coordinator";
ZigbeeClusterReply *reportingReply = endpoint->getInputCluster(ZigbeeClusterLibrary::ClusterIdPowerConfiguration)->configureReporting({reportingConfig});
connect(reportingReply, &ZigbeeClusterReply::finished, this, [reportingReply](){
if (reportingReply->error() != ZigbeeClusterReply::ErrorNoError) {
qCWarning(dcZigbeeTradfri()) << "Failed to configure power cluster attribute reporting" << reportingReply->error();
return;
}
qCDebug(dcZigbeeTradfri()) << "Attribute reporting configuration finished for power cluster" << ZigbeeClusterLibrary::parseAttributeReportingStatusRecords(reportingReply->responseFrame().payload);
});
});
});
}
void IntegrationPluginZigbeeTradfri::initOnOffCluster(ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint)
{
qCDebug(dcZigbeeTradfri()) << "Bind power configuration cluster to coordinator";
ZigbeeDeviceObjectReply * zdoReply = node->deviceObject()->requestBindShortAddress(endpoint->endpointId(), ZigbeeClusterLibrary::ClusterIdOnOff, 0x0000);
connect(zdoReply, &ZigbeeDeviceObjectReply::finished, node, [=](){
if (zdoReply->error() != ZigbeeDeviceObjectReply::ErrorNoError) {
qCWarning(dcZigbeeTradfri()) << "Failed to bind on/off cluster to coordinator" << zdoReply->error();
return;
}
qCDebug(dcZigbeeTradfri()) << "Bind on/off cluster to coordinator finished successfully";
});
}
void IntegrationPluginZigbeeTradfri::initLevelControlCluster(ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint)
{
qCDebug(dcZigbeeTradfri()) << "Bind power level cluster to coordinator";
ZigbeeDeviceObjectReply * zdoReply = node->deviceObject()->requestBindShortAddress(endpoint->endpointId(), ZigbeeClusterLibrary::ClusterIdLevelControl, 0x0000);
connect(zdoReply, &ZigbeeDeviceObjectReply::finished, node, [=](){
if (zdoReply->error() != ZigbeeDeviceObjectReply::ErrorNoError) {
qCWarning(dcZigbeeTradfri()) << "Failed to bind level cluster to coordinator" << zdoReply->error();
return;
}
qCDebug(dcZigbeeTradfri()) << "Bind level cluster to coordinator finished successfully";
});
}

View File

@ -57,6 +57,7 @@ public:
void thingRemoved(Thing *thing) override;
private:
PluginTimer *m_presenceTimer = nullptr;
QHash<Thing*, ZigbeeNode*> m_thingNodes;
QHash<ThingClassId, ParamTypeId> m_ieeeAddressParamTypeIds;
@ -68,6 +69,17 @@ private:
QHash<ThingClassId, StateTypeId> m_versionStateTypeIds;
ZigbeeNodeEndpoint *findEndpoint(Thing *thing);
void createThing(const ThingClassId &thingClassId, const QUuid &networkUuid, ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint);
void initOnOffSwitch(ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint);
void initRemote(ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint);
void readBindings(ZigbeeNode *node);
void initPowerConfigurationCluster(ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint);
void initOnOffCluster(ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint);
void initLevelControlCluster(ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint);
};
#endif // INTEGRATIONPLUGINZIGBEETRADFRI_H