Add tradfri range extender and fix motion sensor. Start implementing hue outdoor sensor

This commit is contained in:
Simon Stürz 2020-11-28 14:27:31 +01:00
parent 10cc4ed8db
commit cb2a6f9f4c
6 changed files with 528 additions and 136 deletions

View File

@ -100,6 +100,122 @@
]
}
]
},
{
"name": "outdoorSensor",
"displayName": "Hue outdoor sensor",
"id": "031c45d1-e782-4f91-9f05-259d6b81c7ef",
"setupMethod": "JustAdd",
"createMethods": [ "Auto" ],
"interfaces": [ "presencesensor", "lightsensor", "temperaturesensor", "batterylevel", "wirelessconnectable" ],
"paramTypes": [
{
"id": "a866df99-7e2a-40e1-b82e-529c03846d5e",
"name": "ieeeAddress",
"displayName": "IEEE adress",
"type": "QString",
"defaultValue": "00:00:00:00:00:00:00:00"
},
{
"id": "99f4ff28-6d61-4e96-bfc4-c32aa05bb256",
"name": "networkUuid",
"displayName": "Zigbee network UUID",
"type": "QString",
"defaultValue": ""
}
],
"stateTypes": [
{
"id": "0d0a5ee1-41f2-4b5a-a3c0-7c8cca1c064d",
"name": "connected",
"displayName": "Connected",
"displayNameEvent": "Connected changed",
"type": "bool",
"cached": false,
"defaultValue": false
},
{
"id": "d4f85c34-896a-4bab-af71-8b80d9889815",
"name": "signalStrength",
"displayName": "Signal strength",
"displayNameEvent": "Signal strength changed",
"defaultValue": 0,
"maxValue": 100,
"minValue": 0,
"type": "uint",
"unit": "Percentage"
},
{
"id": "18cb67e1-220f-41d7-9145-1816fc5d38d0",
"name": "version",
"displayName": "Version",
"displayNameEvent": "Version changed",
"type": "QString",
"cached": true,
"defaultValue": ""
},
{
"id": "00a4450b-d2fe-4e2b-92c8-1ab3a98d1628",
"name": "batteryLevel",
"displayName": "Battery",
"displayNameEvent": "Battery changed",
"type": "int",
"unit": "Percentage",
"defaultValue": 0,
"minValue": 0,
"maxValue": 100
},
{
"id": "9341f65a-b0aa-4648-82eb-e8400779f817",
"name": "batteryCritical",
"displayName": "Battery critical",
"displayNameEvent": "Battery critical changed",
"type": "bool",
"defaultValue": false
},
{
"id": "66e3b840-4f4c-4f4a-a71a-69fb751f2a3b",
"name": "isPresent",
"displayName": "Present",
"displayNameEvent": "Present changed",
"type": "bool",
"defaultValue": true,
"ioType": "digitalInput"
},
{
"id": "128777a9-75e7-4cc6-b196-691d2ffddbc9",
"name": "lastSeenTime",
"displayName": "Last seen time",
"displayNameEvent": "Last seen time changed",
"type": "int",
"unit": "UnixTime",
"defaultValue": 0
},
{
"id": "8077e335-1d18-4e3f-9b5e-6f71af4a33b1",
"name": "lightIntensity",
"displayName": "Light intensity",
"displayNameEvent": "Light intensity changed",
"type": "double",
"unit": "Lux",
"defaultValue": 0
},
{
"id": "978f069c-57c0-4e73-a81b-c593bc2e7624",
"name": "temperature",
"displayName": "Temperature",
"displayNameEvent": "Temperature changed",
"type": "double",
"unit": "DegreeCelsius",
"defaultValue": 0.0
}
],
"actionTypes": [
],
"eventTypes": [
]
}
]
}

View File

