iobroker.zigbee 1.8.18 → 1.8.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +418 -415
- package/admin/adapter-settings.js +244 -244
- package/admin/admin.js +2981 -2981
- package/admin/i18n/de/translations.json +108 -108
- package/admin/i18n/en/translations.json +108 -108
- package/admin/i18n/es/translations.json +102 -102
- package/admin/i18n/fr/translations.json +108 -108
- package/admin/i18n/it/translations.json +102 -102
- package/admin/i18n/nl/translations.json +108 -108
- package/admin/i18n/pl/translations.json +108 -108
- package/admin/i18n/pt/translations.json +102 -102
- package/admin/i18n/ru/translations.json +108 -108
- package/admin/i18n/uk/translations.json +108 -108
- package/admin/i18n/zh-cn/translations.json +102 -102
- package/admin/img/philips_hue_lom001.png +0 -0
- package/admin/index.html +159 -159
- package/admin/index_m.html +1356 -1356
- package/admin/moment.min.js +1 -1
- package/admin/shuffle.min.js +2 -2
- package/admin/tab_m.html +1009 -1009
- package/admin/vis-network.min.css +1 -1
- package/admin/vis-network.min.js +27 -27
- package/admin/words.js +110 -110
- package/docs/de/basedocu.md +19 -19
- package/docs/de/readme.md +126 -126
- package/docs/en/readme.md +128 -128
- package/docs/flashing_via_arduino_(en).md +110 -110
- package/docs/ru/readme.md +28 -28
- package/docs/tutorial/groups-1.png +0 -0
- package/docs/tutorial/groups-2.png +0 -0
- package/docs/tutorial/tab-dev-1.png +0 -0
- package/io-package.json +14 -5
- package/lib/backup.js +171 -171
- package/lib/binding.js +319 -319
- package/lib/colors.js +465 -465
- package/lib/commands.js +534 -534
- package/lib/developer.js +145 -145
- package/lib/devices.js +3135 -3135
- package/lib/exclude.js +162 -162
- package/lib/exposes.js +913 -913
- package/lib/groups.js +345 -345
- package/lib/json.js +59 -59
- package/lib/networkmap.js +55 -55
- package/lib/ota.js +198 -198
- package/lib/rgb.js +297 -297
- package/lib/seriallist.js +48 -48
- package/lib/states.js +6420 -6420
- package/lib/statescontroller.js +672 -672
- package/lib/tools.js +54 -54
- package/lib/utils.js +163 -163
- package/lib/zbBaseExtension.js +36 -36
- package/lib/zbDelayedAction.js +144 -144
- package/lib/zbDeviceAvailability.js +319 -319
- package/lib/zbDeviceConfigure.js +147 -147
- package/lib/zbDeviceEvent.js +48 -48
- package/lib/zigbeecontroller.js +989 -989
- package/main.js +60 -35
- package/package.json +6 -4
- package/support/docgen.js +93 -93
package/lib/tools.js
CHANGED
|
@@ -1,55 +1,55 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const axios = require('axios');
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Tests whether the given variable is a real object and not an Array
|
|
7
|
-
* @param {any} it The variable to test
|
|
8
|
-
* @returns {it is Record<string, any>}
|
|
9
|
-
*/
|
|
10
|
-
function isObject(it) {
|
|
11
|
-
// This is necessary because:
|
|
12
|
-
// typeof null === 'object'
|
|
13
|
-
// typeof [] === 'object'
|
|
14
|
-
// [] instanceof Object === true
|
|
15
|
-
return Object.prototype.toString.call(it) === '[object Object]';
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Tests whether the given variable is really an Array
|
|
20
|
-
* @param {any} it The variable to test
|
|
21
|
-
* @returns {it is any[]}
|
|
22
|
-
*/
|
|
23
|
-
function isArray(it) {
|
|
24
|
-
if (Array.isArray != null)
|
|
25
|
-
return Array.isArray(it);
|
|
26
|
-
return Object.prototype.toString.call(it) === '[object Array]';
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Translates text using the Google Translate API
|
|
31
|
-
* @param {string} text The text to translate
|
|
32
|
-
* @param {string} targetLang The target languate
|
|
33
|
-
* @returns {Promise<string>}
|
|
34
|
-
*/
|
|
35
|
-
async function translateText(text, targetLang) {
|
|
36
|
-
if (targetLang === 'en')
|
|
37
|
-
return text;
|
|
38
|
-
try {
|
|
39
|
-
const url = `http://translate.googleapis.com/translate_a/single?client=gtx&sl=en&tl=${targetLang}&dt=t&q=${encodeURIComponent(text)}&ie=UTF-8&oe=UTF-8`;
|
|
40
|
-
const response = await axios({url, timeout: 5000});
|
|
41
|
-
if (isArray(response.data)) {
|
|
42
|
-
// we got a valid response
|
|
43
|
-
return response.data[0][0][0];
|
|
44
|
-
}
|
|
45
|
-
throw new Error('Invalid response for translate request');
|
|
46
|
-
} catch (e) {
|
|
47
|
-
throw new Error(`Could not translate to "${targetLang}": ${e}`);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
module.exports = {
|
|
52
|
-
isArray,
|
|
53
|
-
isObject,
|
|
54
|
-
translateText
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const axios = require('axios');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Tests whether the given variable is a real object and not an Array
|
|
7
|
+
* @param {any} it The variable to test
|
|
8
|
+
* @returns {it is Record<string, any>}
|
|
9
|
+
*/
|
|
10
|
+
function isObject(it) {
|
|
11
|
+
// This is necessary because:
|
|
12
|
+
// typeof null === 'object'
|
|
13
|
+
// typeof [] === 'object'
|
|
14
|
+
// [] instanceof Object === true
|
|
15
|
+
return Object.prototype.toString.call(it) === '[object Object]';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Tests whether the given variable is really an Array
|
|
20
|
+
* @param {any} it The variable to test
|
|
21
|
+
* @returns {it is any[]}
|
|
22
|
+
*/
|
|
23
|
+
function isArray(it) {
|
|
24
|
+
if (Array.isArray != null)
|
|
25
|
+
return Array.isArray(it);
|
|
26
|
+
return Object.prototype.toString.call(it) === '[object Array]';
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Translates text using the Google Translate API
|
|
31
|
+
* @param {string} text The text to translate
|
|
32
|
+
* @param {string} targetLang The target languate
|
|
33
|
+
* @returns {Promise<string>}
|
|
34
|
+
*/
|
|
35
|
+
async function translateText(text, targetLang) {
|
|
36
|
+
if (targetLang === 'en')
|
|
37
|
+
return text;
|
|
38
|
+
try {
|
|
39
|
+
const url = `http://translate.googleapis.com/translate_a/single?client=gtx&sl=en&tl=${targetLang}&dt=t&q=${encodeURIComponent(text)}&ie=UTF-8&oe=UTF-8`;
|
|
40
|
+
const response = await axios({url, timeout: 5000});
|
|
41
|
+
if (isArray(response.data)) {
|
|
42
|
+
// we got a valid response
|
|
43
|
+
return response.data[0][0][0];
|
|
44
|
+
}
|
|
45
|
+
throw new Error('Invalid response for translate request');
|
|
46
|
+
} catch (e) {
|
|
47
|
+
throw new Error(`Could not translate to "${targetLang}": ${e}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
module.exports = {
|
|
52
|
+
isArray,
|
|
53
|
+
isObject,
|
|
54
|
+
translateText
|
|
55
55
|
};
|
package/lib/utils.js
CHANGED
|
@@ -1,163 +1,163 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const zigbeeHerdsmanConverters = require('zigbee-herdsman-converters');
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Converts a bulb level of range [0...254] to an adapter level of range [0...100]
|
|
7
|
-
* @param {number} the bulb level of range [0...254]
|
|
8
|
-
* @returns {the calculated adapter level}
|
|
9
|
-
*/
|
|
10
|
-
function bulbLevelToAdapterLevel(bulbLevel) {
|
|
11
|
-
// Convert from bulb levels [0...254] to adapter levels [0...100]:
|
|
12
|
-
// - Bulb level 0 is a forbidden value according to the ZigBee spec "ZigBee Cluster Library
|
|
13
|
-
// (for ZigBee 3.0) User Guide", but some bulbs (HUE) accept this value and interpret this
|
|
14
|
-
// value as "switch the bulb off".
|
|
15
|
-
// - A bulb level of "1" is the "minimum possible level" which should mean "bulb off",
|
|
16
|
-
// but there are bulbs that do not switch off (they need "0", some IKEA bulbs are affected).
|
|
17
|
-
// - No visible difference was seen between bulb level 1 and 2 on HUE LCT012 bulbs.
|
|
18
|
-
//
|
|
19
|
-
// Conclusion:
|
|
20
|
-
// - We map adapter level "0" to the (forbidden) bulb level "0" that seems to switch all
|
|
21
|
-
// known bulbs.
|
|
22
|
-
// - Bulb level "1" is not used, but if received nevertheless, it is converted to
|
|
23
|
-
// adapter level "0" (off).
|
|
24
|
-
// - Bulb level range [2...254] is linearly mapped to adapter level range [1...100].
|
|
25
|
-
if (bulbLevel >= 2) {
|
|
26
|
-
// Perform linear mapping of range [2...254] to [1...100]
|
|
27
|
-
return Math.round((bulbLevel - 2) * 99 / 252) + 1;
|
|
28
|
-
} else {
|
|
29
|
-
// The bulb is considered off. Even a bulb level of "1" is considered as off.
|
|
30
|
-
return 0;
|
|
31
|
-
} // else
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Converts an adapter level of range [0...100] to a bulb level of range [0...254]
|
|
36
|
-
* @param {number} the adapter level of range [0...100]
|
|
37
|
-
* @returns {the calculated bulb level}
|
|
38
|
-
*/
|
|
39
|
-
function adapterLevelToBulbLevel(adapterLevel) {
|
|
40
|
-
// Convert from adapter levels [0...100] to bulb levels [0...254].
|
|
41
|
-
// This is the inverse of function bulbLevelToAdapterLevel().
|
|
42
|
-
// Please read the comments there regarding the rules applied here for mapping the values.
|
|
43
|
-
if (adapterLevel) {
|
|
44
|
-
// Perform linear mapping of range [1...100] to [2...254]
|
|
45
|
-
return Math.round((adapterLevel - 1) * 252 / 99) + 2;
|
|
46
|
-
} else {
|
|
47
|
-
// Switch the bulb off. Some bulbs need "0" (IKEA), others "1" (HUE), and according to the
|
|
48
|
-
// ZigBee docs "1" is the "minimum possible level"... we choose "0" here which seems to work.
|
|
49
|
-
return 0;
|
|
50
|
-
} // else
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function bytesArrayToWordArray(ba) {
|
|
54
|
-
const wa = [];
|
|
55
|
-
for (let i = 0; i < ba.length; i++) {
|
|
56
|
-
wa[(i / 2) | 0] |= ba[i] << (8 * (i % 2));
|
|
57
|
-
}
|
|
58
|
-
return wa;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// If the value is greater than 1000, kelvin is assumed.
|
|
62
|
-
// If smaller, it is assumed to be mired.
|
|
63
|
-
function toMired(t) {
|
|
64
|
-
let miredValue = t;
|
|
65
|
-
if (t > 1000) {
|
|
66
|
-
miredValue = miredKelvinConversion(t);
|
|
67
|
-
}
|
|
68
|
-
return miredValue;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function miredKelvinConversion(t) {
|
|
72
|
-
return (1000000 / t).toFixed();
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Converts a decimal number to a hex string with zero-padding
|
|
77
|
-
* @param {number} decimal The number to convert
|
|
78
|
-
* @param {number} padding The desired length of the hex string, padded with zeros
|
|
79
|
-
* @returns {it is string}
|
|
80
|
-
*/
|
|
81
|
-
function decimalToHex(decimal, padding) {
|
|
82
|
-
let hex = Number(decimal).toString(16);
|
|
83
|
-
padding = typeof padding === 'undefined' || padding === null ? 2 : padding;
|
|
84
|
-
|
|
85
|
-
while (hex.length < padding) {
|
|
86
|
-
hex = '0' + hex;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
return hex;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function getZbId(adapterDevId) {
|
|
93
|
-
const idx = adapterDevId.indexOf('group');
|
|
94
|
-
if (idx > 0) {
|
|
95
|
-
return adapterDevId.substr(idx + 6);
|
|
96
|
-
}
|
|
97
|
-
return `0x${adapterDevId.split('.')[2]}`;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
function getAdId(adapter, id) {
|
|
101
|
-
return `${adapter.namespace}.${id.split('.')[2]}`; // iobroker device id
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
function flatten(arr) {
|
|
105
|
-
return arr.reduce((flat, toFlatten) =>
|
|
106
|
-
flat.concat(Array.isArray(toFlatten) ? flatten(toFlatten) : toFlatten), []);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const forceEndDevice = flatten(
|
|
110
|
-
['QBKG03LM', 'QBKG04LM', 'ZNMS13LM', 'ZNMS12LM']
|
|
111
|
-
.map(model => zigbeeHerdsmanConverters.devices.find((d) => d.model === model))
|
|
112
|
-
.map(mappedModel => mappedModel.zigbeeModel));
|
|
113
|
-
|
|
114
|
-
// Xiaomi uses 4151 and 4447 (lumi.plug) as manufacturer ID.
|
|
115
|
-
const xiaomiManufacturerID = [4151, 4447];
|
|
116
|
-
const ikeaTradfriManufacturerID = [4476];
|
|
117
|
-
|
|
118
|
-
function sanitizeImageParameter(parameter) {
|
|
119
|
-
const replaceByDash = [/\?/g, /&/g, /[^a-z\d\-_./:]/gi, /[/]/gi];
|
|
120
|
-
let sanitized = parameter;
|
|
121
|
-
replaceByDash.forEach(r => sanitized = sanitized.replace(r, '-'));
|
|
122
|
-
return sanitized;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
function getDeviceIcon(definition) {
|
|
126
|
-
let icon = definition.icon;
|
|
127
|
-
if (icon) {
|
|
128
|
-
icon = icon.replace('${model}', sanitizeImageParameter(definition.model));
|
|
129
|
-
}
|
|
130
|
-
if (!icon) {
|
|
131
|
-
icon = `https://www.zigbee2mqtt.io/images/devices/${sanitizeImageParameter(definition.model)}.jpg`;
|
|
132
|
-
}
|
|
133
|
-
return icon;
|
|
134
|
-
}
|
|
135
|
-
function getModelRegEx( model) {
|
|
136
|
-
const stripModel = (model) ? model.replace(/\0.*$/g, '').trim() : '';
|
|
137
|
-
return stripModel;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
function getEntityInfo(entity) {
|
|
141
|
-
if (entity) return `Type: ${entity.type} Name: ${entity.name}`
|
|
142
|
-
return `getEntityInfo: Illegal Entity ${JSON.stringify(entity)}`
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
exports.secondsToMilliseconds = seconds => seconds * 1000;
|
|
146
|
-
exports.bulbLevelToAdapterLevel = bulbLevelToAdapterLevel;
|
|
147
|
-
exports.adapterLevelToBulbLevel = adapterLevelToBulbLevel;
|
|
148
|
-
exports.bytesArrayToWordArray = bytesArrayToWordArray;
|
|
149
|
-
exports.toMired = toMired;
|
|
150
|
-
exports.miredKelvinConversion = miredKelvinConversion;
|
|
151
|
-
exports.decimalToHex = decimalToHex;
|
|
152
|
-
exports.getZbId = getZbId;
|
|
153
|
-
exports.getAdId = getAdId;
|
|
154
|
-
exports.getModelRegEx = getModelRegEx;
|
|
155
|
-
exports.isRouter = device => device.type === 'Router' && !forceEndDevice.includes(device.modelID);
|
|
156
|
-
exports.isBatteryPowered = device => device.powerSource && device.powerSource === 'Battery';
|
|
157
|
-
exports.isXiaomiDevice = device =>
|
|
158
|
-
device.modelID !== 'lumi.router' &&
|
|
159
|
-
xiaomiManufacturerID.includes(device.manufacturerID) &&
|
|
160
|
-
(!device.manufacturerName || !device.manufacturerName.startsWith('Trust'));
|
|
161
|
-
exports.isIkeaTradfriDevice = device => ikeaTradfriManufacturerID.includes(device.manufacturerID);
|
|
162
|
-
exports.getDeviceIcon = getDeviceIcon;
|
|
163
|
-
exports.getEntityInfo = getEntityInfo;
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const zigbeeHerdsmanConverters = require('zigbee-herdsman-converters');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Converts a bulb level of range [0...254] to an adapter level of range [0...100]
|
|
7
|
+
* @param {number} the bulb level of range [0...254]
|
|
8
|
+
* @returns {the calculated adapter level}
|
|
9
|
+
*/
|
|
10
|
+
function bulbLevelToAdapterLevel(bulbLevel) {
|
|
11
|
+
// Convert from bulb levels [0...254] to adapter levels [0...100]:
|
|
12
|
+
// - Bulb level 0 is a forbidden value according to the ZigBee spec "ZigBee Cluster Library
|
|
13
|
+
// (for ZigBee 3.0) User Guide", but some bulbs (HUE) accept this value and interpret this
|
|
14
|
+
// value as "switch the bulb off".
|
|
15
|
+
// - A bulb level of "1" is the "minimum possible level" which should mean "bulb off",
|
|
16
|
+
// but there are bulbs that do not switch off (they need "0", some IKEA bulbs are affected).
|
|
17
|
+
// - No visible difference was seen between bulb level 1 and 2 on HUE LCT012 bulbs.
|
|
18
|
+
//
|
|
19
|
+
// Conclusion:
|
|
20
|
+
// - We map adapter level "0" to the (forbidden) bulb level "0" that seems to switch all
|
|
21
|
+
// known bulbs.
|
|
22
|
+
// - Bulb level "1" is not used, but if received nevertheless, it is converted to
|
|
23
|
+
// adapter level "0" (off).
|
|
24
|
+
// - Bulb level range [2...254] is linearly mapped to adapter level range [1...100].
|
|
25
|
+
if (bulbLevel >= 2) {
|
|
26
|
+
// Perform linear mapping of range [2...254] to [1...100]
|
|
27
|
+
return Math.round((bulbLevel - 2) * 99 / 252) + 1;
|
|
28
|
+
} else {
|
|
29
|
+
// The bulb is considered off. Even a bulb level of "1" is considered as off.
|
|
30
|
+
return 0;
|
|
31
|
+
} // else
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Converts an adapter level of range [0...100] to a bulb level of range [0...254]
|
|
36
|
+
* @param {number} the adapter level of range [0...100]
|
|
37
|
+
* @returns {the calculated bulb level}
|
|
38
|
+
*/
|
|
39
|
+
function adapterLevelToBulbLevel(adapterLevel) {
|
|
40
|
+
// Convert from adapter levels [0...100] to bulb levels [0...254].
|
|
41
|
+
// This is the inverse of function bulbLevelToAdapterLevel().
|
|
42
|
+
// Please read the comments there regarding the rules applied here for mapping the values.
|
|
43
|
+
if (adapterLevel) {
|
|
44
|
+
// Perform linear mapping of range [1...100] to [2...254]
|
|
45
|
+
return Math.round((adapterLevel - 1) * 252 / 99) + 2;
|
|
46
|
+
} else {
|
|
47
|
+
// Switch the bulb off. Some bulbs need "0" (IKEA), others "1" (HUE), and according to the
|
|
48
|
+
// ZigBee docs "1" is the "minimum possible level"... we choose "0" here which seems to work.
|
|
49
|
+
return 0;
|
|
50
|
+
} // else
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function bytesArrayToWordArray(ba) {
|
|
54
|
+
const wa = [];
|
|
55
|
+
for (let i = 0; i < ba.length; i++) {
|
|
56
|
+
wa[(i / 2) | 0] |= ba[i] << (8 * (i % 2));
|
|
57
|
+
}
|
|
58
|
+
return wa;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// If the value is greater than 1000, kelvin is assumed.
|
|
62
|
+
// If smaller, it is assumed to be mired.
|
|
63
|
+
function toMired(t) {
|
|
64
|
+
let miredValue = t;
|
|
65
|
+
if (t > 1000) {
|
|
66
|
+
miredValue = miredKelvinConversion(t);
|
|
67
|
+
}
|
|
68
|
+
return miredValue;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function miredKelvinConversion(t) {
|
|
72
|
+
return (1000000 / t).toFixed();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Converts a decimal number to a hex string with zero-padding
|
|
77
|
+
* @param {number} decimal The number to convert
|
|
78
|
+
* @param {number} padding The desired length of the hex string, padded with zeros
|
|
79
|
+
* @returns {it is string}
|
|
80
|
+
*/
|
|
81
|
+
function decimalToHex(decimal, padding) {
|
|
82
|
+
let hex = Number(decimal).toString(16);
|
|
83
|
+
padding = typeof padding === 'undefined' || padding === null ? 2 : padding;
|
|
84
|
+
|
|
85
|
+
while (hex.length < padding) {
|
|
86
|
+
hex = '0' + hex;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return hex;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function getZbId(adapterDevId) {
|
|
93
|
+
const idx = adapterDevId.indexOf('group');
|
|
94
|
+
if (idx > 0) {
|
|
95
|
+
return adapterDevId.substr(idx + 6);
|
|
96
|
+
}
|
|
97
|
+
return `0x${adapterDevId.split('.')[2]}`;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function getAdId(adapter, id) {
|
|
101
|
+
return `${adapter.namespace}.${id.split('.')[2]}`; // iobroker device id
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function flatten(arr) {
|
|
105
|
+
return arr.reduce((flat, toFlatten) =>
|
|
106
|
+
flat.concat(Array.isArray(toFlatten) ? flatten(toFlatten) : toFlatten), []);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const forceEndDevice = flatten(
|
|
110
|
+
['QBKG03LM', 'QBKG04LM', 'ZNMS13LM', 'ZNMS12LM']
|
|
111
|
+
.map(model => zigbeeHerdsmanConverters.devices.find((d) => d.model === model))
|
|
112
|
+
.map(mappedModel => mappedModel.zigbeeModel));
|
|
113
|
+
|
|
114
|
+
// Xiaomi uses 4151 and 4447 (lumi.plug) as manufacturer ID.
|
|
115
|
+
const xiaomiManufacturerID = [4151, 4447];
|
|
116
|
+
const ikeaTradfriManufacturerID = [4476];
|
|
117
|
+
|
|
118
|
+
function sanitizeImageParameter(parameter) {
|
|
119
|
+
const replaceByDash = [/\?/g, /&/g, /[^a-z\d\-_./:]/gi, /[/]/gi];
|
|
120
|
+
let sanitized = parameter;
|
|
121
|
+
replaceByDash.forEach(r => sanitized = sanitized.replace(r, '-'));
|
|
122
|
+
return sanitized;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function getDeviceIcon(definition) {
|
|
126
|
+
let icon = definition.icon;
|
|
127
|
+
if (icon) {
|
|
128
|
+
icon = icon.replace('${model}', sanitizeImageParameter(definition.model));
|
|
129
|
+
}
|
|
130
|
+
if (!icon) {
|
|
131
|
+
icon = `https://www.zigbee2mqtt.io/images/devices/${sanitizeImageParameter(definition.model)}.jpg`;
|
|
132
|
+
}
|
|
133
|
+
return icon;
|
|
134
|
+
}
|
|
135
|
+
function getModelRegEx( model) {
|
|
136
|
+
const stripModel = (model) ? model.replace(/\0.*$/g, '').trim() : '';
|
|
137
|
+
return stripModel;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function getEntityInfo(entity) {
|
|
141
|
+
if (entity) return `Type: ${entity.type} Name: ${entity.name}`
|
|
142
|
+
return `getEntityInfo: Illegal Entity ${JSON.stringify(entity)}`
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
exports.secondsToMilliseconds = seconds => seconds * 1000;
|
|
146
|
+
exports.bulbLevelToAdapterLevel = bulbLevelToAdapterLevel;
|
|
147
|
+
exports.adapterLevelToBulbLevel = adapterLevelToBulbLevel;
|
|
148
|
+
exports.bytesArrayToWordArray = bytesArrayToWordArray;
|
|
149
|
+
exports.toMired = toMired;
|
|
150
|
+
exports.miredKelvinConversion = miredKelvinConversion;
|
|
151
|
+
exports.decimalToHex = decimalToHex;
|
|
152
|
+
exports.getZbId = getZbId;
|
|
153
|
+
exports.getAdId = getAdId;
|
|
154
|
+
exports.getModelRegEx = getModelRegEx;
|
|
155
|
+
exports.isRouter = device => device.type === 'Router' && !forceEndDevice.includes(device.modelID);
|
|
156
|
+
exports.isBatteryPowered = device => device.powerSource && device.powerSource === 'Battery';
|
|
157
|
+
exports.isXiaomiDevice = device =>
|
|
158
|
+
device.modelID !== 'lumi.router' &&
|
|
159
|
+
xiaomiManufacturerID.includes(device.manufacturerID) &&
|
|
160
|
+
(!device.manufacturerName || !device.manufacturerName.startsWith('Trust'));
|
|
161
|
+
exports.isIkeaTradfriDevice = device => ikeaTradfriManufacturerID.includes(device.manufacturerID);
|
|
162
|
+
exports.getDeviceIcon = getDeviceIcon;
|
|
163
|
+
exports.getEntityInfo = getEntityInfo;
|
package/lib/zbBaseExtension.js
CHANGED
|
@@ -1,36 +1,36 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/*eslint no-unused-vars: ['off']*/
|
|
4
|
-
|
|
5
|
-
class BaseExtension {
|
|
6
|
-
constructor(zigbee, options) {
|
|
7
|
-
this.zigbee = zigbee;
|
|
8
|
-
this.name = 'BaseExtension';
|
|
9
|
-
this.elevate_debug = false;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
info(message, data) {
|
|
13
|
-
this.zigbee.info(message, data);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
error(message, data) {
|
|
17
|
-
this.zigbee.error(`${this.name}:${message}`, data);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
warn(message, data) {
|
|
21
|
-
this.zigbee.warn(`${this.name}:${message}`, data);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
debug(message, data) {
|
|
25
|
-
if (this.elevate_debug)
|
|
26
|
-
this.zigbee.warn(`DE ${this.name}:${message}`, data);
|
|
27
|
-
else
|
|
28
|
-
this.zigbee.debug(`${this.name}:${message}`, data);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
sendError(error, message) {
|
|
32
|
-
this.zigbee.sendError(error, message);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
module.exports = BaseExtension;
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/*eslint no-unused-vars: ['off']*/
|
|
4
|
+
|
|
5
|
+
class BaseExtension {
|
|
6
|
+
constructor(zigbee, options) {
|
|
7
|
+
this.zigbee = zigbee;
|
|
8
|
+
this.name = 'BaseExtension';
|
|
9
|
+
this.elevate_debug = false;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
info(message, data) {
|
|
13
|
+
this.zigbee.info(message, data);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
error(message, data) {
|
|
17
|
+
this.zigbee.error(`${this.name}:${message}`, data);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
warn(message, data) {
|
|
21
|
+
this.zigbee.warn(`${this.name}:${message}`, data);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
debug(message, data) {
|
|
25
|
+
if (this.elevate_debug)
|
|
26
|
+
this.zigbee.warn(`DE ${this.name}:${message}`, data);
|
|
27
|
+
else
|
|
28
|
+
this.zigbee.debug(`${this.name}:${message}`, data);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
sendError(error, message) {
|
|
32
|
+
this.zigbee.sendError(error, message);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
module.exports = BaseExtension;
|