diff --git a/zigbee-generic/integrationpluginzigbee-generic.json b/zigbee-generic/integrationpluginzigbee-generic.json index b5a16145..b9fed263 100644 --- a/zigbee-generic/integrationpluginzigbee-generic.json +++ b/zigbee-generic/integrationpluginzigbee-generic.json @@ -8,6 +8,62 @@ "displayName": "nymea", "id": "2062d64d-3232-433c-88bc-0d33c0ba2ba6", "thingClasses": [ + { + "id": "ca9af6cf-2d15-4d54-ba07-3d2ce03445b8", + "name": "thermostat", + "displayName": "Zigbee Thermostat", + "createMethods": ["auto"], + "interfaces": ["thermostat", "wirelessconnectable"], + "paramTypes": [ + { + "id": "f38746d8-0084-43a3-b645-3ec743ea5fbc", + "name": "ieeeAddress", + "displayName": "IEEE adress", + "type": "QString", + "defaultValue": "00:00:00:00:00:00:00:00" + }, + { + "id": "4a92b536-de4c-4701-8117-9bb26dd51c3e", + "name": "networkUuid", + "displayName": "Zigbee network UUID", + "type": "QString", + "defaultValue": "" + } + ], + "stateTypes": [ + { + "id": "88ad3957-2912-4de1-b53d-b360565dd361", + "name": "targetTemperature", + "displayName": "Target temperature", + "displayNameEvent": "Target temperature changed", + "type": "double", + "unit": "DegreeCelsius", + "minValue": 0, + "maxValue": 50, + "defaultValue": 0 + }, + { + "id": "e9fb2b10-96da-4b70-afda-46e948399af8", + "name": "connected", + "displayName": "Connected", + "displayNameEvent": "Connected/disconnected", + "type": "bool", + "cached": false, + "defaultValue": false + }, + { + "id": "8f0512ab-ced2-4dcb-90fe-aaa532efd0dd", + "name": "signalStrength", + "displayName": "Signal strength", + "displayNameEvent": "Signal strength changed", + "defaultValue": 0, + "maxValue": 100, + "minValue": 0, + "type": "uint", + "unit": "Percentage" + } + ] + } ] } ] diff --git a/zigbee-generic/integrationpluginzigbeegeneric.cpp b/zigbee-generic/integrationpluginzigbeegeneric.cpp index 08921540..50ad31cf 100644 --- a/zigbee-generic/integrationpluginzigbeegeneric.cpp +++ b/zigbee-generic/integrationpluginzigbeegeneric.cpp @@ -37,7 +37,13 @@ IntegrationPluginZigbeeGeneric::IntegrationPluginZigbeeGeneric() { + m_ieeeAddressParamTypeIds[thermostatThingClassId] = thermostatThingIeeeAddressParamTypeId; + m_networkUuidParamTypeIds[thermostatThingClassId] = thermostatThingNetworkUuidParamTypeId; + + m_connectedStateTypeIds[thermostatThingClassId] = thermostatConnectedStateTypeId; + + m_signalStrengthStateTypeIds[thermostatThingClassId] = thermostatSignalStrengthStateTypeId; } QString IntegrationPluginZigbeeGeneric::name() const @@ -47,36 +53,29 @@ QString IntegrationPluginZigbeeGeneric::name() const bool IntegrationPluginZigbeeGeneric::handleNode(ZigbeeNode *node, const QUuid &networkUuid) { - foreach (ZigbeeNodeEndpoint *endpoint, node->endpoints()) { - qCDebug(dcZigbeeGeneric) << "Endpoint profile:" << endpoint->profile() << endpoint->deviceId() << networkUuid; -// if ((endpoint->profile() == Zigbee::ZigbeeProfile::ZigbeeProfileLightLink && endpoint->deviceId() == Zigbee::LightLinkDevice::LightLinkDeviceOnOff -// (endpoint->profile() == Zigbee::ZigbeeProfile::ZigbeeProfileHomeAutomation && endpoint->deviceId() == Zigbee::HomeAutomationDeviceOnOf> + qCDebug(dcZigbeeGeneric()) << "handleNode called for:" << node; -// // Create generic power socket -// qCDebug(dcZigbee()) << "This device is an power socket"; -// if (myThings().filterByThingClassId(genericPowerSocketThingClassId) -// .filterByParam(genericPowerSocketThingIeeeAddressParamTypeId, node->extendedAddress().toString()) -// .isEmpty()) { -// qCDebug(dcZigbee()) << "Adding new generic power socket"; -// ThingDescriptor descriptor(genericPowerSocketThingClassId); -// QString deviceClassName = supportedThings().findById(genericPowerSocketThingClassId).displayName(); -// descriptor.setTitle(QString("%1 (%2 - %3)").arg(deviceClassName).arg(endpoint->manufacturerName()).arg(endpoint->modelIdentifier())); -// ParamList params; -// params.append(Param(genericPowerSocketThingIeeeAddressParamTypeId, node->extendedAddress().toString())); -// params.append(Param(genericPowerSocketThingManufacturerParamTypeId, endpoint->manufacturerName())); -// params.append(Param(genericPowerSocketThingModelParamTypeId, endpoint->modelIdentifier())); -// descriptor.setParams(params); -// descriptor.setParentId(networkManagerDevice->id()); -// emit autoThingsAppeared({descriptor}); -// } else { -// qCDebug(dcZigbee()) << "The device for this node has already been created."; -// } -// return true; -// } + QHash devicesThingClassIdsMap; + devicesThingClassIdsMap.insert(Zigbee::HomeAutomationDeviceThermostat, thermostatThingClassId); + + bool handled = false; + + foreach (ZigbeeNodeEndpoint *endpoint, node->endpoints()) { + + if (devicesThingClassIdsMap.contains(endpoint->deviceId())) { + ThingClassId thingClassId = devicesThingClassIdsMap.value(endpoint->deviceId()); + ThingDescriptor descriptor(thingClassId, endpoint->modelIdentifier(), endpoint->manufacturerName()); + ParamList params; + params << Param(m_ieeeAddressParamTypeIds.value(thingClassId), node->extendedAddress().toString()); + params << Param(m_networkUuidParamTypeIds.value(thingClassId), networkUuid.toString()); + descriptor.setParams(params); + emit autoThingsAppeared({descriptor}); + + handled = true; + } } - - return false; + return handled; } void IntegrationPluginZigbeeGeneric::init() @@ -86,6 +85,53 @@ void IntegrationPluginZigbeeGeneric::init() void IntegrationPluginZigbeeGeneric::setupThing(ThingSetupInfo *info) { + if (!hardwareManager()->zigbeeResource()->available()) { + qCWarning(dcZigbeeGeneric()) << "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(dcZigbeeGeneric()) << "Nework uuid:" << networkUuid; + ZigbeeAddress zigbeeAddress = ZigbeeAddress(thing->paramValue(m_ieeeAddressParamTypeIds.value(thing->thingClassId())).toString()); + ZigbeeNode *node = hardwareManager()->zigbeeResource()->getNode(networkUuid, zigbeeAddress); + if (!node) { + qCWarning(dcZigbeeGeneric()) << "Zigbee node for" << info->thing()->name() << "not found.ยด"; + info->finish(Thing::ThingErrorHardwareNotAvailable); + return; + } + + ZigbeeNodeEndpoint *endpoint = node->getEndpoint(0x01); + if (!endpoint) { + qCWarning(dcZigbeeGeneric()) << "Zigbee endpoint 1 not found on" << thing->name(); + info->finish(Thing::ThingErrorSetupFailed); + return; + } + + // 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){ + if (thing->paramValue(m_networkUuidParamTypeIds.value(thing->thingClassId())).toUuid() == networkUuid) { + thing->setStateValue(m_connectedStateTypeIds.value(thing->thingClassId()), state == ZigbeeNetwork::StateRunning); + } + }); + + // Update signal strength + thing->setStateValue(m_signalStrengthStateTypeIds.value(thing->thingClassId()), qRound(node->lqi() * 100.0 / 255.0)); + connect(node, &ZigbeeNode::lqiChanged, thing, [this, thing](quint8 lqi){ + uint signalStrength = qRound(lqi * 100.0 / 255.0); + qCDebug(dcZigbeeGeneric()) << thing << "signal strength changed" << signalStrength << "%"; + thing->setStateValue(m_signalStrengthStateTypeIds.value(thing->thingClassId()), signalStrength); + }); + + // Type specific setup + if (thing->thingClassId() == thermostatThingClassId) { + // TODO: Thermostat cluster is missing + // ZigbeeClusterThermostat *thermostatCluster = endpoint->inputCluster(ZigbeeClusterLibrary::ClusterIdThermostat); + } + + + info->finish(Thing::ThingErrorNoError); } diff --git a/zigbee-generic/integrationpluginzigbeegeneric.h b/zigbee-generic/integrationpluginzigbeegeneric.h index a678b50a..feb7f821 100644 --- a/zigbee-generic/integrationpluginzigbeegeneric.h +++ b/zigbee-generic/integrationpluginzigbeegeneric.h @@ -56,6 +56,12 @@ public: void thingRemoved(Thing *thing) override; private: + + QHash m_ieeeAddressParamTypeIds; + QHash m_networkUuidParamTypeIds; + + QHash m_connectedStateTypeIds; + QHash m_signalStrengthStateTypeIds; }; #endif // INTEGRATIONPLUGINZIGBEEGENERIC_H diff --git a/zigbee-lumi/integrationpluginzigbeelumi.cpp b/zigbee-lumi/integrationpluginzigbeelumi.cpp index 4a019298..62991ccb 100644 --- a/zigbee-lumi/integrationpluginzigbeelumi.cpp +++ b/zigbee-lumi/integrationpluginzigbeelumi.cpp @@ -167,7 +167,7 @@ void IntegrationPluginZigbeeLumi::setupThing(ThingSetupInfo *info) if (thing->thingClassId() == lumiMagnetSensorThingClassId) { ZigbeeClusterOnOff *onOffCluster = endpoint->inputCluster(ZigbeeClusterLibrary::ClusterIdOnOff); if (onOffCluster) { - thing->setStateValue(lumiMagnetSensorClosedStateTypeId, !onOffCluster->powered()); +// thing->setStateValue(lumiMagnetSensorClosedStateTypeId, !onOffCluster->powered()); connect(onOffCluster, &ZigbeeClusterOnOff::powerChanged, thing, [thing](bool power){ qCDebug(dcZigbeeLumi()) << thing << "state changed" << (power ? "closed" : "open"); thing->setStateValue(lumiMagnetSensorClosedStateTypeId, !power); @@ -180,7 +180,7 @@ void IntegrationPluginZigbeeLumi::setupThing(ThingSetupInfo *info) if (thing->thingClassId() == lumiMotionSensorThingClassId) { ZigbeeClusterOccupancySensing *occupancyCluster = endpoint->inputCluster(ZigbeeClusterLibrary::ClusterIdOccupancySensing); if (occupancyCluster) { - thing->setStateValue(lumiMotionSensorIsPresentStateTypeId, occupancyCluster->occupancy()); +// thing->setStateValue(lumiMotionSensorIsPresentStateTypeId, occupancyCluster->occupancy()); connect(occupancyCluster, &ZigbeeClusterOccupancySensing::occupancyChanged, thing, [thing](bool occupancy){ qCDebug(dcZigbeeLumi()) << "occupancy changed" << occupancy; thing->setStateValue(lumiMotionSensorIsPresentStateTypeId, occupancy); @@ -207,7 +207,7 @@ void IntegrationPluginZigbeeLumi::setupThing(ThingSetupInfo *info) ZigbeeClusterIlluminanceMeasurment *illuminanceCluster = endpoint->inputCluster(ZigbeeClusterLibrary::ClusterIdIlluminanceMeasurement); if (illuminanceCluster) { - thing->setStateValue(lumiHTSensorTemperatureStateTypeId, illuminanceCluster->illuminance()); +// thing->setStateValue(lumiHTSensorTemperatureStateTypeId, illuminanceCluster->illuminance()); connect(illuminanceCluster, &ZigbeeClusterIlluminanceMeasurment::illuminanceChanged, thing, [thing](quint16 illuminance){ thing->setStateValue(lumiMotionSensorLightIntensityStateTypeId, illuminance); }); @@ -219,7 +219,7 @@ void IntegrationPluginZigbeeLumi::setupThing(ThingSetupInfo *info) if (thing->thingClassId() == lumiHTSensorThingClassId) { ZigbeeClusterTemperatureMeasurement *temperatureCluster = endpoint->inputCluster(ZigbeeClusterLibrary::ClusterIdTemperatureMeasurement); if (temperatureCluster) { - thing->setStateValue(lumiHTSensorTemperatureStateTypeId, temperatureCluster->temperature()); +// thing->setStateValue(lumiHTSensorTemperatureStateTypeId, temperatureCluster->temperature()); connect(temperatureCluster, &ZigbeeClusterTemperatureMeasurement::temperatureChanged, thing, [thing](double temperature){ thing->setStateValue(lumiHTSensorTemperatureStateTypeId, temperature); }); @@ -229,7 +229,7 @@ void IntegrationPluginZigbeeLumi::setupThing(ThingSetupInfo *info) ZigbeeClusterRelativeHumidityMeasurement *humidityCluster = endpoint->inputCluster(ZigbeeClusterLibrary::ClusterIdRelativeHumidityMeasurement); if (humidityCluster) { - thing->setStateValue(lumiHTSensorHumidityStateTypeId, humidityCluster->humidity()); +// thing->setStateValue(lumiHTSensorHumidityStateTypeId, humidityCluster->humidity()); connect(humidityCluster, &ZigbeeClusterRelativeHumidityMeasurement::humidityChanged, thing, [thing](double humidity){ thing->setStateValue(lumiHTSensorHumidityStateTypeId, humidity); });