@ -36,15 +36,19 @@
IntegrationPluginZigbeePhilipsHue::IntegrationPluginZigbeePhilipsHue()
{
m_ieeeAddressParamTypeIds[dimmerSwitchThingClassId] = dimmerSwitchThingIeeeAddressParamTypeId;
m_ieeeAddressParamTypeIds[outdoorSensorThingClassId] = outdoorSensorThingIeeeAddressParamTypeId;
m_networkUuidParamTypeIds[dimmerSwitchThingClassId] = dimmerSwitchThingNetworkUuidParamTypeId;
m_networkUuidParamTypeIds[outdoorSensorThingClassId] = outdoorSensorThingNetworkUuidParamTypeId;
m_connectedStateTypeIds[dimmerSwitchThingClassId] = dimmerSwitchConnectedStateTypeId;
m_connectedStateTypeIds[outdoorSensorThingClassId] = outdoorSensorConnectedStateTypeId;
m_signalStrengthStateTypeIds[dimmerSwitchThingClassId] = dimmerSwitchSignalStrengthStateTypeId;
m_signalStrengthStateTypeIds[outdoorSensorThingClassId] = outdoorSensorSignalStrengthStateTypeId;
m_versionStateTypeIds[dimmerSwitchThingClassId] = dimmerSwitchVersionStateTypeId;
m_versionStateTypeIds[outdoorSensorThingClassId] = outdoorSensorVersionStateTypeId;
}
QString IntegrationPluginZigbeePhilipsHue::name() const
@ -66,8 +70,8 @@ bool IntegrationPluginZigbeePhilipsHue::handleNode(ZigbeeNode *node, const QUuid
// Dimmer switch
if (endpointOne->profile() == Zigbee::ZigbeeProfileLightLink &&
endpointOne->deviceId() == Zigbee::LightLinkDeviceNonColourSceneController
&& endpoinTwo->profile() == Zigbee::ZigbeeProfileHomeAutomation &&
endpointOne->deviceId() == Zigbee::LightLinkDeviceNonColourSceneController &&
endpoinTwo->profile() == Zigbee::ZigbeeProfileHomeAutomation &&
endpoinTwo->deviceId() == Zigbee::HomeAutomationDeviceSimpleSensor) {
qCDebug(dcZigbeePhilipsHue()) << "Handeling Hue dimmer switch" << node << endpointOne << endpoinTwo;
@ -75,6 +79,18 @@ bool IntegrationPluginZigbeePhilipsHue::handleNode(ZigbeeNode *node, const QUuid
initDimmerSwitch(node);
return true;
}
// Outdoor sensor
if (endpointOne->profile() == Zigbee::ZigbeeProfileLightLink &&
endpointOne->deviceId() == Zigbee::LightLinkDeviceOnOffSensor &&
endpoinTwo->profile() == Zigbee::ZigbeeProfileHomeAutomation &&
endpoinTwo->deviceId() == Zigbee::HomeAutomationDeviceOccupacySensor) {
qCDebug(dcZigbeePhilipsHue()) << "Handeling Hue outdoor sensor" << node << endpointOne << endpoinTwo;
createThing(outdoorSensorThingClassId, networkUuid, node);
initOutdoorSensor(node);
return true;
}
}
return handled;
@ -124,7 +140,6 @@ void IntegrationPluginZigbeePhilipsHue::setupThing(ThingSetupInfo *info)
thing->setStateValue(m_signalStrengthStateTypeIds.value(thing->thingClassId()), signalStrength);
});
// Thing specific setup
if (thing->thingClassId() == dimmerSwitchThingClassId) {
ZigbeeNodeEndpoint *endpointZll = node->getEndpoint(0x01);
@ -133,7 +148,6 @@ 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<ZigbeeClusterOnOff>(ZigbeeClusterLibrary::ClusterIdOnOff);
if (!onOffCluster) {
@ -191,6 +205,81 @@ void IntegrationPluginZigbeePhilipsHue::setupThing(ThingSetupInfo *info)
thing->setStateValue(dimmerSwitchBatteryCriticalStateTypeId, (percentage < 10.0));
});
}
}
if (thing->thingClassId() == outdoorSensorThingClassId) {
ZigbeeNodeEndpoint *endpointHa = node->getEndpoint(0x02);
// Set the version
thing->setStateValue(m_versionStateTypeIds.value(thing->thingClassId()), endpointHa->softwareBuildId());
// Get battery level changes
ZigbeeClusterPowerConfiguration *powerCluster = endpointHa->inputCluster<ZigbeeClusterPowerConfiguration>(ZigbeeClusterLibrary::ClusterIdPowerConfiguration);
if (!powerCluster) {
qCWarning(dcZigbeePhilipsHue()) << "Could not find power configuration cluster on" << thing << endpointHa;
} else {
// Only set the initial state if the attribute already exists
if (powerCluster->hasAttribute(ZigbeeClusterPowerConfiguration::AttributeBatteryPercentageRemaining)) {
thing->setStateValue(outdoorSensorBatteryLevelStateTypeId, powerCluster->batteryPercentage());
thing->setStateValue(outdoorSensorBatteryCriticalStateTypeId, (powerCluster->batteryPercentage() < 10.0));
}
connect(powerCluster, &ZigbeeClusterPowerConfiguration::batteryPercentageChanged, thing, [=](double percentage){
qCDebug(dcZigbeePhilipsHue()) << "Battery percentage changed" << percentage << "%" << thing;
thing->setStateValue(outdoorSensorBatteryLevelStateTypeId, percentage);
thing->setStateValue(outdoorSensorBatteryCriticalStateTypeId, (percentage < 10.0));
});
}
ZigbeeClusterOccupancySensing *occupancyCluster = endpointHa->inputCluster<ZigbeeClusterOccupancySensing>(ZigbeeClusterLibrary::ClusterIdOccupancySensing);
if (!occupancyCluster) {
qCWarning(dcZigbeePhilipsHue()) << "Occupancy cluster not found on" << thing;
} else {
if (occupancyCluster->hasAttribute(ZigbeeClusterOccupancySensing::AttributeOccupancy)) {
thing->setStateValue(outdoorSensorIsPresentStateTypeId, occupancyCluster->occupied());
thing->setStateValue(outdoorSensorLastSeenTimeStateTypeId, QDateTime::currentMSecsSinceEpoch() / 1000);
}
connect(occupancyCluster, &ZigbeeClusterOccupancySensing::occupancyChanged, thing, [thing](bool occupancy){
qCDebug(dcZigbeePhilipsHue()) << "occupancy changed" << occupancy;
// Only change the state if the it changed to true, it will be disabled by the timer
if (occupancy) {
thing->setStateValue(outdoorSensorIsPresentStateTypeId, occupancy);
//m_presenceTimer->start();
}
thing->setStateValue(outdoorSensorLastSeenTimeStateTypeId, QDateTime::currentMSecsSinceEpoch() / 1000);
});
// if (!m_presenceTimer) {
// m_presenceTimer = hardwareManager()->pluginTimerManager()->registerTimer(1);
// }
// connect(m_presenceTimer, &PluginTimer::timeout, thing, [thing](){
// if (thing->stateValue(outdoorSensorIsPresentStateTypeId).toBool()) {
// int timeout = thing->setting(outdoorSensorSettingsTimeoutParamTypeId).toInt();
// QDateTime lastSeenTime = QDateTime::fromMSecsSinceEpoch(thing->stateValue(lumiMotionSensorLastSeenTimeStateTypeId).toULongLong() * 1000);
// if (lastSeenTime.addSecs(timeout) < QDateTime::currentDateTime()) {
// thing->setStateValue(lumiMotionSensorIsPresentStateTypeId, false);
// }
// }
// });
}
ZigbeeClusterTemperatureMeasurement *temperatureCluster = endpointHa->inputCluster<ZigbeeClusterTemperatureMeasurement>(ZigbeeClusterLibrary::ClusterIdTemperatureMeasurement);
if (!temperatureCluster) {
qCWarning(dcZigbeePhilipsHue()) << "Could not find the temperature measurement server cluster on" << thing << endpointHa;
} else {
// Only set the state if the cluster actually has the attribute
if (temperatureCluster->hasAttribute(ZigbeeClusterTemperatureMeasurement::AttributeMeasuredValue)) {
thing->setStateValue(outdoorSensorTemperatureStateTypeId, temperatureCluster->temperature());
}
connect(temperatureCluster, &ZigbeeClusterTemperatureMeasurement::temperatureChanged, thing, [thing](double temperature){
qCDebug(dcZigbeePhilipsHue()) << thing << "temperature changed" << temperature << "°C";
thing->setStateValue(outdoorSensorTemperatureStateTypeId, temperature);
});
}
}
@ -319,3 +408,149 @@ void IntegrationPluginZigbeePhilipsHue::initDimmerSwitch(ZigbeeNode *node)
});
}
void IntegrationPluginZigbeePhilipsHue::initOutdoorSensor(ZigbeeNode *node)
{
//ZigbeeNodeEndpoint *endpointZll = node->getEndpoint(0x01);
ZigbeeNodeEndpoint *endpointHa = node->getEndpoint(0x02);
// Get the current configured bindings for this node
ZigbeeReply *reply = node->removeAllBindings();
connect(reply, &ZigbeeReply::finished, node, [=](){
if (reply->error() != ZigbeeReply::ErrorNoError) {
qCWarning(dcZigbeePhilipsHue()) << "Failed to remove all bindings for initialization of" << node;
}
// Read battery, bind and configure attribute reporting for battery
if (!endpointHa->hasInputCluster(ZigbeeClusterLibrary::ClusterIdPowerConfiguration)) {
qCWarning(dcZigbeePhilipsHue()) << "Failed to initialize the power configuration cluster because the cluster could not be found" << node << endpointHa;
return;
}
qCDebug(dcZigbeePhilipsHue()) << "Read 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";
}
// 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();
} else {
qCDebug(dcZigbeePhilipsHue()) << "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 = 300;
reportingConfig.maxReportingInterval = 2700;
reportingConfig.reportableChange = ZigbeeDataType(static_cast<quint8>(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);
}
// Init occupancy sensing cluster
qCDebug(dcZigbeePhilipsHue()) << "Bind occupancy cluster to coordinator IEEE address";
ZigbeeDeviceObjectReply * zdoReply = node->deviceObject()->requestBindIeeeAddress(endpointHa->endpointId(), ZigbeeClusterLibrary::ClusterIdOccupancySensing,
hardwareManager()->zigbeeResource()->coordinatorAddress(node->networkUuid()), 0x01);
connect(zdoReply, &ZigbeeDeviceObjectReply::finished, node, [=](){
if (zdoReply->error() != ZigbeeDeviceObjectReply::ErrorNoError) {
qCWarning(dcZigbeePhilipsHue()) << "Failed to bind occupancy cluster to coordinator" << zdoReply->error();
} else {
qCDebug(dcZigbeePhilipsHue()) << "Bind occupency cluster to coordinator finished successfully";
}
// Configure attribute reporting for temperature
ZigbeeClusterLibrary::AttributeReportingConfiguration reportingConfig;
reportingConfig.attributeId = ZigbeeClusterTemperatureMeasurement::AttributeMeasuredValue;
reportingConfig.dataType = Zigbee::BitMap8;
reportingConfig.minReportingInterval = 1;
reportingConfig.maxReportingInterval = 10;
reportingConfig.reportableChange = ZigbeeDataType(static_cast<quint8>(1)).data();
qCDebug(dcZigbeePhilipsHue()) << "Configure attribute reporting for occupancy cluster to coordinator";
ZigbeeClusterReply *reportingReply = endpointHa->getInputCluster(ZigbeeClusterLibrary::ClusterIdOccupancySensing)->configureReporting({reportingConfig});
connect(reportingReply, &ZigbeeClusterReply::finished, this, [=](){
if (reportingReply->error() != ZigbeeClusterReply::ErrorNoError) {
qCWarning(dcZigbeePhilipsHue()) << "Failed to configure occupancy cluster attribute reporting" << reportingReply->error();
} else {
qCDebug(dcZigbeePhilipsHue()) << "Attribute reporting configuration finished for occupancy cluster" << ZigbeeClusterLibrary::parseAttributeReportingStatusRecords(reportingReply->responseFrame().payload);
}
// Init temperature measurment cluster
qCDebug(dcZigbeePhilipsHue()) << "Bind temperature cluster to coordinator IEEE address";
ZigbeeDeviceObjectReply * zdoReply = node->deviceObject()->requestBindIeeeAddress(endpointHa->endpointId(), ZigbeeClusterLibrary::ClusterIdTemperatureMeasurement,
hardwareManager()->zigbeeResource()->coordinatorAddress(node->networkUuid()), 0x01);
connect(zdoReply, &ZigbeeDeviceObjectReply::finished, node, [=](){
if (zdoReply->error() != ZigbeeDeviceObjectReply::ErrorNoError) {
qCWarning(dcZigbeePhilipsHue()) << "Failed to bind temperature cluster to coordinator" << zdoReply->error();
} else {
qCDebug(dcZigbeePhilipsHue()) << "Bind temperature cluster to coordinator finished successfully";
}
// Read current temperature
if (!endpointHa->hasInputCluster(ZigbeeClusterLibrary::ClusterIdTemperatureMeasurement)) {
qCWarning(dcZigbeePhilipsHue()) << "Failed to initialize the temperature cluster because the cluster could not be found" << node << endpointHa;
return;
}
qCDebug(dcZigbeePhilipsHue()) << "Read temperature cluster attributes" << node;
ZigbeeClusterReply *readAttributeReply = endpointHa->getInputCluster(ZigbeeClusterLibrary::ClusterIdTemperatureMeasurement)->readAttributes({ZigbeeClusterTemperatureMeasurement::AttributeMeasuredValue});
connect(readAttributeReply, &ZigbeeClusterReply::finished, node, [=](){
if (readAttributeReply->error() != ZigbeeClusterReply::ErrorNoError) {
qCWarning(dcZigbeePhilipsHue()) << "Failed to read temperature cluster attributes" << readAttributeReply->error();
} else {
qCDebug(dcZigbeePhilipsHue()) << "Read temperature cluster attributes finished successfully";
}
// Configure attribute reporting
// Configure attribute reporting for temperature
ZigbeeClusterLibrary::AttributeReportingConfiguration reportingConfig;
reportingConfig.attributeId = ZigbeeClusterTemperatureMeasurement::AttributeMeasuredValue;
reportingConfig.dataType = Zigbee::Int16;
reportingConfig.minReportingInterval = 300;
reportingConfig.maxReportingInterval = 600;
reportingConfig.reportableChange = ZigbeeDataType(static_cast<qint16>(10)).data();
qCDebug(dcZigbeePhilipsHue()) << "Configure attribute reporting for temperature cluster to coordinator";
ZigbeeClusterReply *reportingReply = endpointHa->getInputCluster(ZigbeeClusterLibrary::ClusterIdTemperatureMeasurement)->configureReporting({reportingConfig});
connect(reportingReply, &ZigbeeClusterReply::finished, this, [=](){
if (reportingReply->error() != ZigbeeClusterReply::ErrorNoError) {
qCWarning(dcZigbeePhilipsHue()) << "Failed to configure temperature cluster attribute reporting" << reportingReply->error();
} else {
qCDebug(dcZigbeePhilipsHue()) << "Attribute reporting configuration finished for temperature cluster" << ZigbeeClusterLibrary::parseAttributeReportingStatusRecords(reportingReply->responseFrame().payload);
}
});
});
});
});
});
});
});
});
});
}

