@evilbunny/node-red-contrib-zigbee2mqtt 3.2.6 → 3.2.8

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/README.md CHANGED
@@ -5,15 +5,15 @@ I've made other improvements, such as more state options to support ZigBee devic
5
5
 
6
6
  # @evilbunny/node-red-contrib-zigbee2mqtt
7
7
  [![platform](https://img.shields.io/badge/platform-Node--RED-red?logo=nodered)](https://nodered.org)
8
- [![Min Node Version](https://img.shields.io/node/v/node-red-contrib-zigbee2mqtt.svg)](https://nodejs.org/en/)
8
+ [![Min Node Version](https://img.shields.io/node/v/@evilbunny/node-red-contrib-zigbee2mqtt.svg)](https://nodejs.org/en/)
9
9
  [![GitHub version](https://img.shields.io/github/package-json/v/evilbunny2008/node-red-contrib-zigbee2mqtt?logo=npm)](https://www.npmjs.com/package/node-red-contrib-zigbee2mqtt)
10
10
  [![GitHub stars](https://img.shields.io/github/stars/evilbunny2008/node-red-contrib-zigbee2mqtt)](https://github.com/evilbunny2008/node-red-contrib-zigbee2mqtt/stargazers)
11
- [![Package Quality](https://packagequality.com/shield/node-red-contrib-zigbee2mqtt.svg)](https://packagequality.com/#?package=node-red-contrib-zigbee2mqtt)
11
+ [![Package Quality](https://packagequality.com/shield/@evilbunny/node-red-contrib-zigbee2mqtt.svg)](https://packagequality.com/#?package=node-red-contrib-zigbee2mqtt)
12
12
 
13
13
  [![issues](https://img.shields.io/github/issues/evilbunny2008/node-red-contrib-zigbee2mqtt?logo=github)](https://github.com/evilbunny2008/node-red-contrib-zigbee2mqtt/issues)
14
14
  ![GitHub last commit](https://img.shields.io/github/last-commit/evilbunny2008/node-red-contrib-zigbee2mqtt)
15
- ![NPM Total Downloads](https://img.shields.io/npm/dt/node-red-contrib-zigbee2mqtt.svg)
16
- ![NPM Downloads per month](https://img.shields.io/npm/dm/node-red-contrib-zigbee2mqtt)
15
+ ![NPM Total Downloads](https://img.shields.io/npm/dt/@evilbunny/node-red-contrib-zigbee2mqtt.svg)
16
+ ![NPM Downloads per month](https://img.shields.io/npm/dm/@evilbunny/node-red-contrib-zigbee2mqtt)
17
17
  ![Repo size](https://img.shields.io/github/repo-size/evilbunny2008/node-red-contrib-zigbee2mqtt)
18
18
 
19
19
  Node-Red Nodes for Zigbee2mqtt connectivity.
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  },
6
6
  "name": "@evilbunny/node-red-contrib-zigbee2mqtt",
7
7
  "description": "Fork of Zigbee2mqtt connectivity nodes for node-red",
8
- "version": "3.2.6",
8
+ "version": "3.2.8",
9
9
  "publishConfig": {
10
10
  "access": "public"
11
11
  },
@@ -0,0 +1,416 @@
1
+ class Zigbee2MqttEditor {
2
+ constructor(node, config = {}) {
3
+ this.node = node;
4
+ this.devices = null;
5
+ this.config = Object.assign( {
6
+ allow_empty:false
7
+ }, config);
8
+ this.device_id = node.device_id||null;
9
+ this.property = node.state||null;
10
+ this.optionsValue = node.optionsValue||null;
11
+ this.optionsType = node.optionsType||null;
12
+ this.refresh = false;
13
+
14
+ return this;
15
+ }
16
+
17
+ bind() {
18
+ let that = this;
19
+ that.getRefreshBtn().off('click').on('click', () => {
20
+ that.refresh = true;
21
+ that.build();
22
+ });
23
+ that.getServerInput().off('change').on('change', (e) => {
24
+ // console.log('bind: getServerInput');
25
+ that.property = null;
26
+ that.refresh = true;
27
+ that.build();
28
+ });
29
+ that.getDeviceIdInput().off('change').on('change', () => {
30
+ that.device_id = that.getDeviceIdInput().multipleSelect('getSelects', 'value');
31
+ if (!that.isMultiple()) { //no need to build for multiple
32
+ // console.log('bind: getDeviceIdInput');
33
+ that.build();
34
+ } else {
35
+ that.setFriendlyName();
36
+ }
37
+ });
38
+ if (that.getDeviceOptionsInput()) {
39
+ that.getDeviceOptionsInput().off('change').on('change', (event, type, value) => {
40
+ // console.log('bind: getDeviceOptionsInput');
41
+ that.optionsValue = value;
42
+ that.optionsType = type;
43
+ that.buildDeviceOptionsHelpBlock();
44
+ });
45
+ }
46
+ that.getEnableMultipleCheckbox().off('change').on('change', (e) => {
47
+ // that.property = null;
48
+ // that.refresh = true;
49
+ // console.log('bind: getEnableMultipleCheckbox');
50
+ that.build();
51
+ });
52
+ }
53
+
54
+ async build() {
55
+ let that = this;
56
+ // console.log('build : '+(this.refresh?'true':false));
57
+ await that.buildDeviceIdInput().then(()=>{
58
+ that.buildDevicePropertyInput();
59
+ that.buildDeviceOptionsInput();
60
+ });
61
+ that.bind();
62
+ }
63
+
64
+ async buildDeviceIdInput() {
65
+ let that = this;
66
+ that.getFilterChanges().closest('.form-row').toggle(!that.isMultiple());
67
+ // console.log('BUILD buildDeviceIdInput');
68
+
69
+ let params = {
70
+ single: !that.isMultiple(),
71
+ minimumCountSelected: !that.isMultiple()?1:0,
72
+ numberDisplayed: 1,
73
+ maxHeight: 300,
74
+ dropWidth: 320,
75
+ width: 320,
76
+ filter: true,
77
+ formatAllSelected:function(){return RED._("@evilbunny/node-red-contrib-zigbee2mqtt/server:editor.select_device")}
78
+ };
79
+ if (that.config.allow_empty && !that.isMultiple()) {
80
+ params.formatAllSelected = function(){return RED._("@evilbunny/node-red-contrib-zigbee2mqtt/server:editor.msg_topic")};
81
+ }
82
+
83
+ that.getDeviceIdInput().children().remove();
84
+ that.getDeviceIdInput().multipleSelect('destroy').multipleSelect(params).multipleSelect('disable');
85
+
86
+ let data = await that.getDevices();
87
+
88
+ if (that.config.allow_empty && !that.isMultiple()) {
89
+ that.getDeviceIdInput().html('<option value="msg.topic">msg.topic</option>');
90
+ }
91
+
92
+ let html = '';
93
+
94
+ //groups
95
+ let groups = data[1];
96
+ groups.sort(function(a, b) {
97
+ return (a.friendly_name || '').localeCompare(b.friendly_name || '', undefined, {sensitivity: 'base'});
98
+ });
99
+ if (groups.length) {
100
+ html = $('<optgroup/>', {label: RED._("@evilbunny/node-red-contrib-zigbee2mqtt/server:editor.groups")});
101
+ html.appendTo(that.getDeviceIdInput());
102
+ $.each(groups, function(index, value) {
103
+ let text = '';
104
+ if ("devices" in value && typeof (value.devices) != 'undefined' && value.devices.length > 0) {
105
+ text = ' (' + value.devices.length + ')';
106
+ }
107
+ $('<option value="' + value.id + '" data-friendly_name="' + value.friendly_name + '">' + value.friendly_name + text + '</option>')
108
+ .appendTo(html);
109
+ });
110
+ }
111
+
112
+ //devices
113
+ let devices = data[0];
114
+ devices.sort(function(a, b) {
115
+ return (a.friendly_name || '').localeCompare(b.friendly_name || '', undefined, {sensitivity: 'base'});
116
+ });
117
+ if (devices.length) {
118
+ html = $('<optgroup/>', {label: RED._("@evilbunny/node-red-contrib-zigbee2mqtt/server:editor.devices")});
119
+ html.appendTo(that.getDeviceIdInput());
120
+ $.each(devices, function(index, value) {
121
+ var model = '';
122
+ if ("definition" in value && value.definition && "model" in value.definition && typeof (value.definition.model) !== undefined) {
123
+ model = ' (' + value.definition.model + ')';
124
+ }
125
+ $('<option value="' + value.ieee_address + '" data-friendly_name="' + value.friendly_name + '">' + value.friendly_name + model + '</option>')
126
+ .appendTo(html);
127
+ });
128
+ }
129
+
130
+ that.getDeviceIdInput().multipleSelect('enable');
131
+ that.getDeviceIdInput().multipleSelect('refresh');
132
+
133
+ that.setDeviceValue();
134
+ that.setFriendlyName();
135
+ return this;
136
+ }
137
+
138
+ async buildDevicePropertyInput() {
139
+ let that = this;
140
+ if (!that.getDevicePropertyInput()) return;
141
+ that.getDevicePropertyInput().closest('.form-row').toggle(!that.isMultiple());
142
+ if (that.isMultiple()) return;
143
+
144
+ // console.log('BUILD buildDevicePropertyInput');
145
+
146
+ that.getDevicePropertyInput().children().remove();
147
+ that.getDevicePropertyInput().multipleSelect('destroy').multipleSelect({
148
+ numberDisplayed: 1,
149
+ dropWidth: 320,
150
+ width: 320,
151
+ single: !(typeof $(this).attr('multiple') !== typeof undefined && $(this).attr('multiple') !== false)
152
+ }).multipleSelect('disable');
153
+
154
+ that.getDevicePropertyInput().html('<option value="0">'+ RED._("@evilbunny/node-red-contrib-zigbee2mqtt/server:editor.complete_payload")+'</option>');
155
+
156
+ let html = '';
157
+ let device = that.getDevice();
158
+
159
+ if (device && 'definition' in device && device.definition && 'exposes' in device.definition) {
160
+ html = $('<optgroup/>', {label: RED._("@evilbunny/node-red-contrib-zigbee2mqtt/server:editor.zigbee2mqtt")});
161
+ html.appendTo(that.getDevicePropertyInput());
162
+
163
+ $.each(device.definition.exposes, function(index, value) {
164
+ if ('features' in value) {
165
+ $.each(value.features, function(index2, value2) {
166
+ if ('property' in value2) {
167
+ let endpoint = value.endpoint || value2.endpoint;
168
+ let label = value2.property + (value2.unit ? ', ' + value2.unit : '');
169
+ $('<option value="' + value2.property + '">' + label + '</option>')
170
+ .appendTo(html);
171
+ }
172
+ });
173
+ } else if ('property' in value) {
174
+ let label = value.property + (value.unit ? ', ' + value.unit : '');
175
+ $('<option value="' + value.property + '">' + label + '</option>')
176
+ .appendTo(html);
177
+ }
178
+ });
179
+ }
180
+
181
+ if (device && 'homekit' in device && device.homekit && Object.keys(device.homekit).length) {
182
+ html = $('<optgroup/>', {label: RED._("@evilbunny/node-red-contrib-zigbee2mqtt/server:editor.homekit")});
183
+ html.appendTo(that.getDevicePropertyInput());
184
+
185
+ $.each(device.homekit, function (index, value) {
186
+ $('<option value="homekit_' + index + '">' + index + '</option>').appendTo(html);
187
+ });
188
+ }
189
+ that.getDevicePropertyInput().multipleSelect('enable');
190
+ if (that.getDevicePropertyInput().find('option[value='+that.property+']').length) {
191
+ that.getDevicePropertyInput().val(that.property);
192
+ } else {
193
+ that.getDevicePropertyInput().val(that.getDevicePropertyInput().find('option').eq(0).attr('value'));
194
+ }
195
+ that.getDevicePropertyInput().multipleSelect('refresh');
196
+ }
197
+
198
+ buildLabel(name, property, unit) {
199
+ let label = name + (unit ? ', ' + unit : '');
200
+ if (property !== name) {
201
+ // e.g. property "state_l1" vs name "state" -> suffix "l1"
202
+ // or property "inching_control_1" vs name "inching_control" -> suffix "1"
203
+ let suffix = property.slice(name.length).replace(/^_/, '');
204
+ if (suffix) {
205
+ label += ' (' + suffix + ')';
206
+ }
207
+ }
208
+ return label;
209
+ }
210
+
211
+ buildDeviceOptionsInput() {
212
+ let that = this;
213
+ if (!that.getDeviceOptionsInput()) return;
214
+ that.getDeviceOptionsTypeHelpBlock().hide().find('div').text('').closest('.form-tips').find('span').text('');
215
+ that.getDeviceOptionsInput().closest('.form-row').toggle(!that.isMultiple());
216
+ if (that.isMultiple()) return;
217
+
218
+ // console.log('BUILD buildDeviceOptionsInput');
219
+ let device = that.getDevice();
220
+ let options = [];
221
+ options.push({'value': 'nothing', 'label': RED._("@evilbunny/node-red-contrib-zigbee2mqtt/server:editor.nothing"), options:['']});
222
+ options.push('msg');
223
+ options.push('json');
224
+ if (device && 'definition' in device && device.definition && 'options' in device.definition) {
225
+ $.each(device.definition.options, function(k, v) {
226
+ options.push({'value': v.property, 'label': v.name});
227
+ });
228
+ }
229
+ that.getDeviceOptionsInput().typedInput({
230
+ default: 'nothing',
231
+ value: that.optionsType,
232
+ typeField: that.getDeviceOptionsTypeInput(),
233
+ });
234
+ that.getDeviceOptionsInput().typedInput('types', options);
235
+ that.getDeviceOptionsInput().typedInput('type', that.optionsType || 'nothing');
236
+ that.getDeviceOptionsInput().typedInput('value', that.optionsValue || '');
237
+ that.buildDeviceOptionsHelpBlock();
238
+ }
239
+
240
+ buildDeviceOptionsHelpBlock() {
241
+ let that = this;
242
+ if (!that.getDeviceOptionsTypeHelpBlock()) return;
243
+
244
+ that.getDeviceOptionsTypeHelpBlock().hide().find('div').text('').closest('.form-tips').find('span').text('');
245
+ if (that.isMultiple()) return;
246
+
247
+ // console.log('BUILD buildDeviceOptionsHelpBlock');
248
+
249
+ let device = that.getDevice();
250
+ let selectedOption = null;
251
+ if (device && 'definition' in device && device.definition && 'options' in device.definition) {
252
+ $.each(device.definition.options, function(k, v) {
253
+ if ('json' === that.optionsType) {
254
+ let json = {};
255
+ $.each(device.definition.options, function(k, v2) {
256
+ if ('property' in v2) {
257
+ let defaultVal = '';
258
+ if ('type' in v2) {
259
+ if (v2.type==='numeric') {
260
+ defaultVal = 0;
261
+ if ('value_min' in v2) {
262
+ defaultVal = v2.value_min;
263
+ }
264
+ } else if (v2.type==='binary') {
265
+ defaultVal = false;
266
+ }
267
+ }
268
+ json[v2.property] = defaultVal;
269
+ }
270
+ });
271
+ selectedOption = {'name':'JSON', 'description':JSON.stringify(json, null, 4)};
272
+ return false;
273
+ }
274
+ if (v.property === that.optionsType) {
275
+ selectedOption = v;
276
+ return false;
277
+ }
278
+ });
279
+ }
280
+
281
+ if (selectedOption && 'description' in selectedOption && selectedOption.description) {
282
+ that.getDeviceOptionsTypeHelpBlock().show().find('div').text(selectedOption.name).closest('.form-tips').find('span').text(selectedOption.description);
283
+ }
284
+ }
285
+
286
+ async getDevices() {
287
+ let that = this;
288
+ if (that.devices === null || that.refresh) {
289
+ const response = await fetch('/zigbee2mqtt-eb/getDevices?' + new URLSearchParams({
290
+ controllerID: that.getServerInput().val()
291
+ }).toString(), {
292
+ method: 'GET',
293
+ cache: 'no-cache',
294
+ headers: {
295
+ 'Content-Type': 'application/json'
296
+ }
297
+ });
298
+ that.refresh = false;
299
+ that.devices = await response.json();
300
+ return that.devices;
301
+ } else {
302
+ return await new Promise(function(resolve, reject) {
303
+ resolve(that.devices);
304
+ });
305
+ }
306
+ }
307
+
308
+ getDevice() {
309
+ let that = this;
310
+ let devices = that.devices[0];
311
+ let device = null;
312
+
313
+ if (devices.length && that.device_id) {
314
+ let selectedDevice = typeof(that.device_id) === 'object' ? that.device_id[0] : that.device_id;
315
+ $.each(devices, function (index, item) {
316
+ if (item.ieee_address === selectedDevice) {
317
+ device = item;
318
+ return false;
319
+ }
320
+ });
321
+ }
322
+ return device;
323
+ }
324
+
325
+ getDeviceIdInput() {
326
+ return $('#node-input-device_id');
327
+ }
328
+
329
+ getDevicePropertyInput() {
330
+ let $elem = $('#node-input-state');
331
+ return $elem.length?$elem:null;
332
+ }
333
+
334
+ getDeviceOptionsInput() {
335
+ let $elem = $('#node-input-optionsValue');
336
+ return $elem.length?$elem:null;
337
+ }
338
+
339
+ getDeviceOptionsTypeInput() {
340
+ let $elem = $('#node-input-optionsType');
341
+ return $elem.length?$elem:null;
342
+ }
343
+
344
+ getDeviceOptionsTypeHelpBlock() {
345
+ return $('.optionsType_description');
346
+ }
347
+
348
+ getDeviceFriendlyNameInput() {
349
+ return $('#node-input-friendly_name');
350
+ }
351
+
352
+ getServerInput() {
353
+ return $('#node-input-server');
354
+ }
355
+
356
+ getRefreshBtn() {
357
+ return $('#force-refresh');
358
+ }
359
+
360
+ getFilterChanges() {
361
+ return $('#node-input-filterChanges');
362
+ }
363
+
364
+ getEnableMultipleCheckbox() {
365
+ return $('#node-input-enableMultiple');
366
+ }
367
+
368
+ isMultiple() {
369
+ return this.getEnableMultipleCheckbox().is(':checked');
370
+ }
371
+
372
+ setDeviceValue() {
373
+ let that = this;
374
+ if (that.isMultiple()) {
375
+ if (typeof(that.device_id) == 'string') {
376
+ that.device_id = [that.device_id];
377
+ }
378
+ if (that.device_id) {
379
+ that.getDeviceIdInput().multipleSelect('setSelects', that.device_id);
380
+ }
381
+ } else if (that.device_id && that.device_id.length) {
382
+ if (typeof(that.device_id) == 'object') {
383
+ that.device_id = that.device_id[0]; //get the first device
384
+ }
385
+ if (that.getDeviceIdInput().find('option[value="'+that.device_id+'"]').length) {
386
+ that.getDeviceIdInput().val(that.device_id);
387
+ }
388
+ // that.getDeviceIdInput().multipleSelect('check', that.device_id); //does not work
389
+ that.getDeviceIdInput().multipleSelect('refresh');
390
+ } else {
391
+ that.device_id = null;
392
+ }
393
+ }
394
+
395
+ setFriendlyName() {
396
+ let that = this;
397
+ if (that.isMultiple()) {
398
+ if (typeof(that.device_id) == 'string') {
399
+ that.device_id = [that.device_id];
400
+ }
401
+ if (!that.device_id) {
402
+ that.device_id = [];
403
+ }
404
+ that.getDeviceFriendlyNameInput().val(that.device_id.length + ' ' + RED._("@evilbunny/node-red-contrib-zigbee2mqtt/server:editor.selected"));
405
+ } else if (that.device_id && that.device_id.length) {
406
+ if (typeof(that.device_id) == 'object') {
407
+ that.device_id = that.device_id[0]; //get the first device
408
+ }
409
+ if (that.getDeviceIdInput().find('option[value="'+that.device_id+'"]').length) {
410
+ that.getDeviceFriendlyNameInput().val(that.getDeviceIdInput().multipleSelect('getSelects', 'text'));
411
+ }
412
+ } else {
413
+ that.getDeviceFriendlyNameInput().val('');
414
+ }
415
+ }
416
+ }