Add dimmable and color temperature light to generic lights plugin
This commit is contained in:
parent
13d3c5d463
commit
771929363b
@ -56,8 +56,8 @@
|
|||||||
{
|
{
|
||||||
"id": "7548ac08-4ea1-4dcd-98b9-9d58bbc06ab7",
|
"id": "7548ac08-4ea1-4dcd-98b9-9d58bbc06ab7",
|
||||||
"name": "connected",
|
"name": "connected",
|
||||||
"displayName": "Available",
|
"displayName": "Connected",
|
||||||
"displayNameEvent": "Available changed",
|
"displayNameEvent": "Connected changed",
|
||||||
"type": "bool",
|
"type": "bool",
|
||||||
"cached": false,
|
"cached": false,
|
||||||
"defaultValue": false
|
"defaultValue": false
|
||||||
@ -102,6 +102,239 @@
|
|||||||
],
|
],
|
||||||
"eventTypes": [
|
"eventTypes": [
|
||||||
|
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "dimmableLight",
|
||||||
|
"displayName": "Dimmable Light",
|
||||||
|
"id": "b2eeb554-ca5c-46b5-a897-bc443fb186ea",
|
||||||
|
"setupMethod": "JustAdd",
|
||||||
|
"createMethods": [ "Auto" ],
|
||||||
|
"interfaces": [ "dimmablelight", "alert", "wirelessconnectable" ],
|
||||||
|
"paramTypes": [
|
||||||
|
{
|
||||||
|
"id": "beab891f-c1b0-4b2c-88c4-216c8d19f9e2",
|
||||||
|
"name": "ieeeAddress",
|
||||||
|
"displayName": "IEEE address",
|
||||||
|
"type": "QString",
|
||||||
|
"defaultValue": "00:00:00:00:00:00:00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "81a82ed2-c465-4e53-a208-157d8473b16c",
|
||||||
|
"name": "networkUuid",
|
||||||
|
"displayName": "Zigbee network UUID",
|
||||||
|
"type": "QString",
|
||||||
|
"defaultValue": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "9472299b-b91b-456a-95fe-bf80d1a266a1",
|
||||||
|
"name": "endpointId",
|
||||||
|
"displayName": "Endpoint ID",
|
||||||
|
"type": "uint",
|
||||||
|
"defaultValue": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "d9044272-2d7d-46d2-b7bf-37239014f2df",
|
||||||
|
"name": "manufacturer",
|
||||||
|
"displayName": "Manufacturer",
|
||||||
|
"type": "QString",
|
||||||
|
"defaultValue": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "3e971afc-a6f1-429c-a902-69cba865c222",
|
||||||
|
"name": "model",
|
||||||
|
"displayName": "Model",
|
||||||
|
"type": "QString",
|
||||||
|
"defaultValue": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"stateTypes": [
|
||||||
|
{
|
||||||
|
"id": "2dc93a53-c506-41a5-9242-b5d045f51c40",
|
||||||
|
"name": "connected",
|
||||||
|
"displayName": "Connected",
|
||||||
|
"displayNameEvent": "Connected changed",
|
||||||
|
"type": "bool",
|
||||||
|
"cached": false,
|
||||||
|
"defaultValue": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ab75980d-12f5-4ddc-be2d-dc54514a3b75",
|
||||||
|
"name": "signalStrength",
|
||||||
|
"displayName": "Signal strength",
|
||||||
|
"displayNameEvent": "Signal strength changed",
|
||||||
|
"defaultValue": 0,
|
||||||
|
"maxValue": 100,
|
||||||
|
"minValue": 0,
|
||||||
|
"type": "uint",
|
||||||
|
"unit": "Percentage"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "9390d2a6-7a17-4b42-8b35-cdb8b3d4d723",
|
||||||
|
"name": "version",
|
||||||
|
"displayName": "Version",
|
||||||
|
"displayNameEvent": "Version changed",
|
||||||
|
"type": "QString",
|
||||||
|
"cached": true,
|
||||||
|
"defaultValue": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "19d82a60-0009-4cff-966d-3b1c4fe358e1",
|
||||||
|
"name": "power",
|
||||||
|
"displayName": "Power",
|
||||||
|
"displayNameEvent": "Power changed",
|
||||||
|
"displayNameAction": "Set power",
|
||||||
|
"type": "bool",
|
||||||
|
"defaultValue": false,
|
||||||
|
"writable": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "6376376f-3221-4e6b-bce3-41872a6fc98a",
|
||||||
|
"name": "brightness",
|
||||||
|
"displayName": "Brightness",
|
||||||
|
"displayNameEvent": "Brightness changed",
|
||||||
|
"displayNameAction": "Set brightness",
|
||||||
|
"maxValue": 100,
|
||||||
|
"minValue": 0,
|
||||||
|
"type": "int",
|
||||||
|
"defaultValue": 100,
|
||||||
|
"writable": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"actionTypes": [
|
||||||
|
{
|
||||||
|
"id": "a04d9e2b-e854-4361-bb34-2da74f8f028f",
|
||||||
|
"name": "alert",
|
||||||
|
"displayName": "Identify"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"eventTypes": [
|
||||||
|
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "colorTemperatureLight",
|
||||||
|
"displayName": "Color Temperature Light",
|
||||||
|
"id": "498815c1-a6a3-48e9-9c9b-bff974694b26",
|
||||||
|
"setupMethod": "JustAdd",
|
||||||
|
"createMethods": [ "Auto" ],
|
||||||
|
"interfaces": [ "colortemperaturelight", "alert", "wirelessconnectable" ],
|
||||||
|
"paramTypes": [
|
||||||
|
{
|
||||||
|
"id": "60abc695-c945-4e08-8ea3-21c4f05ff2f4",
|
||||||
|
"name": "ieeeAddress",
|
||||||
|
"displayName": "IEEE address",
|
||||||
|
"type": "QString",
|
||||||
|
"defaultValue": "00:00:00:00:00:00:00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "2f1799f0-76da-4147-89a0-1db93bb74872",
|
||||||
|
"name": "networkUuid",
|
||||||
|
"displayName": "Zigbee network UUID",
|
||||||
|
"type": "QString",
|
||||||
|
"defaultValue": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "8511c361-79c2-420c-93c2-89e97da1baff",
|
||||||
|
"name": "endpointId",
|
||||||
|
"displayName": "Endpoint ID",
|
||||||
|
"type": "uint",
|
||||||
|
"defaultValue": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "3b688ee1-dd08-4863-a221-70c4ccc062e9",
|
||||||
|
"name": "manufacturer",
|
||||||
|
"displayName": "Manufacturer",
|
||||||
|
"type": "QString",
|
||||||
|
"defaultValue": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "c58bef19-87e3-484c-9a5c-c0daa23092a3",
|
||||||
|
"name": "model",
|
||||||
|
"displayName": "Model",
|
||||||
|
"type": "QString",
|
||||||
|
"defaultValue": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"stateTypes": [
|
||||||
|
{
|
||||||
|
"id": "dd4eb1fe-4ddd-42fa-97bf-df690728c866",
|
||||||
|
"name": "connected",
|
||||||
|
"displayName": "Connected",
|
||||||
|
"displayNameEvent": "Connected changed",
|
||||||
|
"type": "bool",
|
||||||
|
"cached": false,
|
||||||
|
"defaultValue": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "45e5b606-1e4d-42b8-9ee1-0442751247ef",
|
||||||
|
"name": "signalStrength",
|
||||||
|
"displayName": "Signal strength",
|
||||||
|
"displayNameEvent": "Signal strength changed",
|
||||||
|
"defaultValue": 0,
|
||||||
|
"maxValue": 100,
|
||||||
|
"minValue": 0,
|
||||||
|
"type": "uint",
|
||||||
|
"unit": "Percentage"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "477a1531-5f87-4f21-9b32-01161be93cd2",
|
||||||
|
"name": "version",
|
||||||
|
"displayName": "Version",
|
||||||
|
"displayNameEvent": "Version changed",
|
||||||
|
"type": "QString",
|
||||||
|
"cached": true,
|
||||||
|
"defaultValue": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "a8c56366-5b2d-4f4b-b910-38a87851c484",
|
||||||
|
"name": "power",
|
||||||
|
"displayName": "Power",
|
||||||
|
"displayNameEvent": "Power changed",
|
||||||
|
"displayNameAction": "Set power",
|
||||||
|
"type": "bool",
|
||||||
|
"defaultValue": false,
|
||||||
|
"writable": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b72177f9-b673-49b5-864a-345936e5c821",
|
||||||
|
"name": "brightness",
|
||||||
|
"displayName": "Brightness",
|
||||||
|
"displayNameEvent": "Brightness changed",
|
||||||
|
"displayNameAction": "Set brightness",
|
||||||
|
"maxValue": 100,
|
||||||
|
"minValue": 0,
|
||||||
|
"type": "int",
|
||||||
|
"defaultValue": 100,
|
||||||
|
"writable": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "69e94f88-a664-4bb9-ba04-2c3ea6cd7569",
|
||||||
|
"name": "colorTemperature",
|
||||||
|
"displayName": "Color temperature scaled",
|
||||||
|
"displayNameEvent": "Color temperature scaled changed",
|
||||||
|
"displayNameAction": "Set color temperature scaled",
|
||||||
|
"defaultValue": 100,
|
||||||
|
"minValue": 0,
|
||||||
|
"maxValue": 200,
|
||||||
|
"type": "int",
|
||||||
|
"writable": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"actionTypes": [
|
||||||
|
{
|
||||||
|
"id": "4f8a38f4-652f-40e1-86dd-028dcd64349c",
|
||||||
|
"name": "alert",
|
||||||
|
"displayName": "Identify"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "a3e8d1c7-81c7-4255-b410-3bc4d5c7b4e9",
|
||||||
|
"name": "removeFromNetwork",
|
||||||
|
"displayName": "Remove from network"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"eventTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@ -37,17 +37,40 @@
|
|||||||
|
|
||||||
IntegrationPluginZigbeeGenericLights::IntegrationPluginZigbeeGenericLights()
|
IntegrationPluginZigbeeGenericLights::IntegrationPluginZigbeeGenericLights()
|
||||||
{
|
{
|
||||||
|
// Common thing params map
|
||||||
m_ieeeAddressParamTypeIds[onOffLightThingClassId] = onOffLightThingIeeeAddressParamTypeId;
|
m_ieeeAddressParamTypeIds[onOffLightThingClassId] = onOffLightThingIeeeAddressParamTypeId;
|
||||||
|
m_ieeeAddressParamTypeIds[dimmableLightThingClassId] = dimmableLightThingIeeeAddressParamTypeId;
|
||||||
|
m_ieeeAddressParamTypeIds[colorTemperatureLightThingClassId] = colorTemperatureLightThingIeeeAddressParamTypeId;
|
||||||
|
|
||||||
m_networkUuidParamTypeIds[onOffLightThingClassId] = onOffLightThingNetworkUuidParamTypeId;
|
m_networkUuidParamTypeIds[onOffLightThingClassId] = onOffLightThingNetworkUuidParamTypeId;
|
||||||
|
m_networkUuidParamTypeIds[dimmableLightThingClassId] = dimmableLightThingNetworkUuidParamTypeId;
|
||||||
|
m_networkUuidParamTypeIds[colorTemperatureLightThingClassId] = colorTemperatureLightThingNetworkUuidParamTypeId;
|
||||||
|
|
||||||
m_endpointIdParamTypeIds[onOffLightThingClassId] = onOffLightThingEndpointIdParamTypeId;
|
m_endpointIdParamTypeIds[onOffLightThingClassId] = onOffLightThingEndpointIdParamTypeId;
|
||||||
|
m_endpointIdParamTypeIds[dimmableLightThingClassId] = dimmableLightThingEndpointIdParamTypeId;
|
||||||
|
m_endpointIdParamTypeIds[colorTemperatureLightThingClassId] = colorTemperatureLightThingEndpointIdParamTypeId;
|
||||||
|
|
||||||
|
m_manufacturerIdParamTypeIds[onOffLightThingClassId] = onOffLightThingManufacturerParamTypeId;
|
||||||
|
m_manufacturerIdParamTypeIds[dimmableLightThingClassId] = dimmableLightThingManufacturerParamTypeId;
|
||||||
|
m_manufacturerIdParamTypeIds[colorTemperatureLightThingClassId] = colorTemperatureLightThingManufacturerParamTypeId;
|
||||||
|
|
||||||
|
m_modelIdParamTypeIds[onOffLightThingClassId] = onOffLightThingModelParamTypeId;
|
||||||
|
m_modelIdParamTypeIds[dimmableLightThingClassId] = dimmableLightThingModelParamTypeId;
|
||||||
|
m_modelIdParamTypeIds[colorTemperatureLightThingClassId] = colorTemperatureLightThingModelParamTypeId;
|
||||||
|
|
||||||
|
|
||||||
|
// Common sates map
|
||||||
m_connectedStateTypeIds[onOffLightThingClassId] = onOffLightConnectedStateTypeId;
|
m_connectedStateTypeIds[onOffLightThingClassId] = onOffLightConnectedStateTypeId;
|
||||||
|
m_connectedStateTypeIds[dimmableLightThingClassId] = dimmableLightConnectedStateTypeId;
|
||||||
|
m_connectedStateTypeIds[colorTemperatureLightThingClassId] = colorTemperatureLightConnectedStateTypeId;
|
||||||
|
|
||||||
m_signalStrengthStateTypeIds[onOffLightThingClassId] = onOffLightSignalStrengthStateTypeId;
|
m_signalStrengthStateTypeIds[onOffLightThingClassId] = onOffLightSignalStrengthStateTypeId;
|
||||||
|
m_signalStrengthStateTypeIds[dimmableLightThingClassId] = dimmableLightSignalStrengthStateTypeId;
|
||||||
|
m_signalStrengthStateTypeIds[colorTemperatureLightThingClassId] = colorTemperatureLightSignalStrengthStateTypeId;
|
||||||
|
|
||||||
m_versionStateTypeIds[onOffLightThingClassId] = onOffLightVersionStateTypeId;
|
m_versionStateTypeIds[onOffLightThingClassId] = onOffLightVersionStateTypeId;
|
||||||
|
m_versionStateTypeIds[dimmableLightThingClassId] = dimmableLightVersionStateTypeId;
|
||||||
|
m_versionStateTypeIds[colorTemperatureLightThingClassId] = colorTemperatureLightVersionStateTypeId;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString IntegrationPluginZigbeeGenericLights::name() const
|
QString IntegrationPluginZigbeeGenericLights::name() const
|
||||||
@ -63,20 +86,29 @@ bool IntegrationPluginZigbeeGenericLights::handleNode(ZigbeeNode *node, const QU
|
|||||||
(endpoint->profile() == Zigbee::ZigbeeProfile::ZigbeeProfileHomeAutomation &&
|
(endpoint->profile() == Zigbee::ZigbeeProfile::ZigbeeProfileHomeAutomation &&
|
||||||
endpoint->deviceId() == Zigbee::HomeAutomationDeviceOnOffLight)) {
|
endpoint->deviceId() == Zigbee::HomeAutomationDeviceOnOffLight)) {
|
||||||
|
|
||||||
ThingDescriptor descriptor(onOffLightThingClassId);
|
qCDebug(dcZigbeeGenericLights()) << "Handeling on/off light for" << node << endpoint;
|
||||||
QString deviceClassName = supportedThings().findById(onOffLightThingClassId).displayName();
|
createLightThing(onOffLightThingClassId, networkUuid, node, endpoint);
|
||||||
descriptor.setTitle(QString("%1 (%2 - %3)").arg(deviceClassName).arg(endpoint->manufacturerName()).arg(endpoint->modelIdentifier()));
|
return true;
|
||||||
qCDebug(dcZigbeeGenericLights()) << "Handeling generic on/off light" << descriptor.title();
|
}
|
||||||
|
|
||||||
ParamList params;
|
if ((endpoint->profile() == Zigbee::ZigbeeProfile::ZigbeeProfileLightLink &&
|
||||||
params.append(Param(onOffLightThingNetworkUuidParamTypeId, networkUuid.toString()));
|
endpoint->deviceId() == Zigbee::LightLinkDevice::LightLinkDeviceDimmableLight) ||
|
||||||
params.append(Param(onOffLightThingIeeeAddressParamTypeId, node->extendedAddress().toString()));
|
(endpoint->profile() == Zigbee::ZigbeeProfile::ZigbeeProfileHomeAutomation &&
|
||||||
params.append(Param(onOffLightThingEndpointIdParamTypeId, endpoint->endpointId()));
|
endpoint->deviceId() == Zigbee::HomeAutomationDeviceDimmableLight)) {
|
||||||
params.append(Param(onOffLightThingModelParamTypeId, endpoint->modelIdentifier()));
|
|
||||||
params.append(Param(onOffLightThingManufacturerParamTypeId, endpoint->manufacturerName()));
|
|
||||||
descriptor.setParams(params);
|
|
||||||
emit autoThingsAppeared({descriptor});
|
|
||||||
|
|
||||||
|
qCDebug(dcZigbeeGenericLights()) << "Handeling dimmable light for" << node << endpoint;
|
||||||
|
createLightThing(dimmableLightThingClassId, networkUuid, node, endpoint);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if ((endpoint->profile() == Zigbee::ZigbeeProfileLightLink &&
|
||||||
|
endpoint->deviceId() == Zigbee::LightLinkDeviceColourTemperatureLight) ||
|
||||||
|
(endpoint->profile() == Zigbee::ZigbeeProfileHomeAutomation &&
|
||||||
|
endpoint->deviceId() == Zigbee::HomeAutomationDeviceColourTemperatureLight)) {
|
||||||
|
|
||||||
|
qCDebug(dcZigbeeGenericLights()) << "Handeling color temperature light for" << node << endpoint;
|
||||||
|
createLightThing(colorTemperatureLightThingClassId, networkUuid, node, endpoint);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -93,6 +125,10 @@ void IntegrationPluginZigbeeGenericLights::handleRemoveNode(ZigbeeNode *node, co
|
|||||||
qCDebug(dcZigbeeGenericLights()) << node << "for" << thing << "has left the network.";
|
qCDebug(dcZigbeeGenericLights()) << node << "for" << thing << "has left the network.";
|
||||||
m_thingNodes.remove(thing);
|
m_thingNodes.remove(thing);
|
||||||
emit autoThingDisappeared(thing->id());
|
emit autoThingDisappeared(thing->id());
|
||||||
|
|
||||||
|
if (m_colorTemperatureRanges.contains(thing)) {
|
||||||
|
m_colorTemperatureRanges.remove(thing);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,6 +182,8 @@ void IntegrationPluginZigbeeGenericLights::setupThing(ThingSetupInfo *info)
|
|||||||
thing->setStateValue(m_versionStateTypeIds.value(thing->thingClassId()), endpoint->softwareBuildId());
|
thing->setStateValue(m_versionStateTypeIds.value(thing->thingClassId()), endpoint->softwareBuildId());
|
||||||
|
|
||||||
// Thing specific setup
|
// Thing specific setup
|
||||||
|
|
||||||
|
// On/Off light
|
||||||
if (thing->thingClassId() == onOffLightThingClassId) {
|
if (thing->thingClassId() == onOffLightThingClassId) {
|
||||||
|
|
||||||
// Get the on/off cluster
|
// Get the on/off cluster
|
||||||
@ -156,6 +194,10 @@ void IntegrationPluginZigbeeGenericLights::setupThing(ThingSetupInfo *info)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (onOffCluster->hasAttribute(ZigbeeClusterOnOff::AttributeOnOff)) {
|
||||||
|
thing->setStateValue(onOffLightPowerStateTypeId, onOffCluster->power());
|
||||||
|
}
|
||||||
|
|
||||||
// Update the power state if the node power value changes
|
// Update the power state if the node power value changes
|
||||||
connect(onOffCluster, &ZigbeeClusterOnOff::powerChanged, thing, [thing](bool power){
|
connect(onOffCluster, &ZigbeeClusterOnOff::powerChanged, thing, [thing](bool power){
|
||||||
qCDebug(dcZigbeeGenericLights()) << thing << "power state changed" << power;
|
qCDebug(dcZigbeeGenericLights()) << thing << "power state changed" << power;
|
||||||
@ -170,6 +212,134 @@ void IntegrationPluginZigbeeGenericLights::setupThing(ThingSetupInfo *info)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Dimmable light
|
||||||
|
if (thing->thingClassId() == dimmableLightThingClassId) {
|
||||||
|
|
||||||
|
// Get the on/off cluster
|
||||||
|
ZigbeeClusterOnOff *onOffCluster = endpoint->inputCluster<ZigbeeClusterOnOff>(ZigbeeClusterLibrary::ClusterIdOnOff);
|
||||||
|
if (!onOffCluster) {
|
||||||
|
qCWarning(dcZigbeeGenericLights()) << "Could not find on/off cluster for" << thing << "in" << node;
|
||||||
|
info->finish(Thing::ThingErrorHardwareFailure);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only set the state if the cluster actually has the attribute
|
||||||
|
if (onOffCluster->hasAttribute(ZigbeeClusterOnOff::AttributeOnOff)) {
|
||||||
|
thing->setStateValue(dimmableLightPowerStateTypeId, onOffCluster->power());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the power state if the node power value changes
|
||||||
|
connect(onOffCluster, &ZigbeeClusterOnOff::powerChanged, thing, [thing](bool power){
|
||||||
|
qCDebug(dcZigbeeGenericLights()) << thing << "power state changed" << power;
|
||||||
|
thing->setStateValue(dimmableLightPowerStateTypeId, power);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Get the level cluster
|
||||||
|
ZigbeeClusterLevelControl *levelCluster = endpoint->inputCluster<ZigbeeClusterLevelControl>(ZigbeeClusterLibrary::ClusterIdLevelControl);
|
||||||
|
if (!levelCluster) {
|
||||||
|
qCWarning(dcZigbeeGenericLights()) << "Could not find level cluster for" << thing << "in" << node;
|
||||||
|
info->finish(Thing::ThingErrorHardwareFailure);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only set the state if the cluster actually has the attribute
|
||||||
|
if (levelCluster->hasAttribute(ZigbeeClusterLevelControl::AttributeCurrentLevel)) {
|
||||||
|
int percentage = qRound(levelCluster->currentLevel() * 100.0 / 255.0);
|
||||||
|
thing->setStateValue(dimmableLightBrightnessStateTypeId, percentage);
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(levelCluster, &ZigbeeClusterLevelControl::currentLevelChanged, thing, [thing](quint8 level){
|
||||||
|
int percentage = qRound(level * 100.0 / 255.0);
|
||||||
|
qCDebug(dcZigbeeGenericLights()) << thing << "level state changed" << level << percentage << "%";
|
||||||
|
thing->setStateValue(dimmableLightBrightnessStateTypeId, percentage);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Read the states once the node gets reachable
|
||||||
|
connect(node, &ZigbeeNode::reachableChanged, thing, [thing, this](bool reachable){
|
||||||
|
if (reachable) {
|
||||||
|
readLightPowerState(thing);
|
||||||
|
readLightLevelState(thing);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Color temperature light
|
||||||
|
if (thing->thingClassId() == colorTemperatureLightThingClassId) {
|
||||||
|
|
||||||
|
// Get the on/off cluster
|
||||||
|
ZigbeeClusterOnOff *onOffCluster = endpoint->inputCluster<ZigbeeClusterOnOff>(ZigbeeClusterLibrary::ClusterIdOnOff);
|
||||||
|
if (!onOffCluster) {
|
||||||
|
qCWarning(dcZigbeeGenericLights()) << "Could not find on/off cluster for" << thing << "in" << node;
|
||||||
|
info->finish(Thing::ThingErrorHardwareFailure);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only set the state if the cluster actually has the attribute
|
||||||
|
if (onOffCluster->hasAttribute(ZigbeeClusterOnOff::AttributeOnOff)) {
|
||||||
|
thing->setStateValue(colorTemperatureLightPowerStateTypeId, onOffCluster->power());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the power state if the node power value changes
|
||||||
|
connect(onOffCluster, &ZigbeeClusterOnOff::powerChanged, thing, [thing](bool power){
|
||||||
|
qCDebug(dcZigbeeGenericLights()) << thing << "power state changed" << power;
|
||||||
|
thing->setStateValue(colorTemperatureLightPowerStateTypeId, power);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Get the level cluster
|
||||||
|
ZigbeeClusterLevelControl *levelCluster = endpoint->inputCluster<ZigbeeClusterLevelControl>(ZigbeeClusterLibrary::ClusterIdLevelControl);
|
||||||
|
if (!levelCluster) {
|
||||||
|
qCWarning(dcZigbeeGenericLights()) << "Could not find level cluster for" << thing << "in" << node;
|
||||||
|
info->finish(Thing::ThingErrorHardwareFailure);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only set the state if the cluster actually has the attribute
|
||||||
|
if (levelCluster->hasAttribute(ZigbeeClusterLevelControl::AttributeCurrentLevel)) {
|
||||||
|
int percentage = qRound(levelCluster->currentLevel() * 100.0 / 255.0);
|
||||||
|
thing->setStateValue(colorTemperatureLightBrightnessStateTypeId, percentage);
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(levelCluster, &ZigbeeClusterLevelControl::currentLevelChanged, thing, [thing](quint8 level){
|
||||||
|
int percentage = qRound(level * 100.0 / 255.0);
|
||||||
|
qCDebug(dcZigbeeGenericLights()) << thing << "level state changed" << level << percentage << "%";
|
||||||
|
thing->setStateValue(colorTemperatureLightBrightnessStateTypeId, percentage);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Get color cluster
|
||||||
|
ZigbeeClusterColorControl *colorCluster = endpoint->inputCluster<ZigbeeClusterColorControl>(ZigbeeClusterLibrary::ClusterIdColorControl);
|
||||||
|
if (!colorCluster) {
|
||||||
|
qCWarning(dcZigbeeGenericLights()) << "Could not find color cluster for" << thing << "in" << node;
|
||||||
|
info->finish(Thing::ThingErrorHardwareFailure);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only set the state if the cluster actually has the attribute
|
||||||
|
if (colorCluster->hasAttribute(ZigbeeClusterColorControl::AttributeColorTemperatureMireds)) {
|
||||||
|
int mappedValue = mapColorTemperatureToScaledValue(thing, colorCluster->colorTemperatureMireds());
|
||||||
|
thing->setStateValue(colorTemperatureLightColorTemperatureStateTypeId, mappedValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(colorCluster, &ZigbeeClusterColorControl::colorTemperatureMiredsChanged, thing, [this, thing](quint16 colorTemperatureMired){
|
||||||
|
qCDebug(dcZigbeeGenericLights()) << "Actual color temperature is" << colorTemperatureMired << "mireds";
|
||||||
|
int mappedValue = mapColorTemperatureToScaledValue(thing, colorTemperatureMired);
|
||||||
|
qCDebug(dcZigbeeGenericLights()) << "Mapped color temperature is" << mappedValue;
|
||||||
|
thing->setStateValue(colorTemperatureLightColorTemperatureStateTypeId, mappedValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Read the states once the node gets reachable
|
||||||
|
connect(node, &ZigbeeNode::reachableChanged, thing, [thing, this](bool reachable){
|
||||||
|
if (reachable) {
|
||||||
|
readColorTemperatureRange(thing);
|
||||||
|
readLightPowerState(thing);
|
||||||
|
readLightLevelState(thing);
|
||||||
|
readLightColorTemperatureState(thing);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
info->finish(Thing::ThingErrorNoError);
|
info->finish(Thing::ThingErrorNoError);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,10 +353,10 @@ void IntegrationPluginZigbeeGenericLights::executeAction(ThingActionInfo *info)
|
|||||||
// Get the node
|
// Get the node
|
||||||
Thing *thing = info->thing();
|
Thing *thing = info->thing();
|
||||||
ZigbeeNode *node = m_thingNodes.value(thing);
|
ZigbeeNode *node = m_thingNodes.value(thing);
|
||||||
// if (!node->reachable()) {
|
if (!node->reachable()) {
|
||||||
// info->finish(Thing::ThingErrorHardwareNotAvailable);
|
info->finish(Thing::ThingErrorHardwareNotAvailable);
|
||||||
// return;
|
return;
|
||||||
// }
|
}
|
||||||
|
|
||||||
// Get the endpoint
|
// Get the endpoint
|
||||||
ZigbeeNodeEndpoint *endpoint = findEndpoint(thing);
|
ZigbeeNodeEndpoint *endpoint = findEndpoint(thing);
|
||||||
@ -195,58 +365,71 @@ void IntegrationPluginZigbeeGenericLights::executeAction(ThingActionInfo *info)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// On/Off light
|
||||||
if (thing->thingClassId() == onOffLightThingClassId) {
|
if (thing->thingClassId() == onOffLightThingClassId) {
|
||||||
|
|
||||||
if (info->action().actionTypeId() == onOffLightAlertActionTypeId) {
|
if (info->action().actionTypeId() == onOffLightAlertActionTypeId) {
|
||||||
// Get the identify server cluster from the endpoint
|
executeAlertAction(info, endpoint);
|
||||||
ZigbeeClusterIdentify *identifyCluster = endpoint->inputCluster<ZigbeeClusterIdentify>(ZigbeeClusterLibrary::ClusterIdIdentify);
|
|
||||||
if (!identifyCluster) {
|
|
||||||
qCWarning(dcZigbeeGenericLights()) << "Could not find identify cluster for" << thing << "in" << node;
|
|
||||||
info->finish(Thing::ThingErrorHardwareFailure);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send the command trough the network
|
|
||||||
ZigbeeClusterReply *reply = identifyCluster->identify(2);
|
|
||||||
connect(reply, &ZigbeeClusterReply::finished, this, [reply, info](){
|
|
||||||
// Note: reply will be deleted automatically
|
|
||||||
if (reply->error() != ZigbeeClusterReply::ErrorNoError) {
|
|
||||||
info->finish(Thing::ThingErrorHardwareFailure);
|
|
||||||
} else {
|
|
||||||
info->finish(Thing::ThingErrorNoError);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (info->action().actionTypeId() == onOffLightPowerActionTypeId) {
|
if (info->action().actionTypeId() == onOffLightPowerActionTypeId) {
|
||||||
// Get the on/off server cluster from the endpoint
|
// Get the on/off server cluster from the endpoint
|
||||||
ZigbeeClusterOnOff *onOffCluster = endpoint->inputCluster<ZigbeeClusterOnOff>(ZigbeeClusterLibrary::ClusterIdOnOff);
|
|
||||||
if (!onOffCluster) {
|
|
||||||
qCWarning(dcZigbeeGenericLights()) << "Could not find on/off cluster for" << thing << "in" << node;
|
|
||||||
info->finish(Thing::ThingErrorHardwareFailure);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send the command trough the network
|
|
||||||
bool power = info->action().param(onOffLightPowerActionPowerParamTypeId).value().toBool();
|
bool power = info->action().param(onOffLightPowerActionPowerParamTypeId).value().toBool();
|
||||||
qCDebug(dcZigbeeGenericLights()) << "Set power for" << thing << "to" << power;
|
executePowerAction(info, endpoint, onOffLightPowerStateTypeId, power);
|
||||||
ZigbeeClusterReply *reply = (power ? onOffCluster->commandOn() : onOffCluster->commandOff());
|
|
||||||
connect(reply, &ZigbeeClusterReply::finished, thing, [reply, info, power, thing](){
|
|
||||||
// Note: reply will be deleted automatically
|
|
||||||
if (reply->error() != ZigbeeClusterReply::ErrorNoError) {
|
|
||||||
qCWarning(dcZigbeeGenericLights()) << "Failed to set power on" << thing << reply->error();
|
|
||||||
info->finish(Thing::ThingErrorHardwareFailure);
|
|
||||||
} else {
|
|
||||||
info->finish(Thing::ThingErrorNoError);
|
|
||||||
qCDebug(dcZigbeeGenericLights()) << "Set power finished successfully for" << thing;
|
|
||||||
thing->setStateValue(onOffLightPowerStateTypeId, power);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Dimmable light
|
||||||
|
if (thing->thingClassId() == dimmableLightThingClassId) {
|
||||||
|
if (info->action().actionTypeId() == dimmableLightAlertActionTypeId) {
|
||||||
|
executeAlertAction(info, endpoint);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info->action().actionTypeId() == dimmableLightPowerActionTypeId) {
|
||||||
|
bool power = info->action().param(dimmableLightPowerActionPowerParamTypeId).value().toBool();
|
||||||
|
executePowerAction(info, endpoint, dimmableLightPowerStateTypeId, power);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info->action().actionTypeId() == dimmableLightBrightnessActionTypeId) {
|
||||||
|
int brightness = info->action().param(dimmableLightBrightnessActionBrightnessParamTypeId).value().toInt();
|
||||||
|
quint8 level = static_cast<quint8>(qRound(255.0 * brightness / 100.0));
|
||||||
|
executeBrightnessAction(info, endpoint, dimmableLightPowerStateTypeId, dimmableLightBrightnessStateTypeId, brightness, level);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Color temperature light
|
||||||
|
if (thing->thingClassId() == colorTemperatureLightThingClassId) {
|
||||||
|
if (info->action().actionTypeId() == colorTemperatureLightAlertActionTypeId) {
|
||||||
|
executeAlertAction(info, endpoint);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info->action().actionTypeId() == colorTemperatureLightPowerActionTypeId) {
|
||||||
|
bool power = info->action().param(colorTemperatureLightPowerActionPowerParamTypeId).value().toBool();
|
||||||
|
executePowerAction(info, endpoint, colorTemperatureLightPowerStateTypeId, power);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info->action().actionTypeId() == colorTemperatureLightBrightnessActionTypeId) {
|
||||||
|
int brightness = info->action().param(colorTemperatureLightBrightnessActionBrightnessParamTypeId).value().toInt();
|
||||||
|
quint8 level = static_cast<quint8>(qRound(255.0 * brightness / 100.0));
|
||||||
|
executeBrightnessAction(info, endpoint, colorTemperatureLightPowerStateTypeId, colorTemperatureLightBrightnessStateTypeId, brightness, level);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info->action().actionTypeId() == colorTemperatureLightColorTemperatureActionTypeId) {
|
||||||
|
int colorTemperatureScaled = info->action().param(colorTemperatureLightColorTemperatureActionColorTemperatureParamTypeId).value().toInt();
|
||||||
|
executeColorTemperatureAction(info, endpoint, colorTemperatureLightColorTemperatureStateTypeId, colorTemperatureScaled);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
info->finish(Thing::ThingErrorUnsupportedFeature);
|
info->finish(Thing::ThingErrorUnsupportedFeature);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,6 +440,10 @@ void IntegrationPluginZigbeeGenericLights::thingRemoved(Thing *thing)
|
|||||||
QUuid networkUuid = thing->paramValue(m_networkUuidParamTypeIds.value(thing->thingClassId())).toUuid();
|
QUuid networkUuid = thing->paramValue(m_networkUuidParamTypeIds.value(thing->thingClassId())).toUuid();
|
||||||
hardwareManager()->zigbeeResource()->removeNodeFromNetwork(networkUuid, node);
|
hardwareManager()->zigbeeResource()->removeNodeFromNetwork(networkUuid, node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_colorTemperatureRanges.contains(thing)) {
|
||||||
|
m_colorTemperatureRanges.remove(thing);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ZigbeeNodeEndpoint *IntegrationPluginZigbeeGenericLights::findEndpoint(Thing *thing)
|
ZigbeeNodeEndpoint *IntegrationPluginZigbeeGenericLights::findEndpoint(Thing *thing)
|
||||||
@ -271,6 +458,118 @@ ZigbeeNodeEndpoint *IntegrationPluginZigbeeGenericLights::findEndpoint(Thing *th
|
|||||||
return node->getEndpoint(endpointId);
|
return node->getEndpoint(endpointId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IntegrationPluginZigbeeGenericLights::createLightThing(const ThingClassId &thingClassId, const QUuid &networkUuid, ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint)
|
||||||
|
{
|
||||||
|
ThingDescriptor descriptor(thingClassId);
|
||||||
|
QString deviceClassName = supportedThings().findById(thingClassId).displayName();
|
||||||
|
descriptor.setTitle(QString("%1 (%2 - %3)").arg(deviceClassName).arg(endpoint->manufacturerName()).arg(endpoint->modelIdentifier()));
|
||||||
|
|
||||||
|
ParamList params;
|
||||||
|
params.append(Param(m_networkUuidParamTypeIds[thingClassId], networkUuid.toString()));
|
||||||
|
params.append(Param(m_ieeeAddressParamTypeIds[thingClassId], node->extendedAddress().toString()));
|
||||||
|
params.append(Param(m_endpointIdParamTypeIds[thingClassId], endpoint->endpointId()));
|
||||||
|
params.append(Param(m_modelIdParamTypeIds[thingClassId], endpoint->modelIdentifier()));
|
||||||
|
params.append(Param(m_manufacturerIdParamTypeIds[thingClassId], endpoint->manufacturerName()));
|
||||||
|
descriptor.setParams(params);
|
||||||
|
emit autoThingsAppeared({descriptor});
|
||||||
|
}
|
||||||
|
|
||||||
|
void IntegrationPluginZigbeeGenericLights::executeAlertAction(ThingActionInfo *info, ZigbeeNodeEndpoint *endpoint)
|
||||||
|
{
|
||||||
|
Thing *thing = info->thing();
|
||||||
|
ZigbeeClusterIdentify *identifyCluster = endpoint->inputCluster<ZigbeeClusterIdentify>(ZigbeeClusterLibrary::ClusterIdIdentify);
|
||||||
|
if (!identifyCluster) {
|
||||||
|
qCWarning(dcZigbeeGenericLights()) << "Could not find identify cluster for" << thing << "in" << m_thingNodes.value(thing);
|
||||||
|
info->finish(Thing::ThingErrorHardwareFailure);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the command trough the network
|
||||||
|
ZigbeeClusterReply *reply = identifyCluster->identify(2);
|
||||||
|
connect(reply, &ZigbeeClusterReply::finished, this, [reply, info](){
|
||||||
|
// Note: reply will be deleted automatically
|
||||||
|
if (reply->error() != ZigbeeClusterReply::ErrorNoError) {
|
||||||
|
info->finish(Thing::ThingErrorHardwareFailure);
|
||||||
|
} else {
|
||||||
|
info->finish(Thing::ThingErrorNoError);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void IntegrationPluginZigbeeGenericLights::executePowerAction(ThingActionInfo *info, ZigbeeNodeEndpoint *endpoint, const StateTypeId &powerStateTypeId, bool power)
|
||||||
|
{
|
||||||
|
Thing *thing = info->thing();
|
||||||
|
ZigbeeClusterOnOff *onOffCluster = endpoint->inputCluster<ZigbeeClusterOnOff>(ZigbeeClusterLibrary::ClusterIdOnOff);
|
||||||
|
if (!onOffCluster) {
|
||||||
|
qCWarning(dcZigbeeGenericLights()) << "Could not find on/off cluster for" << thing << "in" << m_thingNodes.value(thing);
|
||||||
|
info->finish(Thing::ThingErrorHardwareFailure);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the command trough the network
|
||||||
|
qCDebug(dcZigbeeGenericLights()) << "Set power for" << info->thing() << "to" << power;
|
||||||
|
ZigbeeClusterReply *reply = (power ? onOffCluster->commandOn() : onOffCluster->commandOff());
|
||||||
|
connect(reply, &ZigbeeClusterReply::finished, info, [=](){
|
||||||
|
// Note: reply will be deleted automatically
|
||||||
|
if (reply->error() != ZigbeeClusterReply::ErrorNoError) {
|
||||||
|
qCWarning(dcZigbeeGenericLights()) << "Failed to set power on" << thing << reply->error();
|
||||||
|
info->finish(Thing::ThingErrorHardwareFailure);
|
||||||
|
} else {
|
||||||
|
info->finish(Thing::ThingErrorNoError);
|
||||||
|
qCDebug(dcZigbeeGenericLights()) << "Set power finished successfully for" << thing;
|
||||||
|
thing->setStateValue(powerStateTypeId, power);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void IntegrationPluginZigbeeGenericLights::executeBrightnessAction(ThingActionInfo *info, ZigbeeNodeEndpoint *endpoint, const StateTypeId &powerStateTypeId, const StateTypeId &brightnessStateTypeId, int brightness, quint8 level)
|
||||||
|
{
|
||||||
|
Thing *thing = info->thing();
|
||||||
|
ZigbeeClusterLevelControl *levelCluster = endpoint->inputCluster<ZigbeeClusterLevelControl>(ZigbeeClusterLibrary::ClusterIdLevelControl);
|
||||||
|
if (!levelCluster) {
|
||||||
|
qCWarning(dcZigbeeGenericLights()) << "Could not find level control cluster for" << thing << "in" << m_thingNodes.value(thing);
|
||||||
|
info->finish(Thing::ThingErrorHardwareFailure);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ZigbeeClusterReply *reply = levelCluster->commandMoveToLevelWithOnOff(level);
|
||||||
|
connect(reply, &ZigbeeClusterReply::finished, info, [=](){
|
||||||
|
// Note: reply will be deleted automatically
|
||||||
|
if (reply->error() != ZigbeeClusterReply::ErrorNoError) {
|
||||||
|
info->finish(Thing::ThingErrorHardwareFailure);
|
||||||
|
} else {
|
||||||
|
info->finish(Thing::ThingErrorNoError);
|
||||||
|
thing->setStateValue(powerStateTypeId, (brightness > 0 ? true : false));
|
||||||
|
thing->setStateValue(brightnessStateTypeId, brightness);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void IntegrationPluginZigbeeGenericLights::executeColorTemperatureAction(ThingActionInfo *info, ZigbeeNodeEndpoint *endpoint, const StateTypeId &colorTemperatureStateTypeId, int colorTemperatureScaled)
|
||||||
|
{
|
||||||
|
Thing *thing = info->thing();
|
||||||
|
ZigbeeClusterColorControl *colorCluster = endpoint->inputCluster<ZigbeeClusterColorControl>(ZigbeeClusterLibrary::ClusterIdColorControl);
|
||||||
|
if (!colorCluster) {
|
||||||
|
qCWarning(dcZigbeeGenericLights()) << "Could not find color control cluster for" << thing << "in" << m_thingNodes.value(thing);
|
||||||
|
info->finish(Thing::ThingErrorHardwareFailure);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scale the value to the actual color temperature of this lamp
|
||||||
|
quint16 colorTemperature = mapScaledValueToColorTemperature(thing, colorTemperatureScaled);
|
||||||
|
// Note: time unit is 1/10 s
|
||||||
|
ZigbeeClusterReply *reply = colorCluster->commandMoveToColorTemperature(colorTemperature, 5);
|
||||||
|
connect(reply, &ZigbeeClusterReply::finished, info, [=](){
|
||||||
|
// Note: reply will be deleted automatically
|
||||||
|
if (reply->error() != ZigbeeClusterReply::ErrorNoError) {
|
||||||
|
info->finish(Thing::ThingErrorHardwareFailure);
|
||||||
|
} else {
|
||||||
|
info->finish(Thing::ThingErrorNoError);
|
||||||
|
thing->setStateValue(colorTemperatureStateTypeId, colorTemperatureScaled);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void IntegrationPluginZigbeeGenericLights::readLightPowerState(Thing *thing)
|
void IntegrationPluginZigbeeGenericLights::readLightPowerState(Thing *thing)
|
||||||
{
|
{
|
||||||
// Get the node
|
// Get the node
|
||||||
@ -297,3 +596,180 @@ void IntegrationPluginZigbeeGenericLights::readLightPowerState(Thing *thing)
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IntegrationPluginZigbeeGenericLights::readLightLevelState(Thing *thing)
|
||||||
|
{
|
||||||
|
// Get the node
|
||||||
|
ZigbeeNode *node = m_thingNodes.value(thing);
|
||||||
|
if (!node->reachable())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Get the endpoint
|
||||||
|
ZigbeeNodeEndpoint *endpoint = findEndpoint(thing);
|
||||||
|
if (!endpoint)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Get the level server cluster from the endpoint
|
||||||
|
ZigbeeClusterLevelControl *levelCluster = endpoint->inputCluster<ZigbeeClusterLevelControl>(ZigbeeClusterLibrary::ClusterIdLevelControl);
|
||||||
|
if (!levelCluster)
|
||||||
|
return;
|
||||||
|
|
||||||
|
qCDebug(dcZigbeeGenericLights()) << "Reading level value for" << thing << "on" << node;
|
||||||
|
ZigbeeClusterReply *reply = levelCluster->readAttributes({ZigbeeClusterLevelControl::AttributeCurrentLevel});
|
||||||
|
connect(reply, &ZigbeeClusterReply::finished, thing, [thing, reply](){
|
||||||
|
if (reply->error() != ZigbeeClusterReply::ErrorNoError) {
|
||||||
|
qCWarning(dcZigbeeGenericLights()) << "Failed to read level cluster attribute from" << thing << reply->error();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void IntegrationPluginZigbeeGenericLights::readLightColorTemperatureState(Thing *thing)
|
||||||
|
{
|
||||||
|
ZigbeeNodeEndpoint *endpoint = findEndpoint(thing);
|
||||||
|
if (!endpoint) {
|
||||||
|
qCWarning(dcZigbeeGenericLights()) << "Failed to read color temperature for" << thing << "because the node could not be found";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the color server cluster from the endpoint
|
||||||
|
ZigbeeClusterColorControl *colorCluster = endpoint->inputCluster<ZigbeeClusterColorControl>(ZigbeeClusterLibrary::ClusterIdColorControl);
|
||||||
|
if (!colorCluster) {
|
||||||
|
qCWarning(dcZigbeeGenericLights()) << "Failed to read color temperature for" << thing << "because the color cluster could not be found on" << endpoint;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ZigbeeClusterReply *reply = colorCluster->readAttributes({ZigbeeClusterColorControl::AttributeColorTemperatureMireds});
|
||||||
|
connect(reply, &ZigbeeClusterReply::finished, thing, [=](){
|
||||||
|
if (reply->error() != ZigbeeClusterReply::ErrorNoError) {
|
||||||
|
qCWarning(dcZigbeeGenericLights()) << "Failed to read ColorControl cluster attribute color temperature" << reply->error();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void IntegrationPluginZigbeeGenericLights::readColorTemperatureRange(Thing *thing)
|
||||||
|
{
|
||||||
|
ZigbeeNodeEndpoint *endpoint = findEndpoint(thing);
|
||||||
|
if (!endpoint) {
|
||||||
|
qCWarning(dcZigbeeGenericLights()) << "Failed to read color temperature range for" << thing << "because the node could not be found";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the color server cluster from the endpoint
|
||||||
|
ZigbeeClusterColorControl *colorCluster = endpoint->inputCluster<ZigbeeClusterColorControl>(ZigbeeClusterLibrary::ClusterIdColorControl);
|
||||||
|
if (!colorCluster) {
|
||||||
|
qCWarning(dcZigbeeGenericLights()) << "Failed to read color temperature range for" << thing << "because the color cluster could not be found on" << endpoint;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we can use the cached values from the database
|
||||||
|
if (readCachedColorTemperatureRange(thing, colorCluster)) {
|
||||||
|
qCDebug(dcZigbeeGenericLights()) << "Using cached color temperature mireds interval for mapping" << thing << "[" << m_colorTemperatureRanges[thing].minValue << "," << m_colorTemperatureRanges[thing].maxValue << "] mired";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init with default values
|
||||||
|
m_colorTemperatureRanges[thing] = ColorTemperatureRange();
|
||||||
|
|
||||||
|
// We need to read them from the lamp
|
||||||
|
ZigbeeClusterReply *reply = colorCluster->readAttributes({ZigbeeClusterColorControl::AttributeColorTempPhysicalMinMireds, ZigbeeClusterColorControl::AttributeColorTempPhysicalMaxMireds});
|
||||||
|
connect(reply, &ZigbeeClusterReply::finished, thing, [=](){
|
||||||
|
if (reply->error() != ZigbeeClusterReply::ErrorNoError) {
|
||||||
|
qCWarning(dcZigbeeGenericLights()) << "Reading color temperature range attributes finished with error" << reply->error();
|
||||||
|
qCWarning(dcZigbeeGenericLights()) << "Failed to read color temperature min/max interval values. Using default values for" << thing << "[" << m_colorTemperatureRanges[thing].minValue << "," << m_colorTemperatureRanges[thing].maxValue << "] mired";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<ZigbeeClusterLibrary::ReadAttributeStatusRecord> attributeStatusRecords = ZigbeeClusterLibrary::parseAttributeStatusRecords(reply->responseFrame().payload);
|
||||||
|
if (attributeStatusRecords.count() != 2) {
|
||||||
|
qCWarning(dcZigbeeGenericLights()) << "Did not receive temperature min/max interval values from" << thing;
|
||||||
|
qCWarning(dcZigbeeGenericLights()) << "Using default values for" << thing << "[" << m_colorTemperatureRanges[thing].minValue << "," << m_colorTemperatureRanges[thing].maxValue << "] mired" ;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the attribute status records
|
||||||
|
foreach (const ZigbeeClusterLibrary::ReadAttributeStatusRecord &attributeStatusRecord, attributeStatusRecords) {
|
||||||
|
if (attributeStatusRecord.attributeId == ZigbeeClusterColorControl::AttributeColorTempPhysicalMinMireds) {
|
||||||
|
bool valueOk = false;
|
||||||
|
quint16 minMiredsValue = attributeStatusRecord.dataType.toUInt16(&valueOk);
|
||||||
|
if (!valueOk) {
|
||||||
|
qCWarning(dcZigbeeGenericLights()) << "Failed to read color temperature min mireds attribute value and convert it" << attributeStatusRecord;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_colorTemperatureRanges[thing].minValue = minMiredsValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attributeStatusRecord.attributeId == ZigbeeClusterColorControl::AttributeColorTempPhysicalMaxMireds) {
|
||||||
|
bool valueOk = false;
|
||||||
|
quint16 maxMiredsValue = attributeStatusRecord.dataType.toUInt16(&valueOk);
|
||||||
|
if (!valueOk) {
|
||||||
|
qCWarning(dcZigbeeGenericLights()) << "Failed to read color temperature max mireds attribute value and convert it" << attributeStatusRecord;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_colorTemperatureRanges[thing].maxValue = maxMiredsValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qCDebug(dcZigbeeGenericLights()) << "Using lamp specific color temperature mireds interval for mapping" << thing << "[" << m_colorTemperatureRanges[thing].minValue << "," << m_colorTemperatureRanges[thing].maxValue << "] mired";
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IntegrationPluginZigbeeGenericLights::readCachedColorTemperatureRange(Thing *thing, ZigbeeClusterColorControl *colorCluster)
|
||||||
|
{
|
||||||
|
if (colorCluster->hasAttribute(ZigbeeClusterColorControl::AttributeColorTempPhysicalMinMireds) && colorCluster->hasAttribute(ZigbeeClusterColorControl::AttributeColorTempPhysicalMaxMireds)) {
|
||||||
|
ZigbeeClusterAttribute minMiredsAttribute = colorCluster->attribute(ZigbeeClusterColorControl::AttributeColorTempPhysicalMinMireds);
|
||||||
|
bool valueOk = false;
|
||||||
|
quint16 minMiredsValue = minMiredsAttribute.dataType().toUInt16(&valueOk);
|
||||||
|
if (!valueOk) {
|
||||||
|
qCWarning(dcZigbeeGenericLights()) << "Failed to read color temperature min mireds attribute value and convert it" << minMiredsAttribute;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ZigbeeClusterAttribute maxMiredsAttribute = colorCluster->attribute(ZigbeeClusterColorControl::AttributeColorTempPhysicalMaxMireds);
|
||||||
|
quint16 maxMiredsValue = maxMiredsAttribute.dataType().toUInt16(&valueOk);
|
||||||
|
if (!valueOk) {
|
||||||
|
qCWarning(dcZigbeeGenericLights()) << "Failed to read color temperature max mireds attribute value and convert it" << maxMiredsAttribute;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ColorTemperatureRange range;
|
||||||
|
range.minValue = minMiredsValue;
|
||||||
|
range.maxValue = maxMiredsValue;
|
||||||
|
m_colorTemperatureRanges[thing] = range;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
quint16 IntegrationPluginZigbeeGenericLights::mapScaledValueToColorTemperature(Thing *thing, int scaledColorTemperature)
|
||||||
|
{
|
||||||
|
// Make sure we have values to scale
|
||||||
|
if (!m_colorTemperatureRanges.contains(thing)) {
|
||||||
|
m_colorTemperatureRanges[thing] = ColorTemperatureRange();
|
||||||
|
}
|
||||||
|
|
||||||
|
double percentage = static_cast<double>((scaledColorTemperature - m_minScaleValue)) / (m_maxScaleValue - m_minScaleValue);
|
||||||
|
//qCDebug(dcZigbeeGenericLights()) << "Mapping color temperature value" << scaledColorTemperature << "between" << m_minScaleValue << m_maxScaleValue << "is" << percentage * 100 << "%";
|
||||||
|
double mappedValue = (m_colorTemperatureRanges[thing].maxValue - m_colorTemperatureRanges[thing].minValue) * percentage + m_colorTemperatureRanges[thing].minValue;
|
||||||
|
//qCDebug(dcZigbeeGenericLights()) << "Mapping color temperature value" << scaledColorTemperature << "is" << mappedValue << "mireds";
|
||||||
|
return static_cast<quint16>(qRound(mappedValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
int IntegrationPluginZigbeeGenericLights::mapColorTemperatureToScaledValue(Thing *thing, quint16 colorTemperature)
|
||||||
|
{
|
||||||
|
// Make sure we have values to scale
|
||||||
|
if (!m_colorTemperatureRanges.contains(thing)) {
|
||||||
|
m_colorTemperatureRanges[thing] = ColorTemperatureRange();
|
||||||
|
}
|
||||||
|
|
||||||
|
double percentage = static_cast<double>((colorTemperature - m_colorTemperatureRanges[thing].minValue)) / (m_colorTemperatureRanges[thing].maxValue - m_colorTemperatureRanges[thing].minValue);
|
||||||
|
//qCDebug(dcZigbee()) << "Mapping color temperature value" << colorTemperature << "mirred" << m_minColorTemperature << m_maxColorTemperature << "is" << percentage * 100 << "%";
|
||||||
|
double mappedValue = (m_maxScaleValue - m_minScaleValue) * percentage + m_minScaleValue;
|
||||||
|
//qCDebug(dcZigbee()) << "Mapping color temperature value" << colorTemperature << "results into the scaled value of" << mappedValue;
|
||||||
|
return static_cast<int>(qRound(mappedValue));
|
||||||
|
}
|
||||||
|
|||||||
@ -54,19 +54,54 @@ public:
|
|||||||
void thingRemoved(Thing *thing) override;
|
void thingRemoved(Thing *thing) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// Common thing params
|
||||||
QHash<ThingClassId, ParamTypeId> m_ieeeAddressParamTypeIds;
|
QHash<ThingClassId, ParamTypeId> m_ieeeAddressParamTypeIds;
|
||||||
QHash<ThingClassId, ParamTypeId> m_networkUuidParamTypeIds;
|
QHash<ThingClassId, ParamTypeId> m_networkUuidParamTypeIds;
|
||||||
QHash<ThingClassId, ParamTypeId> m_endpointIdParamTypeIds;
|
QHash<ThingClassId, ParamTypeId> m_endpointIdParamTypeIds;
|
||||||
|
QHash<ThingClassId, ParamTypeId> m_modelIdParamTypeIds;
|
||||||
|
QHash<ThingClassId, ParamTypeId> m_manufacturerIdParamTypeIds;
|
||||||
|
|
||||||
|
// Common states
|
||||||
QHash<ThingClassId, StateTypeId> m_connectedStateTypeIds;
|
QHash<ThingClassId, StateTypeId> m_connectedStateTypeIds;
|
||||||
QHash<ThingClassId, StateTypeId> m_signalStrengthStateTypeIds;
|
QHash<ThingClassId, StateTypeId> m_signalStrengthStateTypeIds;
|
||||||
QHash<ThingClassId, StateTypeId> m_versionStateTypeIds;
|
QHash<ThingClassId, StateTypeId> m_versionStateTypeIds;
|
||||||
|
|
||||||
QHash<Thing *, ZigbeeNode *> m_thingNodes;
|
QHash<Thing *, ZigbeeNode *> m_thingNodes;
|
||||||
|
|
||||||
|
// Get the endpoint for the given thing
|
||||||
ZigbeeNodeEndpoint *findEndpoint(Thing *thing);
|
ZigbeeNodeEndpoint *findEndpoint(Thing *thing);
|
||||||
|
|
||||||
|
void createLightThing(const ThingClassId &thingClassId, const QUuid &networkUuid, ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint);
|
||||||
|
|
||||||
|
// Action help methods since they work all the same
|
||||||
|
void executeAlertAction(ThingActionInfo *info, ZigbeeNodeEndpoint *endpoint);
|
||||||
|
void executePowerAction(ThingActionInfo *info, ZigbeeNodeEndpoint *endpoint, const StateTypeId &powerStateTypeId, bool power);
|
||||||
|
void executeBrightnessAction(ThingActionInfo *info, ZigbeeNodeEndpoint *endpoint, const StateTypeId &powerStateTypeId, const StateTypeId &brightnessStateTypeId, int brightness, quint8 level);
|
||||||
|
void executeColorTemperatureAction(ThingActionInfo *info, ZigbeeNodeEndpoint *endpoint, const StateTypeId &colorTemperatureStateTypeId, int colorTemperatureScaled);
|
||||||
|
|
||||||
|
// Read state values from the node
|
||||||
void readLightPowerState(Thing *thing);
|
void readLightPowerState(Thing *thing);
|
||||||
|
void readLightLevelState(Thing *thing);
|
||||||
|
void readLightColorTemperatureState(Thing *thing);
|
||||||
|
|
||||||
|
// Color temperature information handling
|
||||||
|
typedef struct ColorTemperatureRange {
|
||||||
|
quint16 minValue = 250;
|
||||||
|
quint16 maxValue = 450;
|
||||||
|
} ColorTemperatureRange;
|
||||||
|
|
||||||
|
// Color temperature scaling range defined in
|
||||||
|
// all color temperature states/actions (the slider min/max)
|
||||||
|
int m_minScaleValue = 0;
|
||||||
|
int m_maxScaleValue = 200;
|
||||||
|
|
||||||
|
QHash<Thing *, ColorTemperatureRange> m_colorTemperatureRanges;
|
||||||
|
|
||||||
|
void readColorTemperatureRange(Thing *thing);
|
||||||
|
bool readCachedColorTemperatureRange(Thing *thing, ZigbeeClusterColorControl *colorCluster);
|
||||||
|
quint16 mapScaledValueToColorTemperature(Thing *thing, int scaledColorTemperature);
|
||||||
|
int mapColorTemperatureToScaledValue(Thing *thing, quint16 colorTemperature);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // INTEGRATIONPLUGINZIGBEEGENERICLIGHTS_H
|
#endif // INTEGRATIONPLUGINZIGBEEGENERICLIGHTS_H
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user