@evilbunny/node-red-contrib-zigbee2mqtt 0.0.0 → 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.
Files changed (68) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +62 -43
  3. package/api.html +22 -0
  4. package/api.js +176 -0
  5. package/examples/Basic usage.json +316 -0
  6. package/icons/homekit-logo.png +0 -0
  7. package/icons/icon-color.png +0 -0
  8. package/icons/icon.png +0 -0
  9. package/icons/icon.pxd/QuickLook/Icon.tiff +0 -0
  10. package/icons/icon.pxd/QuickLook/Thumbnail.tiff +0 -0
  11. package/icons/icon.pxd/data/15644CBE-CB2C-4054-AAB1-064FBC3F72DB +0 -0
  12. package/icons/icon.pxd/data/2D35D9CD-316A-4D5B-A1D9-1C09DB37FAC9 +0 -0
  13. package/icons/icon.pxd/data/BF7855C2-D304-4F39-84A3-141656F5E18C +0 -0
  14. package/icons/icon.pxd/data/selection/meta +33 -0
  15. package/icons/icon.pxd/data/selection/shapeSelection/meta +12 -0
  16. package/icons/icon.pxd/data/selection/shapeSelection/path +0 -0
  17. package/icons/icon.pxd/data/selectionForContentTransform/meta +33 -0
  18. package/icons/icon.pxd/data/selectionForContentTransform/shapeSelection/meta +12 -0
  19. package/icons/icon.pxd/data/selectionForContentTransform/shapeSelection/path +0 -0
  20. package/icons/icon.pxd/metadata.info +0 -0
  21. package/nodes/bridge.html +523 -0
  22. package/nodes/bridge.js +124 -0
  23. package/nodes/get.html +106 -0
  24. package/nodes/get.js +47 -0
  25. package/nodes/in.html +125 -0
  26. package/nodes/in.js +123 -0
  27. package/nodes/locales/en-US/bridge.html +23 -0
  28. package/nodes/locales/en-US/bridge.json +84 -0
  29. package/nodes/locales/en-US/get.html +19 -0
  30. package/nodes/locales/en-US/get.json +23 -0
  31. package/nodes/locales/en-US/in.html +24 -0
  32. package/nodes/locales/en-US/in.json +43 -0
  33. package/nodes/locales/en-US/out.html +24 -0
  34. package/nodes/locales/en-US/out.json +31 -0
  35. package/nodes/locales/en-US/server.html +12 -0
  36. package/nodes/locales/en-US/server.json +36 -0
  37. package/nodes/locales/sk-SK/bridge.html +23 -0
  38. package/nodes/locales/sk-SK/bridge.json +84 -0
  39. package/nodes/locales/sk-SK/get.html +18 -0
  40. package/nodes/locales/sk-SK/get.json +21 -0
  41. package/nodes/locales/sk-SK/in.html +23 -0
  42. package/nodes/locales/sk-SK/in.json +43 -0
  43. package/nodes/locales/sk-SK/out.html +23 -0
  44. package/nodes/locales/sk-SK/out.json +21 -0
  45. package/nodes/locales/sk-SK/server.html +12 -0
  46. package/nodes/locales/sk-SK/server.json +36 -0
  47. package/nodes/out.html +300 -0
  48. package/nodes/out.js +361 -0
  49. package/nodes/server.html +96 -0
  50. package/nodes/server.js +854 -0
  51. package/package.json +42 -6
  52. package/readme/1.png +0 -0
  53. package/readme/2.png +0 -0
  54. package/readme/3.png +0 -0
  55. package/readme/4.png +0 -0
  56. package/readme/5.png +0 -0
  57. package/readme/options.gif +0 -0
  58. package/resources/Zigbee2mqttHelper.js +470 -0
  59. package/resources/css/common.css +56 -0
  60. package/resources/css/multiple-select.css +183 -0
  61. package/resources/js/multiple-select.js +10 -0
  62. package/resources/js/multiple-select.min.js +10 -0
  63. package/resources/js/node-red-contrib-zigbee2mqtt-helpers.js +416 -0
  64. package/resources/js/zigbee2mqtt-helpers.js +416 -0
  65. package/resources/tokeninput/jquery.tokeninput.js +861 -0
  66. package/resources/tokeninput/token-input-facebook.css +134 -0
  67. package/resources/tokeninput/token-input-mac.css +204 -0
  68. package/resources/tokeninput/token-input.css +113 -0
