mirror of
https://github.com/Koenkk/zigbee2mqtt.git
synced 2024-11-16 18:39:09 -07:00
setup automatic attribute change reporting
This commit is contained in:
parent
fdf97180b0
commit
09e0fe8e3f
@ -1,14 +1,22 @@
|
|||||||
const settings = require('../util/settings');
|
const zigbeeShepherdConverters = require('zigbee-shepherd-converters');
|
||||||
const utils = require('../util/utils');
|
|
||||||
|
|
||||||
const candidates = {
|
const candidates = {
|
||||||
'genOnOff': ['onOff'],
|
'genOnOff': {
|
||||||
'genLevelCtrl': ['currentLevel'],
|
attrs: ['onOff'],
|
||||||
'lightingColorCtrl': ['colorTemperature', 'currentX', 'currentY'],
|
reportIntervalMin: 3,
|
||||||
|
reportIntervalMax: 300,
|
||||||
|
reportableChange: 0,
|
||||||
|
},
|
||||||
|
'genLevelCtrl': {
|
||||||
|
attrs: ['currentLevel'],
|
||||||
|
},
|
||||||
|
'lightingColorCtrl': {
|
||||||
|
attrs: ['colorTemperature', 'currentX', 'currentY'],
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const reportInterval = {
|
const reportInterval = {
|
||||||
min: 1,
|
min: 3,
|
||||||
max: 3600,
|
max: 3600,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -22,41 +30,38 @@ class DeviceReport {
|
|||||||
this.publishEntityState = publishEntityState;
|
this.publishEntityState = publishEntityState;
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldReport(ieeeAddr) {
|
|
||||||
const device = settings.getDevice(ieeeAddr);
|
|
||||||
return device && device.report;
|
|
||||||
}
|
|
||||||
|
|
||||||
getEndpoints() {
|
|
||||||
return this.zigbee.getAllClients()
|
|
||||||
.filter((d) => this.shouldReport(d.ieeeAddr))
|
|
||||||
.map((d) => this.zigbee.getEndpoint(d.ieeeAddr))
|
|
||||||
.filter((e) => e);
|
|
||||||
}
|
|
||||||
|
|
||||||
setupReporting(endpoint) {
|
setupReporting(endpoint) {
|
||||||
Object.values(endpoint.clusters).filter((c) => c).forEach((c) => {
|
Object.values(endpoint.clusters).filter((c) => c).forEach((c) => {
|
||||||
const cluster = c.attrs.cid;
|
const cluster = c.attrs.cid;
|
||||||
if (candidates[cluster]) {
|
if (candidates[cluster]) {
|
||||||
const attributes = candidates[cluster].filter((a) => c.attrs.hasOwnProperty(a));
|
const candidate = candidates[cluster];
|
||||||
this.zigbee.bind(endpoint, cluster);
|
const attributeNames = candidate.attrs.filter((a) => c.attrs.hasOwnProperty(a));
|
||||||
|
const attributes = [];
|
||||||
attributes.forEach((attribute) => {
|
attributeNames.forEach((attribute) => {
|
||||||
this.zigbee.report(
|
attributes.push({
|
||||||
endpoint,
|
attr: attribute,
|
||||||
cluster,
|
min: candidate.hasOwnProperty('reportIntervalMin')?
|
||||||
attribute,
|
candidate.reportIntervalMin:reportInterval.min,
|
||||||
reportInterval.min,
|
max: candidate.hasOwnProperty('reportIntervalMax')?
|
||||||
reportInterval.max,
|
candidate.reportIntervalMax:reportInterval.max,
|
||||||
reportableChange);
|
change: candidate.hasOwnProperty('reportableChange')?
|
||||||
|
candidate.reportableChange:reportableChange,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
this.zigbee.report(endpoint, cluster, attributes);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onZigbeeStarted() {
|
onZigbeeStarted() {
|
||||||
const endpoints = this.getEndpoints();
|
this.zigbee.getAllClients().forEach((device) => {
|
||||||
endpoints.forEach((e) => this.setupReporting(e));
|
const mappedDevice = zigbeeShepherdConverters.findByZigbeeModel(device.modelId);
|
||||||
|
|
||||||
|
if (mappedDevice && mappedDevice.report) {
|
||||||
|
const endpoint = (typeof(mappedDevice.report)=='number')?this.zigbee.getEndpoint(device.ieeeAddr, mappedDevice.report):this.zigbee.getEndpoint(device.ieeeAddr);
|
||||||
|
this.setupReporting(endpoint);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onZigbeeMessage(message, device, mappedDevice) {
|
onZigbeeMessage(message, device, mappedDevice) {
|
||||||
@ -65,9 +70,13 @@ class DeviceReport {
|
|||||||
// Ikea TRADFRI tend to forget their reporting after powered off.
|
// Ikea TRADFRI tend to forget their reporting after powered off.
|
||||||
// Re-setup reporting.
|
// Re-setup reporting.
|
||||||
// https://github.com/Koenkk/zigbee2mqtt/issues/966
|
// https://github.com/Koenkk/zigbee2mqtt/issues/966
|
||||||
if (device && message.type === 'endDeviceAnnce' && utils.isIkeaTradfriDevice(device) &&
|
// The mappedDevice.report property is now used as boolean
|
||||||
this.shouldReport(device.ieeeAddr)) {
|
// the property might contain more information about how to
|
||||||
const endpoint = this.zigbee.getEndpoint(device.ieeeAddr);
|
// configure reporting. For instance the endpointIds to use.
|
||||||
|
if (device && mappedDevice
|
||||||
|
&& (message.type=='endDeviceAnnce' || message.type=='devIncoming')
|
||||||
|
&& mappedDevice.report) {
|
||||||
|
const endpoint = (typeof(mappedDevice.report)=='number')?this.zigbee.getEndpoint(device.ieeeAddr, mappedDevice.report):this.zigbee.getEndpoint(device.ieeeAddr);
|
||||||
if (endpoint) {
|
if (endpoint) {
|
||||||
this.setupReporting(endpoint);
|
this.setupReporting(endpoint);
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,8 @@ const logger = require('./util/logger');
|
|||||||
const settings = require('./util/settings');
|
const settings = require('./util/settings');
|
||||||
const data = require('./util/data');
|
const data = require('./util/data');
|
||||||
const utils = require('./util/utils');
|
const utils = require('./util/utils');
|
||||||
const ZigbeeQueue = require('./util/zigbeeQueue');
|
|
||||||
const cieApp = require('./zapp/cie');
|
const cieApp = require('./zapp/cie');
|
||||||
|
const Queue = require('queue');
|
||||||
const zclId = require('zcl-id');
|
const zclId = require('zcl-id');
|
||||||
|
|
||||||
const advancedSettings = settings.get().advanced;
|
const advancedSettings = settings.get().advanced;
|
||||||
@ -32,6 +32,8 @@ const defaultCfg = {
|
|||||||
disDefaultRsp: 0,
|
disDefaultRsp: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const delay = 170;
|
||||||
|
|
||||||
logger.debug(`Using zigbee-shepherd with settings: '${JSON.stringify(shepherdSettings)}'`);
|
logger.debug(`Using zigbee-shepherd with settings: '${JSON.stringify(shepherdSettings)}'`);
|
||||||
|
|
||||||
class Zigbee {
|
class Zigbee {
|
||||||
@ -41,7 +43,8 @@ class Zigbee {
|
|||||||
this.onError = this.onError.bind(this);
|
this.onError = this.onError.bind(this);
|
||||||
this.messageHandler = null;
|
this.messageHandler = null;
|
||||||
|
|
||||||
this.queue = new ZigbeeQueue();
|
this.queue = new Queue();
|
||||||
|
this.queue.concurrency = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
start(messageHandler, callback) {
|
start(messageHandler, callback) {
|
||||||
@ -51,7 +54,7 @@ class Zigbee {
|
|||||||
|
|
||||||
this.shepherd.start((error) => {
|
this.shepherd.start((error) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
logger.info('Error while starting zigbee-shepherd, attempting to fix... (takes 60 seconds)');
|
logger.info('Error while starting zigbee-shepherd, attemping to fix... (takes 60 seconds)');
|
||||||
this.shepherd.controller._znp.close((() => null));
|
this.shepherd.controller._znp.close((() => null));
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@ -117,6 +120,7 @@ class Zigbee {
|
|||||||
|
|
||||||
// Wait some time before we start the queue, many calls skip this queue which hangs the stick
|
// Wait some time before we start the queue, many calls skip this queue which hangs the stick
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
this.queue.autostart = true;
|
||||||
this.queue.start();
|
this.queue.start();
|
||||||
}, 2000);
|
}, 2000);
|
||||||
|
|
||||||
@ -236,7 +240,7 @@ class Zigbee {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.queue.push(entityID, (queueCallback) => {
|
this.queue.push((queueCallback) => {
|
||||||
logger.info(
|
logger.info(
|
||||||
`Zigbee publish to ${entityType} '${entityID}', ${cid} - ${cmd} - ` +
|
`Zigbee publish to ${entityType} '${entityID}', ${cid} - ${cmd} - ` +
|
||||||
`${JSON.stringify(zclData)} - ${JSON.stringify(cfg)} - ${ep}`
|
`${JSON.stringify(zclData)} - ${JSON.stringify(cfg)} - ${ep}`
|
||||||
@ -253,8 +257,6 @@ class Zigbee {
|
|||||||
if (callback) {
|
if (callback) {
|
||||||
callback(error, rsp);
|
callback(error, rsp);
|
||||||
}
|
}
|
||||||
|
|
||||||
queueCallback(error);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (cmdType === 'functional' && entity.functional) {
|
if (cmdType === 'functional' && entity.functional) {
|
||||||
@ -264,6 +266,8 @@ class Zigbee {
|
|||||||
} else {
|
} else {
|
||||||
logger.error(`Unknown zigbee publish cmdType ${cmdType}`);
|
logger.error(`Unknown zigbee publish cmdType ${cmdType}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setTimeout(() => queueCallback(), delay);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -271,7 +275,7 @@ class Zigbee {
|
|||||||
const device = this.shepherd._findDevByAddr(ieeeAddr);
|
const device = this.shepherd._findDevByAddr(ieeeAddr);
|
||||||
|
|
||||||
if (device) {
|
if (device) {
|
||||||
this.queue.push(ieeeAddr, (queueCallback) => {
|
this.queue.push((queueCallback) => {
|
||||||
logger.debug(`Ping ${ieeeAddr}`);
|
logger.debug(`Ping ${ieeeAddr}`);
|
||||||
this.shepherd.controller.checkOnline(device, (error) => {
|
this.shepherd.controller.checkOnline(device, (error) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
@ -283,9 +287,9 @@ class Zigbee {
|
|||||||
if (cb) {
|
if (cb) {
|
||||||
cb(error);
|
cb(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
queueCallback(error);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
setTimeout(() => queueCallback(), delay);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -293,7 +297,7 @@ class Zigbee {
|
|||||||
bind(ep, cluster, target=this.getCoordinator()) {
|
bind(ep, cluster, target=this.getCoordinator()) {
|
||||||
const log = `for ${ep.device.ieeeAddr} - ${cluster}`;
|
const log = `for ${ep.device.ieeeAddr} - ${cluster}`;
|
||||||
|
|
||||||
this.queue.push(ep.device.ieeeAddr, (queueCallback) => {
|
this.queue.push((queueCallback) => {
|
||||||
logger.debug(`Setup binding ${log}`);
|
logger.debug(`Setup binding ${log}`);
|
||||||
ep.bind(cluster, target, (error) => {
|
ep.bind(cluster, target, (error) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
@ -301,29 +305,53 @@ class Zigbee {
|
|||||||
} else {
|
} else {
|
||||||
logger.debug(`Successfully setup binding ${log}`);
|
logger.debug(`Successfully setup binding ${log}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
queueCallback(error);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
setTimeout(() => queueCallback(), delay);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
report(ep, cluster, attribute, min, max, change) {
|
/* setup reporting.
|
||||||
const attrId = zclId.attr(cluster, attribute).value;
|
* attributes is an array of attribute objects.
|
||||||
const dataType = zclId.attrType(cluster, attribute).value;
|
* each attribute object contains the following properties:
|
||||||
const cfg = {direction: 0, attrId, dataType, minRepIntval: min, maxRepIntval: max, repChange: change};
|
* attr the attribute name,
|
||||||
const log = `for ${ep.device.ieeeAddr} - ${cluster} - ${attribute}`;
|
* min the minimal time between reports in seconds,
|
||||||
|
* max the maximum time between reports in seconds,
|
||||||
this.queue.push(ep.device.ieeeAddr, (queueCallback) => {
|
* change the minimum amount of change before sending a report
|
||||||
logger.debug(`Setup reporting ${log}`);
|
*/
|
||||||
ep.foundation(cluster, 'configReport', [cfg], defaultCfg, (error) => {
|
report(ep, cluster, attributes) {
|
||||||
if (error) {
|
const cfgArr = [];
|
||||||
logger.error(`Failed to setup reporting ${log} - (${error})`);
|
for(let i=0; i<attributes.length; i++) {
|
||||||
} else {
|
const attribute = attributes[i];
|
||||||
logger.debug(`Successfully setup reporting ${log}`);
|
const attrId = zclId.attr(cluster, attribute.attr).value;
|
||||||
}
|
const dataType = zclId.attrType(cluster, attribute.attr).value;
|
||||||
|
cfgArr.push({
|
||||||
queueCallback(error);
|
direction: 0,
|
||||||
|
attrId,
|
||||||
|
dataType,
|
||||||
|
minRepIntval: attribute.min,
|
||||||
|
maxRepIntval: attribute.max,
|
||||||
|
repChange: attribute.change,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
const log=`for ${ep.device.ieeeAddr} - ${cluster} - ${attributes.length}`;
|
||||||
|
|
||||||
|
this.queue.push((queueCallback) => {
|
||||||
|
logger.debug(`Setup reporting ${log}`);
|
||||||
|
ep.bind(cluster, this.getCoordinator(), (error) => {
|
||||||
|
if (error) {
|
||||||
|
logger.error(`Failed to bind for reporting ${log} - (${error})`);
|
||||||
|
} else {
|
||||||
|
ep.foundation(cluster, 'configReport', cfgArr, defaultCfg, (error) => {
|
||||||
|
if (error) {
|
||||||
|
logger.error(`Failed to setup reporting ${log} - (${error})`);
|
||||||
|
} else {
|
||||||
|
logger.debug(`Successfully setup reporting ${log}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
setTimeout(() => queueCallback(), delay);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user