View File

@ -70,6 +70,7 @@ private:
void createThing(const ThingClassId &thingClassId, const QUuid &networkUuid, ZigbeeNode *node);
void initDimmerSwitch(ZigbeeNode *node);
void initOutdoorSensor(ZigbeeNode *node);
};

View File

@ -350,6 +350,74 @@
],
"eventTypes": [
]
},
{
"name": "signalRepeater",
"displayName": "TRÅDFRI signal repeater",
"id": "ac51f426-195e-472a-837f-4ba7e4666e6e",
"setupMethod": "JustAdd",
"createMethods": [ "Auto" ],
"interfaces": [ "wirelessconnectable" ],
"paramTypes": [
{
"id": "fcaf0646-ef18-4e16-afde-39823437b7f4",
"name": "ieeeAddress",
"displayName": "IEEE adress",
"type": "QString",
"defaultValue": "00:00:00:00:00:00:00:00"
},
{
"id": "5ce9a369-545b-4848-a6d7-6d3bc40779a2",
"name": "networkUuid",
"displayName": "Zigbee network UUID",
"type": "QString",
"defaultValue": ""
},
{
"id": "3ff01f62-83dc-4856-a7f0-5605091845cc",
"name": "endpointId",
"displayName": "Endpoint ID",
"type": "uint",
"defaultValue": 1
}
],
"stateTypes": [
{
"id": "7415ea93-9b72-4740-b3b1-a2ebb73d1db5",
"name": "connected",
"displayName": "Connected",
"displayNameEvent": "Connected changed",
"type": "bool",
"cached": false,
"defaultValue": false
},
{
"id": "05758f08-33a8-485b-8572-f87021e3af36",
"name": "signalStrength",
"displayName": "Signal strength",
"displayNameEvent": "Signal strength changed",
"defaultValue": 0,
"maxValue": 100,
"minValue": 0,
"type": "uint",
"unit": "Percentage"
},
{
"id": "a7ad66f1-7b4c-4458-8a13-e1bb9467bc17",
"name": "version",
"displayName": "Version",
"displayNameEvent": "Version changed",
"type": "QString",
"cached": true,
"defaultValue": ""
}
],
"actionTypes": [
],
"eventTypes": [
]
}
]

