Implement and improve lumi zigbee sensors according to library changes

This commit is contained in:
Simon Stürz 2020-11-12 13:26:11 +01:00
parent 3cdc3414da
commit 1748cdaafe
3 changed files with 385 additions and 82 deletions

View File

@ -83,16 +83,97 @@
}
],
"actionTypes": [
],
"eventTypes": [
]
},
{
"name": "lumiWeatherSensor",
"displayName": "Weather sensor",
"id": "0b582616-0b05-4ac9-8b59-51b66079b571",
"setupMethod": "JustAdd",
"createMethods": [ "Auto" ],
"interfaces": [ "pressuresensor", "temperaturesensor", "humiditysensor", "wirelessconnectable" ],
"paramTypes": [
{
"id": "ce853c00-d175-45bc-a3d9-3c8c93d88099",
"name": "removeFromNetwork",
"displayName": "Remove from network"
"id": "ef1129e2-bdf8-423e-9c11-fa8b406f960b",
"name": "ieeeAddress",
"displayName": "IEEE adress",
"type": "QString",
"defaultValue": "00:00:00:00:00:00:00:00"
},
{
"id": "4a9d9427-52cf-4c13-9b71-496145b18476",
"name": "test1",
"displayName": "Test 1"
"id": "fe4cae8c-195c-4f38-ad88-cebd4adbb337",
"name": "networkUuid",
"displayName": "Zigbee network UUID",
"type": "QString",
"defaultValue": ""
}
],
"stateTypes": [
{
"id": "003f7e37-326b-455d-828b-5a9493b56a69",
"name": "connected",
"displayName": "Available",
"displayNameEvent": "Available changed",
"type": "bool",
"cached": false,
"defaultValue": false
},
{
"id": "684f642e-08ed-4912-b7a9-597baef400c0",
"name": "signalStrength",
"displayName": "Signal strength",
"displayNameEvent": "Signal strength changed",
"defaultValue": 0,
"maxValue": 100,
"minValue": 0,
"type": "uint",
"unit": "Percentage"
},
{
"id": "cc644362-19ca-489d-8122-4d1bc55f100f",
"name": "version",
"displayName": "Version",
"displayNameEvent": "Version changed",
"type": "QString",
"cached": true,
"defaultValue": ""
},
{
"id": "b1641cec-3bf6-4654-b9c0-b81acb3b4481",
"name": "temperature",
"displayName": "Temperature",
"displayNameEvent": "Temperature changed",
"type": "double",
"unit": "DegreeCelsius",
"defaultValue": 0.0
},
{
"id": "27a1e85a-f654-48d8-905d-05b3e2bc499e",
"name": "humidity",
"displayName": "Humidity",
"displayNameEvent": "Humidity changed",
"maxValue": 100,
"minValue": 0,
"unit": "Percentage",
"type": "double",
"defaultValue": 0.0
},
{
"id": "7c3861f3-a9db-407e-9459-90a511d7f797",
"name": "pressure",
"displayName": "Pressure",
"displayNameEvent": "Pressure changed",
"unit": "MilliBar",
"type": "double",
"defaultValue": 0.0
}
],
"actionTypes": [
],
"eventTypes": [
@ -161,11 +242,7 @@
}
],
"actionTypes": [
{
"id": "2bae0e2d-0c20-475d-a6cd-2e685e6b0626",
"name": "removeFromNetwork",
"displayName": "Remove from network"
}
],
"eventTypes": [
@ -226,11 +303,6 @@
}
],
"actionTypes": [
{
"id": "65b43aad-1784-4e85-ad5a-ce01cc5757bd",
"name": "removeFromNetwork",
"displayName": "Remove from network"
}
],
"eventTypes": [
{
@ -337,11 +409,7 @@
}
],
"actionTypes": [
{
"id": "2797c499-ca7f-4cf6-a732-c8e3cd580e56",
"name": "removeFromNetwork",
"displayName": "Remove from network"
}
],
"eventTypes": [
@ -418,15 +486,76 @@
}
],
"actionTypes": [
{
"id": "ed085bda-5608-419d-88e2-81ebcb9f26e1",
"name": "removeFromNetwork",
"displayName": "Remove from network"
}
],
"eventTypes": [
]
},
{
"name": "lumiVibrationSensor",
"displayName": "Vibration sensor",
"id": "775be45e-08d8-4ae8-bc95-5721a5317856",
"setupMethod": "JustAdd",
"createMethods": [ "Auto" ],
"interfaces": [ "wirelessconnectable" ],
"paramTypes": [
{
"id": "324cd9a0-3381-490b-9537-88b65e0093bf",
"name": "ieeeAddress",
"displayName": "IEEE adress",
"type": "QString",
"defaultValue": "00:00:00:00:00:00:00:00"
},
{
"id": "4e758a2e-32dc-44d8-872f-f656f30aca82",
"name": "networkUuid",
"displayName": "Zigbee network UUID",
"type": "QString",
"defaultValue": ""
}
],
"stateTypes": [
{
"id": "12970c44-5c59-4c6b-a244-6bc413d32c66",
"name": "connected",
"displayName": "Available",
"displayNameEvent": "Available changed",
"type": "bool",
"cached": false,
"defaultValue": false
},
{
"id": "eb8bbd63-1f1c-4499-8385-06fbe8e301db",
"name": "signalStrength",
"displayName": "Signal strength",
"displayNameEvent": "Signal strength changed",
"defaultValue": 0,
"maxValue": 100,
"minValue": 0,
"type": "uint",
"unit": "Percentage"
},
{
"id": "8a4aceef-c8ee-487a-946c-20f26fc2d5ed",
"name": "version",
"displayName": "Version",
"displayNameEvent": "Version changed",
"type": "QString",
"cached": true,
"defaultValue": ""
}
],
"actionTypes": [
],
"eventTypes": [
{
"id": "ce98b90b-f4fb-431d-a7c9-534fd2b76ba4",
"name": "vibrationDetected",
"displayName": "Vibration detected"
}
]
}
]
}

