node-red-contrib-huemagic 3.0.0 → 4.0.3

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 (138) hide show
  1. package/README.md +764 -603
  2. package/examples/Hue Bridge.json +189 -35
  3. package/examples/Hue Brightness.json +124 -14
  4. package/examples/Hue Buttons.json +88 -0
  5. package/examples/Hue Group.json +575 -63
  6. package/examples/Hue Light.json +805 -58
  7. package/examples/Hue Magic.json +69 -24
  8. package/examples/Hue Motion.json +110 -42
  9. package/examples/Hue Rules.json +105 -37
  10. package/examples/Hue Scene.json +145 -28
  11. package/examples/Hue Temperature.json +124 -14
  12. package/huemagic/animations/019-christmas.json +30 -0
  13. package/huemagic/animations/020-superhero.json +56 -0
  14. package/huemagic/animations/021-neoncity.json +52 -0
  15. package/huemagic/animations/previews/006-fireplace.gif +0 -0
  16. package/huemagic/animations/previews/013-strobo.gif +0 -0
  17. package/huemagic/animations/previews/014-randomrainbow.gif +0 -0
  18. package/huemagic/animations/previews/016-milkyway.gif +0 -0
  19. package/huemagic/animations/previews/018-forest.gif +0 -0
  20. package/huemagic/animations/previews/019-christmas.gif +0 -0
  21. package/huemagic/animations/previews/020-superhero.gif +0 -0
  22. package/huemagic/animations/previews/021-neoncity.gif +0 -0
  23. package/huemagic/hue-bridge-config.html +105 -61
  24. package/huemagic/hue-bridge-config.js +631 -319
  25. package/huemagic/hue-bridge.html +41 -21
  26. package/huemagic/hue-bridge.js +190 -396
  27. package/huemagic/hue-brightness.html +127 -59
  28. package/huemagic/hue-brightness.js +111 -55
  29. package/huemagic/hue-buttons.html +197 -0
  30. package/huemagic/hue-buttons.js +158 -0
  31. package/huemagic/hue-group.html +137 -66
  32. package/huemagic/hue-group.js +387 -403
  33. package/huemagic/hue-light.html +132 -64
  34. package/huemagic/hue-light.js +544 -378
  35. package/huemagic/hue-magic.html +36 -20
  36. package/huemagic/hue-magic.js +49 -27
  37. package/huemagic/hue-motion.html +127 -59
  38. package/huemagic/hue-motion.js +105 -101
  39. package/huemagic/hue-rules.html +121 -50
  40. package/huemagic/hue-rules.js +89 -63
  41. package/huemagic/hue-scene.html +95 -114
  42. package/huemagic/hue-scene.js +64 -95
  43. package/huemagic/hue-temperature.html +127 -59
  44. package/huemagic/hue-temperature.js +120 -56
  45. package/huemagic/icons/hue-bridge.png +0 -0
  46. package/huemagic/icons/hue-bridge.svg +3 -0
  47. package/huemagic/icons/hue-brightness.png +0 -0
  48. package/huemagic/icons/hue-brightness.svg +11 -0
  49. package/huemagic/icons/hue-buttons.png +0 -0
  50. package/huemagic/icons/hue-buttons.svg +4 -0
  51. package/huemagic/icons/hue-group.png +0 -0
  52. package/huemagic/icons/hue-group.svg +12 -0
  53. package/huemagic/icons/hue-light.png +0 -0
  54. package/huemagic/icons/hue-light.svg +3 -0
  55. package/huemagic/icons/hue-magic.png +0 -0
  56. package/huemagic/icons/hue-magic.svg +3 -0
  57. package/huemagic/icons/hue-motion.png +0 -0
  58. package/huemagic/icons/hue-motion.svg +9 -0
  59. package/huemagic/icons/hue-rules.png +0 -0
  60. package/huemagic/icons/hue-rules.svg +4 -0
  61. package/huemagic/icons/hue-scene.png +0 -0
  62. package/huemagic/icons/hue-scene.svg +5 -0
  63. package/huemagic/icons/hue-temperature.png +0 -0
  64. package/huemagic/icons/hue-temperature.svg +7 -0
  65. package/huemagic/locales/de/hue-bridge-config.html +17 -12
  66. package/huemagic/locales/de/hue-bridge-config.json +7 -5
  67. package/huemagic/locales/de/hue-bridge.html +64 -145
  68. package/huemagic/locales/de/hue-bridge.json +6 -17
  69. package/huemagic/locales/de/hue-brightness.html +50 -50
  70. package/huemagic/locales/de/hue-brightness.json +6 -5
  71. package/huemagic/locales/de/hue-buttons.html +58 -0
  72. package/huemagic/locales/de/{hue-button.json → hue-buttons.json} +7 -11
  73. package/huemagic/locales/de/hue-group.html +46 -122
  74. package/huemagic/locales/de/hue-group.json +5 -5
  75. package/huemagic/locales/de/hue-light.html +68 -116
  76. package/huemagic/locales/de/hue-light.json +5 -5
  77. package/huemagic/locales/de/hue-magic.html +65 -57
  78. package/huemagic/locales/de/hue-magic.json +2 -1
  79. package/huemagic/locales/de/hue-motion.html +47 -50
  80. package/huemagic/locales/de/hue-motion.json +5 -5
  81. package/huemagic/locales/de/hue-rules.html +68 -73
  82. package/huemagic/locales/de/hue-rules.json +5 -2
  83. package/huemagic/locales/de/hue-scene.html +23 -32
  84. package/huemagic/locales/de/hue-scene.json +5 -2
  85. package/huemagic/locales/de/hue-temperature.html +51 -48
  86. package/huemagic/locales/de/hue-temperature.json +6 -5
  87. package/huemagic/locales/en-US/hue-bridge-config.html +17 -12
  88. package/huemagic/locales/en-US/hue-bridge-config.json +5 -3
  89. package/huemagic/locales/en-US/hue-bridge.html +64 -145
  90. package/huemagic/locales/en-US/hue-bridge.json +5 -16
  91. package/huemagic/locales/en-US/hue-brightness.html +50 -50
  92. package/huemagic/locales/en-US/hue-brightness.json +5 -2
  93. package/huemagic/locales/en-US/hue-buttons.html +58 -0
  94. package/huemagic/locales/en-US/{hue-button.json → hue-buttons.json} +6 -4
  95. package/huemagic/locales/en-US/hue-group.html +46 -122
  96. package/huemagic/locales/en-US/hue-group.json +4 -2
  97. package/huemagic/locales/en-US/hue-light.html +68 -116
  98. package/huemagic/locales/en-US/hue-light.json +4 -2
  99. package/huemagic/locales/en-US/hue-magic.html +65 -57
  100. package/huemagic/locales/en-US/hue-magic.json +3 -1
  101. package/huemagic/locales/en-US/hue-motion.html +47 -50
  102. package/huemagic/locales/en-US/hue-motion.json +4 -2
  103. package/huemagic/locales/en-US/hue-rules.html +68 -73
  104. package/huemagic/locales/en-US/hue-rules.json +5 -2
  105. package/huemagic/locales/en-US/hue-scene.html +23 -32
  106. package/huemagic/locales/en-US/hue-scene.json +5 -2
  107. package/huemagic/locales/en-US/hue-temperature.html +51 -48
  108. package/huemagic/locales/en-US/hue-temperature.json +5 -2
  109. package/huemagic/utils/api.js +290 -0
  110. package/huemagic/utils/color.js +382 -0
  111. package/{utils → huemagic/utils}/merge.js +4 -6
  112. package/huemagic/utils/messages.js +447 -0
  113. package/package.json +9 -15
  114. package/examples/Hue Button.json +0 -40
  115. package/examples/Hue Switch.json +0 -40
  116. package/examples/Hue Tap.json +0 -40
  117. package/huemagic/hue-button.html +0 -132
  118. package/huemagic/hue-button.js +0 -143
  119. package/huemagic/hue-switch.html +0 -132
  120. package/huemagic/hue-switch.js +0 -163
  121. package/huemagic/hue-tap.html +0 -132
  122. package/huemagic/hue-tap.js +0 -123
  123. package/huemagic/icons/hue-button.png +0 -0
  124. package/huemagic/icons/hue-switch.png +0 -0
  125. package/huemagic/icons/hue-tap.png +0 -0
  126. package/huemagic/locales/de/hue-button.html +0 -64
  127. package/huemagic/locales/de/hue-switch.html +0 -64
  128. package/huemagic/locales/de/hue-switch.json +0 -35
  129. package/huemagic/locales/de/hue-tap.html +0 -55
  130. package/huemagic/locales/de/hue-tap.json +0 -27
  131. package/huemagic/locales/en-US/hue-button.html +0 -64
  132. package/huemagic/locales/en-US/hue-switch.html +0 -64
  133. package/huemagic/locales/en-US/hue-switch.json +0 -35
  134. package/huemagic/locales/en-US/hue-tap.html +0 -55
  135. package/huemagic/locales/en-US/hue-tap.json +0 -27
  136. package/utils/color.js +0 -9
  137. package/utils/messages.js +0 -658
  138. package/utils/rgb.js +0 -188
