diff --git a/zigbee-lumi/integrationpluginzigbee-lumi.json b/zigbee-lumi/integrationpluginzigbee-lumi.json index 927efef4..3c8f0e55 100644 --- a/zigbee-lumi/integrationpluginzigbee-lumi.json +++ b/zigbee-lumi/integrationpluginzigbee-lumi.json @@ -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" + } + ] } ] } diff --git a/zigbee-lumi/integrationpluginzigbeelumi.cpp b/zigbee-lumi/integrationpluginzigbeelumi.cpp index 62991ccb..af152394 100644 --- a/zigbee-lumi/integrationpluginzigbeelumi.cpp +++ b/zigbee-lumi/integrationpluginzigbeelumi.cpp @@ -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 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(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(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(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(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(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(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(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(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(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); + } } diff --git a/zigbee-lumi/integrationpluginzigbeelumi.h b/zigbee-lumi/integrationpluginzigbeelumi.h index e2fed597..17670aac 100644 --- a/zigbee-lumi/integrationpluginzigbeelumi.h +++ b/zigbee-lumi/integrationpluginzigbeelumi.h @@ -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 m_connectedStateTypeIds; QHash m_signalStrengthStateTypeIds; + QHash m_versionStateTypeIds; + + QHash m_knownLumiDevices; + + QHash m_nodeThings; PluginTimer *m_presenceTimer = nullptr; };