package/package.json CHANGED
@@ -1,10 +1,46 @@
1
1
  {
2
+ "author": {
3
+ "name": "evilbunny2008",
4
+ "email": "34211365+evilbunny2008@users.noreply.github.com"
5
+ },
2
6
  "name": "@evilbunny/node-red-contrib-zigbee2mqtt",
3
- "version": "0.0.0",
4
- "description": "OIDC trusted publishing setup package for @evilbunny/node-red-contrib-zigbee2mqtt",
7
+ "description": "Fork of Zigbee2mqtt connectivity nodes for node-red",
8
+ "version": "3.2.8",
9
+ "publishConfig": {
10
+ "access": "public"
11
+ },
12
+ "dependencies": {
13
+ "@viz-js/viz": "^3.28.0",
14
+ "jquery": "^4.0.0",
15
+ "mqtt": "^5.15.2"
16
+ },
5
17
  "keywords": [
6
- "oidc",
7
- "trusted-publishing",
8
- "setup"
9
- ]
18
+ "zigbee2mqtt",
19
+ "z2m",
20
+ "mqtt",
21
+ "node-red"
22
+ ],
23
+ "license": "GPL",
24
+ "node-red": {
25
+ "version": ">=1.3.0",
26
+ "nodes": {
27
+ "in": "nodes/in.js",
28
+ "get": "nodes/get.js",
29
+ "out": "nodes/out.js",
30
+ "bridge": "nodes/bridge.js",
31
+ "server": "nodes/server.js",
32
+ "api": "api.js"
33
+ }
34
+ },
35
+ "engines": {
36
+ "node": ">=22.9.0"
37
+ },
38
+ "homepage": "https://github.com/evilbunny2008/node-red-contrib-zigbee2mqtt",
39
+ "repository": {
40
+ "type": "git",
41
+ "url": "git+https://github.com/evilbunny2008/node-red-contrib-zigbee2mqtt.git"
42
+ },
43
+ "bugs": {
44
+ "url": "https://github.com/evilbunny2008/node-red-contrib-zigbee2mqtt/issues"
45
+ }
10
46
  }