@@ -0,0 +1,197 @@
1
+ <script type="text/x-red" data-template-name="hue-buttons">
2
+ <div class="form-row">
3
+ <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="hue-buttons.config.name"></span></label>
4
+ <input type="text" id="node-input-name" data-i18n="[placeholder]hue-buttons.config.input-name" style="width: calc(100% - 105px)">
5
+ </div>
6
+ <div class="form-row">
7
+ <label for="node-input-bridge"><i class="fa fa-server"></i> Bridge</label>
8
+ <input type="text" id="node-input-bridge" style="width: calc(100% - 105px)">
9
+ </div>
10
+ <div class="form-row">
11
+ <label for="node-input-sensorid"><i class="fa fa-hand-pointer-o"></i> <span data-i18n="hue-buttons.config.button"></span></label>
12
+ <div style="display: inline-flex; width: calc(100% - 105px)">
13
+ <div id="input-select-toggle" style="flex-grow: 1;">
14
+ <input type="text" id="node-input-sensorid" placeholder="00000000-0000-0000-0000-000000000000" style="width: 100%"/>
15
+ </div>
16
+ <button id="node-config-input-scan-buttons-sensors" type="button" class="red-ui-button" style="margin-left: 10px;">
17
+ <i class="fa fa-search"></i>
18
+ </button>
19
+ </div>
20
+ </div>
21
+ <div class="form-row" style="margin-top: 30px">
22
+ <div style="display: inline-flex; width: calc(100% - 105px)">
23
+ <input type="checkbox" id="node-input-skipevents" style="flex: 15px;">
24
+ <span data-i18n="hue-buttons.config.skipevents-node" style="width: 100%; margin-left: 10px;"></span>
25
+ </div>
26
+ </div>
27
+ <div class="form-row">
28
+ <div style="display: inline-flex; width: calc(100% - 105px)">
29
+ <input type="checkbox" id="node-input-initevents" style="flex: 15px;">
30
+ <span data-i18n="hue-buttons.config.sendinitevents-node" style="width: 100%; margin-left: 10px;"></span>
31
+ </div>
32
+ </div>
33
+ </script>
34
+
35
+ <script type="text/javascript">
36
+ RED.nodes.registerType('hue-buttons',{
37
+ category: 'HueMagic',
38
+ color: '#347aeb',
39
+ defaults: {
40
+ name: { value:"" },
41
+ bridge: { type: "hue-bridge", required: true },
42
+ sensorid: { value:"", required: false },
43
+ skipevents: { value: false },
44
+ initevents: { value: false }
45
+ },
46
+ align: 'left',
47
+ icon: "hue-buttons.png",
48
+ inputs: 1,
49
+ outputs: 1,
50
+ label: function() {
51
+ return this.name || this._("hue-buttons.node.title");
52
+ },
53
+ paletteLabel: function() {
54
+ return this._("hue-buttons.node.title");
55
+ },
56
+ inputLabels: function() {
57
+ return this._("hue-buttons.node.input");
58
+ },
59
+ outputLabels: function() {
60
+ return this._("hue-buttons.node.output");
61
+ },
62
+ oneditprepare: function()
63
+ {
64
+ const scope = this;
65
+ let options = [];
66
+
67
+ function manualInput()
68
+ {
69
+ // GET CURRENT SELECTED VALUE
70
+ var current = $('#node-input-sensorid').val();
71
+
72
+ // REMOVE SELECT FIELD
73
+ $('#input-select-toggle').empty();
74
+
75
+ // CREATE NEW INPUT FIELD
76
+ $('#input-select-toggle').append('<input type="text" id="node-input-sensorid" placeholder="00000000-0000-0000-0000-000000000000" style="width: 100%" value="'+current+'" />');
77
+
78
+ // CHANGE BUTTON ICON
79
+ var button = $("#node-config-input-scan-buttons-sensors");
80
+ var buttonIcon = button.find("i");
81
+ buttonIcon.removeClass("fa-pencil");
82
+ buttonIcon.addClass("fa-search");
83
+ }
84
+
85
+ function searchAndSelect()
86
+ {
87
+ // GET CURRENT BRIDGE CONFIGURATION
88
+ var bridgeConfig = RED.nodes.node($('#node-input-bridge option:selected').val());
89
+ if(!bridgeConfig) { return false; }
90
+
91
+ // GET CURRENT SELECTED VALUE
92
+ var current = $('#node-input-sensorid').val();
93
+
94
+ // TRIGGER SEARCHING NOTIFICATION
95
+ var notification = RED.notify(scope._("hue-buttons.config.searching"), { type: "compact", modal: true, fixed: true });
96
+
97
+ // GET THE SENSORS
98
+ $.get('hue/resources', { bridge: bridgeConfig.bridge, key: bridgeConfig.key, type: "button" })
99
+ .done( function(data) {
100
+ var allResources = JSON.parse(data);
101
+ if(allResources.length <= 0)
102
+ {
103
+ notification.close();
104
+ RED.notify(scope._("hue-buttons.config.none-found"), { type: "error" });
105
+ return false;
106
+ }
107
+
108
+ // SET OPTIONS
109
+ allResources.forEach(function(resource)
110
+ {
111
+ if(resource.model)
112
+ {
113
+ options[resource.id] = { value: resource.id, label: resource.name + " ("+resource.model+")" };
114
+ }
115
+ else
116
+ {
117
+ options[resource.id] = { value: resource.id, label: resource.name };
118
+ }
119
+ });
120
+
121
+ // SELECT CURRENT VALUE
122
+ $("#node-input-sensorid").typedInput({
123
+ types: [
124
+ {
125
+ value: current,
126
+ options: Object.values(options)
127
+ }
128
+ ]
129
+ });
130
+
131
+ // CHANGE BUTTON ICON
132
+ var button = $("#node-config-input-scan-buttons-sensors");
133
+ var buttonIcon = button.find("i");
134
+ buttonIcon.removeClass("fa-search");
135
+ buttonIcon.addClass("fa-pencil");
136
+
137
+ // CLOSE SEARCH NOTIFICATION
138
+ notification.close();
139
+ })
140
+ .fail(function()
141
+ {
142
+ notification.close();
143
+ RED.notify(scope._("hue-buttons.config.unknown-error"), "error");
144
+ });
145
+ }
146
+
147
+ // CHANGED SENSOR ID? -> REPLACE NAME (IF POSSIBLE)
148
+ $(document).on('change', '#node-input-sensorid', function(e)
149
+ {
150
+ let currentSelectedOptionID = $(e.currentTarget).val();
151
+ let currentSelectedOptionValue = (currentSelectedOptionID.length > 0 && options[currentSelectedOptionID]) ? options[currentSelectedOptionID].label : false;
152
+
153
+ if(currentSelectedOptionValue !== false)
154
+ {
155
+ $('#node-input-name').val(currentSelectedOptionValue.split(" (")[0]);
156
+ }
157
+ });
158
+
159
+ // TOGGLE SELECT/INPUT FIELD
160
+ $('#node-config-input-scan-buttons-sensors').click(function()
161
+ {
162
+ if($('#input-select-toggle').find(".red-ui-typedInput-container").length > 0)
163
+ {
164
+ manualInput();
165
+ }
166
+ else
167
+ {
168
+ searchAndSelect();
169
+ }
170
+ });
171
+ },
172
+ button: {
173
+ enabled: function() {
174
+ return (this.sensorid && this.sensorid.length > 1)
175
+ },
176
+ visible: function() {
177
+ return (this.sensorid && this.sensorid.length > 1)
178
+ },
179
+ onclick: function()
180
+ {
181
+ const node = this;
182
+ if(node.bridge)
183
+ {
184
+ $.ajax({
185
+ url: "inject/" + node.id,
186
+ type: "POST",
187
+ data: JSON.stringify({ __user_inject_props__: "status"}),
188
+ contentType: "application/json; charset=utf-8",
189
+ success: function (resp) {
190
+ RED.notify(node.name + ": " + node._("hue-buttons.node.statusmsg"), { type: "success", id: "status", timeout: 2000 });
191
+ }
192
+ });
193
+ }
194
+ }
195
+ }
196
+ });
197
+ </script>
@@ -0,0 +1,158 @@
1
+ module.exports = function(RED)
2
+ {
3
+ "use strict";
4
+
5
+ function HueButtons(config)
6
+ {
7
+ RED.nodes.createNode(this, config);
8
+
9
+ const scope = this;
10
+ const bridge = RED.nodes.getNode(config.bridge);
11
+
12
+ // NODE UI STATUS TIMEOUT
13
+ this.timeout = null;
14
+
15
+ // SAVE LAST COMMAND
16
+ this.lastCommand = null;
17
+
18
+ //
19
+ // CHECK CONFIG
20
+ if(bridge == null)
21
+ {
22
+ this.status({fill: "red", shape: "ring", text: "hue-buttons.node.not-configured"});
23
+ return false;
24
+ }
25
+
26
+ //
27
+ // UNIVERSAL MODE?
28
+ if(!config.sensorid)
29
+ {
30
+ this.status({fill: "grey", shape: "dot", text: "hue-buttons.node.universal"});
31
+ }
32
+
33
+ //
34
+ // UPDATE STATE
35
+ if(config.sensorid)
36
+ {
37
+ scope.status({fill: "grey", shape: "dot", text: "hue-buttons.node.waiting"});
38
+ }
39
+
40
+ //
41
+ // SUBSCRIBE TO UPDATES FROM THE BRIDGE
42
+ bridge.subscribe("button", config.sensorid, function(info)
43
+ {
44
+ let currentState = bridge.get("button", info.id);
45
+
46
+ // RESOURCE FOUND?
47
+ if(currentState !== false)
48
+ {
49
+ // SEND MESSAGE
50
+ if(!config.skipevents && currentState.payload.button !== false && (config.initevents || info.suppressMessage == false))
51
+ {
52
+ // SET LAST COMMAND
53
+ if(scope.lastCommand !== null)
54
+ {
55
+ currentState.command = scope.lastCommand;
56
+ }
57
+
58
+ // SEND STATE
59
+ scope.send(currentState);
60
+
61
+ // RESET LAST COMMAND
62
+ scope.lastCommand = null;
63
+ }
64
+
65
+ // NOT IN UNIVERAL MODE? -> CHANGE UI STATES
66
+ if(config.sensorid)
67
+ {
68
+ if(currentState.payload.button === false)
69
+ {
70
+ scope.status({fill: "grey", shape: "dot", text: "hue-buttons.node.waiting"});
71
+ }
72
+ else
73
+ {
74
+ var action = "";
75
+ switch (currentState.payload.action)
76
+ {
77
+ case "initial_press":
78
+ action = "(pressed)";
79
+ break;
80
+ case "repeat":
81
+ action = "(repeated)";
82
+ break;
83
+ case "short_release":
84
+ action = "(short press)";
85
+ break;
86
+ case "long_release":
87
+ action = "(long press)";
88
+ break;
89
+ case "double_short_release":
90
+ action = "(double pressed)";
91
+ break;
92
+ default:
93
+ action = "(pressed)";
94
+ }
95
+
96
+ scope.status({fill: "blue", shape: "dot", text: "Button #"+ currentState.payload.button + " " + action });
97
+
98
+ // RESET TO WAITING AFTER 3 SECONDS
99
+ if(scope.timeout !== null) { clearTimeout(scope.timeout); };
100
+ scope.timeout = setTimeout(function()
101
+ {
102
+ scope.status({fill: "grey", shape: "dot", text: "hue-buttons.node.waiting"});
103
+
104
+ // REMOVE OLD BUTTON STATES
105
+ for (const [oneButtonID, oneButton] of Object.entries(bridge.resources[config.sensorid]["services"]["button"]))
106
+ {
107
+ delete bridge.resources[config.sensorid]["services"]["button"][oneButtonID]["button"];
108
+ }
109
+ }, 3000);
110
+ }
111
+ }
112
+ }
113
+ });
114
+
115
+ //
116
+ // ON COMMAND
117
+ this.on('input', function(msg, send, done)
118
+ {
119
+ // REDEFINE SEND AND DONE IF NOT AVAILABLE
120
+ send = send || function() { scope.send.apply(scope,arguments); }
121
+ done = done || function() { scope.done.apply(scope,arguments); }
122
+
123
+ // SAVE LAST COMMAND
124
+ scope.lastCommand = RED.util.cloneMessage(msg);
125
+
126
+ // DEFINE SENSOR ID
127
+ const tempSensorID = (!config.sensorid && typeof msg.topic != 'undefined' && bridge.validResourceID.test(msg.topic) === true) ? msg.topic : config.sensorid;
128
+ if(!tempSensorID)
129
+ {
130
+ scope.error("Please submit a valid button ID.");
131
+ return false;
132
+ }
133
+
134
+ let currentState = bridge.get("button", tempSensorID);
135
+
136
+ // GET CURRENT STATE
137
+ if( (typeof msg.payload != 'undefined' && typeof msg.payload.status != 'undefined') || (typeof msg.__user_inject_props__ != 'undefined' && msg.__user_inject_props__ == "status") )
138
+ {
139
+ // SET LAST COMMAND
140
+ if(scope.lastCommand !== null)
141
+ {
142
+ currentState.command = scope.lastCommand;
143
+ }
144
+
145
+ // SEND STATE
146
+ scope.send(currentState);
147
+
148
+ // RESET LAST COMMAND
149
+ scope.lastCommand = null;
150
+
151
+ if(done) { done(); }
152
+ return true;
153
+ }
154
+ });
155
+ }
156
+
157
+ RED.nodes.registerType("hue-buttons", HueButtons);
158
+ }
@@ -1,38 +1,34 @@
1
1
  <script type="text/x-red" data-template-name="hue-group">
