node-red-contrib-knx-ultimate 2.2.6 → 2.2.10

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.
@@ -60,9 +60,9 @@ module.exports = function (RED) {
60
60
  node.handleSend = (msg) => {
61
61
  if (node.currentHUEDevice === undefined) {
62
62
  node.setNodeStatusHue({
63
- fill: "red",
63
+ fill: "grey",
64
64
  shape: "ring",
65
- text: "Currently not ready.",
65
+ text: "Initializing. Please wait.",
66
66
  payload: "",
67
67
  });
68
68
  return;
@@ -126,6 +126,18 @@ module.exports = function (RED) {
126
126
  fill: "green", shape: "dot", text: "KNX->HUE", payload: JSON.stringify(msg.payload),
127
127
  });
128
128
  break;
129
+ case config.GALightKelvin:
130
+ msg.payload = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptLightKelvin));
131
+ let retMirek = hueColorConverter.ColorConverter.scale(msg.payload, [0, 65535], [153, 500]);
132
+ state = { color_temperature: { mirek: retMirek } };
133
+ node.serverHue.hueManager.writeHueQueueAdd(node.hueDevice, state, node.isGrouped_light === false ? "setLight" : "setGroupedLight");
134
+ node.setNodeStatusHue({
135
+ fill: "green",
136
+ shape: "dot",
137
+ text: "KNX->HUE",
138
+ payload: state,
139
+ });
140
+ break;
129
141
  case config.GADaylightSensor:
130
142
  if (config.enableDayNightLighting === "yes") {
131
143
  node.DayTime = Boolean(dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptDaylightSensor)));
@@ -168,12 +180,6 @@ module.exports = function (RED) {
168
180
  payload: state,
169
181
  });
170
182
  }
171
- node.setNodeStatusHue({
172
- fill: "green",
173
- shape: "dot",
174
- text: "KNX->HUE",
175
- payload: msg.payload,
176
- });
177
183
  break;
178
184
  case config.GALightBrightness:
179
185
  msg.payload = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptLightBrightness));
@@ -424,6 +430,9 @@ module.exports = function (RED) {
424
430
  // IMPORTANT: exit if no button last_event present.
425
431
  if (_event.initializingAtStart === true && node.readStatusAtStartup === "no") return;
426
432
 
433
+ // Output the msg to the flow
434
+ node.send(_event);
435
+
427
436
  if (_event.hasOwnProperty("on")) {
428
437
  node.updateKNXLightState(_event.on.on);
429
438
  // In case of switch off, set the dim to zero
@@ -433,13 +442,6 @@ module.exports = function (RED) {
433
442
  ) {
434
443
  node.updateKNXBrightnessState(0);
435
444
  node.currentHUEDevice.dimming.brightness = 0;
436
- } else {
437
- // // Sends the previous brightness value
438
- // try {
439
- // node.updateKNXBrightnessState(node.currentHUEDevice.dimming.brightness);
440
- // } catch (error) {
441
- // /* empty */
442
- // }
443
445
  }
444
446
  node.currentHUEDevice.on.on = _event.on.on;
445
447
  }
@@ -473,24 +475,9 @@ module.exports = function (RED) {
473
475
  }
474
476
  if (_event.hasOwnProperty("color_temperature") && _event.color_temperature.mirek !== undefined) {
475
477
  node.updateKNXLightHSVState(_event.color_temperature.mirek);
478
+ node.updateKNXLightKelvinState(_event.color_temperature.mirek);
476
479
  node.currentHUEDevice.color_temperature.mirek = _event.color_temperature.mirek;
477
480
  }
478
-
479
- // // Update the current HUE Device with the new _event
480
- // function copiaOggettoRicorsivo(objDestinazione, objOrigine) {
481
- // for (const prop in objOrigine) {
482
- // if (typeof objOrigine[prop] === "object" && objOrigine[prop] !== null) {
483
- // // Se la proprietà è un oggetto, copiamola in modo ricorsivo
484
- // objDestinazione[prop] = objDestinazione[prop] || {};
485
- // copiaOggettoRicorsivo(objDestinazione[prop], objOrigine[prop]);
486
- // } else {
487
- // // Altrimenti, copia il valore della proprietà
488
- // objDestinazione[prop] = objOrigine[prop];
489
- // }
490
- // }
491
- // }
492
- // // Copia l'oggettoOrigine nell'oggettoDestinazione mantenendo le proprietà esistenti
493
- // copiaOggettoRicorsivo(node.currentHUEDevice, _event);
494
481
  }
