#include <QDataStream>
#include "de_web_plugin_private.h"
#include "air_quality.h"
const std::array<KeyValMapAirQuality, 6> RStateAirQualityVocLevelGer = { { {65, QLatin1String("excellent")}, {220, QLatin1String("good")}, {660, QLatin1String("moderate")}, {2200, QLatin1String("poor")},
{5000, QLatin1String("unhealthy")}, {65535, QLatin1String("out of scale")} } };
\param ind the APS level data indication containing the ZCL packet
\param zclFrame the actual ZCL frame which holds the Thermostat cluster command or attribute
*/
void DeRestPluginPrivate::handleAirQualityClusterIndication(const deCONZ::ApsDataIndication &ind, deCONZ::ZclFrame &zclFrame)
{
Sensor *sensor = getSensorNodeForAddressAndEndpoint(ind.srcAddress(), ind.srcEndpoint(), QLatin1String("ZHAAirQuality"));
if (!sensor)
{
DBG_Printf(DBG_INFO, "No air quality sensor found for " FMT_MAC ", endpoint: 0x%02X\n", FMT_MAC_CAST(ind.srcAddress().ext()), ind.srcEndpoint());
return;
}
QDataStream stream(zclFrame.payload());
stream.setByteOrder(QDataStream::LittleEndian);
bool isReadAttr = false;
bool isReporting = false;
if (zclFrame.isProfileWideCommand() && zclFrame.commandId() == deCONZ::ZclReadAttributesResponseId)
{
isReadAttr = true;
}
if (zclFrame.isProfileWideCommand() && zclFrame.commandId() == deCONZ::ZclReportAttributesId)
{
isReporting = true;
}
if (isReadAttr || isReporting)
{
const NodeValue::UpdateType updateType = isReadAttr ? NodeValue::UpdateByZclRead : NodeValue::UpdateByZclReport;
bool configUpdated = false;
bool stateUpdated = false;
while (!stream.atEnd())
{
quint16 attrId;
quint8 attrTypeId;
stream >> attrId;
if (isReadAttr)
{
quint8 status;
stream >> status;
if (status != deCONZ::ZclSuccessStatus)
{
continue;
}
}
stream >> attrTypeId;
deCONZ::ZclAttribute attr(attrId, attrTypeId, QLatin1String(""), deCONZ::ZclRead, false);
if (!attr.readFromStream(stream))
{
continue;
}
quint32 level = UINT32_MAX;
QString airquality;
switch (attrId)
{
case 0x4004:
{
if (ind.clusterId() == BOSCH_AIR_QUALITY_CLUSTER_ID && sensor->modelId() == QLatin1String("AIR"))
{
level = attr.numericValue().u16;
const auto match = lessThenKeyValue(level, RStateAirQualityVocLevelGer);
if (match.key)
{
airquality = match.value;
}
}
}
break;
default:
break;
}
if (level != UINT32_MAX)
{
ResourceItem *item = sensor->item(RStateAirQualityPpb);
if (item)
{
if (updateType == NodeValue::UpdateByZclReport)
{
stateUpdated = true;
}
if (item->toNumber() != level)
{
item->setValue(level);
enqueueEvent(Event(RSensors, RStateAirQualityPpb, sensor->id(), item));
stateUpdated = true;
}
}
item = sensor->item(RStateAirQuality);
if (item)
{
if (updateType == NodeValue::UpdateByZclReport)
{
stateUpdated = true;
}
if (item->toString() != airquality)
{
item->setValue(airquality);
enqueueEvent(Event(RSensors, RStateAirQuality, sensor->id(), item));
stateUpdated = true;
}
}
sensor->setZclValue(updateType, ind.srcEndpoint(), ind.clusterId(), attrId, attr.numericValue());
}
}
if (stateUpdated)
{
sensor->updateStateTimestamp();
enqueueEvent(Event(RSensors, RStateLastUpdated, sensor->id()));
}
if (configUpdated || stateUpdated)
{
updateSensorEtag(&*sensor);
sensor->setNeedSaveDatabase(true);
queSaveDb(DB_SENSORS, DB_SHORT_SAVE_DELAY);
}
}
}