2
2
  <div class="form-row">
3
3
  <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="hue-group.config.name"></span></label>
4
- <input type="text" id="node-input-name" data-i18n="[placeholder]hue-group.config.input-name">
4
+ <input type="text" id="node-input-name" data-i18n="[placeholder]hue-group.config.input-name" style="width: calc(100% - 105px)">
5
5
  </div>
6
6
  <div class="form-row">
7
7
  <label for="node-input-bridge"><i class="fa fa-server"></i> Bridge</label>
8
- <input type="text" id="node-input-bridge">
8
+ <input type="text" id="node-input-bridge" style="width: calc(100% - 105px)">
9
9
  </div>
10
10
  <div class="form-row">
11
11
  <label for="node-input-groupid"><i class="fa fa-lightbulb-o"></i> <span data-i18n="hue-group.config.group"></span></label>
12
- <div style="display: inline-block; position: relative; width: 70%; height: 20px;">
13
- <div style="position: absolute; left: 0px; right: 40px;">
14
- <input type="text" id="node-input-groupid" placeholder="1" style="width: 100%"/>
12
+ <div style="display: inline-flex; width: calc(100% - 105px)">
13
+ <div id="input-select-toggle" style="flex-grow: 1;">
14
+ <input type="text" id="node-input-groupid" placeholder="00000000-0000-0000-0000-000000000000" style="width: 100%"/>
15
15
  </div>
