forked from Koenkk/zigbee-herdsman-converters
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
117 lines (100 loc) · 4.67 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
'use strict';
const devices = require('./devices');
const toZigbee = require('./converters/toZigbee');
const fromZigbee = require('./converters/fromZigbee');
const byZigbeeModel = new Map();
const withFingerprint = [];
function arrayEquals(as, bs) {
if (as.length !== bs.length) return false;
for (const a of as) if (!bs.includes(a)) return false;
return true;
}
for (const device of devices) {
if (device.hasOwnProperty('fingerprint')) {
withFingerprint.push(device);
}
if (device.hasOwnProperty('zigbeeModel')) {
for (const zigbeeModel of device.zigbeeModel) {
byZigbeeModel.set(zigbeeModel.toLowerCase(), device);
}
}
}
function findByZigbeeModel(model) {
if (!model) {
return null;
}
model = model.toLowerCase();
let definition = byZigbeeModel.get(model);
if (!definition) {
definition = byZigbeeModel.get(model.replace(/\0.*$/g, '').trim());
}
return definition;
}
function findByDevice(device) {
let definition = findByZigbeeModel(device.modelID);
if (!definition) {
// Find by fingerprint
loop:
for (const definitionWithFingerprint of withFingerprint) {
for (const fingerprint of definitionWithFingerprint.fingerprint) {
if (fingerprintMatch(fingerprint, device)) {
definition = definitionWithFingerprint;
break loop;
}
}
}
}
return definition;
}
function fingerprintMatch(fingerprint, device) {
let match =
(!fingerprint.applicationVersion || device.applicationVersion === fingerprint.applicationVersion) &&
(!fingerprint.manufacturerID || device.manufacturerID === fingerprint.manufacturerID) &&
(!fingerprint.type || device.type === fingerprint.type) &&
(!fingerprint.dateCode || device.dateCode === fingerprint.dateCode) &&
(!fingerprint.hardwareVersion || device.hardwareVersion === fingerprint.hardwareVersion) &&
(!fingerprint.manufacturerName || device.manufacturerName === fingerprint.manufacturerName) &&
(!fingerprint.modelID || device.modelID === fingerprint.modelID) &&
(!fingerprint.powerSource || device.powerSource === fingerprint.powerSource) &&
(!fingerprint.softwareBuildID || device.softwareBuildID === fingerprint.softwareBuildID) &&
(!fingerprint.stackVersion || device.stackVersion === fingerprint.stackVersion) &&
(!fingerprint.zclVersion || device.zclVersion === fingerprint.zclVersion) &&
(!fingerprint.endpoints ||
arrayEquals(device.endpoints.map((e) => e.ID), fingerprint.endpoints.map((e) => e.ID)));
if (match && fingerprint.endpoints) {
for (const fingerprintEndpoint of fingerprint.endpoints) {
const deviceEndpoint = device.getEndpoint(fingerprintEndpoint.ID);
match = match &&
(!fingerprintEndpoint.deviceID || deviceEndpoint.deviceID === fingerprintEndpoint.deviceID) &&
(!fingerprintEndpoint.profileID || deviceEndpoint.profileID === fingerprintEndpoint.profileID) &&
(!fingerprintEndpoint.inputClusters ||
arrayEquals(deviceEndpoint.inputClusters, fingerprintEndpoint.inputClusters)) &&
(!fingerprintEndpoint.outputClusters ||
arrayEquals(deviceEndpoint.outputClusters, fingerprintEndpoint.outputClusters));
}
}
return match;
}
module.exports = {
devices,
findByZigbeeModel,
findByDevice,
toZigbeeConverters: toZigbee,
fromZigbeeConverters: fromZigbee,
// Can be used to handle events for devices which are not fully paired yet (no modelID).
// Example usecase: https://github.com/Koenkk/zigbee2mqtt/issues/2399#issuecomment-570583325
onEvent: async (type, data, device) => {
// support Legrand security protocol
// when pairing, a powered device will send a read frame to every device on the network
// it expects at least one answer. The payload contains the number of seconds
// since when the device is powered. If the value is too high, it will leave & not pair
// 23 works, 200 doesn't
if (data.meta && data.meta.manufacturerCode === 0x1021 && type === 'message' && data.type === 'read' &&
data.cluster === 'genBasic' && data.data && data.data.includes(61440)) {
const endpoint = device.getEndpoint(1);
const options = {manufacturerCode: 0x1021, disableDefaultResponse: true};
const payload = {0xf00: {value: 23, type: 35}};
await endpoint.readResponse('genBasic', data.meta.zclTransactionSequenceNumber, payload, options);
}
},
};