View File

@ -44,25 +44,49 @@ IntegrationPluginZigbeeLumi::IntegrationPluginZigbeeLumi()
m_networkUuidParamTypeIds[lumiMagnetSensorThingClassId] = lumiMagnetSensorThingNetworkUuidParamTypeId;
m_networkUuidParamTypeIds[lumiMotionSensorThingClassId] = lumiMotionSensorThingNetworkUuidParamTypeId;
m_networkUuidParamTypeIds[lumiWaterSensorThingClassId] = lumiWaterSensorThingNetworkUuidParamTypeId;
m_networkUuidParamTypeIds[lumiWeatherSensorThingClassId] = lumiWeatherSensorThingNetworkUuidParamTypeId;
m_networkUuidParamTypeIds[lumiVibrationSensorThingClassId] = lumiVibrationSensorThingNetworkUuidParamTypeId;
m_zigbeeAddressParamTypeIds[lumiHTSensorThingClassId] = lumiHTSensorThingIeeeAddressParamTypeId;
m_zigbeeAddressParamTypeIds[lumiButtonSensorThingClassId] = lumiButtonSensorThingIeeeAddressParamTypeId;
m_zigbeeAddressParamTypeIds[lumiMagnetSensorThingClassId] = lumiMagnetSensorThingIeeeAddressParamTypeId;
m_zigbeeAddressParamTypeIds[lumiMotionSensorThingClassId] = lumiMotionSensorThingIeeeAddressParamTypeId;
m_zigbeeAddressParamTypeIds[lumiWaterSensorThingClassId] = lumiWaterSensorThingIeeeAddressParamTypeId;
m_zigbeeAddressParamTypeIds[lumiWeatherSensorThingClassId] = lumiWeatherSensorThingIeeeAddressParamTypeId;
m_zigbeeAddressParamTypeIds[lumiVibrationSensorThingClassId] = lumiVibrationSensorThingIeeeAddressParamTypeId;
m_connectedStateTypeIds[lumiHTSensorThingClassId] = lumiHTSensorConnectedStateTypeId;
m_connectedStateTypeIds[lumiButtonSensorThingClassId] = lumiButtonSensorConnectedStateTypeId;
m_connectedStateTypeIds[lumiMagnetSensorThingClassId] = lumiMagnetSensorConnectedStateTypeId;
m_connectedStateTypeIds[lumiMotionSensorThingClassId] = lumiMotionSensorConnectedStateTypeId;
m_connectedStateTypeIds[lumiWaterSensorThingClassId] = lumiWaterSensorConnectedStateTypeId;
m_connectedStateTypeIds[lumiWeatherSensorThingClassId] = lumiWeatherSensorConnectedStateTypeId;
m_connectedStateTypeIds[lumiVibrationSensorThingClassId] = lumiVibrationSensorConnectedStateTypeId;
m_versionStateTypeIds[lumiHTSensorThingClassId] = lumiHTSensorVersionStateTypeId;
m_versionStateTypeIds[lumiButtonSensorThingClassId] = lumiButtonSensorVersionStateTypeId;
m_versionStateTypeIds[lumiMagnetSensorThingClassId] = lumiMagnetSensorVersionStateTypeId;
m_versionStateTypeIds[lumiMotionSensorThingClassId] = lumiMotionSensorVersionStateTypeId;
m_versionStateTypeIds[lumiWaterSensorThingClassId] = lumiWaterSensorVersionStateTypeId;
m_versionStateTypeIds[lumiWeatherSensorThingClassId] = lumiWeatherSensorVersionStateTypeId;
m_versionStateTypeIds[lumiVibrationSensorThingClassId] = lumiVibrationSensorVersionStateTypeId;
m_signalStrengthStateTypeIds[lumiHTSensorThingClassId] = lumiHTSensorSignalStrengthStateTypeId;
m_signalStrengthStateTypeIds[lumiButtonSensorThingClassId] = lumiButtonSensorSignalStrengthStateTypeId;
m_signalStrengthStateTypeIds[lumiMagnetSensorThingClassId] = lumiMagnetSensorSignalStrengthStateTypeId;
m_signalStrengthStateTypeIds[lumiMotionSensorThingClassId] = lumiMotionSensorSignalStrengthStateTypeId;
m_signalStrengthStateTypeIds[lumiWaterSensorThingClassId] = lumiWaterSensorSignalStrengthStateTypeId;
m_signalStrengthStateTypeIds[lumiWeatherSensorThingClassId] = lumiWeatherSensorSignalStrengthStateTypeId;
m_signalStrengthStateTypeIds[lumiVibrationSensorThingClassId] = lumiVibrationSensorSignalStrengthStateTypeId;
// Known model identifier
m_knownLumiDevices.insert("lumi.sensor_ht", lumiHTSensorThingClassId);
m_knownLumiDevices.insert("lumi.sensor_magnet", lumiMagnetSensorThingClassId);
m_knownLumiDevices.insert("lumi.sensor_switch", lumiButtonSensorThingClassId);
m_knownLumiDevices.insert("lumi.sensor_motion", lumiMotionSensorThingClassId);
m_knownLumiDevices.insert("lumi.sensor_wleak", lumiWaterSensorThingClassId);
m_knownLumiDevices.insert("lumi.weather", lumiWeatherSensorThingClassId);
m_knownLumiDevices.insert("lumi.vibration", lumiVibrationSensorThingClassId);
}
QString IntegrationPluginZigbeeLumi::name() const
@ -73,29 +97,25 @@ QString IntegrationPluginZigbeeLumi::name() const
bool IntegrationPluginZigbeeLumi::handleNode(ZigbeeNode *node, const QUuid &networkUuid)
{
// Check if this is Lumi
// Note: Lumi / Xiaomi / Aquara devices are not in the specs, so no enum here
if (node->nodeDescriptor().manufacturerCode != 0x1037) {
return false;
}
// Note: Lumi / Xiaomi / Aquara devices are not in the specs, some older models do not
// send the node descriptor or use a inconsistent manufacturer code. We use the manufacturer
// name for matching since that has shown to be most constant
foreach (ZigbeeNodeEndpoint *endpoint, node->endpoints()) {
// Get the model identifier if present from the first endpoint. Also this is out of spec
if (!endpoint->hasInputCluster(ZigbeeClusterLibrary::ClusterIdBasic)) {
qCWarning(dcZigbeeLumi()) << "This lumi device does not have the basic input cluster yet.";
qCDebug(dcZigbeeLumi()) << "This lumi endpoint does not have the basic input cluster, so we skipp it" << endpoint;
continue;
}
QHash<QString, ThingClassId> knownLumiDevices;
knownLumiDevices.insert("lumi.sensor_ht", lumiHTSensorThingClassId);
knownLumiDevices.insert("lumi.sensor_magnet", lumiMagnetSensorThingClassId);
knownLumiDevices.insert("lumi.sensor_switch", lumiButtonSensorThingClassId);
knownLumiDevices.insert("lumi.sensor_motion", lumiMotionSensorThingClassId);
knownLumiDevices.insert("lumi.sensor_water", lumiWaterSensorThingClassId);
// Basic cluster exists, so we should have the manufacturer name
if (!endpoint->manufacturerName().toLower().startsWith("lumi")) {
return false;
}
ThingClassId thingClassId;
foreach (const QString &knownLumi, knownLumiDevices.keys()) {
foreach (const QString &knownLumi, m_knownLumiDevices.keys()) {
if (endpoint->modelIdentifier().startsWith(knownLumi)) {
thingClassId = knownLumiDevices.value(knownLumi);
thingClassId = m_knownLumiDevices.value(knownLumi);
break;
}
}
@ -117,6 +137,18 @@ bool IntegrationPluginZigbeeLumi::handleNode(ZigbeeNode *node, const QUuid &netw
return false;
}
void IntegrationPluginZigbeeLumi::handleRemoveNode(ZigbeeNode *node, const QUuid &networkUuid)
{
Q_UNUSED(networkUuid)
if (m_nodeThings.values().contains(node)) {
Thing *thing = m_nodeThings.key(node);
qCDebug(dcZigbeeLumi()) << "Node for" << thing << node << "has left the network. Removing thing";
m_nodeThings.remove(thing);
emit autoThingDisappeared(thing->id());
}
}
void IntegrationPluginZigbeeLumi::init()
{
hardwareManager()->zigbeeResource()->registerHandler(this);
@ -124,15 +156,11 @@ void IntegrationPluginZigbeeLumi::init()
void IntegrationPluginZigbeeLumi::setupThing(ThingSetupInfo *info)
{
if (!hardwareManager()->zigbeeResource()->available()) {
qCWarning(dcZigbeeLumi()) << "Zigbee is not available. Not setting up" << info->thing()->name();
info->finish(Thing::ThingErrorHardwareNotAvailable);
return;
}
Thing *thing = info->thing();
QUuid networkUuid = thing->paramValue(m_networkUuidParamTypeIds.value(thing->thingClassId())).toUuid();
qCDebug(dcZigbeeLumi()) << "Nework uuid:" << networkUuid;
ZigbeeAddress zigbeeAddress = ZigbeeAddress(thing->paramValue(m_zigbeeAddressParamTypeIds.value(thing->thingClassId())).toString());
ZigbeeNode *node = hardwareManager()->zigbeeResource()->getNode(networkUuid, zigbeeAddress);
if (!node) {
qCWarning(dcZigbeeLumi()) << "Zigbee node for" << info->thing()->name() << "not found.´";
@ -140,6 +168,7 @@ void IntegrationPluginZigbeeLumi::setupThing(ThingSetupInfo *info)
return;
}
// Get the endpoint of interest (0x01) for this device
ZigbeeNodeEndpoint *endpoint = node->getEndpoint(0x01);
if (!endpoint) {
qCWarning(dcZigbeeLumi()) << "Zigbee endpoint 1 not found on" << thing->name();
@ -147,6 +176,9 @@ void IntegrationPluginZigbeeLumi::setupThing(ThingSetupInfo *info)
return;
}
m_nodeThings.insert(thing, node);
// General signals and states
// Update connected state
thing->setStateValue(m_connectedStateTypeIds.value(thing->thingClassId()), hardwareManager()->zigbeeResource()->networkState(networkUuid) == ZigbeeNetwork::StateRunning);
connect(hardwareManager()->zigbeeResource(), &ZigbeeHardwareResource::networkStateChanged, thing, [thing, this](const QUuid &networkUuid, ZigbeeNetwork::State state){
@ -163,11 +195,18 @@ void IntegrationPluginZigbeeLumi::setupThing(ThingSetupInfo *info)
thing->setStateValue(m_signalStrengthStateTypeIds.value(thing->thingClassId()), signalStrength);
});
// Set the version
thing->setStateValue(m_versionStateTypeIds.value(thing->thingClassId()), endpoint->softwareBuildId());
if (thing->thingClassId() == lumiMagnetSensorThingClassId) {
ZigbeeClusterOnOff *onOffCluster = endpoint->inputCluster<ZigbeeClusterOnOff>(ZigbeeClusterLibrary::ClusterIdOnOff);
if (onOffCluster) {
// thing->setStateValue(lumiMagnetSensorClosedStateTypeId, !onOffCluster->powered());
// Only set the state if the cluster actually has the attribute
if (onOffCluster->hasAttribute(ZigbeeClusterOnOff::AttributeOnOff)) {
thing->setStateValue(lumiMagnetSensorClosedStateTypeId, !onOffCluster->power());
}
connect(onOffCluster, &ZigbeeClusterOnOff::powerChanged, thing, [thing](bool power){
qCDebug(dcZigbeeLumi()) << thing << "state changed" << (power ? "closed" : "open");
thing->setStateValue(lumiMagnetSensorClosedStateTypeId, !power);
@ -177,13 +216,23 @@ void IntegrationPluginZigbeeLumi::setupThing(ThingSetupInfo *info)
}
}
if (thing->thingClassId() == lumiMotionSensorThingClassId) {
ZigbeeClusterOccupancySensing *occupancyCluster = endpoint->inputCluster<ZigbeeClusterOccupancySensing>(ZigbeeClusterLibrary::ClusterIdOccupancySensing);
if (occupancyCluster) {
// thing->setStateValue(lumiMotionSensorIsPresentStateTypeId, occupancyCluster->occupancy());
connect(occupancyCluster, &ZigbeeClusterOccupancySensing::occupancyChanged, thing, [thing](bool occupancy){
if (occupancyCluster->hasAttribute(ZigbeeClusterOccupancySensing::AttributeOccupancy)) {
thing->setStateValue(lumiMotionSensorIsPresentStateTypeId, occupancyCluster->occupied());
thing->setStateValue(lumiMotionSensorLastSeenTimeStateTypeId, QDateTime::currentMSecsSinceEpoch() / 1000);
}
connect(occupancyCluster, &ZigbeeClusterOccupancySensing::occupancyChanged, thing, [this, thing](bool occupancy){
qCDebug(dcZigbeeLumi()) << "occupancy changed" << occupancy;
thing->setStateValue(lumiMotionSensorIsPresentStateTypeId, occupancy);
// Only change the state if the it changed to true, it will be disabled by the timer
if (occupancy) {
thing->setStateValue(lumiMotionSensorIsPresentStateTypeId, occupancy);
m_presenceTimer->start();
}
thing->setStateValue(lumiMotionSensorLastSeenTimeStateTypeId, QDateTime::currentMSecsSinceEpoch() / 1000);
});
@ -200,15 +249,19 @@ void IntegrationPluginZigbeeLumi::setupThing(ThingSetupInfo *info)
}
}
});
} else {
qCWarning(dcZigbeeLumi()) << "Occupancy cluster not found on" << thing->name();
}
ZigbeeClusterIlluminanceMeasurment *illuminanceCluster = endpoint->inputCluster<ZigbeeClusterIlluminanceMeasurment>(ZigbeeClusterLibrary::ClusterIdIlluminanceMeasurement);
if (illuminanceCluster) {
// thing->setStateValue(lumiHTSensorTemperatureStateTypeId, illuminanceCluster->illuminance());
// Only set the state if the cluster actually has the attribute
if (illuminanceCluster->hasAttribute(ZigbeeClusterIlluminanceMeasurment::AttributeMeasuredValue)) {
thing->setStateValue(lumiMotionSensorLightIntensityStateTypeId, illuminanceCluster->illuminance());
}
connect(illuminanceCluster, &ZigbeeClusterIlluminanceMeasurment::illuminanceChanged, thing, [thing](quint16 illuminance){
qCDebug(dcZigbeeLumi()) << thing << "light intensity changed" << illuminance << "lux";
thing->setStateValue(lumiMotionSensorLightIntensityStateTypeId, illuminance);
});
} else {
@ -216,11 +269,17 @@ void IntegrationPluginZigbeeLumi::setupThing(ThingSetupInfo *info)
}
}
if (thing->thingClassId() == lumiHTSensorThingClassId) {
ZigbeeClusterTemperatureMeasurement *temperatureCluster = endpoint->inputCluster<ZigbeeClusterTemperatureMeasurement>(ZigbeeClusterLibrary::ClusterIdTemperatureMeasurement);
if (temperatureCluster) {
// thing->setStateValue(lumiHTSensorTemperatureStateTypeId, temperatureCluster->temperature());
// Only set the state if the cluster actually has the attribute
if (temperatureCluster->hasAttribute(ZigbeeClusterTemperatureMeasurement::AttributeMeasuredValue)) {
thing->setStateValue(lumiHTSensorTemperatureStateTypeId, temperatureCluster->temperature());
}
connect(temperatureCluster, &ZigbeeClusterTemperatureMeasurement::temperatureChanged, thing, [thing](double temperature){
qCDebug(dcZigbeeLumi()) << thing << "temperature changed" << temperature << "°C";
thing->setStateValue(lumiHTSensorTemperatureStateTypeId, temperature);
});
} else {
@ -229,8 +288,13 @@ void IntegrationPluginZigbeeLumi::setupThing(ThingSetupInfo *info)
ZigbeeClusterRelativeHumidityMeasurement *humidityCluster = endpoint->inputCluster<ZigbeeClusterRelativeHumidityMeasurement>(ZigbeeClusterLibrary::ClusterIdRelativeHumidityMeasurement);
if (humidityCluster) {
// thing->setStateValue(lumiHTSensorHumidityStateTypeId, humidityCluster->humidity());
// Only set the state if the cluster actually has the attribute
if (humidityCluster->hasAttribute(ZigbeeClusterRelativeHumidityMeasurement::AttributeMeasuredValue)) {
thing->setStateValue(lumiHTSensorHumidityStateTypeId, humidityCluster->humidity());
}
connect(humidityCluster, &ZigbeeClusterRelativeHumidityMeasurement::humidityChanged, thing, [thing](double humidity){
qCDebug(dcZigbeeLumi()) << thing << "humidity changed" << humidity << "%";
thing->setStateValue(lumiHTSensorHumidityStateTypeId, humidity);
});
} else {
@ -238,41 +302,141 @@ void IntegrationPluginZigbeeLumi::setupThing(ThingSetupInfo *info)
}
}
if (thing->thingClassId() == lumiWeatherSensorThingClassId) {
ZigbeeClusterTemperatureMeasurement *temperatureCluster = endpoint->inputCluster<ZigbeeClusterTemperatureMeasurement>(ZigbeeClusterLibrary::ClusterIdTemperatureMeasurement);
if (temperatureCluster) {
// Only set the state if the cluster actually has the attribute
if (temperatureCluster->hasAttribute(ZigbeeClusterTemperatureMeasurement::AttributeMeasuredValue)) {
thing->setStateValue(lumiWeatherSensorTemperatureStateTypeId, temperatureCluster->temperature());
}
connect(temperatureCluster, &ZigbeeClusterTemperatureMeasurement::temperatureChanged, thing, [thing](double temperature){
qCDebug(dcZigbeeLumi()) << thing << "temperature changed" << temperature << "°C";
thing->setStateValue(lumiWeatherSensorTemperatureStateTypeId, temperature);
});
} else {
qCWarning(dcZigbeeLumi()) << "Could not find the temperature measurement server cluster on" << thing << endpoint;
}
ZigbeeClusterRelativeHumidityMeasurement *humidityCluster = endpoint->inputCluster<ZigbeeClusterRelativeHumidityMeasurement>(ZigbeeClusterLibrary::ClusterIdRelativeHumidityMeasurement);
if (humidityCluster) {
// Only set the state if the cluster actually has the attribute
if (humidityCluster->hasAttribute(ZigbeeClusterRelativeHumidityMeasurement::AttributeMeasuredValue)) {
thing->setStateValue(lumiWeatherSensorHumidityStateTypeId, humidityCluster->humidity());
}
connect(humidityCluster, &ZigbeeClusterRelativeHumidityMeasurement::humidityChanged, thing, [thing](double humidity){
qCDebug(dcZigbeeLumi()) << thing << "humidity changed" << humidity << "%";
thing->setStateValue(lumiWeatherSensorHumidityStateTypeId, humidity);
});
} else {
qCWarning(dcZigbeeLumi()) << "Could not find the relative humidity measurement server cluster on" << thing << endpoint;
}
ZigbeeClusterPressureMeasurement *pressureCluster = endpoint->inputCluster<ZigbeeClusterPressureMeasurement>(ZigbeeClusterLibrary::ClusterIdPressureMeasurement);
if (pressureCluster) {
// Only set the state if the cluster actually has the attribute
if (pressureCluster->hasAttribute(ZigbeeClusterPressureMeasurement::AttributeMeasuredValue)) {
thing->setStateValue(lumiWeatherSensorPressureStateTypeId, pressureCluster->pressure() * 101);
}
connect(pressureCluster, &ZigbeeClusterPressureMeasurement::pressureChanged, thing, [thing](double pressure){
thing->setStateValue(lumiWeatherSensorPressureStateTypeId, pressure * 101);
});
} else {
qCWarning(dcZigbeeLumi()) << "Could not find the pressure measurement server cluster on" << thing << endpoint;
}
}
if (thing->thingClassId() == lumiWaterSensorThingClassId) {
connect(endpoint, &ZigbeeNodeEndpoint::clusterAttributeChanged, this, [thing](ZigbeeCluster *cluster, const ZigbeeClusterAttribute &attribute){
if (cluster->clusterId() == ZigbeeClusterLibrary::ClusterIdIasZone) {
if (attribute.id() == ZigbeeClusterIasZone::AttributeZoneState) {
bool valueOk = false;
ZigbeeClusterIasZone::ZoneStatusFlags zoneStatus = static_cast<ZigbeeClusterIasZone::ZoneStatusFlags>(attribute.dataType().toUInt16(&valueOk));
if (!valueOk) {
qCWarning(dcZigbeeLumi()) << thing << "failed to convert attribute data to uint16 flag. Not updating the states from" << attribute;
} else {
qCDebug(dcZigbeeLumi()) << thing << "zone status changed" << zoneStatus;
// Water detected gets indicated in the Alarm1 flag
if (zoneStatus.testFlag(ZigbeeClusterIasZone::ZoneStatusAlarm1)) {
thing->setStateValue(lumiWaterSensorWaterDetectedStateTypeId, true);
} else {
thing->setStateValue(lumiWaterSensorWaterDetectedStateTypeId, false);
}
// Battery alarm
if (zoneStatus.testFlag(ZigbeeClusterIasZone::ZoneStatusBattery)) {
thing->setStateValue(lumiWaterSensorBatteryCriticalStateTypeId, true);
} else {
thing->setStateValue(lumiWaterSensorBatteryCriticalStateTypeId, false);
}
}
}
}
});
}
if (thing->thingClassId() == lumiVibrationSensorThingClassId) {
connect(endpoint, &ZigbeeNodeEndpoint::clusterAttributeChanged, this, [this, thing](ZigbeeCluster *cluster, const ZigbeeClusterAttribute &attribute){
if (cluster->clusterId() == ZigbeeClusterLibrary::ClusterIdDoorLock) {
// Note: shoehow the vibration sensor is using the door lock cluster, with undocumented attribitues.
// This device is completly out of spec, so we just recognize the vibration trough tests and it looks like
// attribute id 85 is the indicator for vibration. The payload contains an unsigned int, but not sure what it indicates yet
if (attribute.id() == 85) {
bool valueOk = false;
quint16 value = attribute.dataType().toUInt16(&valueOk);
if (!valueOk) {
qCWarning(dcZigbeeLumi()) << thing << "failed to convert attribute data to uint16." << attribute;
} else {
qCDebug(dcZigbeeLumi()) << thing << "vibration attribute changed" << value;
emitEvent(Event(lumiVibrationSensorVibrationDetectedEventTypeId, thing->id()));
}
}
}
});
}
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()));
// });
// 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;
// }
// 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;
// }
// 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);
// info->finish(Thing::ThingErrorThingClassNotFound);
}
void IntegrationPluginZigbeeLumi::executeAction(ThingActionInfo *info)
@ -282,5 +446,9 @@ void IntegrationPluginZigbeeLumi::executeAction(ThingActionInfo *info)
void IntegrationPluginZigbeeLumi::thingRemoved(Thing *thing)
{
Q_UNUSED(thing)
ZigbeeNode *node = m_nodeThings.take(thing);
if (node) {
QUuid networkUuid = thing->paramValue(m_networkUuidParamTypeIds.value(thing->thingClassId())).toUuid();
hardwareManager()->zigbeeResource()->removeNodeFromNetwork(networkUuid, node);
}
}

View File

@ -49,6 +49,7 @@ public:
QString name() const override;
bool handleNode(ZigbeeNode *node, const QUuid &networkUuid) override;
void handleRemoveNode(ZigbeeNode *node, const QUuid &networkUuid) override;
void init() override;
void setupThing(ThingSetupInfo *info) override;
@ -61,6 +62,11 @@ private:
QHash<ThingClassId, StateTypeId> m_connectedStateTypeIds;
QHash<ThingClassId, StateTypeId> m_signalStrengthStateTypeIds;
QHash<ThingClassId, StateTypeId> m_versionStateTypeIds;
QHash<QString, ThingClassId> m_knownLumiDevices;
QHash<Thing *, ZigbeeNode *> m_nodeThings;
PluginTimer *m_presenceTimer = nullptr;
};