16
- <a id="node-config-input-scan-groups" class="editor-button" style="position: absolute; right: 0px; top: 0px;">
16
+ <button id="node-config-input-scan-groups" type="button" class="red-ui-button" style="margin-left: 10px;">
17
17
  <i class="fa fa-search"></i>
18
- </a>
18
+ </button>
19
19
  </div>
20
20
  </div>
21
- <div class="form-row">
22
- <label for="node-input-colornamer"><i class="fa fa-check-circle"></i> <span data-i18n="hue-group.config.colornamer"></span></label>
23
- <label for="node-input-colornamer" style="width:70%">
24
- <input type="checkbox" id="node-input-colornamer" style="display:inline-block; width:22px; vertical-align:baseline;"><span data-i18n="hue-group.config.colornamer-info"></span>
25
- </label>
26
- </div>
27
- <div class="form-row">
28
- <label for="node-input-skipevents"><i class="fa fa-sign-out"></i> <span data-i18n="hue-group.config.skipevents"></span></label>
29
- <input type="checkbox" id="node-input-skipevents" style="display:inline-block; width:22px; vertical-align:baseline;"><span data-i18n="hue-group.config.skipevents-node"></span>
30
- </label>
21
+ <div class="form-row" style="margin-top: 30px">
22
+ <div style="display: inline-flex; width: calc(100% - 105px)">
23
+ <input type="checkbox" id="node-input-skipevents" style="flex: 15px;">
24
+ <span data-i18n="hue-group.config.skipevents-node" style="width: 100%; margin-left: 10px;"></span>
25
+ </div>
31
26
  </div>