package/readme/1.png ADDED
Binary file
package/readme/2.png ADDED
Binary file
package/readme/3.png ADDED
Binary file
package/readme/4.png ADDED
Binary file
package/readme/5.png ADDED
Binary file
Binary file
@@ -0,0 +1,470 @@
1
+ 'use strict';
2
+
3
+
4
+ class Zigbee2mqttHelper {
5
+ static generateSelector(topic) {
6
+ var transliterate = function(text) {
7
+ text = text
8
+ .replace(/\u0401/g, 'YO')
9
+ .replace(/\u0419/g, 'I')
10
+ .replace(/\u0426/g, 'TS')
11
+ .replace(/\u0423/g, 'U')
12
+ .replace(/\u041A/g, 'K')
13
+ .replace(/\u0415/g, 'E')
14
+ .replace(/\u041D/g, 'N')
15
+ .replace(/\u0413/g, 'G')
16
+ .replace(/\u0428/g, 'SH')
17
+ .replace(/\u0429/g, 'SCH')
18
+ .replace(/\u0417/g, 'Z')
19
+ .replace(/\u0425/g, 'H')
20
+ .replace(/\u042A/g, '')
21
+ .replace(/\u0451/g, 'yo')
22
+ .replace(/\u0439/g, 'i')
23
+ .replace(/\u0446/g, 'ts')
24
+ .replace(/\u0443/g, 'u')
25
+ .replace(/\u043A/g, 'k')
26
+ .replace(/\u0435/g, 'e')
27
+ .replace(/\u043D/g, 'n')
28
+ .replace(/\u0433/g, 'g')
29
+ .replace(/\u0448/g, 'sh')
30
+ .replace(/\u0449/g, 'sch')
31
+ .replace(/\u0437/g, 'z')
32
+ .replace(/\u0445/g, 'h')
33
+ .replace(/\u044A/g, "'")
34
+ .replace(/\u0424/g, 'F')
35
+ .replace(/\u042B/g, 'I')
36
+ .replace(/\u0412/g, 'V')
37
+ .replace(/\u0410/g, 'a')
38
+ .replace(/\u041F/g, 'P')
39
+ .replace(/\u0420/g, 'R')
40
+ .replace(/\u041E/g, 'O')
41
+ .replace(/\u041B/g, 'L')
42
+ .replace(/\u0414/g, 'D')
43
+ .replace(/\u0416/g, 'ZH')
44
+ .replace(/\u042D/g, 'E')
45
+ .replace(/\u0444/g, 'f')
46
+ .replace(/\u044B/g, 'i')
47
+ .replace(/\u0432/g, 'v')
48
+ .replace(/\u0430/g, 'a')
49
+ .replace(/\u043F/g, 'p')
50
+ .replace(/\u0440/g, 'r')
51
+ .replace(/\u043E/g, 'o')
52
+ .replace(/\u043B/g, 'l')
53
+ .replace(/\u0434/g, 'd')
54
+ .replace(/\u0436/g, 'zh')
55
+ .replace(/\u044D/g, 'e')
56
+ .replace(/\u042F/g, 'Ya')
57
+ .replace(/\u0427/g, 'CH')
58
+ .replace(/\u0421/g, 'S')
59
+ .replace(/\u041C/g, 'M')
60
+ .replace(/\u0418/g, 'I')
61
+ .replace(/\u0422/g, 'T')
62
+ .replace(/\u042C/g, "'")
63
+ .replace(/\u0411/g, 'B')
64
+ .replace(/\u042E/g, 'YU')
65
+ .replace(/\u044F/g, 'ya')
66
+ .replace(/\u0447/g, 'ch')
67
+ .replace(/\u0441/g, 's')
68
+ .replace(/\u043C/g, 'm')
69
+ .replace(/\u0438/g, 'i')
70
+ .replace(/\u0442/g, 't')
71
+ .replace(/\u044C/g, "'")
72
+ .replace(/\u0431/g, 'b')
73
+ .replace(/\u044E/g, 'yu');
74
+
75
+ return text;
76
+ };
77
+
78
+ topic = transliterate(topic);
79
+ return topic.split('/').join('_').replace(/[^a-zA-Z0-9_-]/g, '');
80
+ }
81
+
82
+ static convertRange(value, r1, r2) {
83
+ var val = Math.ceil((value - r1[0]) * (r2[1] - r2[0]) / (r1[1] - r1[0]) + r2[0]);
84
+ if (val < r2[0]) val = r2[0];
85
+ if (val > r2[1]) val = r2[1];
86
+ return val;
87
+ }
88
+
89
+ static isJson(str) {
90
+ try {
91
+ JSON.parse(str);
92
+ } catch (e) {
93
+ return false;
94
+ }
95
+ return true;
96
+ }
97
+
98
+ static cie2rgb(x, y, brightness) {
99
+ //Set to maximum brightness if no custom value was given (Not the slick ECMAScript 6 way for compatibility reasons)
100
+ if (brightness === undefined) {
101
+ brightness = 254;
102
+ }
103
+
104
+ var z = 1.0 - x - y;
105
+ var Y = (brightness / 254).toFixed(2);
106
+ var X = (Y / y) * x;
107
+ var Z = (Y / y) * z;
108
+
109
+ //Convert to RGB using Wide RGB D65 conversion
110
+ var red = X * 1.656492 - Y * 0.354851 - Z * 0.255038;
111
+ var green = -X * 0.707196 + Y * 1.655397 + Z * 0.036152;
112
+ var blue = X * 0.051713 - Y * 0.121364 + Z * 1.011530;
113
+
114
+ //If red, green or blue is larger than 1.0 set it back to the maximum of 1.0
115
+ if (red > blue && red > green && red > 1.0) {
116
+
117
+ green = green / red;
118
+ blue = blue / red;
119
+ red = 1.0;
120
+ } else if (green > blue && green > red && green > 1.0) {
121
+
122
+ red = red / green;
123
+ blue = blue / green;
124
+ green = 1.0;
125
+ } else if (blue > red && blue > green && blue > 1.0) {
126
+
127
+ red = red / blue;
128
+ green = green / blue;
129
+ blue = 1.0;
130
+ }
131
+
132
+ //Reverse gamma correction
133
+ red = red <= 0.0031308 ? 12.92 * red : (1.0 + 0.055) * Math.pow(red, (1.0 / 2.4)) - 0.055;
134
+ green = green <= 0.0031308 ? 12.92 * green : (1.0 + 0.055) * Math.pow(green, (1.0 / 2.4)) - 0.055;
135
+ blue = blue <= 0.0031308 ? 12.92 * blue : (1.0 + 0.055) * Math.pow(blue, (1.0 / 2.4)) - 0.055;
136
+
137
+ //Convert normalized decimal to decimal
138
+ red = Math.round(red * 255);
139
+ green = Math.round(green * 255);
140
+ blue = Math.round(blue * 255);
141
+
142
+ if (isNaN(red))
143
+ red = 0;
144
+
145
+ if (isNaN(green))
146
+ green = 0;
147
+
148
+ if (isNaN(blue))
149
+ blue = 0;
150
+
151
+ return {
152
+ r: red,
153
+ g: green,
154
+ b: blue
155
+ };
156
+ }
157
+
158
+ static rgb2hsv(r, g, b) {
159
+ let rabs, gabs, babs, rr, gg, bb, h, s, v, diff, diffc, percentRoundFn;
160
+ rabs = r / 255;
161
+ gabs = g / 255;
162
+ babs = b / 255;
163
+ v = Math.max(rabs, gabs, babs),
164
+ diff = v - Math.min(rabs, gabs, babs);
165
+ diffc = c => (v - c) / 6 / diff + 1 / 2;
166
+ percentRoundFn = num => Math.round(num * 100) / 100;
167
+ if (diff == 0) {
168
+ h = s = 0;
169
+ } else {
170
+ s = diff / v;
171
+ rr = diffc(rabs);
172
+ gg = diffc(gabs);
173
+ bb = diffc(babs);
174
+
175
+ if (rabs === v) {
176
+ h = bb - gg;
177
+ } else if (gabs === v) {
178
+ h = (1 / 3) + rr - bb;
179
+ } else if (babs === v) {
180
+ h = (2 / 3) + gg - rr;
181
+ }
182
+ if (h < 0) {
183
+ h += 1;
184
+ } else if (h > 1) {
185
+ h -= 1;
186
+ }
187
+ }
188
+ return {
189
+ h: Math.round(h * 360),
190
+ s: percentRoundFn(s * 100),
191
+ v: percentRoundFn(v * 100)
192
+ };
193
+ }
194
+
195
+ static payload2homekit(payload) {
196
+ var msg = {};
197
+
198
+ if (!payload) return msg;
199
+
200
+ //Lightbulb
201
+ if ("brightness" in payload) {
202
+ if ("state" in payload && payload.state === "OFF") {
203
+ msg["Lightbulb"] = {"On": false};
204
+ if ("color_temp" in payload) {
205
+ msg["Lightbulb_CT"] = {"On": false};
206
+ }
207
+ if ("color" in payload) {
208
+ msg["Lightbulb_RGB"] = {"On": false};
209
+ }
210
+ if ("color" in payload && "color_temp" in payload) {
211
+ msg["Lightbulb_RGB_CT"] = {"On": false};
212
+ }
213
+ } else {
214
+
215
+ var hue = null;
216
+ var sat = null;
217
+ if ("color" in payload && "hue" in payload.color && "saturation" in payload.color) {
218
+ hue = payload.color.hue;
219
+ sat = payload.color.saturation;
220
+ } else if ("color" in payload && "x" in payload.color) {
221
+ var rgb = Zigbee2mqttHelper.cie2rgb(payload.color.x, payload.color.y, payload.brightness);
222
+ var hsv = Zigbee2mqttHelper.rgb2hsv(rgb.r, rgb.g, rgb.b);
223
+ hue = hsv.h;
224
+ sat = hsv.s;
225
+ }
226
+ var bri = Zigbee2mqttHelper.convertRange(parseInt(payload.brightness), [0, 255], [0, 100]);
227
+ var ct = "color_temp" in payload ? Zigbee2mqttHelper.convertRange(parseInt(payload.color_temp), [150, 500], [150, 500]) : null;
228
+
229
+ msg["Lightbulb"] = {
230
+ "On": true,
231
+ "Brightness": bri
232
+ }
233
+ if ("color_temp" in payload) {
234
+ msg["Lightbulb_CT"] = {
235
+ "On": true,
236
+ "Brightness": bri,
237
+ "ColorTemperature": ct
238
+ }
239
+ }
240
+ if ("color" in payload) {
241
+ msg["Lightbulb_RGB"] = {
242
+ "On": true,
243
+ "Brightness": bri,
244
+ "Hue": hue,
245
+ "Saturation": sat
246
+ }
247
+ }
248
+ if ("color" in payload && "color_temp" in payload) {
249
+ msg["Lightbulb_RGB_CT"] = {
250
+ "On": true,
251
+ "Brightness": bri,
252
+ "Hue": hue,
253
+ "Saturation": sat,
254
+ "ColorTemperature": ct
255
+ }
256
+ }
257
+ }
258
+ }
259
+
260
+ //LockMechanism
261
+ if ("state" in payload && (payload.state === "LOCK" || payload.state === "UNLOCK")) {
262
+ msg["LockMechanism"] = {
263
+ "LockCurrentState": payload.state === "LOCK" ? 1 : 0,
264
+ "LockTargetState": payload.state === "LOCK" ? 1 : 0
265
+ };
266
+ }
267
+
268
+
269
+
270
+ // 0: "stopped"
271
+ // 1: "opening"
272
+ // 2: "closing"
273
+ // public static readonly DECREASING = 0;
274
+ // public static readonly INCREASING = 1;
275
+ // public static readonly STOPPED = 2;
276
+ if ('position' in payload && 'motor_state' in payload) {
277
+ let motor_state = null;
278
+ let position = 0; //closed
279
+ switch (payload.motor_state) {
280
+ case 'closing':
281
+ motor_state = 0;
282
+ position = 0;
283
+ break;
284
+ case 'opening':
285
+ motor_state = 1;
286
+ position = 100;
287
+ break;
288
+ case 'stopped':
289
+ default:
290
+ motor_state = 2;
291
+ position = parseInt(payload.position) || 0;
292
+ break;
293
+ }
294
+
295
+ msg["Window"] = msg["WindowCovering"] = msg["Door"] = {
296
+ "CurrentPosition": parseInt(payload.position),
297
+ "TargetPosition": position,
298
+ "PositionState": motor_state
299
+ };
300
+
301
+ } else if ('position' in payload && 'running' in payload) { //old??
302
+ msg["Window"] = msg["WindowCovering"] = msg["Door"] = {
303
+ "CurrentPosition": parseInt(payload.position),
304
+ "TargetPosition": parseInt(payload.position),
305
+ "PositionState": payload.running ? 1 : 2 //increasing=1, stopped=2
306
+ };
307
+ } else if ('position' in payload) { //no position in payload (eg: ikea)
308
+ msg["Window"] = msg["WindowCovering"] = msg["Door"] = {
309
+ "CurrentPosition": parseInt(payload.position),
310
+ "TargetPosition": parseInt(payload.position),
311
+ "PositionState": 2 //stopped=2, there is no way to get current motor status
312
+ };
313
+ }
314
+
315
+ //TemperatureSensor
316
+ if ('temperature' in payload) {
317
+ msg["TemperatureSensor"] = {
318
+ "CurrentTemperature": parseFloat(payload.temperature)
319
+ };
320
+ }
321
+
322
+ //HumiditySensor
323
+ if ('humidity' in payload) {
324
+ msg["HumiditySensor"] = {
325
+ "CurrentRelativeHumidity": parseFloat(payload.humidity)
326
+ };
327
+ }
328
+
329
+ //LightSensor
330
+ if ('illuminance_lux' in payload) {
331
+ msg["LightSensor"] = {
332
+ "CurrentAmbientLightLevel": parseInt(payload.illuminance_lux)
333
+ };
334
+ }
335
+
336
+ //ContactSensor
337
+ if ('contact' in payload) {
338
+ msg["ContactSensor"] = {
339
+ "ContactSensorState": payload.contact ? 0 : 1
340
+ };
341
+ msg["ContactSensor_Inverse"] = {
342
+ "ContactSensorState": payload.contact ? 1 : 0
343
+ };
344
+ }
345
+
346
+ //MotionSensor, OccupancySensor
347
+ if ('occupancy' in payload) {
348
+ msg["MotionSensor"] = {
349
+ "MotionDetected": payload.occupancy
350
+ };
351
+ msg["OccupancySensor"] = {
352
+ "OccupancyDetected":payload.occupancy?1:0
353
+ };
354
+ }
355
+
356
+ //WaterLeak
357
+ if ('water_leak' in payload) {
358
+ msg["LeakSensor"] = {
359
+ "LeakDetected": payload.water_leak ? 1 : 0
360
+ };
361
+ }
362
+
363
+ //Smoke
364
+ if ('smoke' in payload) {
365
+ msg["SmokeSensor"] = {
366
+ "SmokeDetected": payload.smoke ? 1 : 0
367
+ };
368
+ }
369
+
370
+ //Battery
371
+ // if ("powerSource" in device && "Battery" == device.powerSource && "battery" in payload && parseInt(payload.battery)>0) {
372
+ if ('battery' in payload) {
373
+ msg["Battery"] = {
374
+ "BatteryLevel": parseInt(payload.battery),
375
+ "StatusLowBattery": parseInt(payload.battery) <= 15 ? 1 : 0
376
+ };
377
+ }
378
+
379
+ //Switch
380
+ if ("state" in payload && (payload.state === "ON" || payload.state === "OFF")) {
381
+ msg["Switch"] = {
382
+ "On": payload.state === "ON"
383
+ };
384
+ }
385
+
386
+ return msg;
387
+ }
388
+
389
+ static formatPayload(payload, device) {
390
+ var node = this;
391
+ var result = {};
392
+
393
+ //convert XY to RGB, HSV
394
+ if (payload && "color" in payload && "x" in payload.color) {
395
+ var bri = "brightness" in payload ? payload.brightness : 255;
396
+ var rgb = Zigbee2mqttHelper.cie2rgb(payload.color.x, payload.color.y, bri);
397
+ var hsv = Zigbee2mqttHelper.rgb2hsv(rgb.r, rgb.g, rgb.b);
398
+ result['color'] = {
399
+ "rgb": rgb,
400
+ "hsv": hsv
401
+ };
402
+ }
403
+ return result;
404
+ }
405
+
406
+ static formatMath(data) {
407
+ var result = {};
408
+
409
+ for (var i in data) {
410
+ for (var key in data[i]) {
411
+ var val = data[i][key];
412
+ if (Zigbee2mqttHelper.isNumber(val)) {
413
+ if (!(key in result)) result[key] = {"count": 0, "avg": 0, "min": null, "max": null, "sum": 0};
414
+
415
+ result[key]["count"] += 1;
416
+ result[key]["sum"] = Math.round((result[key]["sum"] + val) * 100) / 100;
417
+ result[key]["min"] = result[key]["min"] == null || val < result[key]["min"] ? val : result[key]["min"];
418
+ result[key]["max"] = result[key]["max"] == null || val > result[key]["max"] ? val : result[key]["max"];
419
+ result[key]["avg"] = Math.round((result[key]["sum"] / result[key]["count"]) * 100) / 100;
420
+ }
421
+ }
422
+ }
423
+
424
+ return result;
425
+ }
426
+
427
+ static statusUpdatedAt() {
428
+ return ' [' + new Date().toLocaleDateString() + ' ' + new Date().toLocaleTimeString() + ']'
429
+ }
430
+
431
+ static isNumber(n)
432
+ {
433
+ return Zigbee2mqttHelper.isInt(n) || Zigbee2mqttHelper.isFloat(n);
434
+ }
435
+
436
+ static isInt(n)
437
+ {
438
+ if (n === 'true' || n === true || n === 'false' || n === false) return false;
439
+ return n !== "" && !isNaN(n) && Math.round(n) === n;
440
+ }
441
+
442
+ static isFloat(n){
443
+ if (n === 'true' || n === true || n === 'false' || n === false) return false;
444
+ return n !== "" && !isNaN(n) && Math.round(n) !== n;
445
+ }
446
+
447
+ // static objectsDiff(obj1, obj2) {
448
+ // if (obj1 && typeof(obj1) === 'object' && obj2 && typeof(obj2) === 'object') {
449
+ // var diffObj = Array.isArray(obj2) ? [] : {}
450
+ // Object.getOwnPropertyNames(obj2).forEach(function(prop) {
451
+ // if (typeof obj2[prop] === 'object') {
452
+ // diffObj[prop] = deepCompare(obj1[prop], obj2[prop])
453
+ // // if it's an array with only length property => empty array => delete
454
+ // // or if it's an object with no own properties => delete
455
+ // if (Array.isArray(diffObj[prop]) && Object.getOwnPropertyNames(diffObj[prop]).length === 1 ||
456
+ // Object.getOwnPropertyNames(diffObj[prop]).length === 0) {
457
+ // delete diffObj[prop]
458
+ // }
459
+ // } else if (obj1[prop] !== obj2[prop]) {
460
+ // diffObj[prop] = obj2[prop]
461
+ // }
462
+ // });
463
+ // return diffObj
464
+ // } else {
465
+ // return null;
466
+ // }
467
+ // }
468
+ }
469
+
470
+ module.exports = Zigbee2mqttHelper;
@@ -0,0 +1,56 @@
1
+ .form-row label.l-width {
2
+ width: 25%;
3
+ min-width: 120px;
4
+ }
5
+ .form-row label.a-width {
6
+ width: auto;
7
+ }
8
+ .s-width {width:70% !important;}
9
+
10
+ .ms-choice {
11
+ height:34px;
12
+ border: 1px solid rgb(204, 204, 204);
13
+ line-height: 34px !important;
14
+ }
15
+ .ms-search input {
16
+ width:100% !important;
17
+ }
18
+ .ms-drop li label {
19
+ width:20px !important;
20
+ }
21
+ .ms-drop li span {
22
+ padding-left:5px !important;
23
+ }
24
+ button.active {
25
+ background-color: #ddd;
26
+ }
27
+ .help_block {
28
+ display: none;
29
+ }
30
+ .help-tips {
31
+ font-style: italic;
32
+ font-size: 12px;
33
+ }
34
+
35
+ .red-ui-editor .form-horizontal li.token-input-input-token-facebook input {
36
+ height:23px !important;
37
+ width: 50px;
38
+ padding: 1px 4px;
39
+ background-color: #fafafa;
40
+ border: 1px dashed #91bacf;
41
+ margin: 3px;
42
+ border-radius: 3px;
43
+ -moz-border-radius: 3px;
44
+ -webkit-border-radius: 3px;
45
+ font-size: 12px;
46
+ }
47
+ li.token-input-token-facebook {
48
+ height:19px !important;
49
+ }
50
+ .tokeninput-label {
51
+ float:left;
52
+ line-height: 23px;
53
+ padding: 3px 5px 0 0;
54
+ }
55
+
56
+ .ms-drop ul > li .disabled {color: #2a2a2a !important;font-weight: bold!important;opacity: 1!important;}