View File

@ -39,26 +39,32 @@ IntegrationPluginZigbeeTradfri::IntegrationPluginZigbeeTradfri()
m_ieeeAddressParamTypeIds[onOffSwitchThingClassId] = onOffSwitchThingIeeeAddressParamTypeId;
m_ieeeAddressParamTypeIds[remoteThingClassId] = remoteThingIeeeAddressParamTypeId;
m_ieeeAddressParamTypeIds[motionSensorThingClassId] = motionSensorThingIeeeAddressParamTypeId;
m_ieeeAddressParamTypeIds[signalRepeaterThingClassId] = signalRepeaterThingIeeeAddressParamTypeId;
m_networkUuidParamTypeIds[onOffSwitchThingClassId] = onOffSwitchThingNetworkUuidParamTypeId;
m_networkUuidParamTypeIds[remoteThingClassId] = remoteThingNetworkUuidParamTypeId;
m_networkUuidParamTypeIds[motionSensorThingClassId] = motionSensorThingNetworkUuidParamTypeId;
m_networkUuidParamTypeIds[signalRepeaterThingClassId] = signalRepeaterThingNetworkUuidParamTypeId;
m_endpointIdParamTypeIds[onOffSwitchThingClassId] = onOffSwitchThingEndpointIdParamTypeId;
m_endpointIdParamTypeIds[remoteThingClassId] = remoteThingEndpointIdParamTypeId;
m_endpointIdParamTypeIds[motionSensorThingClassId] = motionSensorThingEndpointIdParamTypeId;
m_endpointIdParamTypeIds[signalRepeaterThingClassId] = signalRepeaterThingEndpointIdParamTypeId;
m_connectedStateTypeIds[onOffSwitchThingClassId] = onOffSwitchConnectedStateTypeId;
m_connectedStateTypeIds[remoteThingClassId] = remoteConnectedStateTypeId;
m_connectedStateTypeIds[motionSensorThingClassId] = motionSensorConnectedStateTypeId;
m_connectedStateTypeIds[signalRepeaterThingClassId] = signalRepeaterConnectedStateTypeId;
m_signalStrengthStateTypeIds[onOffSwitchThingClassId] = onOffSwitchSignalStrengthStateTypeId;
m_signalStrengthStateTypeIds[remoteThingClassId] = remoteSignalStrengthStateTypeId;
m_signalStrengthStateTypeIds[motionSensorThingClassId] = motionSensorSignalStrengthStateTypeId;
m_signalStrengthStateTypeIds[signalRepeaterThingClassId] = signalRepeaterSignalStrengthStateTypeId;
m_versionStateTypeIds[onOffSwitchThingClassId] = onOffSwitchVersionStateTypeId;
m_versionStateTypeIds[remoteThingClassId] = remoteVersionStateTypeId;
m_versionStateTypeIds[motionSensorThingClassId] = motionSensorVersionStateTypeId;
m_versionStateTypeIds[signalRepeaterThingClassId] = signalRepeaterVersionStateTypeId;
}
QString IntegrationPluginZigbeeTradfri::name() const
@ -84,29 +90,20 @@ bool IntegrationPluginZigbeeTradfri::handleNode(ZigbeeNode *node, const QUuid &n
if (endpoint->profile() == Zigbee::ZigbeeProfileHomeAutomation && endpoint->deviceId() == Zigbee::HomeAutomationDeviceOnOffSensor) {
qCDebug(dcZigbeeTradfri()) << "Handeling TRADFRI motion sensor" << node << endpoint;
createThing(motionSensorThingClassId, networkUuid, node, endpoint);
initMotionSensor(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);
// });
// }
initRemote(node, endpoint);
handled = true;
}
if (endpoint->profile() == Zigbee::ZigbeeProfileHomeAutomation && endpoint->deviceId() == Zigbee::HomeAutomationDeviceRangeExtender) {
qCDebug(dcZigbeeTradfri()) << "Handeling TRADFRI signal repeater" << node << endpoint;
createThing(signalRepeaterThingClassId, networkUuid, node, endpoint);
handled = true;
}
}
@ -170,6 +167,24 @@ void IntegrationPluginZigbeeTradfri::setupThing(ThingSetupInfo *info)
// Thing specific setup
if (thing->thingClassId() == onOffSwitchThingClassId) {
// 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(onOffSwitchBatteryLevelStateTypeId, powerCluster->batteryPercentage());
thing->setStateValue(onOffSwitchBatteryCriticalStateTypeId, (powerCluster->batteryPercentage() < 10.0));
}
connect(powerCluster, &ZigbeeClusterPowerConfiguration::batteryPercentageChanged, thing, [=](double percentage){
qCDebug(dcZigbeeTradfri()) << "Battery percentage changed" << percentage << "%" << thing;
thing->setStateValue(onOffSwitchBatteryLevelStateTypeId, percentage);
thing->setStateValue(onOffSwitchBatteryCriticalStateTypeId, (percentage < 10.0));
});
}
// Receive on/off commands
ZigbeeClusterOnOff *onOffCluster = endpoint->outputCluster<ZigbeeClusterOnOff>(ZigbeeClusterLibrary::ClusterIdOnOff);
if (!onOffCluster) {
@ -208,7 +223,9 @@ void IntegrationPluginZigbeeTradfri::setupThing(ThingSetupInfo *info)
}
});
}
}
if (thing->thingClassId() == motionSensorThingClassId) {
// Get battery level changes
ZigbeeClusterPowerConfiguration *powerCluster = endpoint->inputCluster<ZigbeeClusterPowerConfiguration>(ZigbeeClusterLibrary::ClusterIdPowerConfiguration);
if (!powerCluster) {
@ -216,19 +233,17 @@ void IntegrationPluginZigbeeTradfri::setupThing(ThingSetupInfo *info)
} else {
// Only set the initial state if the attribute already exists
if (powerCluster->hasAttribute(ZigbeeClusterPowerConfiguration::AttributeBatteryPercentageRemaining)) {
thing->setStateValue(onOffSwitchBatteryLevelStateTypeId, powerCluster->batteryPercentage());
thing->setStateValue(onOffSwitchBatteryCriticalStateTypeId, (powerCluster->batteryPercentage() < 10.0));
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(onOffSwitchBatteryLevelStateTypeId, percentage);
thing->setStateValue(onOffSwitchBatteryCriticalStateTypeId, (percentage < 10.0));
thing->setStateValue(motionSensorBatteryLevelStateTypeId, percentage);
thing->setStateValue(motionSensorBatteryCriticalStateTypeId, (percentage < 10.0));
});
}
}
if (thing->thingClassId() == motionSensorThingClassId) {
// Create plugintimer if required
if (!m_presenceTimer) {
m_presenceTimer = hardwareManager()->pluginTimerManager()->registerTimer(1);
@ -255,41 +270,9 @@ void IntegrationPluginZigbeeTradfri::setupThing(ThingSetupInfo *info)
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) {
@ -307,6 +290,20 @@ void IntegrationPluginZigbeeTradfri::setupThing(ThingSetupInfo *info)
thing->setStateValue(remoteBatteryCriticalStateTypeId, (percentage < 10.0));
});
}
// 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")));
}
});
}
}
info->finish(Thing::ThingErrorNoError);
@ -538,94 +535,74 @@ void IntegrationPluginZigbeeTradfri::initRemote(ZigbeeNode *node, ZigbeeNodeEndp
});
}
void IntegrationPluginZigbeeTradfri::readBindings(ZigbeeNode *node)
void IntegrationPluginZigbeeTradfri::initMotionSensor(ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint)
{
qCDebug(dcZigbeeTradfri()) << "Read binding table from node" << node;
ZigbeeReply *reply = node->readBindingTableEntries();
// 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 read binding table from" << node;
} else {
foreach (const ZigbeeDeviceProfile::BindingTableListRecord &binding, node->bindingTableRecords()) {
qCDebug(dcZigbeeTradfri()) << node << binding;
}
}
});
}
if (reply->error() != ZigbeeReply::ErrorNoError) {
qCWarning(dcZigbeeTradfri()) << "Failed to remove all bindings for initialization of" << node;
}
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;
}
// Read battery, bind and configure attribute reporting for battery
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);
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 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();
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()) << "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 = 300;
reportingConfig.maxReportingInterval = 2700;
reportingConfig.reportableChange = ZigbeeDataType(static_cast<quint8>(1)).data();
qCDebug(dcZigbeeTradfri()) << "Read power configuration cluster attributes finished successfully";
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();
// 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";
qCDebug(dcZigbeeTradfri()) << "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<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";
});
});
});
});
});
}
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

@ -73,12 +73,7 @@ private:
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);
void initMotionSensor(ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint);
};