32
27
  <div class="form-row">
33
- <label for="node-input-universalevents"><i class="fa fa-bullhorn"></i> <span data-i18n="hue-group.config.universalevents"></span></label>
34
- <input type="checkbox" id="node-input-universalevents" style="display:inline-block; width:22px; vertical-align:baseline;"><span data-i18n="hue-group.config.universalevents-node"></span>
35
- </label>
28
+ <div style="display: inline-flex; width: calc(100% - 105px)">
29
+ <input type="checkbox" id="node-input-initevents" style="flex: 15px;">
30
+ <span data-i18n="hue-group.config.sendinitevents-node" style="width: 100%; margin-left: 10px;"></span>
31
+ </div>
36
32
  </div>
37
33
  </script>
38
34
 
@@ -41,97 +37,172 @@
41
37
  category: 'HueMagic',
42
38
  color: '#f94f8b',
43
39
  defaults: {
44
- name: {value:""},
45
- bridge: {type: "hue-bridge", required: true},
46
- groupid: {value:"", required: false, validate: function(id) {
47
- if(id.length < 1) { return true; }
48
- else if(!isNaN(id)) { return true; }
49
- else { return false; }
50
- }},
51
- colornamer: {value: true},
52
- skipevents: {value: false},
53
- universalevents: {value: false}
40
+ name: { value:"" },
41
+ bridge: { type: "hue-bridge", required: true },
42
+ groupid: { value:"", required: false },
43
+ skipevents: { value: false },
44
+ initevents: { value: false }
54
45
  },
