iobroker.zigbee2mqtt 3.0.9 → 3.0.13
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 +1 -1
- package/README.md +20 -245
- package/io-package.json +54 -55
- package/lib/check.js +6 -0
- package/lib/colors.js +26 -6
- package/lib/deviceController.js +69 -17
- package/lib/exposes.js +123 -157
- package/lib/imageController.js +52 -3
- package/lib/messages.js +10 -0
- package/lib/mqttServerController.js +21 -5
- package/lib/nonGenericDevicesExtension.js +6 -2
- package/lib/rgb.js +81 -27
- package/lib/statesController.js +62 -11
- package/lib/utils.js +54 -7
- package/lib/websocketController.js +29 -0
- package/lib/z2mController.js +19 -0
- package/main.js +8 -10
- package/package.json +16 -31
package/lib/utils.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Converts a bulb level of range [0...254] to an adapter level of range [0...100]
|
|
3
|
+
*
|
|
4
|
+
* @param bulbLevel
|
|
3
5
|
*/
|
|
4
6
|
function bulbLevelToAdapterLevel(bulbLevel) {
|
|
5
7
|
// Convert from bulb levels [0...254] to adapter levels [0...100]:
|
|
@@ -19,14 +21,16 @@ function bulbLevelToAdapterLevel(bulbLevel) {
|
|
|
19
21
|
if (bulbLevel >= 2) {
|
|
20
22
|
// Perform linear mapping of range [2...254] to [1...100]
|
|
21
23
|
return Math.round(((bulbLevel - 2) * 99) / 252) + 1;
|
|
22
|
-
}
|
|
24
|
+
}
|
|
23
25
|
// The bulb is considered off. Even a bulb level of "1" is considered as off.
|
|
24
26
|
return 0;
|
|
25
|
-
|
|
27
|
+
// else
|
|
26
28
|
}
|
|
27
29
|
|
|
28
30
|
/**
|
|
29
31
|
* Converts an adapter level of range [0...100] to a bulb level of range [0...254]
|
|
32
|
+
*
|
|
33
|
+
* @param adapterLevel
|
|
30
34
|
*/
|
|
31
35
|
function adapterLevelToBulbLevel(adapterLevel) {
|
|
32
36
|
// Convert from adapter levels [0...100] to bulb levels [0...254].
|
|
@@ -35,13 +39,17 @@ function adapterLevelToBulbLevel(adapterLevel) {
|
|
|
35
39
|
if (adapterLevel) {
|
|
36
40
|
// Perform linear mapping of range [1...100] to [2...254]
|
|
37
41
|
return Math.round(((adapterLevel - 1) * 252) / 99) + 2;
|
|
38
|
-
}
|
|
42
|
+
}
|
|
39
43
|
// Switch the bulb off. Some bulbs need "0" (IKEA), others "1" (HUE), and according to the
|
|
40
44
|
// ZigBee docs "1" is the "minimum possible level"... we choose "0" here which seems to work.
|
|
41
45
|
return 0;
|
|
42
|
-
|
|
46
|
+
// else
|
|
43
47
|
}
|
|
44
48
|
|
|
49
|
+
/**
|
|
50
|
+
*
|
|
51
|
+
* @param ba
|
|
52
|
+
*/
|
|
45
53
|
function bytesArrayToWordArray(ba) {
|
|
46
54
|
const wa = [];
|
|
47
55
|
for (let i = 0; i < ba.length; i++) {
|
|
@@ -52,6 +60,10 @@ function bytesArrayToWordArray(ba) {
|
|
|
52
60
|
|
|
53
61
|
// If the value is greater than 1000, kelvin is assumed.
|
|
54
62
|
// If smaller, it is assumed to be mired.
|
|
63
|
+
/**
|
|
64
|
+
*
|
|
65
|
+
* @param t
|
|
66
|
+
*/
|
|
55
67
|
function toMired(t) {
|
|
56
68
|
let miredValue = t;
|
|
57
69
|
if (t > 1000) {
|
|
@@ -60,12 +72,19 @@ function toMired(t) {
|
|
|
60
72
|
return miredValue;
|
|
61
73
|
}
|
|
62
74
|
|
|
75
|
+
/**
|
|
76
|
+
*
|
|
77
|
+
* @param t
|
|
78
|
+
*/
|
|
63
79
|
function miredKelvinConversion(t) {
|
|
64
80
|
return Math.round(1000000 / t);
|
|
65
81
|
}
|
|
66
82
|
|
|
67
83
|
/**
|
|
68
84
|
* Converts a decimal number to a hex string with zero-padding
|
|
85
|
+
*
|
|
86
|
+
* @param decimal
|
|
87
|
+
* @param padding
|
|
69
88
|
*/
|
|
70
89
|
function decimalToHex(decimal, padding) {
|
|
71
90
|
let hex = Number(decimal).toString(16);
|
|
@@ -78,32 +97,60 @@ function decimalToHex(decimal, padding) {
|
|
|
78
97
|
return hex;
|
|
79
98
|
}
|
|
80
99
|
|
|
100
|
+
/**
|
|
101
|
+
*
|
|
102
|
+
* @param adapterDevId
|
|
103
|
+
*/
|
|
81
104
|
function getZbId(adapterDevId) {
|
|
82
105
|
const idx = adapterDevId.indexOf('group');
|
|
83
|
-
if (idx > 0)
|
|
84
|
-
|
|
106
|
+
if (idx > 0) {
|
|
107
|
+
return adapterDevId.substr(idx + 6);
|
|
108
|
+
}
|
|
109
|
+
return `0x${ adapterDevId.split('.')[2]}`;
|
|
85
110
|
}
|
|
86
111
|
|
|
112
|
+
/**
|
|
113
|
+
*
|
|
114
|
+
* @param adapter
|
|
115
|
+
* @param id
|
|
116
|
+
*/
|
|
87
117
|
function getAdId(adapter, id) {
|
|
88
|
-
return adapter.namespace
|
|
118
|
+
return `${adapter.namespace }.${ id.split('.')[2]}`; // iobroker device id
|
|
89
119
|
}
|
|
90
120
|
|
|
121
|
+
/**
|
|
122
|
+
*
|
|
123
|
+
* @param array
|
|
124
|
+
*/
|
|
91
125
|
function clearArray(array) {
|
|
92
126
|
while (array.length > 0) {
|
|
93
127
|
array.pop();
|
|
94
128
|
}
|
|
95
129
|
}
|
|
96
130
|
|
|
131
|
+
/**
|
|
132
|
+
*
|
|
133
|
+
* @param source
|
|
134
|
+
* @param target
|
|
135
|
+
*/
|
|
97
136
|
function moveArray(source, target) {
|
|
98
137
|
while (source.length > 0) {
|
|
99
138
|
target.push(source.shift());
|
|
100
139
|
}
|
|
101
140
|
}
|
|
102
141
|
|
|
142
|
+
/**
|
|
143
|
+
*
|
|
144
|
+
* @param item
|
|
145
|
+
*/
|
|
103
146
|
function isObject(item) {
|
|
104
147
|
return typeof item === 'object' && !Array.isArray(item) && item !== null;
|
|
105
148
|
}
|
|
106
149
|
|
|
150
|
+
/**
|
|
151
|
+
*
|
|
152
|
+
* @param item
|
|
153
|
+
*/
|
|
107
154
|
function isJson(item) {
|
|
108
155
|
let value = typeof item !== 'string' ? JSON.stringify(item) : item;
|
|
109
156
|
try {
|
|
@@ -6,11 +6,21 @@ let ping;
|
|
|
6
6
|
let pingTimeout;
|
|
7
7
|
let autoRestartTimeout;
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
*
|
|
11
|
+
*/
|
|
9
12
|
class WebsocketController {
|
|
13
|
+
/**
|
|
14
|
+
*
|
|
15
|
+
* @param adapter
|
|
16
|
+
*/
|
|
10
17
|
constructor(adapter) {
|
|
11
18
|
this.adapter = adapter;
|
|
12
19
|
}
|
|
13
20
|
|
|
21
|
+
/**
|
|
22
|
+
*
|
|
23
|
+
*/
|
|
14
24
|
initWsClient() {
|
|
15
25
|
try {
|
|
16
26
|
let wsURL = `${this.adapter.config.wsScheme}://${this.adapter.config.wsServerIP}:${this.adapter.config.wsServerPort}/api`;
|
|
@@ -53,6 +63,10 @@ class WebsocketController {
|
|
|
53
63
|
}
|
|
54
64
|
}
|
|
55
65
|
|
|
66
|
+
/**
|
|
67
|
+
*
|
|
68
|
+
* @param message
|
|
69
|
+
*/
|
|
56
70
|
send(message) {
|
|
57
71
|
if (wsClient.readyState !== WebSocket.OPEN) {
|
|
58
72
|
this.adapter.log.warn('Cannot set State, no websocket connection to Zigbee2MQTT!');
|
|
@@ -61,6 +75,9 @@ class WebsocketController {
|
|
|
61
75
|
wsClient.send(message);
|
|
62
76
|
}
|
|
63
77
|
|
|
78
|
+
/**
|
|
79
|
+
*
|
|
80
|
+
*/
|
|
64
81
|
sendPingToServer() {
|
|
65
82
|
//this.logDebug('Send ping to server');
|
|
66
83
|
wsClient.ping();
|
|
@@ -69,6 +86,9 @@ class WebsocketController {
|
|
|
69
86
|
}, wsHeartbeatIntervall);
|
|
70
87
|
}
|
|
71
88
|
|
|
89
|
+
/**
|
|
90
|
+
*
|
|
91
|
+
*/
|
|
72
92
|
wsHeartbeat() {
|
|
73
93
|
clearTimeout(pingTimeout);
|
|
74
94
|
pingTimeout = setTimeout(() => {
|
|
@@ -77,6 +97,9 @@ class WebsocketController {
|
|
|
77
97
|
}, wsHeartbeatIntervall + 3000);
|
|
78
98
|
}
|
|
79
99
|
|
|
100
|
+
/**
|
|
101
|
+
*
|
|
102
|
+
*/
|
|
80
103
|
async autoRestart() {
|
|
81
104
|
this.adapter.log.warn(`Start try again in ${restartTimeout / 1000} seconds...`);
|
|
82
105
|
autoRestartTimeout = setTimeout(() => {
|
|
@@ -84,12 +107,18 @@ class WebsocketController {
|
|
|
84
107
|
}, restartTimeout);
|
|
85
108
|
}
|
|
86
109
|
|
|
110
|
+
/**
|
|
111
|
+
*
|
|
112
|
+
*/
|
|
87
113
|
closeConnection() {
|
|
88
114
|
if (wsClient && wsClient.readyState !== WebSocket.CLOSED) {
|
|
89
115
|
wsClient.close();
|
|
90
116
|
}
|
|
91
117
|
}
|
|
92
118
|
|
|
119
|
+
/**
|
|
120
|
+
*
|
|
121
|
+
*/
|
|
93
122
|
async allTimerClear() {
|
|
94
123
|
clearTimeout(pingTimeout);
|
|
95
124
|
clearTimeout(ping);
|
package/lib/z2mController.js
CHANGED
|
@@ -1,4 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
*/
|
|
1
4
|
class Z2mController {
|
|
5
|
+
/**
|
|
6
|
+
*
|
|
7
|
+
* @param adapter
|
|
8
|
+
* @param deviceCache
|
|
9
|
+
* @param groupCache
|
|
10
|
+
* @param logCustomizations
|
|
11
|
+
*/
|
|
2
12
|
constructor(adapter, deviceCache, groupCache, logCustomizations) {
|
|
3
13
|
this.adapter = adapter;
|
|
4
14
|
this.groupCache = groupCache;
|
|
@@ -6,6 +16,11 @@ class Z2mController {
|
|
|
6
16
|
this.logCustomizations = logCustomizations;
|
|
7
17
|
}
|
|
8
18
|
|
|
19
|
+
/**
|
|
20
|
+
*
|
|
21
|
+
* @param id
|
|
22
|
+
* @param state
|
|
23
|
+
*/
|
|
9
24
|
async createZ2MMessage(id, state) {
|
|
10
25
|
const splitedID = id.split('.');
|
|
11
26
|
if (splitedID.length < 4) {
|
|
@@ -114,6 +129,10 @@ class Z2mController {
|
|
|
114
129
|
return controlObj;
|
|
115
130
|
}
|
|
116
131
|
|
|
132
|
+
/**
|
|
133
|
+
*
|
|
134
|
+
* @param messageObj
|
|
135
|
+
*/
|
|
117
136
|
async proxyZ2MLogs(messageObj) {
|
|
118
137
|
const logMessage = messageObj.payload.message;
|
|
119
138
|
if (this.logCustomizations.logfilter.some((x) => logMessage.includes(x))) {
|
package/main.js
CHANGED
|
@@ -68,8 +68,7 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
const logfilterState = await this.getStateAsync('info.logfilter');
|
|
71
|
-
if (logfilterState && logfilterState.val) {
|
|
72
|
-
// @ts-ignore
|
|
71
|
+
if (logfilterState && logfilterState.val) {
|
|
73
72
|
logCustomizations.logfilter = String(logfilterState.val)
|
|
74
73
|
.split(';')
|
|
75
74
|
.filter((x) => x); // filter removes empty strings here
|
|
@@ -111,9 +110,8 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
111
110
|
`mqtt://${this.config.externalMqttServerIP}:${this.config.externalMqttServerPort}`,
|
|
112
111
|
mqttClientOptions
|
|
113
112
|
);
|
|
114
|
-
}
|
|
113
|
+
} else {
|
|
115
114
|
// Internal MQTT-Server
|
|
116
|
-
else {
|
|
117
115
|
mqttServerController = new MqttServerController(this);
|
|
118
116
|
await mqttServerController.createMQTTServer();
|
|
119
117
|
await this.delay(1500);
|
|
@@ -137,9 +135,8 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
137
135
|
const newMessage = `{"payload":${payload.toString() == '' ? '"null"' : payload.toString()},"topic":"${topic.slice(topic.search('/') + 1)}"}`;
|
|
138
136
|
this.messageParse(newMessage);
|
|
139
137
|
});
|
|
140
|
-
}
|
|
138
|
+
} else if (this.config.connectionType == 'ws') {
|
|
141
139
|
// Websocket
|
|
142
|
-
else if (this.config.connectionType == 'ws') {
|
|
143
140
|
if (this.config.wsServerIP == '') {
|
|
144
141
|
this.log.warn('Please configure the Websoket connection!');
|
|
145
142
|
return;
|
|
@@ -316,9 +313,8 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
316
313
|
} catch (e) {
|
|
317
314
|
this.log.error(e);
|
|
318
315
|
}
|
|
319
|
-
}
|
|
316
|
+
} else if (this.config.connectionType == 'ws') {
|
|
320
317
|
// Websocket
|
|
321
|
-
else if (this.config.connectionType == 'ws') {
|
|
322
318
|
try {
|
|
323
319
|
if (websocketController) {
|
|
324
320
|
websocketController.closeConnection();
|
|
@@ -352,6 +348,8 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
352
348
|
this.log.error(e);
|
|
353
349
|
}
|
|
354
350
|
|
|
351
|
+
this.setState('info.connection', false, true);
|
|
352
|
+
|
|
355
353
|
callback();
|
|
356
354
|
}
|
|
357
355
|
|
|
@@ -382,8 +380,8 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
382
380
|
if (require.main !== module) {
|
|
383
381
|
// Export the constructor in compact mode
|
|
384
382
|
/**
|
|
385
|
-
|
|
386
|
-
|
|
383
|
+
* @param {Partial<core.AdapterOptions>} [options]
|
|
384
|
+
*/
|
|
387
385
|
module.exports = (options) => new Zigbee2mqtt(options);
|
|
388
386
|
} else {
|
|
389
387
|
// otherwise start the instance directly
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iobroker.zigbee2mqtt",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.13",
|
|
4
4
|
"description": "Zigbee2MQTT adapter for ioBroker",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Dennis Rathjen and Arthur Rupp",
|
|
@@ -24,43 +24,28 @@
|
|
|
24
24
|
"node": ">= 20"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@iobroker/adapter-core": "^3.2
|
|
27
|
+
"@iobroker/adapter-core": "^3.3.2",
|
|
28
28
|
"@iobroker/dm-utils": "^1.0.10",
|
|
29
29
|
"aedes": "^0.51.3",
|
|
30
30
|
"aedes-persistence-nedb": "^2.0.3",
|
|
31
|
-
"mqtt": "^5.
|
|
31
|
+
"mqtt": "^5.14.1",
|
|
32
32
|
"net": "^1.0.2",
|
|
33
33
|
"node-schedule": "^2.1.1",
|
|
34
|
-
"sharp": "^0.
|
|
35
|
-
"ws": "^8.18.
|
|
34
|
+
"sharp": "^0.34.5",
|
|
35
|
+
"ws": "^8.18.3"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"@alcalzone/release-script": "^
|
|
39
|
-
"@alcalzone/release-script-plugin-iobroker": "^
|
|
40
|
-
"@alcalzone/release-script-plugin-license": "^
|
|
41
|
-
"@alcalzone/release-script-plugin-manual-review": "^
|
|
42
|
-
"@iobroker/adapter-dev": "^1.
|
|
43
|
-
"@iobroker/testing": "^5.
|
|
44
|
-
"@
|
|
45
|
-
"@
|
|
46
|
-
"@types/
|
|
47
|
-
"@types/
|
|
48
|
-
"
|
|
49
|
-
"@types/node-schedule": "^2.1.7",
|
|
50
|
-
"@types/proxyquire": "^1.3.31",
|
|
51
|
-
"@types/sinon": "^17.0.3",
|
|
52
|
-
"@types/sinon-chai": "^4.0.0",
|
|
53
|
-
"chai": "^5.2.0",
|
|
54
|
-
"chai-as-promised": "^8.0.1",
|
|
55
|
-
"eslint": "^9.25.1",
|
|
56
|
-
"eslint-config-prettier": "^10.1.5",
|
|
57
|
-
"eslint-plugin-prettier": "^5.2.3",
|
|
58
|
-
"mocha": "^11.0.1",
|
|
59
|
-
"prettier": "^3.5.3",
|
|
60
|
-
"proxyquire": "^2.1.3",
|
|
61
|
-
"sinon": "^19.0.2",
|
|
62
|
-
"sinon-chai": "^4.0.0",
|
|
63
|
-
"typescript": "~5.8.2"
|
|
38
|
+
"@alcalzone/release-script": "^5.0.0",
|
|
39
|
+
"@alcalzone/release-script-plugin-iobroker": "^4.0.0",
|
|
40
|
+
"@alcalzone/release-script-plugin-license": "^4.0.0",
|
|
41
|
+
"@alcalzone/release-script-plugin-manual-review": "^4.0.0",
|
|
42
|
+
"@iobroker/adapter-dev": "^1.5.0",
|
|
43
|
+
"@iobroker/testing": "^5.2.2",
|
|
44
|
+
"@iobroker/eslint-config": "^2.1.0",
|
|
45
|
+
"@tsconfig/node14": "^14.1.8",
|
|
46
|
+
"@types/node": "^25.0.3",
|
|
47
|
+
"@types/node-schedule": "^2.1.8",
|
|
48
|
+
"typescript": "~5.9.2"
|
|
64
49
|
},
|
|
65
50
|
"main": "main.js",
|
|
66
51
|
"files": [
|