node-red-contrib-knx-ultimate 2.1.40 → 2.1.42
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +13 -0
- package/CHANGELOG.md +12 -1
- package/nodes/hue-config.html +4 -12
- package/nodes/hue-config.js +194 -100
- package/nodes/knxUltimateHueLight.html +102 -541
- package/nodes/knxUltimateHueLight.js +16 -4
- package/nodes/knxUltimateHueLightSensor.js +1 -1
- package/nodes/knxUltimateHueTapDial.js +127 -93
- package/nodes/utils/hueEngine.js +122 -231
- package/package.json +15 -11
package/.eslintrc.json
ADDED
package/CHANGELOG.md
CHANGED
|
@@ -6,8 +6,19 @@
|
|
|
6
6
|
|
|
7
7
|
# CHANGELOG
|
|
8
8
|
|
|
9
|
+
<p>
|
|
10
|
+
<b>Version 2.1.42</b> - August 2023<br/>
|
|
11
|
+
- Fixed some issues in getting the hue device's names, when using some non LTS versions of node.js.<br/>
|
|
12
|
+
</p>
|
|
13
|
+
<p>
|
|
14
|
+
<b>Version 2.1.41</b> - August 2023<br/>
|
|
15
|
+
- NEW: HUE Light: you can now control ALL GROUPED LIGHT together.<br/>
|
|
16
|
+
- HUE Light: fixed an issue with the "Link brightness to on/off switch" option, when a json color is selected at daylight or nighttime<br/>
|
|
17
|
+
- HUE Light: New: now you can use both DPT 5.001 and 3.007 in the color temperature, at the same time.<br/>
|
|
18
|
+
</p>
|
|
19
|
+
<p>
|
|
9
20
|
<b>Version 2.1.40</b> - August 2023<br/>
|
|
10
|
-
- HUE Light:
|
|
21
|
+
- HUE Light: Bugfix: color cycle continues to cycle color, even if a FALSE is sent from the group address.<br/>
|
|
11
22
|
</p>
|
|
12
23
|
<p>
|
|
13
24
|
<b>Version 2.1.39</b> - August 2023<br/>
|
package/nodes/hue-config.html
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
}
|
|
19
19
|
$("#getinfocam").click(function () {
|
|
20
20
|
|
|
21
|
-
var myNotification = RED.notify("Please press the Link button on the HUE Bridge
|
|
21
|
+
var myNotification = RED.notify("Please press the Link button on the HUE Bridge",
|
|
22
22
|
{
|
|
23
23
|
modal: true,
|
|
24
24
|
fixed: true,
|
|
@@ -69,13 +69,7 @@
|
|
|
69
69
|
}
|
|
70
70
|
}]
|
|
71
71
|
});
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
72
|
});
|
|
77
|
-
|
|
78
|
-
|
|
79
73
|
},
|
|
80
74
|
oneditsave: function () {
|
|
81
75
|
|
|
@@ -96,9 +90,7 @@
|
|
|
96
90
|
|
|
97
91
|
<div class="form-row">
|
|
98
92
|
<label for="node-config-input-host">
|
|
99
|
-
<i class="fa fa-server"></i>
|
|
100
|
-
<span data-i18n="hue-config.properties.host" </span>
|
|
101
|
-
</label>
|
|
93
|
+
<i class="fa fa-server"></i> IP</label>
|
|
102
94
|
<input type="text" id="node-config-input-host" placeholder="Write here the HUE bridge's IP, then click CONNECT">
|
|
103
95
|
</div>
|
|
104
96
|
<div class="form-row">
|
|
@@ -127,11 +119,11 @@
|
|
|
127
119
|
|
|
128
120
|
<div class="form-row">
|
|
129
121
|
<label for="node-config-input-username"> Username</label>
|
|
130
|
-
<input type="password" id="node-config-input-username" placeholder="">
|
|
122
|
+
<input type="password" id="node-config-input-username" placeholder="" disabled>
|
|
131
123
|
</div>
|
|
132
124
|
<div class="form-row">
|
|
133
125
|
<label for="node-config-input-clientkey"> Bridge Key</label>
|
|
134
|
-
<input type="password" id="node-config-input-clientkey" placeholder="">
|
|
126
|
+
<input type="password" id="node-config-input-clientkey" placeholder="" disabled>
|
|
135
127
|
</div>
|
|
136
128
|
</div>
|
|
137
129
|
|
package/nodes/hue-config.js
CHANGED
|
@@ -1,149 +1,243 @@
|
|
|
1
|
-
|
|
2
|
-
const dptlib = require('
|
|
3
|
-
const
|
|
4
|
-
const loggerEngine = require('./utils/sysLogger
|
|
1
|
+
/* eslint-disable max-len */
|
|
2
|
+
const dptlib = require('../KNXEngine/src/dptlib');
|
|
3
|
+
const HueClass = require('./utils/hueEngine').classHUE;
|
|
4
|
+
const loggerEngine = require('./utils/sysLogger');
|
|
5
5
|
// Helpers
|
|
6
6
|
const sortBy = (field) => (a, b) => {
|
|
7
|
-
if (a[field] > b[field]) { return 1 }
|
|
8
|
-
}
|
|
7
|
+
if (a[field] > b[field]) { return 1; } return -1;
|
|
8
|
+
};
|
|
9
9
|
|
|
10
|
-
const onlyDptKeys = (kv) =>
|
|
11
|
-
return kv[0].startsWith('DPT')
|
|
12
|
-
}
|
|
10
|
+
const onlyDptKeys = (kv) => kv[0].startsWith('DPT');
|
|
13
11
|
|
|
14
|
-
const extractBaseNo = (kv) => {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
}
|
|
12
|
+
const extractBaseNo = (kv) => ({
|
|
13
|
+
subtypes: kv[1].subtypes,
|
|
14
|
+
base: parseInt(kv[1].id.replace('DPT', '')),
|
|
15
|
+
});
|
|
20
16
|
|
|
21
17
|
const convertSubtype = (baseType) => (kv) => {
|
|
22
|
-
const value = `${baseType.base}.${kv[0]}
|
|
18
|
+
const value = `${baseType.base}.${kv[0]}`;
|
|
23
19
|
// let sRet = value + " " + kv[1].name + (kv[1].unit === undefined ? "" : " (" + kv[1].unit + ")");
|
|
24
|
-
const sRet = value
|
|
20
|
+
const sRet = `${value} ${kv[1].name}`;
|
|
25
21
|
return {
|
|
26
22
|
value,
|
|
27
|
-
text: sRet
|
|
28
|
-
}
|
|
29
|
-
}
|
|
23
|
+
text: sRet,
|
|
24
|
+
};
|
|
25
|
+
};
|
|
30
26
|
|
|
31
27
|
const toConcattedSubtypes = (acc, baseType) => {
|
|
32
|
-
const subtypes =
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
.map(convertSubtype(baseType))
|
|
28
|
+
const subtypes = Object.entries(baseType.subtypes)
|
|
29
|
+
.sort(sortBy(0))
|
|
30
|
+
.map(convertSubtype(baseType));
|
|
36
31
|
|
|
37
|
-
return acc.concat(subtypes)
|
|
38
|
-
}
|
|
32
|
+
return acc.concat(subtypes);
|
|
33
|
+
};
|
|
39
34
|
|
|
40
35
|
module.exports = (RED) => {
|
|
41
|
-
'
|
|
36
|
+
RED.httpAdmin.get('/knxUltimateDpts', RED.auth.needsPermission('hue-config.read'), (req, res) => {
|
|
37
|
+
const dpts = Object.entries(dptlib)
|
|
38
|
+
.filter(onlyDptKeys)
|
|
39
|
+
.map(extractBaseNo)
|
|
40
|
+
.sort(sortBy('base'))
|
|
41
|
+
.reduce(toConcattedSubtypes, []);
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
Object.entries(dptlib)
|
|
46
|
-
.filter(onlyDptKeys)
|
|
47
|
-
.map(extractBaseNo)
|
|
48
|
-
.sort(sortBy('base'))
|
|
49
|
-
.reduce(toConcattedSubtypes, [])
|
|
50
|
-
|
|
51
|
-
res.json(dpts)
|
|
52
|
-
})
|
|
43
|
+
res.json(dpts);
|
|
44
|
+
});
|
|
53
45
|
|
|
54
46
|
function hueConfig(config) {
|
|
55
|
-
RED.nodes.createNode(this, config)
|
|
56
|
-
const node = this
|
|
57
|
-
node.host = config.host
|
|
58
|
-
node.nodeClients = [] // Stores the registered clients
|
|
59
|
-
node.loglevel = config.loglevel !== undefined ? config.loglevel : 'error' // 18/02/2020 Loglevel default error
|
|
60
|
-
node.sysLogger = null
|
|
47
|
+
RED.nodes.createNode(this, config);
|
|
48
|
+
const node = this;
|
|
49
|
+
node.host = config.host;
|
|
50
|
+
node.nodeClients = []; // Stores the registered clients
|
|
51
|
+
node.loglevel = config.loglevel !== undefined ? config.loglevel : 'error'; // 18/02/2020 Loglevel default error
|
|
52
|
+
node.sysLogger = null;
|
|
61
53
|
try {
|
|
62
|
-
node.sysLogger = loggerEngine.get({ loglevel: node.loglevel }) //
|
|
63
|
-
} catch (error) { }
|
|
64
|
-
node.name = (config.name === undefined || config.name === '') ? node.host : config.name
|
|
54
|
+
node.sysLogger = loggerEngine.get({ loglevel: node.loglevel }); // New logger to adhere to the loglevel selected in the config-window
|
|
55
|
+
} catch (error) { /* empty */ }
|
|
56
|
+
node.name = (config.name === undefined || config.name === '') ? node.host : config.name;
|
|
65
57
|
|
|
66
58
|
// Init HUE Utility
|
|
67
|
-
node.hueManager = new
|
|
68
|
-
|
|
69
|
-
// Event clip V2
|
|
70
|
-
node.hueManager.on('event', _event => {
|
|
71
|
-
node.nodeClients.forEach(_oClient => {
|
|
72
|
-
const oClient = RED.nodes.getNode(_oClient.id)
|
|
73
|
-
try {
|
|
74
|
-
if (oClient.handleSendHUE !== undefined) oClient.handleSendHUE(_event)
|
|
75
|
-
} catch (error) {
|
|
76
|
-
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.error('Errore node.hueManager.on(event): ' + error.message)
|
|
77
|
-
}
|
|
78
|
-
})
|
|
79
|
-
})
|
|
59
|
+
node.hueManager = new HueClass(node.host, node.credentials.username, node.credentials.clientkey, config.bridgeid, node.sysLogger);
|
|
80
60
|
|
|
81
|
-
|
|
61
|
+
// Connect to Bridge and get the resources
|
|
62
|
+
node.ConnectToHueBridge = async () => {
|
|
63
|
+
await node.hueManager.Connect();
|
|
64
|
+
// Handle events
|
|
82
65
|
try {
|
|
66
|
+
node.hueManager.removeAllListeners();
|
|
67
|
+
} catch (error) { /* empty */ }
|
|
68
|
+
node.hueManager.on('event', (_event) => {
|
|
69
|
+
node.nodeClients.forEach((_oClient) => {
|
|
70
|
+
const oClient = _oClient;
|
|
71
|
+
try {
|
|
72
|
+
if (oClient.handleSendHUE !== undefined) oClient.handleSendHUE(_event);
|
|
73
|
+
} catch (error) {
|
|
74
|
+
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.error(`Errore node.hueManager.on(event): ${error.message}`);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
// Connected
|
|
79
|
+
node.hueManager.on('connected', () => {
|
|
80
|
+
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.info('node.hueManager connected event');
|
|
81
|
+
});
|
|
82
|
+
// Initialize the http wrapper, to use the provided key.
|
|
83
|
+
// This http wrapper is used to get the data from HUE brigde
|
|
84
|
+
try {
|
|
85
|
+
// Load all resources, to avoid too many call to the HUE bridge and speed up the showing of the device mnames, during typing in the config window
|
|
86
|
+
node.hueAllResources = await node.hueManager.hueApiV2.get('/resource');
|
|
87
|
+
node.hueAllRooms = await node.hueManager.hueApiV2.get('/resource/room');
|
|
88
|
+
node.hueAllDevices = await node.hueManager.hueApiV2.get('/resource/device');
|
|
89
|
+
} catch (error) {
|
|
90
|
+
if (this.sysLogger !== undefined && this.sysLogger !== null) this.sysLogger.error(`KNXUltimatehueEngine: classHUE: getting resources: ${error.message}`);
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
(async () => {
|
|
95
|
+
await node.ConnectToHueBridge();
|
|
96
|
+
})();
|
|
97
|
+
|
|
98
|
+
RED.httpAdmin.get('/KNXUltimateGetResourcesHUE', RED.auth.needsPermission('hue-config.read'), (req, res) => {
|
|
99
|
+
try {
|
|
100
|
+
// °°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
|
|
101
|
+
const jRet = node.getResources(req.query.rtype);
|
|
102
|
+
res.json(jRet);
|
|
103
|
+
// °°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
|
|
104
|
+
} catch (error) {
|
|
105
|
+
RED.log.error(`Errore KNXUltimateGetResourcesHUE non gestito ${error.message}`);
|
|
106
|
+
res.json({ devices: error.message });
|
|
83
107
|
(async () => {
|
|
108
|
+
await node.ConnectToHueBridge();
|
|
109
|
+
})();
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// Get all devices and join it with relative rooms, by adding the room name to the device name
|
|
114
|
+
node.getResources = (_rtype) => {
|
|
115
|
+
try {
|
|
116
|
+
// Api V2
|
|
117
|
+
|
|
118
|
+
// Returns capitalized string
|
|
119
|
+
function capStr(s) {
|
|
120
|
+
if (typeof s !== 'string') return '';
|
|
121
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const retArray = [];
|
|
125
|
+
let allResources;
|
|
126
|
+
if (_rtype === 'light' || _rtype === 'grouped_light') {
|
|
127
|
+
allResources = node.hueAllResources.filter((a) => a.type === 'light' || a.type === 'grouped_light');
|
|
128
|
+
} else {
|
|
129
|
+
allResources = node.hueAllResources.filter((a) => a.type === _rtype);
|
|
130
|
+
}
|
|
131
|
+
for (let index = 0; index < allResources.length; index++) {
|
|
132
|
+
const resource = allResources[index];
|
|
133
|
+
// Get the owner
|
|
84
134
|
try {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
135
|
+
let resourceName = '';
|
|
136
|
+
let sRoom = '';
|
|
137
|
+
if (_rtype === 'light' || _rtype === 'grouped_light') {
|
|
138
|
+
// It's a service, having a owner
|
|
139
|
+
const owners = node.hueAllResources.filter((a) => a.id === resource.owner.rid);
|
|
140
|
+
for (let index = 0; index < owners.length; index++) {
|
|
141
|
+
const owner = owners[index];
|
|
142
|
+
if (owner.type === 'bridge_home') {
|
|
143
|
+
resourceName += 'ALL GROUPS and ';
|
|
144
|
+
} else {
|
|
145
|
+
resourceName += `${owner.metadata.name} and `;
|
|
146
|
+
const room = node.hueAllRooms.find((child) => child.children.find((a) => a.rid === owner.id));
|
|
147
|
+
sRoom += room !== undefined ? `${room.metadata.name} + ` : ' + ';
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
sRoom = sRoom.slice(0, -(' + '.length));
|
|
151
|
+
resourceName = resourceName.slice(0, -(' and '.length));
|
|
152
|
+
resourceName += sRoom !== '' ? ` - Room: ${sRoom}` : '';
|
|
153
|
+
retArray.push({ name: `${capStr(resource.type)}: ${resourceName}`, id: resource.id, deviceObject: resource });
|
|
154
|
+
}
|
|
155
|
+
if (_rtype === 'scene') {
|
|
156
|
+
resourceName = resource.metadata.name || '**Name Not Found**';
|
|
157
|
+
// Get the linked zone
|
|
158
|
+
const zone = node.hueAllResources.find((res) => res.id === resource.group.rid);
|
|
159
|
+
resourceName += ` - ${capStr(resource.group.rtype)}: ${zone.metadata.name}`;
|
|
160
|
+
retArray.push({ name: `${capStr(_rtype)}: ${resourceName}`, id: resource.id });
|
|
161
|
+
}
|
|
162
|
+
if (_rtype === 'button') {
|
|
163
|
+
const linkedDevName = node.hueAllResources.find((dev) => dev.type === 'device' && dev.services.find((serv) => serv.rid === resource.id)).metadata.name || '';
|
|
164
|
+
const controlID = resource.metadata !== undefined ? (resource.metadata.control_id || '') : '';
|
|
165
|
+
retArray.push({ name: `${capStr(_rtype)}: ${linkedDevName}, button ${controlID}`, id: resource.id });
|
|
166
|
+
}
|
|
167
|
+
if (_rtype === 'motion') {
|
|
168
|
+
const linkedDevName = node.hueAllResources.find((dev) => dev.type === 'device' && dev.services.find((serv) => serv.rid === resource.id)).metadata.name || '';
|
|
169
|
+
retArray.push({ name: `${capStr(_rtype)}: ${linkedDevName}`, id: resource.id });
|
|
170
|
+
}
|
|
171
|
+
if (_rtype === 'relative_rotary') {
|
|
172
|
+
const linkedDevName = node.hueAllResources.find((dev) => dev.type === 'device' && dev.services.find((serv) => serv.rid === resource.id)).metadata.name || '';
|
|
173
|
+
retArray.push({ name: `Rotary: ${linkedDevName}`, id: resource.id });
|
|
174
|
+
}
|
|
175
|
+
if (_rtype === 'light_level') {
|
|
176
|
+
const Room = node.hueAllRooms.find((room) => room.children.find((child) => child.rid === resource.owner.rid));
|
|
177
|
+
const linkedDevName = node.hueAllResources.find((dev) => dev.type === 'device' && dev.services.find((serv) => serv.rid === resource.id)).metadata.name || '';
|
|
178
|
+
retArray.push({ name: `Light Level: ${linkedDevName}${Room !== undefined ? `, room ${Room.metadata.name}` : ''}`, id: resource.id });
|
|
179
|
+
}
|
|
180
|
+
if (_rtype === 'temperature') {
|
|
181
|
+
const Room = node.hueAllRooms.find((room) => room.children.find((child) => child.rid === resource.owner.rid));
|
|
182
|
+
const linkedDevName = node.hueAllResources.find((dev) => dev.type === 'device' && dev.services.find((serv) => serv.rid === resource.id)).metadata.name || '';
|
|
183
|
+
retArray.push({ name: `Temperature: ${linkedDevName}${Room !== undefined ? `, room ${Room.metadata.name}` : ''}`, id: resource.id });
|
|
184
|
+
}
|
|
185
|
+
if (_rtype === 'device_power') {
|
|
186
|
+
const Room = node.hueAllRooms.find((room) => room.children.find((child) => child.rid === resource.owner.rid));
|
|
187
|
+
const linkedDevName = node.hueAllResources.find((dev) => dev.type === 'device' && dev.services.find((serv) => serv.rid === resource.id)).metadata.name || '';
|
|
188
|
+
retArray.push({ name: `Battery: ${linkedDevName}${Room !== undefined ? `, room ${Room.metadata.name}` : ''}`, id: resource.id });
|
|
189
|
+
}
|
|
190
|
+
} catch (error) {
|
|
191
|
+
retArray.push({ name: `${_rtype}: ERROR ${error.message}`, id: resource.id });
|
|
93
192
|
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
193
|
+
}
|
|
194
|
+
return { devices: retArray };
|
|
195
|
+
} catch (error) {
|
|
196
|
+
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.error(`KNXUltimateHue: hueEngine: classHUE: getDevices: error ${error.message}`);
|
|
197
|
+
return ({ devices: error.message });
|
|
98
198
|
}
|
|
99
|
-
}
|
|
199
|
+
};
|
|
100
200
|
|
|
101
201
|
node.addClient = (_Node) => {
|
|
102
202
|
// Check if node already exists
|
|
103
|
-
if (node.nodeClients.filter(x => x.id === _Node.id).length === 0) {
|
|
203
|
+
if (node.nodeClients.filter((x) => x.id === _Node.id).length === 0) {
|
|
104
204
|
// Add _Node to the clients array
|
|
105
|
-
_Node.setNodeStatusHue({ fill: 'grey', shape: 'ring', text: 'Hue initialized.' })
|
|
106
|
-
|
|
107
|
-
const jNode = {}
|
|
108
|
-
jNode.id = _Node.id
|
|
109
|
-
jNode.topic = _Node.topic
|
|
110
|
-
node.nodeClients.push(jNode)
|
|
205
|
+
_Node.setNodeStatusHue({ fill: 'grey', shape: 'ring', text: 'Hue initialized.' });
|
|
206
|
+
node.nodeClients.push(_Node);
|
|
111
207
|
}
|
|
112
|
-
}
|
|
208
|
+
};
|
|
113
209
|
|
|
114
210
|
node.removeClient = (_Node) => {
|
|
115
211
|
// Remove the client node from the clients array
|
|
116
212
|
try {
|
|
117
|
-
node.nodeClients = node.nodeClients.filter(x => x.id !== _Node.id)
|
|
213
|
+
node.nodeClients = node.nodeClients.filter((x) => x.id !== _Node.id);
|
|
118
214
|
} catch (error) { }
|
|
119
|
-
}
|
|
215
|
+
};
|
|
120
216
|
|
|
121
|
-
node.on('close',
|
|
217
|
+
node.on('close', (done) => {
|
|
122
218
|
try {
|
|
123
|
-
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger = null; loggerEngine.destroy()
|
|
124
|
-
node.nodeClients = []
|
|
219
|
+
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger = null; loggerEngine.destroy();
|
|
220
|
+
node.nodeClients = [];
|
|
125
221
|
node.hueManager.removeAllListeners();
|
|
126
222
|
(async () => {
|
|
127
223
|
try {
|
|
128
|
-
await node.hueManager.close()
|
|
129
|
-
node.hueManager = null
|
|
130
|
-
delete node.hueManager
|
|
131
|
-
} catch (error) {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
})()
|
|
224
|
+
await node.hueManager.close();
|
|
225
|
+
node.hueManager = null;
|
|
226
|
+
delete node.hueManager;
|
|
227
|
+
} catch (error) { /* empty */ }
|
|
228
|
+
done();
|
|
229
|
+
})();
|
|
135
230
|
} catch (error) {
|
|
136
|
-
done()
|
|
137
|
-
console.log(error.message)
|
|
231
|
+
done();
|
|
138
232
|
}
|
|
139
|
-
})
|
|
233
|
+
});
|
|
140
234
|
}
|
|
141
235
|
|
|
142
236
|
// RED.nodes.registerType("hue-config", hue-config);
|
|
143
237
|
RED.nodes.registerType('hue-config', hueConfig, {
|
|
144
238
|
credentials: {
|
|
145
239
|
username: { type: 'password' },
|
|
146
|
-
clientkey: { type: 'password' }
|
|
147
|
-
}
|
|
148
|
-
})
|
|
149
|
-
}
|
|
240
|
+
clientkey: { type: 'password' },
|
|
241
|
+
},
|
|
242
|
+
});
|
|
243
|
+
};
|