46
+ align: 'left',
47
+ icon: "hue-group.png",
55
48
  inputs: 1,
56
49
  outputs: 1,
50
+ label: function() {
51
+ return this.name || this._("hue-group.node.title");
52
+ },
53
+ paletteLabel: function() {
54
+ return this._("hue-group.node.title");
55
+ },
57
56
  inputLabels: function() {
58
57
  return this._("hue-group.node.input");
59
58
  },
60
59
  outputLabels: function() {
61
60
  return this._("hue-group.node.output");
62
61
  },
63
- align: 'right',
64
- icon: "hue-group.png",
65
- paletteLabel: function() {
66
- return this._("hue-group.node.title");
67
- },
68
- label: function() {
69
- return this.name || this._("hue-group.node.title");
70
- },
71
62
  oneditprepare: function()
72
63
  {
73
- var scope = this;
64
+ const scope = this;
65
+ let options = [];
74
66
 
75
- function manualGroupID()
67
+ function manualInput()
76
68
  {
69
+ // GET CURRENT SELECTED VALUE
77
70
  var current = $('#node-input-groupid').val();
78
- $('#node-input-groupid').replaceWith('<input type="text" id="node-input-groupid" style="width: 100%"/>');
79
- $('#node-input-groupid').val(current);
71
+
72
+ // REMOVE SELECT FIELD
73
+ $('#input-select-toggle').empty();
74
+
75
+ // CREATE NEW INPUT FIELD
76
+ $('#input-select-toggle').append('<input type="text" id="node-input-groupid" placeholder="00000000-0000-0000-0000-000000000000" style="width: 100%" value="'+current+'" />');
77
+
78
+ // CHANGE BUTTON ICON
79
+ var button = $("#node-config-input-scan-groups");
80
+ var buttonIcon = button.find("i");
81
+ buttonIcon.removeClass("fa-pencil");
82
+ buttonIcon.addClass("fa-search");
80
83
  }
81
84
 
82
- function searchAndSelectGroupID()
85
+ function searchAndSelect()
83
86
  {
87
+ // GET CURRENT BRIDGE CONFIGURATION
88
+ var bridgeConfig = RED.nodes.node($('#node-input-bridge option:selected').val());
89
+ if(!bridgeConfig) { return false; }
90
+
91
+ // GET CURRENT SELECTED VALUE
84
92
  var current = $('#node-input-groupid').val();
85
- $('#node-input-groupid').replaceWith('<select id="node-input-groupid" style="width: 100%"></select>');
86
- $('#node-input-groupid').append('<option selected="selected" value="null">'+scope._("hue-group.config.searching")+'</option>');
87
93
 
88
- var bridgeConfig = RED.nodes.node($('#node-input-bridge option:selected').val());
89
- $.get('hue/groups', {bridge: bridgeConfig.bridge, key: bridgeConfig.key})
90
- .done(function(data) {
91
- var groups = JSON.parse(data);
94
+ // TRIGGER SEARCHING NOTIFICATION
95
+ var notification = RED.notify(scope._("hue-group.config.searching"), { type: "compact", modal: true, fixed: true });
92
96
 
93
- if(groups.length <= 0)
97
+ // GET THE GROUPS
98
+ $.get('hue/resources', { bridge: bridgeConfig.bridge, key: bridgeConfig.key, type: "group" })
99
+ .done( function(data) {
100
+ var allResources = JSON.parse(data);
101
+ if(allResources.length <= 0)
94
102
  {
95
- RED.notify(scope._("hue-group.config.none-found"), "error");
103
+ notification.close();
104
+ RED.notify(scope._("hue-group.config.none-found"), { type: "error" });
105
+ return false;
96
106
  }
97
107
 
98
- // RESET OPTIONS
99
- $('#node-input-groupid').empty();
100
-
101
108
  // SET GROUPS AS OPTIONS
102
- $('#node-input-groupid').append('<option value="0">'+scope._("hue-group.config.all")+'</option>');
103
- groups.forEach(function(group)
109
+ allResources.forEach(function(resource)
104
110
  {
105
- $('#node-input-groupid').append('<option value="' + group.id + '">' + group.name + '</option>');
111
+ if(!resource.name) // ALL SPECIAL
112
+ {
113
+ options[resource.id] = { value: resource.id, label: "● "+scope._("hue-group.config.all") };
114
+ }
115
+ });
116
+
117
+ allResources.forEach(function(resource)
118
+ {
119
+ if(resource.name) // REGULAR GROUPS
120
+ {
121
+ if(resource.model)
122
+ {
123
+ options[resource.id] = { value: resource.id, label: resource.name + " ("+resource.model+")" };
124
+ }
125
+ else
126
+ {
127
+ options[resource.id] = { value: resource.id, label: resource.name };
128
+ }
129
+ }
106
130
  });
107
131
 
108
132
  // SELECT CURRENT VALUE
109
- $('#node-input-groupid').val(current);
133
+ $("#node-input-groupid").typedInput({
134
+ types: [
135
+ {
136
+ value: current,
137
+ options: Object.values(options)
138
+ }
139
+ ]
140
+ });
141
+
142
+ // CHANGE BUTTON ICON
143
+ var button = $("#node-config-input-scan-groups");
144
+ var buttonIcon = button.find("i");
145
+ buttonIcon.removeClass("fa-search");
146
+ buttonIcon.addClass("fa-pencil");
147
+
148
+ // CLOSE SEARCH NOTIFICATION
149
+ notification.close();
110
150
  })
111
151
  .fail(function()
112
152
  {
153
+ notification.close();
113
154
  RED.notify(scope._("hue-group.config.unknown-error"), "error");
114
155
  });
115
156
  }
116
157
 
117
- $(document).on('change', '#node-input-groupid', function()
158
+ // CHANGED GROUP ID? -> REPLACE NAME (IF POSSIBLE)
159
+ $(document).on('change', '#node-input-groupid', function(e)
118
160
  {
119
- var groupName = $('#node-input-groupid option:selected').text();
120
- if(groupName.length > 0)
161
+ let currentSelectedOptionID = $(e.currentTarget).val();
162
+ let currentSelectedOptionValue = (currentSelectedOptionID.length > 0 && options[currentSelectedOptionID]) ? options[currentSelectedOptionID].label : false;
163
+
164
+ if(currentSelectedOptionValue !== false)
121
165
  {
122
- $('#node-input-name').val(groupName);
166
+ $('#node-input-name').val(currentSelectedOptionValue.split(" (")[0]);
123
167
  }
124
168
  });
125
169
 
170
+ // TOGGLE SELECT/INPUT FIELD
126
171
  $('#node-config-input-scan-groups').click(function()
127
172
  {
128
- if($('#node-input-groupid').prop("tagName") === "INPUT")
173
+ if($('#input-select-toggle').find(".red-ui-typedInput-container").length > 0)
129
174
  {
130
- searchAndSelectGroupID();
131
- } else {
132
- manualGroupID();
175
+ manualInput();
176
+ }
177
+ else
178
+ {
179
+ searchAndSelect();
133
180
  }
134
181
  });
182
+ },
183
+ button: {
184
+ enabled: function() {
185
+ return (this.groupid && this.groupid.length > 1)
186
+ },
187
+ visible: function() {
188
+ return (this.groupid && this.groupid.length > 1)
189
+ },
190
+ onclick: function()
191
+ {
192
+ const node = this;
193
+ if(node.bridge)
194
+ {
195
+ $.ajax({
196
+ url: "inject/" + node.id,
197
+ type: "POST",
198
+ data: JSON.stringify({ __user_inject_props__: "status"}),
199
+ contentType: "application/json; charset=utf-8",
200
+ success: function (resp) {
201
+ RED.notify(node.name + ": " + node._("hue-group.node.statusmsg"), { type: "success", id: "status", timeout: 2000 });
202
+ }
203
+ });
204
+ }
205
+ }
135
206
  }
136
207
  });
137
208
  </script>