node-red-contrib-huemagic 2.8.2 → 4.0.0

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 (165) hide show
  1. package/CHANGELOG.md +229 -0
  2. package/README.md +806 -729
  3. package/examples/Hue Bridge.json +189 -35
  4. package/examples/Hue Brightness.json +124 -14
  5. package/examples/Hue Buttons.json +88 -0
  6. package/examples/Hue Group.json +575 -63
  7. package/examples/Hue Light.json +805 -58
  8. package/examples/Hue Magic.json +69 -24
  9. package/examples/Hue Motion.json +110 -42
  10. package/examples/Hue Rules.json +105 -37
  11. package/examples/Hue Scene.json +145 -28
  12. package/examples/Hue Temperature.json +124 -14
  13. package/huemagic/animations/001-sunny.json +22 -22
  14. package/huemagic/animations/002-cloudy.json +46 -46
  15. package/huemagic/animations/003-nightsky.json +20 -20
  16. package/huemagic/animations/004-sunrise.json +54 -54
  17. package/huemagic/animations/005-sunset.json +46 -46
  18. package/huemagic/animations/006-fireplace.json +74 -74
  19. package/huemagic/animations/007-spring.json +31 -31
  20. package/huemagic/animations/008-autmn.json +23 -23
  21. package/huemagic/animations/009-winter.json +23 -23
  22. package/huemagic/animations/010-dirtyfog.json +23 -23
  23. package/huemagic/animations/011-lgbt.json +49 -50
  24. package/huemagic/animations/012-rasta.json +28 -29
  25. package/huemagic/animations/013-strobo.json +30 -31
  26. package/huemagic/animations/014-randomrainbow.json +30 -31
  27. package/huemagic/animations/015-sos.json +158 -159
  28. package/huemagic/animations/016-milkyway.json +83 -0
  29. package/huemagic/animations/017-beach.json +40 -0
  30. package/huemagic/animations/018-forest.json +43 -0
  31. package/huemagic/animations/019-christmas.json +30 -0
  32. package/huemagic/animations/020-superhero.json +56 -0
  33. package/huemagic/animations/021-neoncity.json +52 -0
  34. package/huemagic/animations/previews/001-sunny.gif +0 -0
  35. package/huemagic/animations/previews/002-cloudy.gif +0 -0
  36. package/huemagic/animations/previews/003-nightsky.gif +0 -0
  37. package/huemagic/animations/previews/004-sunrise.gif +0 -0
  38. package/huemagic/animations/previews/005-sunset.gif +0 -0
  39. package/huemagic/animations/previews/006-fireplace.gif +0 -0
  40. package/huemagic/animations/previews/007-spring.gif +0 -0
  41. package/huemagic/animations/previews/008-autmn.gif +0 -0
  42. package/huemagic/animations/previews/009-winter.gif +0 -0
  43. package/huemagic/animations/previews/010-dirtyfog.gif +0 -0
  44. package/huemagic/animations/previews/011-lgbt.gif +0 -0
  45. package/huemagic/animations/previews/012-rasta.gif +0 -0
  46. package/huemagic/animations/previews/013-strobo.gif +0 -0
  47. package/huemagic/animations/previews/014-randomrainbow.gif +0 -0
  48. package/huemagic/animations/previews/015-sos.gif +0 -0
  49. package/huemagic/animations/previews/016-milkyway.gif +0 -0
  50. package/huemagic/animations/previews/017-beach.gif +0 -0
  51. package/huemagic/animations/previews/018-forest.gif +0 -0
  52. package/huemagic/animations/previews/019-christmas.gif +0 -0
  53. package/huemagic/animations/previews/020-superhero.gif +0 -0
  54. package/huemagic/animations/previews/021-neoncity.gif +0 -0
  55. package/huemagic/hue-bridge-config.html +105 -61
  56. package/huemagic/hue-bridge-config.js +629 -319
  57. package/huemagic/hue-bridge.html +41 -21
  58. package/huemagic/hue-bridge.js +191 -398
  59. package/huemagic/hue-brightness.html +130 -49
  60. package/huemagic/hue-brightness.js +131 -35
  61. package/huemagic/hue-buttons.html +197 -0
  62. package/huemagic/hue-buttons.js +152 -0
  63. package/huemagic/hue-group.html +137 -60
  64. package/huemagic/hue-group.js +394 -367
  65. package/huemagic/hue-light.html +133 -59
  66. package/huemagic/hue-light.js +551 -336
  67. package/huemagic/hue-magic.html +91 -237
  68. package/huemagic/hue-magic.js +57 -27
  69. package/huemagic/hue-motion.html +128 -50
  70. package/huemagic/hue-motion.js +116 -87
  71. package/huemagic/hue-rules.html +121 -50
  72. package/huemagic/hue-rules.js +84 -64
  73. package/huemagic/hue-scene.html +96 -115
  74. package/huemagic/hue-scene.js +65 -96
  75. package/huemagic/hue-temperature.html +130 -49
  76. package/huemagic/hue-temperature.js +136 -32
  77. package/huemagic/icons/hue-bridge.png +0 -0
  78. package/huemagic/icons/hue-bridge.svg +3 -0
  79. package/huemagic/icons/hue-brightness.png +0 -0
  80. package/huemagic/icons/hue-brightness.svg +11 -0
  81. package/huemagic/icons/hue-buttons.png +0 -0
  82. package/huemagic/icons/hue-buttons.svg +4 -0
  83. package/huemagic/icons/hue-group.png +0 -0
  84. package/huemagic/icons/hue-group.svg +12 -0
  85. package/huemagic/icons/hue-light.png +0 -0
  86. package/huemagic/icons/hue-light.svg +3 -0
  87. package/huemagic/icons/hue-magic.png +0 -0
  88. package/huemagic/icons/hue-magic.svg +3 -0
  89. package/huemagic/icons/hue-motion.png +0 -0
  90. package/huemagic/icons/hue-motion.svg +9 -0
  91. package/huemagic/icons/hue-rules.png +0 -0
  92. package/huemagic/icons/hue-rules.svg +4 -0
  93. package/huemagic/icons/hue-scene.png +0 -0
  94. package/huemagic/icons/hue-scene.svg +5 -0
  95. package/huemagic/icons/hue-temperature.png +0 -0
  96. package/huemagic/icons/hue-temperature.svg +7 -0
  97. package/huemagic/locales/de/hue-bridge-config.html +17 -12
  98. package/huemagic/locales/de/hue-bridge-config.json +7 -5
  99. package/huemagic/locales/de/hue-bridge.html +64 -145
  100. package/huemagic/locales/de/hue-bridge.json +6 -17
  101. package/huemagic/locales/de/hue-brightness.html +52 -38
  102. package/huemagic/locales/de/hue-brightness.json +7 -2
  103. package/huemagic/locales/de/hue-buttons.html +58 -0
  104. package/huemagic/locales/de/hue-buttons.json +28 -0
  105. package/huemagic/locales/de/hue-group.html +46 -122
  106. package/huemagic/locales/de/hue-group.json +4 -2
  107. package/huemagic/locales/de/hue-light.html +68 -116
  108. package/huemagic/locales/de/hue-light.json +5 -2
  109. package/huemagic/locales/de/hue-magic.html +65 -57
  110. package/huemagic/locales/de/hue-magic.json +2 -1
  111. package/huemagic/locales/de/hue-motion.html +48 -44
  112. package/huemagic/locales/de/hue-motion.json +6 -3
  113. package/huemagic/locales/de/hue-rules.html +68 -73
  114. package/huemagic/locales/de/hue-rules.json +5 -2
  115. package/huemagic/locales/de/hue-scene.html +23 -32
  116. package/huemagic/locales/de/hue-scene.json +5 -2
  117. package/huemagic/locales/de/hue-temperature.html +53 -36
  118. package/huemagic/locales/de/hue-temperature.json +7 -2
  119. package/huemagic/locales/en-US/hue-bridge-config.html +17 -12
  120. package/huemagic/locales/en-US/hue-bridge-config.json +5 -3
  121. package/huemagic/locales/en-US/hue-bridge.html +64 -145
  122. package/huemagic/locales/en-US/hue-bridge.json +5 -16
  123. package/huemagic/locales/en-US/hue-brightness.html +52 -38
  124. package/huemagic/locales/en-US/hue-brightness.json +10 -3
  125. package/huemagic/locales/en-US/hue-buttons.html +58 -0
  126. package/huemagic/locales/en-US/hue-buttons.json +34 -0
  127. package/huemagic/locales/en-US/hue-group.html +46 -122
  128. package/huemagic/locales/en-US/hue-group.json +7 -3
  129. package/huemagic/locales/en-US/hue-light.html +68 -116
  130. package/huemagic/locales/en-US/hue-light.json +8 -3
  131. package/huemagic/locales/en-US/hue-magic.html +65 -57
  132. package/huemagic/locales/en-US/hue-magic.json +3 -1
  133. package/huemagic/locales/en-US/hue-motion.html +48 -44
  134. package/huemagic/locales/en-US/hue-motion.json +9 -4
  135. package/huemagic/locales/en-US/hue-rules.html +68 -73
  136. package/huemagic/locales/en-US/hue-rules.json +5 -2
  137. package/huemagic/locales/en-US/hue-scene.html +23 -32
  138. package/huemagic/locales/en-US/hue-scene.json +5 -2
  139. package/huemagic/locales/en-US/hue-temperature.html +53 -36
  140. package/huemagic/locales/en-US/hue-temperature.json +10 -3
  141. package/huemagic/utils/api.js +290 -0
  142. package/huemagic/utils/color.js +382 -0
  143. package/{utils → huemagic/utils}/merge.js +9 -6
  144. package/huemagic/utils/messages.js +447 -0
  145. package/package.json +12 -12
  146. package/examples/Hue Switch.json +0 -40
  147. package/examples/Hue Tap.json +0 -40
  148. package/huemagic/assets/anime.js +0 -8
  149. package/huemagic/hue-switch.html +0 -119
  150. package/huemagic/hue-switch.js +0 -113
  151. package/huemagic/hue-tap.html +0 -119
  152. package/huemagic/hue-tap.js +0 -74
  153. package/huemagic/icons/hue-button.png +0 -0
  154. package/huemagic/icons/hue-switch.png +0 -0
  155. package/huemagic/icons/hue-tap.png +0 -0
  156. package/huemagic/locales/de/hue-switch.html +0 -50
  157. package/huemagic/locales/de/hue-switch.json +0 -31
  158. package/huemagic/locales/de/hue-tap.html +0 -41
  159. package/huemagic/locales/de/hue-tap.json +0 -23
  160. package/huemagic/locales/en-US/hue-switch.html +0 -50
  161. package/huemagic/locales/en-US/hue-switch.json +0 -31
  162. package/huemagic/locales/en-US/hue-tap.html +0 -41
  163. package/huemagic/locales/en-US/hue-tap.json +0 -23
  164. package/utils/messages.js +0 -571
  165. package/utils/rgb.js +0 -188
