2019-02-22 13:06:24 -07:00
|
|
|
const zigbeeShepherdConverters = require('zigbee-shepherd-converters');
|
2019-02-26 12:21:35 -07:00
|
|
|
const logger = require('../util/logger');
|
2019-04-01 10:51:14 -07:00
|
|
|
const CC2530Router = zigbeeShepherdConverters.devices.find((d) => d.model === 'CC2530.ROUTER');
|
2019-04-16 08:56:28 -07:00
|
|
|
const utils = require('../util/utils');
|
2019-02-01 11:04:49 -07:00
|
|
|
|
|
|
|
const candidates = {
|
2019-02-22 13:06:24 -07:00
|
|
|
'genOnOff': {
|
|
|
|
attrs: ['onOff'],
|
|
|
|
reportIntervalMin: 3,
|
|
|
|
reportIntervalMax: 300,
|
|
|
|
reportableChange: 0,
|
|
|
|
},
|
|
|
|
'genLevelCtrl': {
|
|
|
|
attrs: ['currentLevel'],
|
|
|
|
},
|
|
|
|
'lightingColorCtrl': {
|
|
|
|
attrs: ['colorTemperature', 'currentX', 'currentY'],
|
|
|
|
},
|
2019-02-01 11:04:49 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
const reportInterval = {
|
2019-02-22 13:06:24 -07:00
|
|
|
min: 3,
|
2019-02-01 11:04:49 -07:00
|
|
|
max: 3600,
|
|
|
|
};
|
|
|
|
|
2019-03-21 11:43:15 -07:00
|
|
|
const reportableChange = 1;
|
2019-02-01 11:04:49 -07:00
|
|
|
|
2019-02-13 12:55:14 -07:00
|
|
|
class DeviceReport {
|
2019-02-04 10:36:49 -07:00
|
|
|
constructor(zigbee, mqtt, state, publishEntityState) {
|
2019-02-01 11:04:49 -07:00
|
|
|
this.zigbee = zigbee;
|
|
|
|
this.mqtt = mqtt;
|
|
|
|
this.state = state;
|
2019-02-04 10:36:49 -07:00
|
|
|
this.publishEntityState = publishEntityState;
|
2019-02-01 11:04:49 -07:00
|
|
|
}
|
|
|
|
|
2019-02-26 12:21:35 -07:00
|
|
|
setupReporting(mappedDevice, device) {
|
|
|
|
let epId = null;
|
|
|
|
|
2019-04-01 10:51:14 -07:00
|
|
|
if (mappedDevice === CC2530Router) {
|
|
|
|
logger.debug('Not setting up reporting for CC2530 router');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-02-26 12:21:35 -07:00
|
|
|
// Check if this device uses a different epId.
|
|
|
|
if (mappedDevice.hasOwnProperty('ep')) {
|
|
|
|
const eps = mappedDevice.ep(device);
|
|
|
|
epId = eps[''] || null;
|
|
|
|
}
|
|
|
|
|
|
|
|
const endpoint = this.zigbee.getEndpoint(device.ieeeAddr, epId);
|
|
|
|
|
|
|
|
if (!endpoint) {
|
|
|
|
logger.error(`Failed to setup reporting for ${device.ieeeAddr}, endpoint not found`);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
logger.debug(`Setting up reporting for ${device.ieeeAddr}`);
|
2019-02-01 11:04:49 -07:00
|
|
|
Object.values(endpoint.clusters).filter((c) => c).forEach((c) => {
|
|
|
|
const cluster = c.attrs.cid;
|
|
|
|
if (candidates[cluster]) {
|
2019-02-22 13:06:24 -07:00
|
|
|
const candidate = candidates[cluster];
|
2019-03-30 10:20:07 -07:00
|
|
|
let attributeNames = candidate.attrs.filter((a) => c.attrs.hasOwnProperty(a));
|
|
|
|
|
|
|
|
// Sometimes a cluster has no attributes, in this case setup reporting for all attributes.
|
|
|
|
attributeNames = attributeNames.length ? attributeNames : candidate.attrs;
|
|
|
|
|
2019-02-26 12:21:35 -07:00
|
|
|
const attributes = attributeNames.map((attribute) => {
|
|
|
|
return {
|
2019-02-22 13:06:24 -07:00
|
|
|
attr: attribute,
|
|
|
|
min: candidate.hasOwnProperty('reportIntervalMin')?
|
|
|
|
candidate.reportIntervalMin:reportInterval.min,
|
|
|
|
max: candidate.hasOwnProperty('reportIntervalMax')?
|
|
|
|
candidate.reportIntervalMax:reportInterval.max,
|
|
|
|
change: candidate.hasOwnProperty('reportableChange')?
|
|
|
|
candidate.reportableChange:reportableChange,
|
2019-02-26 12:21:35 -07:00
|
|
|
};
|
2019-02-01 11:04:49 -07:00
|
|
|
});
|
2019-02-26 12:21:35 -07:00
|
|
|
|
|
|
|
if (attributes.length > 0) {
|
|
|
|
this.zigbee.report(endpoint, cluster, attributes);
|
|
|
|
}
|
2019-02-01 11:04:49 -07:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-04-16 08:56:28 -07:00
|
|
|
shouldSetupReporting(device) {
|
|
|
|
return utils.isRouter(device) && !utils.isBatteryPowered(device);
|
|
|
|
}
|
|
|
|
|
2019-02-01 11:04:49 -07:00
|
|
|
onZigbeeStarted() {
|
2019-04-16 08:56:28 -07:00
|
|
|
this.zigbee.getAllClients()
|
|
|
|
.filter((d) => this.shouldSetupReporting(d))
|
|
|
|
.forEach((device) => {
|
|
|
|
const mappedDevice = zigbeeShepherdConverters.findByZigbeeModel(device.modelId);
|
2019-02-22 13:06:24 -07:00
|
|
|
|
2019-04-16 08:56:28 -07:00
|
|
|
if (mappedDevice) {
|
|
|
|
this.setupReporting(mappedDevice, device);
|
|
|
|
}
|
|
|
|
});
|
2019-02-01 11:04:49 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
onZigbeeMessage(message, device, mappedDevice) {
|
2019-02-26 12:21:35 -07:00
|
|
|
// Handle messages of type endDeviceAnnce and devIncoming.
|
2019-02-01 11:04:49 -07:00
|
|
|
// This message is typically send when a device comes online after being powered off
|
|
|
|
// Ikea TRADFRI tend to forget their reporting after powered off.
|
|
|
|
// Re-setup reporting.
|
|
|
|
// https://github.com/Koenkk/zigbee2mqtt/issues/966
|
2019-04-16 08:56:28 -07:00
|
|
|
if (device && mappedDevice && ['endDeviceAnnce', 'devIncoming'].includes(message.type) &&
|
|
|
|
this.shouldSetupReporting(device)) {
|
2019-02-26 12:21:35 -07:00
|
|
|
this.setupReporting(mappedDevice, device);
|
2019-02-01 11:04:49 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-13 12:55:14 -07:00
|
|
|
module.exports = DeviceReport;
|