495
482
  } catch (error) {
496
483
  node.status({
@@ -617,7 +604,32 @@ module.exports = function (RED) {
617
604
  }
618
605
  };
619
606
 
620
-
607
+ node.updateKNXLightKelvinState = function updateKNXLightKelvinState(_value) {
608
+ if (config.GALightKelvinState !== undefined && config.GALightKelvinState !== "") {
609
+ const knxMsgPayload = {};
610
+ knxMsgPayload.topic = config.GALightKelvinState;
611
+ knxMsgPayload.dpt = config.dptLightKelvinState;
612
+ if (config.dptLightKelvinState === "7.600") {
613
+ knxMsgPayload.payload = hueColorConverter.ColorConverter.scale(_value, [153, 500], [0, 65535]);
614
+ // Send to KNX bus
615
+ if (knxMsgPayload.topic !== "" && knxMsgPayload.topic !== undefined) {
616
+ node.server.writeQueueAdd({
617
+ grpaddr: knxMsgPayload.topic,
618
+ payload: knxMsgPayload.payload,
619
+ dpt: knxMsgPayload.dpt,
620
+ outputtype: "write",
621
+ nodecallerid: node.id,
622
+ });
623
+ }
624
+ node.setNodeStatusHue({
625
+ fill: "blue",
626
+ shape: "ring",
627
+ text: "HUE->KNX Kelvin",
628
+ payload: knxMsgPayload.payload,
629
+ });
630
+ }
631
+ }
632
+ };
621
633
 
622
634
  // On each deploy, unsubscribe+resubscribe
623
635
  if (node.server) {
@@ -625,11 +637,39 @@ module.exports = function (RED) {
625
637
  node.server.addClient(node);
626
638
  }
627
639
  if (node.serverHue) {
628
- node.serverHue.removeClient(node);
629
- node.serverHue.addClient(node);
640
+ try {
641
+ node.serverHue.removeClient(node);
642
+ node.serverHue.addClient(node);
643
+ } catch (error) {
644
+ RED.log.error("knxUltimateHueLight: if (node.server): " + error.message);
645
+ }
630
646
  }
631
647
 
632
- node.on("input", (msg) => { });
648
+ node.on('input', (msg, send, done) => {
649
+ try {
650
+ const state = RED.util.cloneMessage(msg);
651
+ node.serverHue.hueManager.writeHueQueueAdd(node.hueDevice, state, node.isGrouped_light === false ? "setLight" : "setGroupedLight");
652
+ node.setNodeStatusHue({
653
+ fill: "green",
654
+ shape: "dot",
655
+ text: "->HUE",
656
+ payload: "Flow msg.",
657
+ });
658
+ } catch (error) {
659
+ node.setNodeStatusHue({
660
+ fill: "red",
661
+ shape: "dot",
662
+ text: "->HUE",
663
+ payload: error.message,
664
+ });
665
+ }
666
+ // Once finished, call 'done'.
667
+ // This call is wrapped in a check that 'done' exists
668
+ // so the node will work in earlier versions of Node-RED (<1.0)
669
+ if (done) {
670
+ done();
671
+ }
672
+ });
633
673
 
634
674
  node.on("close", (done) => {
635
675
  if (node.server) {
@@ -227,7 +227,7 @@
227
227
 
228
228
 
229
229
  </script>
230
- <script src="https://kit.fontawesome.com/11f26b4500.js" crossorigin="anonymous"></script>
230
+ <script src="http://localhost:1880/resources/node-red-contrib-knx-ultimate/11f26b4500.js"></script>
231
231
 
232
232
  <script type="text/markdown" data-help-name="knxUltimateHueLightSensor">
233
233
  This node lets you get the events from your HUE motion device.
@@ -11,7 +11,6 @@
11
11
  namemotion: { value: "" },
12
12
  GAmotion: { value: "" },
13
13
  dptmotion: { value: "" },
14
- readStatusAtStartup: { value: "no" },
15
14
 
16
15
  hueDevice: { value: "" }
17
16
  },
@@ -212,21 +211,13 @@
212
211
  <input type="text" id="node-input-namemotion" style="width:200px;margin-left: 5px; text-align: left;">
213
212
  </div>
214
213
 
215
- <div class="form-row">
216
- <label style="width:180px" for="node-input-readStatusAtStartup"><i class="fa fa-play-circle"></i> Read status at startup</label>
217
- <select id="node-input-readStatusAtStartup">
218
- <option value="no">No</option>
219
- <option value="yes">Yes, and emit KNX telegrams.</option>
220
- </select>
221
- </div>
222
-
223
214
  <br/>
224
215
  <br/>
225
216
  <br/>
226
217
 
227
218
 
228
219
  </script>
229
- <script src="https://kit.fontawesome.com/11f26b4500.js" crossorigin="anonymous"></script>
220
+ <script src="http://localhost:1880/resources/node-red-contrib-knx-ultimate/11f26b4500.js"></script>
230
221
 
231
222
  <script type="text/markdown" data-help-name="knxUltimateHueMotion">
232
223
  This node lets you get the events from your HUE motion device.
@@ -244,7 +235,6 @@ Start typing in the GA field, the name or group address of your KNX device, the
244
235
  |Property|Description|
245
236
  |--|--|
246
237
  | Motion | As soon as someone moves in the motion device's range, a *true* KNX value is sent to this group address, otherwise *false* is sent. |
247
- | Read status at startup | Read the status at startup and emit the event to the KNX bus at startup/reconnection. (Default "no")|
248
238
 
249
239
  ### Outputs
250
240
 
@@ -42,7 +42,7 @@ module.exports = function (RED) {
42
42
  if (_event.id === config.hueDevice) {
43
43
 
44
44
  // IMPORTANT: exit if no event presen.
45
- if (_event.initializingAtStart === true && (config.readStatusAtStartup === undefined || config.readStatusAtStartup === "no")) return;
45
+ if (_event.initializingAtStart === true) return;
46
46
  if (!_event.hasOwnProperty("motion") || _event.motion.motion === undefined) return;
47
47
 
48
48
 
@@ -12,7 +12,10 @@
12
12
  GAscene: { value: "" },
13
13
  dptscene: { value: "" },
14
14
  valscene: { value: 0 }, // the scene number or true/false
15
- readStatusAtStartup: { value: "no" },
15
+
16
+ enableNodePINS: { value: "no" },
17
+ outputs: { value: 0 },
18
+ inputs: { value: 0 },
16
19
 
17
20
  hueDevice: { value: "" },
18
21
  hueSceneRecallType: { value: "active" }
@@ -168,7 +171,13 @@
168
171
 
169
172
  },
170
173
  oneditsave: function () {
171
-
174
+ if ($("#node-input-enableNodePINS").val() === "yes") {
175
+ this.outputs = 1;
176
+ this.inputs = 1;
177
+ } else {
178
+ this.outputs = 0;
179
+ this.inputs = 0;
180
+ }
172
181
 
173
182
  },
174
183
  oneditcancel: function () {
@@ -252,13 +261,21 @@
252
261
  <select id="node-input-valscene" style="width:180px;margin-left: 5px; text-align: left;"></select>
253
262
  </div>
254
263
 
264
+ <br/>
265
+ <br/>
266
+
267
+ <p>
268
+ <b>BEHAVIOUR</b>
269
+ </p>
270
+
255
271
  <div class="form-row">
256
- <label style="width:180px" for="node-input-readStatusAtStartup"><i class="fa fa-play-circle"></i> Read status at startup</label>
257
- <select id="node-input-readStatusAtStartup">
258
- <option value="no">No</option>
259
- <option value="yes">Yes, and emit KNX telegrams.</option>
272
+ <label for="node-input-enableNodePINS" style="width:240px;">
273
+ <i class="fa fa-square-o"></i> Node Input/Output PINs
274
+ </label>
275
+ <select id="node-input-enableNodePINS">
276
+ <option value="no">Hide</option>
277
+ <option value="yes">Show node input/output PINs</option>
260
278
  </select>
261
- </div>
262
279
 
263
280
  <br/>
264
281
  <br/>
@@ -266,7 +283,7 @@
266
283
 
267
284
 
268
285
  </script>
269
- <script src="https://kit.fontawesome.com/11f26b4500.js" crossorigin="anonymous"></script>
286
+ <script src="http://localhost:1880/resources/node-red-contrib-knx-ultimate/11f26b4500.js"></script>
270
287
 
271
288
  <script type="text/markdown" data-help-name="knxUltimateHueScene">
272
289
  This node lets you recall a HUE scene, via KNX.
@@ -280,12 +297,18 @@ Start typing in the GA field, the name or group address of your KNX device, the
280
297
  | HUE Bridge | Select the HUE Bridge to be used |
281
298
  | Hue Scene | HUE scene to control. The avaiable scenes start showing up while you're typing.|
282
299
 
300
+ **KNX**
283
301
 
284
302
  |Property|Description|
285
303
  |--|--|
286
304
  | Recall | Choose your group address to be used for recalling the HUE scene. In case of Datapoint 1.x, send *true* to that group address to recall the scene, *false* to switch off all lights belonging to the scene. |
287
305
  | # | Select the KNX scene number. Visible only with datapoint 18.001. |
288
- | Read status at startup | Read the status at startup and emit the event to the KNX bus at startup/reconnection. (Default "no")|
306
+
307
+ **BEHAVIOUR**
308
+
309
+ |Property|Description|
310
+ |--|--|
311
+ | Node Input/Output PINs | Hide or show the input/output PINs. Input/output PINS allow the node to accept msg input from the flow and send msg output to the flow. Input msg must follow the HUE API v.2 Standards. Please refer to the [official HUE Api page](https://developers.meethue.com/develop/hue-api-v2/api-reference/#resource_light__id__put) |
289
312
 
290
313
  <br/>
291
314
 
@@ -75,31 +75,16 @@ module.exports = function (RED) {
75
75
  }
76
76
  };
77
77
 
78
- node.handleSendHUE = _event => {
78
+ node.handleSendHUE = (_event) => {
79
79
  try {
80
80
  if (_event.id === config.hueDevice) {
81
81
 
82
82
  // IMPORTANT: exit if no event presen.
83
- if (_event.initializingAtStart === true && (config.readStatusAtStartup === undefined || config.readStatusAtStartup === "no")) return;
83
+ if (_event.initializingAtStart === true) return;
84
84
 
85
- // const knxMsgPayload = {}
86
- // knxMsgPayload.topic = config.GAmotion
87
- // knxMsgPayload.dpt = config.dptmotion
85
+ // Output the msg to the flow
86
+ node.send(_event);
88
87
 
89
- // if (_event.hasOwnProperty('motion') && _event.motion.hasOwnProperty('motion')) {
90
- // knxMsgPayload.payload = _event.motion.motion_report.motion
91
- // // Send to KNX bus
92
- // if (knxMsgPayload.topic !== '' && knxMsgPayload.topic !== undefined) node.server.writeQueueAdd({ grpaddr: knxMsgPayload.topic, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: 'write', nodecallerid: node.id })
93
- // node.status({ fill: 'green', shape: 'dot', text: 'HUE->KNX ' + JSON.stringify(knxMsgPayload.payload) + ' (' + new Date().getDate() + ', ' + new Date().toLocaleTimeString() + ')' })
94
-
95
- // // Setup the output msg
96
- // knxMsgPayload.name = node.name
97
- // knxMsgPayload.event = 'motion'
98
-
99
- // // Send payload
100
- // knxMsgPayload.rawEvent = _event
101
- // node.send(knxMsgPayload)
102
- // }
103
88
  }
104
89
  } catch (error) {
105
90
  node.status({ fill: 'red', shape: 'dot', text: 'HUE->KNX error ' + error.message + ' (' + new Date().getDate() + ', ' + new Date().toLocaleTimeString() + ')' });
@@ -116,10 +101,31 @@ module.exports = function (RED) {
116
101
  node.serverHue.addClient(node);
117
102
  }
118
103
 
119
- node.on('input', function (msg) {
120
-
104
+ node.on('input', (msg, send, done) => {
105
+ try {
106
+ const state = RED.util.cloneMessage(msg);
107
+ node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, state, 'setScene');
108
+ node.setNodeStatusHue({
109
+ fill: "green",
110
+ shape: "dot",
111
+ text: "->HUE",
112
+ payload: "Flow msg.",
113
+ });
114
+ } catch (error) {
115
+ node.setNodeStatusHue({
116
+ fill: "red",
117
+ shape: "dot",
118
+ text: "->HUE",
119
+ payload: error.message,
120
+ });
121
+ }
122
+ // Once finished, call 'done'.
123
+ // This call is wrapped in a check that 'done' exists
124
+ // so the node will work in earlier versions of Node-RED (<1.0)
125
+ if (done) {
126
+ done();
127
+ }
121
128
  });
122
-
123
129
  node.on('close', function (done) {
124
130
  if (node.server) {
125
131
  node.server.removeClient(node);
@@ -11,7 +11,6 @@
11
11
  namerepeat: { value: "" },
12
12
  GArepeat: { value: "" },
13
13
  dptrepeat: { value: "" },
14
- readStatusAtStartup: { value: "no" },
15
14
 
16
15
  hueDevice: { value: "" }
17
16
  },
@@ -214,23 +213,13 @@
214
213
  <input type="text" id="node-input-namerepeat" style="width:200px;margin-left: 5px; text-align: left;">
215
214
  </div>
216
215
 
217
-
218
- <div class="form-row">
219
- <label style="width:180px" for="node-input-readStatusAtStartup"><i class="fa fa-play-circle"></i> Read status at startup</label>
220
- <select id="node-input-readStatusAtStartup">
221
- <option value="no">No</option>
222
- <option value="yes">Yes, and emit KNX telegrams.</option>
223
- </select>
224
- </div>
225
-
226
-
227
216
  <br/>
228
217
  <br/>
229
218
  <br/>
230
219
 
231
220
 
232
221
  </script>
233
- <script src="https://kit.fontawesome.com/11f26b4500.js" crossorigin="anonymous"></script>
222
+ <script src="http://localhost:1880/resources/node-red-contrib-knx-ultimate/11f26b4500.js"></script>
234
223
 
235
224
  <script type="text/markdown" data-help-name="knxUltimateHueTapDial">
236
225
  This node lets you get the events from your HUE rotary device, for example the Tap Dial.
@@ -252,7 +241,6 @@ or a random color (Datapoint 232.600) to the selected group address.
252
241
  |Property|Description|
253
242
  |--|--|
254
243
  | Rotate | This command is used either to send DIM (increase/decrease), aboslute brightness, or a random color, depending on the selected datapoint. If the random color (datapoint 232.600) is selected, **clockwise rotation** changes random colors and **counterclockwise rotation** set the light to **white** |
255
- | Read status at startup | Read the status at startup and emit the event to the KNX bus at startup/reconnection. (Default "no")|
256
244
 
257
245
  ### Outputs
258
246
 
@@ -51,7 +51,7 @@ module.exports = function (RED) {
51
51
  if (_event.id === config.hueDevice) {
52
52
 
53
53
  // IMPORTANT: exit if no event presen.
54
- if (_event.initializingAtStart === true && (config.readStatusAtStartup === undefined || config.readStatusAtStartup === "no")) return;
54
+ if (_event.initializingAtStart === true) return;
55
55
  if (!_event.hasOwnProperty("relative_rotary")
56
56
  || !_event.relative_rotary.hasOwnProperty("last_event")
57
57
  || _event.relative_rotary.last_event === undefined
@@ -218,13 +218,13 @@
218
218
 
219
219
 
220
220
  </script>
221
- <script src="https://kit.fontawesome.com/11f26b4500.js" crossorigin="anonymous"></script>
221
+ <script src="http://localhost:1880/resources/node-red-contrib-knx-ultimate/11f26b4500.js"></script>
222
222
 
223
- <script type="text/markdown" data-help-name="knxUltimateHueTemperatureSensor"
224
- This node lets you get the events from your HUE temperature device.
225
-
226
- Here you can get the HUE temperature events, that represents a celsius value, evetytime the ambient temp changes.<br/>
227
- Start typing in the GA field, the name or group address of your KNX device, the avaiable devices start showing up while you're typing.
223
+ <script type="text/markdown" data-help-name="knxUltimateHueTemperatureSensor" This node lets you get the events from
224
+ your HUE temperature device. Here you can get the HUE temperature events, that represents a celsius value, evetytime
225
+ the ambient temp changes.<br />
226
+ Start typing in the GA field, the name or group address of your KNX device, the avaiable devices start showing up while
227
+ you're typing.
228
228
 
229
229
  **General**
230
230
  |Property|Description|
@@ -245,13 +245,13 @@ Start typing in the GA field, the name or group address of your KNX device, the
245
245
 
246
246
  ### Details
247
247
 
248
- `msg.payload` is used as the payload of the published message.
249
- It contains the detailed event sent by your Hue devicem so you can use it for whatever you want.
248
+ `msg.payload` is used as the payload of the published message.
249
+ It contains the detailed event sent by your Hue devicem so you can use it for whatever you want.
250
250
 
251
- <br/>
251
+ <br />
252
252
 
253
253
  [Find it useful?](https://www.paypal.me/techtoday)
254
254
 
255
- <br/>
255
+ <br />
256
256
 
257
- </script>
257
+ </script>
@@ -1,6 +1,5 @@
1
-
2
1
  module.exports = function (RED) {
3
- function knxUltimateSceneController (config) {
2
+ function knxUltimateSceneController(config) {
4
3
  const fs = require('fs')
5
4
  const path = require('path')
6
5
  const mkdirp = require('mkdirp')
@@ -43,7 +42,7 @@ module.exports = function (RED) {
43
42
  })
44
43
 
45
44
  // 03/09/2021
46
- async function delay (ms) {
45
+ async function delay(ms) {
47
46
  return new Promise(function (resolve, reject) {
48
47
  try {
49
48
  node.timerWait = setTimeout(resolve, ms)
@@ -53,7 +52,7 @@ module.exports = function (RED) {
53
52
  })
54
53
  }
55
54
 
56
- function setupDirectory (aPath) {
55
+ function setupDirectory(aPath) {
57
56
  try {
58
57
  return fs.statSync(aPath).isDirectory()
59
58
  } catch (e) {
@@ -115,7 +114,7 @@ module.exports = function (RED) {
115
114
  }
116
115
 
117
116
  // 03/09/2021 Async function to allow await delay(x)
118
- async function RecallSceneAsync (_Payload, _ForceEvenControllerIsDisabled) {
117
+ async function RecallSceneAsync(_Payload, _ForceEvenControllerIsDisabled) {
119
118
  let curVal
120
119
  var newVal
121
120
 
@@ -1,6 +1,8 @@
1
1
  const convert = require('color-convert');
2
2
 
3
3
  class ColorConverter {
4
+
5
+
4
6
  static getGamutRanges() {
5
7
  const gamutA = {
6
8
  red: [0.704, 0.296],
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "engines": {
4
4
  "node": ">=16.0.0"
5
5
  },
6
- "version": "2.2.6",
6
+ "version": "2.2.10",
7
7
  "description": "Control your KNX intallation via Node-Red! Single Node KNX IN/OUT with optional ETS group address importer. Easy to use and highly configurable. With integrated Philips HUE devices control.",
8
8
  "dependencies": {
9
9
  "binary-parser": "2.2.1",
@@ -0,0 +1,2 @@
1
+ window.FontAwesomeKitConfig = { "asyncLoading": { "enabled": false }, "autoA11y": { "enabled": true }, "baseUrl": "https://ka-f.fontawesome.com", "baseUrlKit": "https://kit.fontawesome.com", "detectConflictsUntil": null, "iconUploads": {}, "id": 2836081, "license": "free", "method": "css", "minify": { "enabled": true }, "token": "11f26b4500", "v4FontFaceShim": { "enabled": true }, "v4shim": { "enabled": true }, "v5FontFaceShim": { "enabled": true }, "version": "6.4.2" };
2
+ !function (t) { "function" == typeof define && define.amd ? define("kit-loader", t) : t() }((function () { "use strict"; function t(t, e) { var n = Object.keys(t); if (Object.getOwnPropertySymbols) { var r = Object.getOwnPropertySymbols(t); e && (r = r.filter((function (e) { return Object.getOwnPropertyDescriptor(t, e).enumerable }))), n.push.apply(n, r) } return n } function e(e) { for (var n = 1; n < arguments.length; n++) { var o = null != arguments[n] ? arguments[n] : {}; n % 2 ? t(Object(o), !0).forEach((function (t) { r(e, t, o[t]) })) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(o)) : t(Object(o)).forEach((function (t) { Object.defineProperty(e, t, Object.getOwnPropertyDescriptor(o, t)) })) } return e } function n(t) { return (n = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (t) { return typeof t } : function (t) { return t && "function" == typeof Symbol && t.constructor === Symbol && t !== Symbol.prototype ? "symbol" : typeof t })(t) } function r(t, e, n) { return (e = function (t) { var e = function (t, e) { if ("object" != typeof t || null === t) return t; var n = t[Symbol.toPrimitive]; if (void 0 !== n) { var r = n.call(t, e || "default"); if ("object" != typeof r) return r; throw new TypeError("@@toPrimitive must return a primitive value.") } return ("string" === e ? String : Number)(t) }(t, "string"); return "symbol" == typeof e ? e : String(e) }(e)) in t ? Object.defineProperty(t, e, { value: n, enumerable: !0, configurable: !0, writable: !0 }) : t[e] = n, t } function o(t, e) { return function (t) { if (Array.isArray(t)) return t }(t) || function (t, e) { var n = null == t ? null : "undefined" != typeof Symbol && t[Symbol.iterator] || t["@@iterator"]; if (null != n) { var r, o, i, c, a = [], u = !0, f = !1; try { if (i = (n = n.call(t)).next, 0 === e) { if (Object(n) !== n) return; u = !1 } else for (; !(u = (r = i.call(n)).done) && (a.push(r.value), a.length !== e); u = !0); } catch (t) { f = !0, o = t } finally { try { if (!u && null != n.return && (c = n.return(), Object(c) !== c)) return } finally { if (f) throw o } } return a } }(t, e) || function (t, e) { if (!t) return; if ("string" == typeof t) return i(t, e); var n = Object.prototype.toString.call(t).slice(8, -1); "Object" === n && t.constructor && (n = t.constructor.name); if ("Map" === n || "Set" === n) return Array.from(t); if ("Arguments" === n || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return i(t, e) }(t, e) || function () { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.") }() } function i(t, e) { (null == e || e > t.length) && (e = t.length); for (var n = 0, r = new Array(e); n < e; n++)r[n] = t[n]; return r } function c(t, e) { var n = e && e.addOn || "", r = e && e.baseFilename || t.license + n, o = e && e.minify ? ".min" : "", i = e && e.fileSuffix || t.method, c = e && e.subdir || t.method; return t.baseUrl + "/releases/" + ("latest" === t.version ? "latest" : "v".concat(t.version)) + "/" + c + "/" + r + o + "." + i } function a(t, e) { var n = e || ["fa"], r = "." + Array.prototype.join.call(n, ",."), o = t.querySelectorAll(r); Array.prototype.forEach.call(o, (function (e) { var n = e.getAttribute("title"); e.setAttribute("aria-hidden", "true"); var r = !e.nextElementSibling || !e.nextElementSibling.classList.contains("sr-only"); if (n && r) { var o = t.createElement("span"); o.innerHTML = n, o.classList.add("sr-only"), e.parentNode.insertBefore(o, e.nextSibling) } })) } var u, f = function () { }, s = "undefined" != typeof global && void 0 !== global.process && "function" == typeof global.process.emit, l = "undefined" == typeof setImmediate ? setTimeout : setImmediate, d = []; function h() { for (var t = 0; t < d.length; t++)d[t][0](d[t][1]); d = [], u = !1 } function m(t, e) { d.push([t, e]), u || (u = !0, l(h, 0)) } function p(t) { var e = t.owner, n = e._state, r = e._data, o = t[n], i = t.then; if ("function" == typeof o) { n = "fulfilled"; try { r = o(r) } catch (t) { g(i, t) } } v(i, r) || ("fulfilled" === n && b(i, r), "rejected" === n && g(i, r)) } function v(t, e) { var r; try { if (t === e) throw new TypeError("A promises callback cannot return that same promise."); if (e && ("function" == typeof e || "object" === n(e))) { var o = e.then; if ("function" == typeof o) return o.call(e, (function (n) { r || (r = !0, e === n ? y(t, n) : b(t, n)) }), (function (e) { r || (r = !0, g(t, e)) })), !0 } } catch (e) { return r || g(t, e), !0 } return !1 } function b(t, e) { t !== e && v(t, e) || y(t, e) } function y(t, e) { "pending" === t._state && (t._state = "settled", t._data = e, m(A, t)) } function g(t, e) { "pending" === t._state && (t._state = "settled", t._data = e, m(S, t)) } function w(t) { t._then = t._then.forEach(p) } function A(t) { t._state = "fulfilled", w(t) } function S(t) { t._state = "rejected", w(t), !t._handled && s && global.process.emit("unhandledRejection", t._data, t) } function O(t) { global.process.emit("rejectionHandled", t) } function j(t) { if ("function" != typeof t) throw new TypeError("Promise resolver " + t + " is not a function"); if (this instanceof j == !1) throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function."); this._then = [], function (t, e) { function n(t) { g(e, t) } try { t((function (t) { b(e, t) }), n) } catch (t) { n(t) } }(t, this) } j.prototype = { constructor: j, _state: "pending", _then: null, _data: void 0, _handled: !1, then: function (t, e) { var n = { owner: this, then: new this.constructor(f), fulfilled: t, rejected: e }; return !e && !t || this._handled || (this._handled = !0, "rejected" === this._state && s && m(O, this)), "fulfilled" === this._state || "rejected" === this._state ? m(p, n) : this._then.push(n), n.then }, catch: function (t) { return this.then(null, t) } }, j.all = function (t) { if (!Array.isArray(t)) throw new TypeError("You must pass an array to Promise.all()."); return new j((function (e, n) { var r = [], o = 0; function i(t) { return o++, function (n) { r[t] = n, --o || e(r) } } for (var c, a = 0; a < t.length; a++)(c = t[a]) && "function" == typeof c.then ? c.then(i(a), n) : r[a] = c; o || e(r) })) }, j.race = function (t) { if (!Array.isArray(t)) throw new TypeError("You must pass an array to Promise.race()."); return new j((function (e, n) { for (var r, o = 0; o < t.length; o++)(r = t[o]) && "function" == typeof r.then ? r.then(e, n) : e(r) })) }, j.resolve = function (t) { return t && "object" === n(t) && t.constructor === j ? t : new j((function (e) { e(t) })) }, j.reject = function (t) { return new j((function (e, n) { n(t) })) }; var E = "function" == typeof Promise ? Promise : j; function P(t, e) { var n = e.fetch, r = e.XMLHttpRequest, o = e.token, i = t; return o && !function (t) { return t.indexOf("kit-upload.css") > -1 }(t) && ("URLSearchParams" in window ? (i = new URL(t)).searchParams.set("token", o) : i = i + "?token=" + encodeURIComponent(o)), i = i.toString(), new E((function (t, e) { if ("function" == typeof n) n(i, { mode: "cors", cache: "default" }).then((function (t) { if (t.ok) return t.text(); throw new Error("") })).then((function (e) { t(e) })).catch(e); else if ("function" == typeof r) { var o = new r; o.addEventListener("loadend", (function () { this.responseText ? t(this.responseText) : e(new Error("")) }));["abort", "error", "timeout"].map((function (t) { o.addEventListener(t, (function () { e(new Error("")) })) })), o.open("GET", i), o.send() } else { e(new Error("")) } })) } function _(t, e, n) { var r = t; return [[/(url\("?)\.\.\/\.\.\/\.\./g, function (t, n) { return "".concat(n).concat(e) }], [/(url\("?)\.\.\/webfonts/g, function (t, r) { return "".concat(r).concat(e, "/releases/v").concat(n, "/webfonts") }], [/(url\("?)https:\/\/kit-free([^.])*\.fontawesome\.com/g, function (t, n) { return "".concat(n).concat(e) }]].forEach((function (t) { var e = o(t, 2), n = e[0], i = e[1]; r = r.replace(n, i) })), r } function F(t, n) { var r = arguments.length > 2 && void 0 !== arguments[2] ? arguments[2] : function () { }, o = n.document || o, i = a.bind(a, o, ["fa", "fab", "fas", "far", "fal", "fad", "fak"]); t.autoA11y.enabled && r(i); var u = t.subsetPath && t.baseUrl + "/" + t.subsetPath, f = [{ id: "fa-main", addOn: void 0, url: u }]; if (t.v4shim && t.v4shim.enabled && f.push({ id: "fa-v4-shims", addOn: "-v4-shims" }), t.v5FontFaceShim && t.v5FontFaceShim.enabled && f.push({ id: "fa-v5-font-face", addOn: "-v5-font-face" }), t.v4FontFaceShim && t.v4FontFaceShim.enabled && f.push({ id: "fa-v4-font-face", addOn: "-v4-font-face" }), !u && t.customIconsCssPath) { var s = t.customIconsCssPath.indexOf("kit-upload.css") > -1 ? t.baseUrlKit : t.baseUrl, l = s + "/" + t.customIconsCssPath; f.push({ id: "fa-kit-upload", url: l }) } var d = f.map((function (r) { return new E((function (o, i) { var a = r.url || c(t, { addOn: r.addOn, minify: t.minify.enabled }), u = { id: r.id }, f = t.subset ? u : e(e(e({}, n), u), {}, { baseUrl: t.baseUrl, version: t.version, id: r.id, contentFilter: function (t, e) { return _(t, e.baseUrl, e.version) } }); P(a, n).then((function (t) { o(C(t, f)) })).catch(i) })) })); return E.all(d) } function C(t, e) { var n = e.contentFilter || function (t, e) { return t }, r = document.createElement("style"), o = document.createTextNode(n(t, e)); return r.appendChild(o), r.media = "all", e.id && r.setAttribute("id", e.id), e && e.detectingConflicts && e.detectionIgnoreAttr && r.setAttributeNode(document.createAttribute(e.detectionIgnoreAttr)), r } function I(t, n) { n.autoA11y = t.autoA11y.enabled, "pro" === t.license && (n.autoFetchSvg = !0, n.fetchSvgFrom = t.baseUrl + "/releases/" + ("latest" === t.version ? "latest" : "v".concat(t.version)) + "/svgs", n.fetchUploadedSvgFrom = t.uploadsUrl); var r = []; return t.v4shim.enabled && r.push(new E((function (r, o) { P(c(t, { addOn: "-v4-shims", minify: t.minify.enabled }), n).then((function (t) { r(U(t, e(e({}, n), {}, { id: "fa-v4-shims" }))) })).catch(o) }))), r.push(new E((function (r, o) { P(t.subsetPath && t.baseUrl + "/" + t.subsetPath || c(t, { minify: t.minify.enabled }), n).then((function (t) { var o = U(t, e(e({}, n), {}, { id: "fa-main" })); r(function (t, e) { var n = e && void 0 !== e.autoFetchSvg ? e.autoFetchSvg : void 0, r = e && void 0 !== e.autoA11y ? e.autoA11y : void 0; void 0 !== r && t.setAttribute("data-auto-a11y", r ? "true" : "false"); n && (t.setAttributeNode(document.createAttribute("data-auto-fetch-svg")), t.setAttribute("data-fetch-svg-from", e.fetchSvgFrom), t.setAttribute("data-fetch-uploaded-svg-from", e.fetchUploadedSvgFrom)); return t }(o, n)) })).catch(o) }))), E.all(r) } function U(t, e) { var n = document.createElement("SCRIPT"), r = document.createTextNode(t); return n.appendChild(r), n.referrerPolicy = "strict-origin", e.id && n.setAttribute("id", e.id), e && e.detectingConflicts && e.detectionIgnoreAttr && n.setAttributeNode(document.createAttribute(e.detectionIgnoreAttr)), n } function T(t) { var e, n = [], r = document, o = r.documentElement.doScroll, i = (o ? /^loaded|^c/ : /^loaded|^i|^c/).test(r.readyState); i || r.addEventListener("DOMContentLoaded", e = function () { for (r.removeEventListener("DOMContentLoaded", e), i = 1; e = n.shift();)e() }), i ? setTimeout(t, 0) : n.push(t) } function L(t) { "undefined" != typeof MutationObserver && new MutationObserver(t).observe(document, { childList: !0, subtree: !0 }) } try { if (window.FontAwesomeKitConfig) { var k = window.FontAwesomeKitConfig, x = { detectingConflicts: k.detectConflictsUntil && new Date <= new Date(k.detectConflictsUntil), detectionIgnoreAttr: "data-fa-detection-ignore", fetch: window.fetch, token: k.token, XMLHttpRequest: window.XMLHttpRequest, document: document }, M = document.currentScript, N = M ? M.parentElement : document.head; (function () { var t = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}, e = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : {}; return "js" === t.method ? I(t, e) : "css" === t.method ? F(t, e, (function (t) { T(t), L(t) })) : void 0 })(k, x).then((function (t) { t.map((function (t) { try { N.insertBefore(t, M ? M.nextSibling : null) } catch (e) { N.appendChild(t) } })), x.detectingConflicts && M && T((function () { M.setAttributeNode(document.createAttribute(x.detectionIgnoreAttr)); var t = function (t, e) { var n = document.createElement("script"); return e && e.detectionIgnoreAttr && n.setAttributeNode(document.createAttribute(e.detectionIgnoreAttr)), n.src = c(t, { baseFilename: "conflict-detection", fileSuffix: "js", subdir: "js", minify: t.minify.enabled }), n }(k, x); document.body.appendChild(t) })) })).catch((function (t) { console.error("".concat("Font Awesome Kit:", " ").concat(t)) })) } } catch (t) { console.error("".concat("Font Awesome Kit:", " ").concat(t)) } }));