@@ -0,0 +1,447 @@
1
+ const dayjs = require('dayjs');
2
+ const colorUtils = require("./color");
3
+
4
+ //
5
+ // HUE BRIDGE
6
+ class HueBridgeMessage
7
+ {
8
+ constructor(ressource, options = {})
9
+ {
10
+ this.message = {};
11
+ this.message.payload = {};
12
+ this.message.payload.id = ressource.bridgeid;
13
+ this.message.payload.name = ressource.name;
14
+ this.message.payload.factoryNew = ressource.factorynew;
15
+ this.message.payload.replacesBridgeId = ressource.replacesbridgeid ? ressource.replacesbridgeid : false;
16
+ this.message.payload.dataStoreVersion = ressource.datastoreversion;
17
+ this.message.payload.starterKitId = ressource.starterkitid.length > 0 ? ressource.starterkitid : false;
18
+ this.message.payload.softwareVersion = ressource.swversion;
19
+ this.message.payload.apiVersion = ressource.apiversion;
20
+ this.message.payload.zigbeeChannel = ressource.zigbeechannel;
21
+ this.message.payload.macAddress = ressource.mac;
22
+ this.message.payload.ipAddress = ressource.ipaddress;
23
+ this.message.payload.dhcpEnabled = ressource.dhcp;
24
+ this.message.payload.netmask = ressource.netmask;
25
+ this.message.payload.gateway = ressource.gateway;
26
+ this.message.payload.proxyAddress = ressource.proxyaddress == "none" ? false : ressource.proxyaddress;
27
+ this.message.payload.proxyPort = ressource.proxyport;
28
+ this.message.payload.utcTime = ressource.UTC;
29
+ this.message.payload.timeZone = ressource.timezone;
30
+ this.message.payload.localTime = ressource.localtime;
31
+ this.message.payload.portalServicesEnabled = ressource.portalservices;
32
+ this.message.payload.portalConnected = ressource.portalconnection;
33
+ this.message.payload.linkButtonEnabled = ressource.linkbutton;
34
+ this.message.payload.touchlinkEnabled = (ressource["touchlink"] && ressource["touchlink"] == true) ? true : false;
35
+ this.message.payload.autoUpdatesEnabled = options["autoupdate"] ? options["autoupdate"] : false;
36
+ this.message.payload.users = []; // NEW!
37
+ this.message.payload.updated = ressource.updated; // NEW!
38
+
39
+ this.message.payload.model = {};
40
+ this.message.payload.model.id = ressource.modelid;
41
+ this.message.payload.model.manufacturer = "Philips";
42
+ this.message.payload.model.name = "Hue v2";
43
+
44
+ // GET USERS
45
+ for (const [userID, user] of Object.entries(ressource["whitelist"]))
46
+ {
47
+ this.message.payload.users.push({
48
+ user: userID,
49
+ name: user["name"],
50
+ created: user["create date"],
51
+ lastAccess: user["last use date"]
52
+ });
53
+ }
54
+ }
55
+
56
+ get msg()
57
+ {
58
+ return this.message;
59
+ }
60
+ }
61
+
62
+
63
+ //
64
+ // HUE BRIGHTNESS
65
+ class HueBrightnessMessage
66
+ {
67
+ constructor(ressource, options = {})
68
+ {
69
+ const service = Object.values(ressource["services"]["light_level"])[0];
70
+ const connectivity = ressource.services.zigbee_connectivity ? Object.values(ressource.services.zigbee_connectivity)[0] : ((ressource.services.zgp_connectivity) ? Object.values(ressource.services.zgp_connectivity)[0] : false);
71
+
72
+ var realLUX = service.light.light_level - 1;
73
+ realLUX = realLUX / 10000;
74
+ realLUX = Math.round(Math.pow(10, realLUX));
75
+
76
+ this.message = {};
77
+ this.message.payload = {};
78
+ this.message.payload.active = service.enabled; // NEW!
79
+ this.message.payload.reachable = connectivity ? (connectivity.status === "connected") : "unknown"; // NEW!
80
+ this.message.payload.connectionStatus = connectivity ? connectivity.status : "unknown"; // NEW!
81
+ this.message.payload.lux = realLUX;
82
+ this.message.payload.lightLevel = service.light.light_level;
83
+ this.message.payload.dark = (realLUX < 200);
84
+ this.message.payload.daylight = (realLUX > 200);
85
+ this.message.payload.updated = ressource.updated;
86
+
87
+ this.message.info = {};
88
+ this.message.info.id = service.id;
89
+ this.message.info.idV1 = ressource.id_v1 ? ressource.id_v1 : false; // NEW
90
+ this.message.info.uniqueId = ressource.id + "-" + service.id;
91
+ this.message.info.deviceId = ressource.id; // NEW!
92
+ this.message.info.name = ressource.metadata.name;
93
+ this.message.info.type = "light_level";
94
+ this.message.info.softwareVersion = ressource.product_data.software_version;
95
+ this.message.info.battery = Object.values(ressource.services.device_power)[0].power_state.battery_level;
96
+ this.message.info.batteryState = Object.values(ressource.services.device_power)[0].power_state.battery_state; // NEW!
97
+
98
+ this.message.info.model = {};
99
+ this.message.info.model.id = ressource.product_data.model_id;
100
+ this.message.info.model.manufacturer = ressource.product_data.manufacturer_name;
101
+ this.message.info.model.name = ressource.product_data.product_name;
102
+ this.message.info.model.type = ressource.product_data.product_archetype;
103
+ this.message.info.model.certified = ressource.product_data.certified; // NEW
104
+ }
105
+
106
+ get msg()
107
+ {
108
+ return this.message;
109
+ }
110
+ }
111
+
112
+
113
+ //
114
+ // HUE GROUP
115
+ class HueGroupMessage
116
+ {
117
+ constructor(ressource, options = {})
118
+ {
119
+ let service = Object.values(ressource["services"]["grouped_light"])[0];
120
+ service = options.ressources[service.id];
121
+
122
+ this.message = {};
123
+ this.message.payload = {};
124
+ this.message.payload.on = service.on.on;
125
+ this.message.payload.updated = ressource.updated;
126
+
127
+ this.message.info = {};
128
+ this.message.info.id = ressource.id;
129
+ this.message.info.idV1 = ressource.id_v1 ? ressource.id_v1 : false; // NEW
130
+ this.message.info.name = ressource.metadata ? ressource.metadata.name : "all";
131
+ this.message.info.type = "group";
132
+ }
133
+
134
+ get msg()
135
+ {
136
+ return this.message;
137
+ }
138
+ }
139
+
140
+
141
+ //
142
+ // HUE LIGHT
143
+ class HueLightMessage
144
+ {
145
+ constructor(ressource, options = {})
146
+ {
147
+ const service = Object.values(ressource["services"]["light"])[0];
148
+ const connectivity = ressource.services.zigbee_connectivity ? Object.values(ressource.services.zigbee_connectivity)[0] : ((ressource.services.zgp_connectivity) ? Object.values(ressource.services.zgp_connectivity)[0] : false);
149
+
150
+ this.message = {};
151
+ this.message.payload = {};
152
+ this.message.payload.on = service.on.on;
153
+ this.message.payload.brightness = service.dimming ? service.dimming.brightness : false;
154
+ this.message.payload.brightnessLevel = service.dimming ? Math.round((254/100)*this.message.payload.brightness) : false;
155
+ this.message.payload.reachable = connectivity ? (connectivity.status === "connected") : "unknown";
156
+ this.message.payload.connectionStatus = connectivity ? connectivity.status : "unknown"; // NEW!
157
+ this.message.payload.updated = ressource.updated;
158
+
159
+ this.message.info = {};
160
+ this.message.info.id = service.id;
161
+ this.message.info.idV1 = ressource.id_v1 ? ressource.id_v1 : false; // NEW
162
+ this.message.info.uniqueId = ressource.id + "-" + service.id;
163
+ this.message.info.deviceId = ressource.id; // NEW!
164
+ this.message.info.name = service.metadata.name;
165
+ this.message.info.type = "light";
166
+ this.message.info.softwareVersion = ressource.product_data.software_version;
167
+
168
+ this.message.info.model = {};
169
+ this.message.info.model.id = ressource.product_data.model_id;
170
+ this.message.info.model.manufacturer = ressource.product_data.manufacturer_name;
171
+ this.message.info.model.name = ressource.product_data.product_name;
172
+ this.message.info.model.type = ressource.product_data.product_archetype;
173
+ this.message.info.model.certified = ressource.product_data.certified; // NEW
174
+ this.message.info.model.friendsOfHue = true;
175
+
176
+ // HAS COLOR CAPABILITIES?
177
+ if(service["color"])
178
+ {
179
+ let RGB = colorUtils.xyBriToRgb(service.color.xy.x, service.color.xy.y, (service.dimming ? service.dimming.brightness : 100));
180
+ this.message.payload.rgb = [RGB.r, RGB.g, RGB.b];
181
+ this.message.payload.hex = colorUtils.rgbHex(RGB.r, RGB.g, RGB.b);
182
+ this.message.payload.xyColor = service.color.xy; // NEW!
183
+
184
+ if(options.colornames == true)
185
+ {
186
+ var cNamesArray = colorUtils.colornamer(colorUtils.rgbHex(RGB.r, RGB.g, RGB.b));
187
+ this.message.payload.color = cNamesArray.basic[0]["name"];
188
+ }
189
+
190
+ this.message.info.model.colorGamut = service.color.gamut; // NEW
191
+ this.message.info.model.colorGamutType = service.color.gamut_type; // NEW
192
+ }
193
+
194
+ // HAS COLOR TEMPERATURE CAPABILITIES?
195
+ if(service["color_temperature"])
196
+ {
197
+ this.message.payload.colorTemp = service.color_temperature.mirek ? service.color_temperature.mirek : false;
198
+
199
+ if(!this.message.payload.colorTemp) { this.message.payload.colorTempName = "unknown"; }
200
+ else if(this.message.payload.colorTemp < 200) { this.message.payload.colorTempName = "cold"; } // NEW!
201
+ else if(this.message.payload.colorTemp < 350) { this.message.payload.colorTempName = "normal"; }
202
+ else if(this.message.payload.colorTemp < 410) { this.message.payload.colorTempName = "warm"; }
203
+ else { this.message.payload.colorTempName = "hot"; }
204
+ }
205
+
206
+ // HAS GRADIENT COLOR CAPABILITIES?
207
+ if(service["gradient"]) // NEW!
208
+ {
209
+ this.message.payload.gradient = {};
210
+ this.message.payload.gradient.colors = [];
211
+
212
+ for(let gradientColor in service["gradient"]["points"])
213
+ {
214
+ let gradientColorRGB = colorUtils.xyBriToRgb(gradientColor.color.xy.x, gradientColor.color.xy.y, (gradientColor.dimming ? service.dimming.brightness : 100));
215
+
216
+ let oneColorPack = {};
217
+ oneColorPack.rgb = [gradientColorRGB.r, gradientColorRGB.g, gradientColorRGB.b];
218
+ oneColorPack.hex = colorUtils.rgbHex(gradientColorRGB.r, gradientColorRGB.g, gradientColorRGB.b);
219
+ oneColorPack.xyColor = gradientColor.color.xy.x;
220
+
221
+ this.message.payload.gradient.colors.push(oneColorPack);
222
+ }
223
+
224
+ this.message.payload.gradient.numColors = service["gradient"]["points"] ? service["gradient"]["points"].length : 0;
225
+ this.message.payload.gradient.totalColors = service["gradient"]["points_capable"];
226
+ }
227
+ }
228
+
229
+ get msg()
230
+ {
231
+ return this.message;
232
+ }
233
+ }
234
+
235
+
236
+ //
237
+ // HUE MOTION
238
+ class HueMotionMessage
239
+ {
240
+ constructor(ressource, options = {})
241
+ {
242
+ const service = Object.values(ressource["services"]["motion"])[0];
243
+ const connectivity = ressource.services.zigbee_connectivity ? Object.values(ressource.services.zigbee_connectivity)[0] : ((ressource.services.zgp_connectivity) ? Object.values(ressource.services.zgp_connectivity)[0] : false);
244
+
245
+ this.message = {};
246
+ this.message.payload = {
247
+ active: service.enabled,
248
+ reachable: connectivity ? (connectivity.status === "connected") : "unknown",
249
+ connectionStatus: connectivity ? connectivity.status : "unknown", // NEW!
250
+ motion: (service.motion.motion && service.motion.motion_valid),
251
+ updated: ressource.updated
252
+ };
253
+
254
+ this.message.info = {};
255
+ this.message.info.id = service.id;
256
+ this.message.info.idV1 = ressource.id_v1 ? ressource.id_v1 : false; // NEW
257
+ this.message.info.uniqueId = ressource.id + "-" + service.id;
258
+ this.message.info.deviceId = ressource.id; // NEW!
259
+ this.message.info.name = ressource.metadata.name;
260
+ this.message.info.type = "motion";
261
+ this.message.info.softwareVersion = ressource.product_data.software_version;
262
+ this.message.info.battery = Object.values(ressource.services.device_power)[0].power_state.battery_level;
263
+ this.message.info.batteryState = Object.values(ressource.services.device_power)[0].power_state.battery_state; // NEW!
264
+
265
+ this.message.info.model = {};
266
+ this.message.info.model.id = ressource.product_data.model_id;
267
+ this.message.info.model.manufacturer = ressource.product_data.manufacturer_name;
268
+ this.message.info.model.name = ressource.product_data.product_name;
269
+ this.message.info.model.type = ressource.product_data.product_archetype;
270
+ this.message.info.model.certified = ressource.product_data.certified; // NEW
271
+ }
272
+
273
+ get msg()
274
+ {
275
+ return this.message;
276
+ }
277
+ }
278
+
279
+
280
+ //
281
+ // HUE RULES
282
+ class HueRulesMessage
283
+ {
284
+ constructor(ressource, options = {})
285
+ {
286
+ this.message = {};
287
+ this.message.payload = {};
288
+ this.message.payload.enabled = (ressource["status"] == "enabled"); // NEW!
289
+ this.message.payload.triggered = (ressource["lasttriggered"] != null) ? dayjs(ressource["lasttriggered"]).format() : false;
290
+
291
+ this.message.info = {};
292
+ this.message.info.id = ressource["id"];
293
+ this.message.info.created = dayjs(ressource["created"]).format();
294
+ this.message.info.name = ressource["name"];
295
+ this.message.info.timesTriggered = ressource["timestriggered"];
296
+ this.message.info.owner = ressource["_owner"];
297
+ this.message.info.status = ressource["status"];
298
+
299
+ this.message.conditions = ressource["conditions"];
300
+ this.message.actions = ressource["actions"];
301
+ }
302
+
303
+ get msg()
304
+ {
305
+ return this.message;
306
+ }
307
+ }
308
+
309
+
310
+ //
311
+ // HUE BUTTONS
312
+ class HueButtonsMessage
313
+ {
314
+ constructor(ressource, options = {})
315
+ {
316
+ const connectivity = ressource.services.zigbee_connectivity ? Object.values(ressource.services.zigbee_connectivity)[0] : ((ressource.services.zgp_connectivity) ? Object.values(ressource.services.zgp_connectivity)[0] : false);
317
+
318
+ // FIND PRESSED BUTTON
319
+ var pressedButton = false;
320
+ const allButtons = Object.values(ressource.services.button);
321
+
322
+ for (var i = allButtons.length - 1; i >= 0; i--)
323
+ {
324
+ if(allButtons[i]["button"])
325
+ {
326
+ pressedButton = allButtons[i];
327
+ break;
328
+ }
329
+ }
330
+
331
+ this.message = {};
332
+ this.message.payload = {
333
+ reachable: connectivity ? (connectivity.status === "connected") : "unknown", // NEW!
334
+ connectionStatus: connectivity ? connectivity.status : "unknown", // NEW!
335
+ button: pressedButton ? pressedButton.metadata.control_id : false, // NEW
336
+ action: pressedButton ? pressedButton.button.last_event : false, // NEW
337
+ updated: ressource.updated
338
+ };
339
+
340
+ this.message.info = {};
341
+ this.message.info.id = pressedButton ? pressedButton.id : ressource.id;
342
+ this.message.info.idV1 = ressource.id_v1 ? ressource.id_v1 : false; // NEW
343
+ this.message.info.uniqueId = ressource.id + "-" + (pressedButton ? pressedButton.id : "");
344
+ this.message.info.deviceId = ressource.id; // NEW!
345
+ this.message.info.name = ressource.metadata.name;
346
+ this.message.info.type = "button";
347
+ this.message.info.softwareVersion = ressource.product_data.software_version;
348
+ this.message.info.battery = Object.values(ressource.services.device_power)[0].power_state.battery_level;
349
+ this.message.info.batteryState = Object.values(ressource.services.device_power)[0].power_state.battery_state; // NEW!
350
+
351
+ this.message.info.model = {};
352
+ this.message.info.model.id = ressource.product_data.model_id;
353
+ this.message.info.model.manufacturer = ressource.product_data.manufacturer_name;
354
+ this.message.info.model.name = ressource.product_data.product_name;
355
+ this.message.info.model.type = ressource.product_data.product_archetype;
356
+ this.message.info.model.certified = ressource.product_data.certified; // NEW
357
+ }
358
+
359
+ get msg()
360
+ {
361
+ return this.message;
362
+ }
363
+ }
364
+
365
+
366
+ //
367
+ // HUE TEMPERATURE
368
+ class HueTemperatureMessage
369
+ {
370
+ constructor(ressource, options = {})
371
+ {
372
+ const service = Object.values(ressource["services"]["temperature"])[0];
373
+ const connectivity = ressource.services.zigbee_connectivity ? Object.values(ressource.services.zigbee_connectivity)[0] : ((ressource.services.zgp_connectivity) ? Object.values(ressource.services.zgp_connectivity)[0] : false);
374
+
375
+ var deviceValue = service.temperature.temperature;
376
+ var celsius = Math.round(deviceValue * 100) / 100;
377
+ var fahrenheit = Math.round(((celsius * 1.8)+32) * 100) / 100;
378
+
379
+ // TEMPERATURE MESSAGE
380
+ let temperatureMessage = "comfortable";
381
+
382
+ if(celsius < 0) {
383
+ temperatureMessage = "very cold";
384
+ }
385
+ else if(celsius < 11) {
386
+ temperatureMessage = "cold";
387
+ }
388
+ else if(celsius < 16) {
389
+ temperatureMessage = "slightly cold";
390
+ }
391
+ else if(celsius < 22) {
392
+ temperatureMessage = "comfortable";
393
+ }
394
+ else if(celsius < 27) {
395
+ temperatureMessage = "slightly warm";
396
+ }
397
+ else if(celsius < 33) {
398
+ temperatureMessage = "warm";
399
+ }
400
+ else if(celsius < 39) {
401
+ temperatureMessage = "hot";
402
+ }
403
+ else {
404
+ temperatureMessage = "very hot";
405
+ }
406
+
407
+ this.message = {};
408
+ this.message.payload = {
409
+ active: service.enabled, // NEW!
410
+ reachable: connectivity ? (connectivity.status === "connected") : "unknown", // NEW!
411
+ connectionStatus: connectivity ? connectivity.status : "unknown", // NEW!
412
+ celsius: celsius,
413
+ fahrenheit: fahrenheit,
414
+ temperatureIs: temperatureMessage,
415
+ deviceValue: deviceValue,
416
+ updated: ressource.updated
417
+ };
418
+
419
+ this.message.info = {};
420
+ this.message.info.id = service.id;
421
+ this.message.info.idV1 = ressource.id_v1 ? ressource.id_v1 : false; // NEW
422
+ this.message.info.uniqueId = ressource.id + "-" + service.id;
423
+ this.message.info.deviceId = ressource.id; // NEW!
424
+ this.message.info.name = ressource.metadata.name;
425
+ this.message.info.type = "temperature";
426
+ this.message.info.softwareVersion = ressource.product_data.software_version;
427
+ this.message.info.battery = Object.values(ressource.services.device_power)[0].power_state.battery_level;
428
+ this.message.info.batteryState = Object.values(ressource.services.device_power)[0].power_state.battery_state; // NEW!
429
+
430
+ this.message.info.model = {};
431
+ this.message.info.model.id = ressource.product_data.model_id;
432
+ this.message.info.model.manufacturer = ressource.product_data.manufacturer_name;
433
+ this.message.info.model.name = ressource.product_data.product_name;
434
+ this.message.info.model.type = ressource.product_data.product_archetype;
435
+ this.message.info.model.certified = ressource.product_data.certified; // NEW
436
+ }
437
+
438
+ get msg()
439
+ {
440
+ return this.message;
441
+ }
442
+ }
443
+
444
+ //
445
+ // EXPORT
446
+ module.exports = { HueBridgeMessage, HueBrightnessMessage, HueGroupMessage, HueLightMessage, HueMotionMessage, HueRulesMessage, HueButtonsMessage, HueTemperatureMessage }
447
+
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "node-red-contrib-huemagic",
3
- "version": "2.8.2",
4
- "description": "Philips Hue node to control bridges, lights, groups, scenes, rules, taps, switches, motion sensors, temperature sensors and Lux sensors using Node-RED.",
3
+ "version": "4.0.0",
4
+ "description": "Philips Hue node to control bridges, lights, groups, scenes, rules, taps, switches, buttons, motion sensors, temperature sensors and Lux sensors using Node-RED.",
5
5
  "author": "foddy",
6
6
  "homepage": "https://github.com/Foddy/node-red-contrib-huemagic",
7
7
  "license": "Apache-2.0",
@@ -21,6 +21,8 @@
21
21
  "contrib",
22
22
  "philips",
23
23
  "hue",
24
+ "smart-home",
25
+ "home-automation",
24
26
  "api",
25
27
  "iot"
26
28
  ],
@@ -32,8 +34,7 @@
32
34
  "hue-light": "huemagic/hue-light.js",
33
35
  "hue-group": "huemagic/hue-group.js",
34
36
  "hue-scene": "huemagic/hue-scene.js",
35
- "hue-tap": "huemagic/hue-tap.js",
36
- "hue-switch": "huemagic/hue-switch.js",
37
+ "hue-buttons": "huemagic/hue-buttons.js",
37
38
  "hue-motion": "huemagic/hue-motion.js",
38
39
  "hue-temperature": "huemagic/hue-temperature.js",
39
40
  "hue-brightness": "huemagic/hue-brightness.js",
@@ -44,14 +45,13 @@
44
45
  "access": "public"
45
46
  },
46
47
  "dependencies": {
47
- "async": "^3.2.0",
48
- "hex-rgb": "^4.1.0",
49
- "huejay": "^1.9.0",
50
- "moment": "^2.27.0",
51
- "rgb-hex": "^3.0.0",
52
- "request": "^2.88.2",
53
- "colornames": "^1.1.1",
48
+ "async": "^3.2.2",
49
+ "axios": "^0.24.0",
54
50
  "color-namer": "^1.4.0",
55
- "get-image-colors": "^2.0.1"
51
+ "colornames": "^1.1.1",
52
+ "dayjs": "^1.10.7",
53
+ "deep-object-diff": "^1.1.0",
54
+ "eventsource": "^1.1.0",
55
+ "get-image-colors": "^4.0.0"
56
56
  }
57
57
  }
@@ -1,40 +0,0 @@
1
- [
2
- {
3
- "id": "e3a959fd.39da6",
4
- "type": "tab",
5
- "label": "Hue Switch (example)",
6
- "disabled": false,
7
- "info": "Example use of the Hue Switch node"
8
- },
9
- {
10
- "id": "d481c97b.56f63",
11
- "type": "debug",
12
- "z": "e3a959fd.39da6",
13
- "name": "Information",
14
- "active": true,
15
- "tosidebar": true,
16
- "console": false,
17
- "tostatus": false,
18
- "complete": "payload",
19
- "targetType": "msg",
20
- "x": 330,
21
- "y": 60,
22
- "wires": [],
23
- "icon": "font-awesome/fa-info-circle"
24
- },
25
- {
26
- "id": "de4dca53.31075",
27
- "type": "hue-switch",
28
- "z": "e3a959fd.39da6",
29
- "name": "",
30
- "sensorid": "",
31
- "skipevents": false,
32
- "x": 130,
33
- "y": 60,
34
- "wires": [
35
- [
36
- "d481c97b.56f63"
37
- ]
38
- ]
39
- }
40
- ]
@@ -1,40 +0,0 @@
1
- [
2
- {
3
- "id": "e3a959fd.39da6",
4
- "type": "tab",
5
- "label": "Hue Tap (example)",
6
- "disabled": false,
7
- "info": "Example use of the Hue Tap node"
8
- },
9
- {
10
- "id": "d481c97b.56f63",
11
- "type": "debug",
12
- "z": "e3a959fd.39da6",
13
- "name": "Information",
14
- "active": true,
15
- "tosidebar": true,
16
- "console": false,
17
- "tostatus": false,
18
- "complete": "payload",
19
- "targetType": "msg",
20
- "x": 310,
21
- "y": 60,
22
- "wires": [],
23
- "icon": "font-awesome/fa-info-circle"
24
- },
25
- {
26
- "id": "7553e1a.fc48c2",
27
- "type": "hue-tap",
28
- "z": "e3a959fd.39da6",
29
- "name": "",
30
- "sensorid": "",
31
- "skipevents": false,
32
- "x": 120,
33
- "y": 60,
34
- "wires": [
35
- [
36
- "d481c97b.56f63"
37
- ]
38
- ]
39
- }
40
- ]
@@ -1,8 +0,0 @@
1
- /*
2
- * anime.js v3.1.0
3
- * (c) 2019 Julian Garnier
4
- * Released under the MIT license
5
- * animejs.com
6
- */
7
-
8
- !function(n,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):n.anime=e()}(this,function(){"use strict";var n={update:null,begin:null,loopBegin:null,changeBegin:null,change:null,changeComplete:null,loopComplete:null,complete:null,loop:1,direction:"normal",autoplay:!0,timelineOffset:0},e={duration:1e3,delay:0,endDelay:0,easing:"easeOutElastic(1, .5)",round:0},r=["translateX","translateY","translateZ","rotate","rotateX","rotateY","rotateZ","scale","scaleX","scaleY","scaleZ","skew","skewX","skewY","perspective"],t={CSS:{},springs:{}};function a(n,e,r){return Math.min(Math.max(n,e),r)}function o(n,e){return n.indexOf(e)>-1}function u(n,e){return n.apply(null,e)}var i={arr:function(n){return Array.isArray(n)},obj:function(n){return o(Object.prototype.toString.call(n),"Object")},pth:function(n){return i.obj(n)&&n.hasOwnProperty("totalLength")},svg:function(n){return n instanceof SVGElement},inp:function(n){return n instanceof HTMLInputElement},dom:function(n){return n.nodeType||i.svg(n)},str:function(n){return"string"==typeof n},fnc:function(n){return"function"==typeof n},und:function(n){return void 0===n},hex:function(n){return/(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(n)},rgb:function(n){return/^rgb/.test(n)},hsl:function(n){return/^hsl/.test(n)},col:function(n){return i.hex(n)||i.rgb(n)||i.hsl(n)},key:function(r){return!n.hasOwnProperty(r)&&!e.hasOwnProperty(r)&&"targets"!==r&&"keyframes"!==r}};function c(n){var e=/\(([^)]+)\)/.exec(n);return e?e[1].split(",").map(function(n){return parseFloat(n)}):[]}function s(n,e){var r=c(n),o=a(i.und(r[0])?1:r[0],.1,100),u=a(i.und(r[1])?100:r[1],.1,100),s=a(i.und(r[2])?10:r[2],.1,100),f=a(i.und(r[3])?0:r[3],.1,100),l=Math.sqrt(u/o),d=s/(2*Math.sqrt(u*o)),p=d<1?l*Math.sqrt(1-d*d):0,h=1,v=d<1?(d*l-f)/p:-f+l;function g(n){var r=e?e*n/1e3:n;return r=d<1?Math.exp(-r*d*l)*(h*Math.cos(p*r)+v*Math.sin(p*r)):(h+v*r)*Math.exp(-r*l),0===n||1===n?n:1-r}return e?g:function(){var e=t.springs[n];if(e)return e;for(var r=0,a=0;;)if(1===g(r+=1/6)){if(++a>=16)break}else a=0;var o=r*(1/6)*1e3;return t.springs[n]=o,o}}function f(n){return void 0===n&&(n=10),function(e){return Math.round(e*n)*(1/n)}}var l,d,p=function(){var n=11,e=1/(n-1);function r(n,e){return 1-3*e+3*n}function t(n,e){return 3*e-6*n}function a(n){return 3*n}function o(n,e,o){return((r(e,o)*n+t(e,o))*n+a(e))*n}function u(n,e,o){return 3*r(e,o)*n*n+2*t(e,o)*n+a(e)}return function(r,t,a,i){if(0<=r&&r<=1&&0<=a&&a<=1){var c=new Float32Array(n);if(r!==t||a!==i)for(var s=0;s<n;++s)c[s]=o(s*e,r,a);return function(n){return r===t&&a===i?n:0===n||1===n?n:o(f(n),t,i)}}function f(t){for(var i=0,s=1,f=n-1;s!==f&&c[s]<=t;++s)i+=e;var l=i+(t-c[--s])/(c[s+1]-c[s])*e,d=u(l,r,a);return d>=.001?function(n,e,r,t){for(var a=0;a<4;++a){var i=u(e,r,t);if(0===i)return e;e-=(o(e,r,t)-n)/i}return e}(t,l,r,a):0===d?l:function(n,e,r,t,a){for(var u,i,c=0;(u=o(i=e+(r-e)/2,t,a)-n)>0?r=i:e=i,Math.abs(u)>1e-7&&++c<10;);return i}(t,i,i+e,r,a)}}}(),h=(l={linear:function(){return function(n){return n}}},d={Sine:function(){return function(n){return 1-Math.cos(n*Math.PI/2)}},Circ:function(){return function(n){return 1-Math.sqrt(1-n*n)}},Back:function(){return function(n){return n*n*(3*n-2)}},Bounce:function(){return function(n){for(var e,r=4;n<((e=Math.pow(2,--r))-1)/11;);return 1/Math.pow(4,3-r)-7.5625*Math.pow((3*e-2)/22-n,2)}},Elastic:function(n,e){void 0===n&&(n=1),void 0===e&&(e=.5);var r=a(n,1,10),t=a(e,.1,2);return function(n){return 0===n||1===n?n:-r*Math.pow(2,10*(n-1))*Math.sin((n-1-t/(2*Math.PI)*Math.asin(1/r))*(2*Math.PI)/t)}}},["Quad","Cubic","Quart","Quint","Expo"].forEach(function(n,e){d[n]=function(){return function(n){return Math.pow(n,e+2)}}}),Object.keys(d).forEach(function(n){var e=d[n];l["easeIn"+n]=e,l["easeOut"+n]=function(n,r){return function(t){return 1-e(n,r)(1-t)}},l["easeInOut"+n]=function(n,r){return function(t){return t<.5?e(n,r)(2*t)/2:1-e(n,r)(-2*t+2)/2}}}),l);function v(n,e){if(i.fnc(n))return n;var r=n.split("(")[0],t=h[r],a=c(n);switch(r){case"spring":return s(n,e);case"cubicBezier":return u(p,a);case"steps":return u(f,a);default:return u(t,a)}}function g(n){try{return document.querySelectorAll(n)}catch(n){return}}function m(n,e){for(var r=n.length,t=arguments.length>=2?arguments[1]:void 0,a=[],o=0;o<r;o++)if(o in n){var u=n[o];e.call(t,u,o,n)&&a.push(u)}return a}function y(n){return n.reduce(function(n,e){return n.concat(i.arr(e)?y(e):e)},[])}function b(n){return i.arr(n)?n:(i.str(n)&&(n=g(n)||n),n instanceof NodeList||n instanceof HTMLCollection?[].slice.call(n):[n])}function M(n,e){return n.some(function(n){return n===e})}function x(n){var e={};for(var r in n)e[r]=n[r];return e}function w(n,e){var r=x(n);for(var t in n)r[t]=e.hasOwnProperty(t)?e[t]:n[t];return r}function k(n,e){var r=x(n);for(var t in e)r[t]=i.und(n[t])?e[t]:n[t];return r}function O(n){return i.rgb(n)?(r=/rgb\((\d+,\s*[\d]+,\s*[\d]+)\)/g.exec(e=n))?"rgba("+r[1]+",1)":e:i.hex(n)?(t=n.replace(/^#?([a-f\d])([a-f\d])([a-f\d])$/i,function(n,e,r,t){return e+e+r+r+t+t}),a=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(t),"rgba("+parseInt(a[1],16)+","+parseInt(a[2],16)+","+parseInt(a[3],16)+",1)"):i.hsl(n)?function(n){var e,r,t,a=/hsl\((\d+),\s*([\d.]+)%,\s*([\d.]+)%\)/g.exec(n)||/hsla\((\d+),\s*([\d.]+)%,\s*([\d.]+)%,\s*([\d.]+)\)/g.exec(n),o=parseInt(a[1],10)/360,u=parseInt(a[2],10)/100,i=parseInt(a[3],10)/100,c=a[4]||1;function s(n,e,r){return r<0&&(r+=1),r>1&&(r-=1),r<1/6?n+6*(e-n)*r:r<.5?e:r<2/3?n+(e-n)*(2/3-r)*6:n}if(0==u)e=r=t=i;else{var f=i<.5?i*(1+u):i+u-i*u,l=2*i-f;e=s(l,f,o+1/3),r=s(l,f,o),t=s(l,f,o-1/3)}return"rgba("+255*e+","+255*r+","+255*t+","+c+")"}(n):void 0;var e,r,t,a}function C(n){var e=/[+-]?\d*\.?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?(%|px|pt|em|rem|in|cm|mm|ex|ch|pc|vw|vh|vmin|vmax|deg|rad|turn)?$/.exec(n);if(e)return e[1]}function B(n,e){return i.fnc(n)?n(e.target,e.id,e.total):n}function P(n,e){return n.getAttribute(e)}function I(n,e,r){if(M([r,"deg","rad","turn"],C(e)))return e;var a=t.CSS[e+r];if(!i.und(a))return a;var o=document.createElement(n.tagName),u=n.parentNode&&n.parentNode!==document?n.parentNode:document.body;u.appendChild(o),o.style.position="absolute",o.style.width=100+r;var c=100/o.offsetWidth;u.removeChild(o);var s=c*parseFloat(e);return t.CSS[e+r]=s,s}function T(n,e,r){if(e in n.style){var t=e.replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase(),a=n.style[e]||getComputedStyle(n).getPropertyValue(t)||"0";return r?I(n,a,r):a}}function D(n,e){return i.dom(n)&&!i.inp(n)&&(P(n,e)||i.svg(n)&&n[e])?"attribute":i.dom(n)&&M(r,e)?"transform":i.dom(n)&&"transform"!==e&&T(n,e)?"css":null!=n[e]?"object":void 0}function E(n){if(i.dom(n)){for(var e,r=n.style.transform||"",t=/(\w+)\(([^)]*)\)/g,a=new Map;e=t.exec(r);)a.set(e[1],e[2]);return a}}function F(n,e,r,t){var a,u=o(e,"scale")?1:0+(o(a=e,"translate")||"perspective"===a?"px":o(a,"rotate")||o(a,"skew")?"deg":void 0),i=E(n).get(e)||u;return r&&(r.transforms.list.set(e,i),r.transforms.last=e),t?I(n,i,t):i}function N(n,e,r,t){switch(D(n,e)){case"transform":return F(n,e,t,r);case"css":return T(n,e,r);case"attribute":return P(n,e);default:return n[e]||0}}function A(n,e){var r=/^(\*=|\+=|-=)/.exec(n);if(!r)return n;var t=C(n)||0,a=parseFloat(e),o=parseFloat(n.replace(r[0],""));switch(r[0][0]){case"+":return a+o+t;case"-":return a-o+t;case"*":return a*o+t}}function L(n,e){if(i.col(n))return O(n);if(/\s/g.test(n))return n;var r=C(n),t=r?n.substr(0,n.length-r.length):n;return e?t+e:t}function j(n,e){return Math.sqrt(Math.pow(e.x-n.x,2)+Math.pow(e.y-n.y,2))}function S(n){for(var e,r=n.points,t=0,a=0;a<r.numberOfItems;a++){var o=r.getItem(a);a>0&&(t+=j(e,o)),e=o}return t}function q(n){if(n.getTotalLength)return n.getTotalLength();switch(n.tagName.toLowerCase()){case"circle":return o=n,2*Math.PI*P(o,"r");case"rect":return 2*P(a=n,"width")+2*P(a,"height");case"line":return j({x:P(t=n,"x1"),y:P(t,"y1")},{x:P(t,"x2"),y:P(t,"y2")});case"polyline":return S(n);case"polygon":return r=(e=n).points,S(e)+j(r.getItem(r.numberOfItems-1),r.getItem(0))}var e,r,t,a,o}function $(n,e){var r=e||{},t=r.el||function(n){for(var e=n.parentNode;i.svg(e)&&i.svg(e.parentNode);)e=e.parentNode;return e}(n),a=t.getBoundingClientRect(),o=P(t,"viewBox"),u=a.width,c=a.height,s=r.viewBox||(o?o.split(" "):[0,0,u,c]);return{el:t,viewBox:s,x:s[0]/1,y:s[1]/1,w:u/s[2],h:c/s[3]}}function X(n,e){function r(r){void 0===r&&(r=0);var t=e+r>=1?e+r:0;return n.el.getPointAtLength(t)}var t=$(n.el,n.svg),a=r(),o=r(-1),u=r(1);switch(n.property){case"x":return(a.x-t.x)*t.w;case"y":return(a.y-t.y)*t.h;case"angle":return 180*Math.atan2(u.y-o.y,u.x-o.x)/Math.PI}}function Y(n,e){var r=/[+-]?\d*\.?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?/g,t=L(i.pth(n)?n.totalLength:n,e)+"";return{original:t,numbers:t.match(r)?t.match(r).map(Number):[0],strings:i.str(n)||e?t.split(r):[]}}function Z(n){return m(n?y(i.arr(n)?n.map(b):b(n)):[],function(n,e,r){return r.indexOf(n)===e})}function Q(n){var e=Z(n);return e.map(function(n,r){return{target:n,id:r,total:e.length,transforms:{list:E(n)}}})}function V(n,e){var r=x(e);if(/^spring/.test(r.easing)&&(r.duration=s(r.easing)),i.arr(n)){var t=n.length;2===t&&!i.obj(n[0])?n={value:n}:i.fnc(e.duration)||(r.duration=e.duration/t)}var a=i.arr(n)?n:[n];return a.map(function(n,r){var t=i.obj(n)&&!i.pth(n)?n:{value:n};return i.und(t.delay)&&(t.delay=r?0:e.delay),i.und(t.endDelay)&&(t.endDelay=r===a.length-1?e.endDelay:0),t}).map(function(n){return k(n,r)})}function z(n,e){var r=[],t=e.keyframes;for(var a in t&&(e=k(function(n){for(var e=m(y(n.map(function(n){return Object.keys(n)})),function(n){return i.key(n)}).reduce(function(n,e){return n.indexOf(e)<0&&n.push(e),n},[]),r={},t=function(t){var a=e[t];r[a]=n.map(function(n){var e={};for(var r in n)i.key(r)?r==a&&(e.value=n[r]):e[r]=n[r];return e})},a=0;a<e.length;a++)t(a);return r}(t),e)),e)i.key(a)&&r.push({name:a,tweens:V(e[a],n)});return r}function H(n,e){var r;return n.tweens.map(function(t){var a=function(n,e){var r={};for(var t in n){var a=B(n[t],e);i.arr(a)&&1===(a=a.map(function(n){return B(n,e)})).length&&(a=a[0]),r[t]=a}return r.duration=parseFloat(r.duration),r.delay=parseFloat(r.delay),r}(t,e),o=a.value,u=i.arr(o)?o[1]:o,c=C(u),s=N(e.target,n.name,c,e),f=r?r.to.original:s,l=i.arr(o)?o[0]:f,d=C(l)||C(s),p=c||d;return i.und(u)&&(u=f),a.from=Y(l,p),a.to=Y(A(u,l),p),a.start=r?r.end:0,a.end=a.start+a.delay+a.duration+a.endDelay,a.easing=v(a.easing,a.duration),a.isPath=i.pth(o),a.isColor=i.col(a.from.original),a.isColor&&(a.round=1),r=a,a})}var G={css:function(n,e,r){return n.style[e]=r},attribute:function(n,e,r){return n.setAttribute(e,r)},object:function(n,e,r){return n[e]=r},transform:function(n,e,r,t,a){if(t.list.set(e,r),e===t.last||a){var o="";t.list.forEach(function(n,e){o+=e+"("+n+") "}),n.style.transform=o}}};function R(n,e){Q(n).forEach(function(n){for(var r in e){var t=B(e[r],n),a=n.target,o=C(t),u=N(a,r,o,n),i=A(L(t,o||C(u)),u),c=D(a,r);G[c](a,r,i,n.transforms,!0)}})}function W(n,e){return m(y(n.map(function(n){return e.map(function(e){return function(n,e){var r=D(n.target,e.name);if(r){var t=H(e,n),a=t[t.length-1];return{type:r,property:e.name,animatable:n,tweens:t,duration:a.end,delay:t[0].delay,endDelay:a.endDelay}}}(n,e)})})),function(n){return!i.und(n)})}function J(n,e){var r=n.length,t=function(n){return n.timelineOffset?n.timelineOffset:0},a={};return a.duration=r?Math.max.apply(Math,n.map(function(n){return t(n)+n.duration})):e.duration,a.delay=r?Math.min.apply(Math,n.map(function(n){return t(n)+n.delay})):e.delay,a.endDelay=r?a.duration-Math.max.apply(Math,n.map(function(n){return t(n)+n.duration-n.endDelay})):e.endDelay,a}var K=0;var U,_=[],nn=[],en=function(){function n(){U=requestAnimationFrame(e)}function e(e){var r=_.length;if(r){for(var t=0;t<r;){var a=_[t];if(a.paused){var o=_.indexOf(a);o>-1&&(_.splice(o,1),r=_.length)}else a.tick(e);t++}n()}else U=cancelAnimationFrame(U)}return n}();function rn(r){void 0===r&&(r={});var t,o=0,u=0,i=0,c=0,s=null;function f(n){var e=window.Promise&&new Promise(function(n){return s=n});return n.finished=e,e}var l,d,p,h,v,g,y,b,M=(d=w(n,l=r),p=w(e,l),h=z(p,l),v=Q(l.targets),g=W(v,h),y=J(g,p),b=K,K++,k(d,{id:b,children:[],animatables:v,animations:g,duration:y.duration,delay:y.delay,endDelay:y.endDelay}));f(M);function x(){var n=M.direction;"alternate"!==n&&(M.direction="normal"!==n?"normal":"reverse"),M.reversed=!M.reversed,t.forEach(function(n){return n.reversed=M.reversed})}function O(n){return M.reversed?M.duration-n:n}function C(){o=0,u=O(M.currentTime)*(1/rn.speed)}function B(n,e){e&&e.seek(n-e.timelineOffset)}function P(n){for(var e=0,r=M.animations,t=r.length;e<t;){var o=r[e],u=o.animatable,i=o.tweens,c=i.length-1,s=i[c];c&&(s=m(i,function(e){return n<e.end})[0]||s);for(var f=a(n-s.start-s.delay,0,s.duration)/s.duration,l=isNaN(f)?1:s.easing(f),d=s.to.strings,p=s.round,h=[],v=s.to.numbers.length,g=void 0,y=0;y<v;y++){var b=void 0,x=s.to.numbers[y],w=s.from.numbers[y]||0;b=s.isPath?X(s.value,l*x):w+l*(x-w),p&&(s.isColor&&y>2||(b=Math.round(b*p)/p)),h.push(b)}var k=d.length;if(k){g=d[0];for(var O=0;O<k;O++){d[O];var C=d[O+1],B=h[O];isNaN(B)||(g+=C?B+C:B+" ")}}else g=h[0];G[o.type](u.target,o.property,g,u.transforms),o.currentValue=g,e++}}function I(n){M[n]&&!M.passThrough&&M[n](M)}function T(n){var e=M.duration,r=M.delay,l=e-M.endDelay,d=O(n);M.progress=a(d/e*100,0,100),M.reversePlayback=d<M.currentTime,t&&function(n){if(M.reversePlayback)for(var e=c;e--;)B(n,t[e]);else for(var r=0;r<c;r++)B(n,t[r])}(d),!M.began&&M.currentTime>0&&(M.began=!0,I("begin")),!M.loopBegan&&M.currentTime>0&&(M.loopBegan=!0,I("loopBegin")),d<=r&&0!==M.currentTime&&P(0),(d>=l&&M.currentTime!==e||!e)&&P(e),d>r&&d<l?(M.changeBegan||(M.changeBegan=!0,M.changeCompleted=!1,I("changeBegin")),I("change"),P(d)):M.changeBegan&&(M.changeCompleted=!0,M.changeBegan=!1,I("changeComplete")),M.currentTime=a(d,0,e),M.began&&I("update"),n>=e&&(u=0,M.remaining&&!0!==M.remaining&&M.remaining--,M.remaining?(o=i,I("loopComplete"),M.loopBegan=!1,"alternate"===M.direction&&x()):(M.paused=!0,M.completed||(M.completed=!0,I("loopComplete"),I("complete"),!M.passThrough&&"Promise"in window&&(s(),f(M)))))}return M.reset=function(){var n=M.direction;M.passThrough=!1,M.currentTime=0,M.progress=0,M.paused=!0,M.began=!1,M.loopBegan=!1,M.changeBegan=!1,M.completed=!1,M.changeCompleted=!1,M.reversePlayback=!1,M.reversed="reverse"===n,M.remaining=M.loop,t=M.children;for(var e=c=t.length;e--;)M.children[e].reset();(M.reversed&&!0!==M.loop||"alternate"===n&&1===M.loop)&&M.remaining++,P(M.reversed?M.duration:0)},M.set=function(n,e){return R(n,e),M},M.tick=function(n){i=n,o||(o=i),T((i+(u-o))*rn.speed)},M.seek=function(n){T(O(n))},M.pause=function(){M.paused=!0,C()},M.play=function(){M.paused&&(M.completed&&M.reset(),M.paused=!1,_.push(M),C(),U||en())},M.reverse=function(){x(),C()},M.restart=function(){M.reset(),M.play()},M.reset(),M.autoplay&&M.play(),M}function tn(n,e){for(var r=e.length;r--;)M(n,e[r].animatable.target)&&e.splice(r,1)}return"undefined"!=typeof document&&document.addEventListener("visibilitychange",function(){document.hidden?(_.forEach(function(n){return n.pause()}),nn=_.slice(0),rn.running=_=[]):nn.forEach(function(n){return n.play()})}),rn.version="3.1.0",rn.speed=1,rn.running=_,rn.remove=function(n){for(var e=Z(n),r=_.length;r--;){var t=_[r],a=t.animations,o=t.children;tn(e,a);for(var u=o.length;u--;){var i=o[u],c=i.animations;tn(e,c),c.length||i.children.length||o.splice(u,1)}a.length||o.length||t.pause()}},rn.get=N,rn.set=R,rn.convertPx=I,rn.path=function(n,e){var r=i.str(n)?g(n)[0]:n,t=e||100;return function(n){return{property:n,el:r,svg:$(r),totalLength:q(r)*(t/100)}}},rn.setDashoffset=function(n){var e=q(n);return n.setAttribute("stroke-dasharray",e),e},rn.stagger=function(n,e){void 0===e&&(e={});var r=e.direction||"normal",t=e.easing?v(e.easing):null,a=e.grid,o=e.axis,u=e.from||0,c="first"===u,s="center"===u,f="last"===u,l=i.arr(n),d=l?parseFloat(n[0]):parseFloat(n),p=l?parseFloat(n[1]):0,h=C(l?n[1]:n)||0,g=e.start||0+(l?d:0),m=[],y=0;return function(n,e,i){if(c&&(u=0),s&&(u=(i-1)/2),f&&(u=i-1),!m.length){for(var v=0;v<i;v++){if(a){var b=s?(a[0]-1)/2:u%a[0],M=s?(a[1]-1)/2:Math.floor(u/a[0]),x=b-v%a[0],w=M-Math.floor(v/a[0]),k=Math.sqrt(x*x+w*w);"x"===o&&(k=-x),"y"===o&&(k=-w),m.push(k)}else m.push(Math.abs(u-v));y=Math.max.apply(Math,m)}t&&(m=m.map(function(n){return t(n/y)*y})),"reverse"===r&&(m=m.map(function(n){return o?n<0?-1*n:-n:Math.abs(y-n)}))}return g+(l?(p-d)/y:d)*(Math.round(100*m[e])/100)+h}},rn.timeline=function(n){void 0===n&&(n={});var r=rn(n);return r.duration=0,r.add=function(t,a){var o=_.indexOf(r),u=r.children;function c(n){n.passThrough=!0}o>-1&&_.splice(o,1);for(var s=0;s<u.length;s++)c(u[s]);var f=k(t,w(e,n));f.targets=f.targets||n.targets;var l=r.duration;f.autoplay=!1,f.direction=r.direction,f.timelineOffset=i.und(a)?l:A(a,l),c(r),r.seek(f.timelineOffset);var d=rn(f);c(d),u.push(d);var p=J(u,n);return r.delay=p.delay,r.endDelay=p.endDelay,r.duration=p.duration,r.seek(0),r.reset(),r.autoplay&&r.play(),r},r},rn.easing=v,rn.penner=h,rn.random=function(n,e){return Math.floor(Math.random()*(e-n+1))+n},rn});