node-red-contrib-knx-ultimate 2.3.3 → 2.3.5
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/CHANGELOG.md +6 -0
- package/nodes/hue-config.js +2 -2
- package/nodes/knxUltimateAlerter.html +7 -7
- package/nodes/knxUltimateAlerter.js +154 -154
- package/nodes/knxUltimateHueButton.html +1 -1
- package/nodes/knxUltimateHueLight.html +57 -60
- package/nodes/knxUltimateHueLight.js +61 -44
- package/nodes/knxUltimateHueScene.html +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,12 @@
|
|
|
6
6
|
|
|
7
7
|
# CHANGELOG
|
|
8
8
|
|
|
9
|
+
**Version 2.3.5** - Jan 2024<br/>
|
|
10
|
+
- HUE Light: fixed multi HUE Bridge GUI issue.<br/>
|
|
11
|
+
|
|
12
|
+
**Version 2.3.4** - Jan 2024<br/>
|
|
13
|
+
- HUE Light: fixex tab "DIM/Brightness" inaccessible, when "KNX Brightness Status" was set to use the default knx behaviour.<br/>
|
|
14
|
+
|
|
9
15
|
**Version 2.3.3** - Jan 2024<br/>
|
|
10
16
|
- HUE Light: added a warning if you double click a node, with the node still querying the HUE Bridge for the device.<br/>
|
|
11
17
|
|
package/nodes/hue-config.js
CHANGED
|
@@ -207,8 +207,8 @@ module.exports = (RED) => {
|
|
|
207
207
|
shape: "ring",
|
|
208
208
|
text: "Ready :-)",
|
|
209
209
|
});
|
|
210
|
-
_node.currentHUEDevice = cloneDeep(oHUEDevice);
|
|
211
|
-
if (_node.initializingAtStart === true) _node.handleSendHUE(oHUEDevice);
|
|
210
|
+
_node.currentHUEDevice = cloneDeep(oHUEDevice); // Copy by Value and not by ref
|
|
211
|
+
if (_node.initializingAtStart === true) _node.handleSendHUE(oHUEDevice); // Pass by value
|
|
212
212
|
}
|
|
213
213
|
}
|
|
214
214
|
});
|
|
@@ -60,8 +60,8 @@
|
|
|
60
60
|
// Scene configuration
|
|
61
61
|
var previousValueType = { value: "prev", label: this._("switch.previous"), hasValue: false };
|
|
62
62
|
function resizeRule(rule) {
|
|
63
|
-
|
|
64
|
-
|
|
63
|
+
|
|
64
|
+
}
|
|
65
65
|
$("#node-input-rule-container").css('min-height', '350px').css('min-width', '450px').editableList({
|
|
66
66
|
addItem: function (container, i, opt) { // row, index, data
|
|
67
67
|
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
var oTopicField = $("<input/>", { class: "rowRuleTopic", type: "text", placeholder: "GA or devicename", style: "width:20%; margin-left: 5px; text-align: left;" }).appendTo(row);
|
|
81
81
|
var finalspan = $('<span/>', { style: "" }).appendTo(row);
|
|
82
82
|
finalspan.append(' <span class="node-input-rule-index"></span> ');
|
|
83
|
-
var orowRuleDeviceName = $('<input/>', {
|
|
83
|
+
var orowRuleDeviceName = $('<input/>', { maxlength: "14", class: "rowRuleDeviceName", type: "text", style: "width:30%; margin-left: 0px; text-align: left;font-style: italic;", placeholder: "Name (max 14 chars)" }).appendTo(row);
|
|
84
84
|
var orowRuleLongDeviceName = $('<input/>', { class: "rowRuleLongDeviceName", type: "text", style: "width:45%; margin-left: 0px; text-align: left;", placeholder: "Long name" }).appendTo(row);
|
|
85
85
|
|
|
86
86
|
oTopicField.on("change", function () {
|
|
@@ -110,7 +110,7 @@
|
|
|
110
110
|
var sDevName = ui.item.label.split("#")[1].trim();
|
|
111
111
|
try {
|
|
112
112
|
sDevName = sDevName.substr(sDevName.indexOf(")") + 1).trim();
|
|
113
|
-
orowRuleDeviceName.val(sDevName.substr(0,14));
|
|
113
|
+
orowRuleDeviceName.val(sDevName.substr(0, 14));
|
|
114
114
|
orowRuleLongDeviceName.val(sDevName);
|
|
115
115
|
} catch (error) {
|
|
116
116
|
}
|
|
@@ -121,7 +121,7 @@
|
|
|
121
121
|
oTopicField.val(rule.topic);
|
|
122
122
|
orowRuleDeviceName.val(rule.devicename);
|
|
123
123
|
orowRuleLongDeviceName.val(rule.longdevicename);
|
|
124
|
-
oTopicField.change();
|
|
124
|
+
oTopicField.change();
|
|
125
125
|
|
|
126
126
|
},
|
|
127
127
|
removeItem: function (opt) {
|
|
@@ -197,8 +197,8 @@
|
|
|
197
197
|
<div class="form-row">
|
|
198
198
|
<label for="node-input-whentostart"><i class="fa fa-repeat"></i> <span data-i18n="knxUltimateAlerter.properties.node-input-whentostart"></span> </label>
|
|
199
199
|
<select id="node-input-whentostart">
|
|
200
|
-
<option value="manualstart"
|
|
201
|
-
<option value="ifnewalert"
|
|
200
|
+
<option value="manualstart">Start alert cycle manually via incoming message></option>
|
|
201
|
+
<option value="ifnewalert">Start the alert cycle with each new alerted device</option>
|
|
202
202
|
</select>
|
|
203
203
|
</div>
|
|
204
204
|
|
|
@@ -1,103 +1,103 @@
|
|
|
1
1
|
|
|
2
2
|
module.exports = function (RED) {
|
|
3
3
|
function knxUltimateAlerter(config) {
|
|
4
|
-
const fs = require('fs')
|
|
5
|
-
const path = require('path')
|
|
6
|
-
const mkdirp = require('mkdirp')
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const mkdirp = require('mkdirp');
|
|
7
7
|
// const Address = require('./../KNXEngine/src/protocol/KNXAddress')
|
|
8
8
|
// const KnxConstants = require('./../KNXEngine/src/protocol/KNXConstants')
|
|
9
9
|
|
|
10
|
-
RED.nodes.createNode(this, config)
|
|
11
|
-
const node = this
|
|
12
|
-
node.server = RED.nodes.getNode(config.server)
|
|
13
|
-
node.name = config.name || 'KNX Alerter'
|
|
14
|
-
node.listenallga = true // Dont' remove this.
|
|
15
|
-
node.notifyreadrequest = false
|
|
16
|
-
node.notifyresponse = true
|
|
17
|
-
node.notifywrite = true // Dont' remove this.
|
|
18
|
-
node.initialread = false
|
|
19
|
-
node.outputtype = 'write'
|
|
20
|
-
node.outputRBE = 'false'
|
|
21
|
-
node.inputRBE = 'false'
|
|
22
|
-
node.rules = config.rules || [{}]
|
|
23
|
-
node.isalertnode = true // Signal to config node, that this is a node scene controller
|
|
24
|
-
node.userDir = path.join(RED.settings.userDir, 'knxultimatestorage') // 09/03/2020 Storage of ttsultimate (otherwise, at each upgrade to a newer version, the node path is wiped out and recreated, loosing all custom files)
|
|
25
|
-
node.alertedDevices = []
|
|
26
|
-
node.curIndexAlertedDevice = 0
|
|
27
|
-
node.timerSend = null
|
|
28
|
-
node.whentostart = config.whentostart === undefined ? 'ifnewalert' : config.whentostart
|
|
29
|
-
node.timerinterval = (config.timerinterval === undefined || config.timerinterval == '') ? '2' : config.timerinterval
|
|
10
|
+
RED.nodes.createNode(this, config);
|
|
11
|
+
const node = this;
|
|
12
|
+
node.server = RED.nodes.getNode(config.server);
|
|
13
|
+
node.name = config.name || 'KNX Alerter';
|
|
14
|
+
node.listenallga = true; // Dont' remove this.
|
|
15
|
+
node.notifyreadrequest = false;
|
|
16
|
+
node.notifyresponse = true;
|
|
17
|
+
node.notifywrite = true; // Dont' remove this.
|
|
18
|
+
node.initialread = false;
|
|
19
|
+
node.outputtype = 'write';
|
|
20
|
+
node.outputRBE = 'false';
|
|
21
|
+
node.inputRBE = 'false';
|
|
22
|
+
node.rules = config.rules || [{}];
|
|
23
|
+
node.isalertnode = true; // Signal to config node, that this is a node scene controller
|
|
24
|
+
node.userDir = path.join(RED.settings.userDir, 'knxultimatestorage'); // 09/03/2020 Storage of ttsultimate (otherwise, at each upgrade to a newer version, the node path is wiped out and recreated, loosing all custom files)
|
|
25
|
+
node.alertedDevices = [];
|
|
26
|
+
node.curIndexAlertedDevice = 0;
|
|
27
|
+
node.timerSend = null;
|
|
28
|
+
node.whentostart = config.whentostart === undefined ? 'ifnewalert' : config.whentostart;
|
|
29
|
+
node.timerinterval = (config.timerinterval === undefined || config.timerinterval == '') ? '2' : config.timerinterval;
|
|
30
30
|
if (config.initialreadGAInRules === undefined) {
|
|
31
|
-
node.initialread = true
|
|
31
|
+
node.initialread = true;
|
|
32
32
|
} else {
|
|
33
|
-
node.initialread = config.initialreadGAInRules !== '0'
|
|
33
|
+
node.initialread = config.initialreadGAInRules !== '0';
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
try {
|
|
37
|
-
node.sysLogger = require('./utils/sysLogger.js').get({ loglevel: node.server.loglevel || 'error' }) // 08/04/2021 new logger to adhere to the loglevel selected in the config-window
|
|
37
|
+
node.sysLogger = require('./utils/sysLogger.js').get({ loglevel: node.server.loglevel || 'error' }); // 08/04/2021 new logger to adhere to the loglevel selected in the config-window
|
|
38
38
|
} catch (error) {
|
|
39
|
-
node.sysLogger = 'error'
|
|
39
|
+
node.sysLogger = 'error';
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
// Used to call the status update from the config node.
|
|
43
43
|
node.setNodeStatus = ({ fill, shape, text, payload, GA, dpt, devicename }) => {
|
|
44
44
|
try {
|
|
45
|
-
if (node.server === null) return
|
|
45
|
+
if (node.server === null) return;
|
|
46
46
|
// Log only service statuses, not the GA values
|
|
47
|
-
if (dpt !== undefined) return
|
|
48
|
-
if (dpt !== '') return
|
|
47
|
+
if (dpt !== undefined) return;
|
|
48
|
+
if (dpt !== '') return;
|
|
49
49
|
|
|
50
|
-
const dDate = new Date()
|
|
50
|
+
const dDate = new Date();
|
|
51
51
|
// 30/08/2019 Display only the things selected in the config
|
|
52
|
-
GA = (typeof GA === 'undefined' || GA == '') ? '' : '(' + GA + ') '
|
|
53
|
-
devicename = devicename || ''
|
|
54
|
-
dpt = (typeof dpt === 'undefined' || dpt == '') ? '' : ' DPT' + dpt
|
|
55
|
-
payload = typeof payload === 'object' ? JSON.stringify(payload) : payload
|
|
56
|
-
node.status({ fill, shape, text: GA + payload + (node.listenallga === true ? ' ' + devicename : '') + ' (' + dDate.getDate() + ', ' + dDate.toLocaleTimeString() + ' ' + text })
|
|
52
|
+
GA = (typeof GA === 'undefined' || GA == '') ? '' : '(' + GA + ') ';
|
|
53
|
+
devicename = devicename || '';
|
|
54
|
+
dpt = (typeof dpt === 'undefined' || dpt == '') ? '' : ' DPT' + dpt;
|
|
55
|
+
payload = typeof payload === 'object' ? JSON.stringify(payload) : payload;
|
|
56
|
+
node.status({ fill, shape, text: GA + payload + (node.listenallga === true ? ' ' + devicename : '') + ' (' + dDate.getDate() + ', ' + dDate.toLocaleTimeString() + ' ' + text });
|
|
57
57
|
} catch (error) {
|
|
58
58
|
}
|
|
59
|
-
}
|
|
59
|
+
};
|
|
60
60
|
|
|
61
61
|
// Used to call the status update from the config node.
|
|
62
62
|
node.setLocalStatus = ({ fill, shape, text, payload, GA, dpt, devicename }) => {
|
|
63
|
-
const dDate = new Date()
|
|
63
|
+
const dDate = new Date();
|
|
64
64
|
// 30/08/2019 Display only the things selected in the config
|
|
65
|
-
GA = (typeof GA === 'undefined' || GA == '') ? '' : '(' + GA + ') '
|
|
66
|
-
devicename = devicename || ''
|
|
67
|
-
dpt = (typeof dpt === 'undefined' || dpt == '') ? '' : ' DPT' + dpt
|
|
65
|
+
GA = (typeof GA === 'undefined' || GA == '') ? '' : '(' + GA + ') ';
|
|
66
|
+
devicename = devicename || '';
|
|
67
|
+
dpt = (typeof dpt === 'undefined' || dpt == '') ? '' : ' DPT' + dpt;
|
|
68
68
|
try {
|
|
69
|
-
node.status({ fill, shape, text: GA + payload + (node.listenallga === true ? ' ' + devicename : '') + ' (' + dDate.getDate() + ', ' + dDate.toLocaleTimeString() + ' ' + text })
|
|
69
|
+
node.status({ fill, shape, text: GA + payload + (node.listenallga === true ? ' ' + devicename : '') + ' (' + dDate.getDate() + ', ' + dDate.toLocaleTimeString() + ' ' + text });
|
|
70
70
|
} catch (error) {
|
|
71
71
|
}
|
|
72
|
-
}
|
|
72
|
+
};
|
|
73
73
|
|
|
74
74
|
// This function is called by the knx-ultimate config node, to output a msg.payload.
|
|
75
75
|
node.handleSend = msg => {
|
|
76
76
|
try {
|
|
77
|
-
if (!msg.knx.dpt.startsWith('1.')) return
|
|
77
|
+
if (!msg.knx.dpt.startsWith('1.')) return;
|
|
78
78
|
} catch (error) {
|
|
79
|
-
return
|
|
79
|
+
return;
|
|
80
80
|
}
|
|
81
|
-
let bFound = false // 24/04/2021 true if the cycle below found a match, otherwise false
|
|
81
|
+
let bFound = false; // 24/04/2021 true if the cycle below found a match, otherwise false
|
|
82
82
|
|
|
83
83
|
// Update the node.rules with the values taken from the file, if any, otherwise leave the default value
|
|
84
84
|
for (let i = 0; i < node.rules.length; i++) {
|
|
85
85
|
// rule is { topic: rowRuleTopic, devicename: rowRuleDeviceName, longdevicename: rowRuleLongDeviceName}
|
|
86
|
-
var rule = node.rules[i]
|
|
86
|
+
var rule = node.rules[i];
|
|
87
87
|
if (msg.topic === rule.topic) {
|
|
88
|
-
if (msg.payload
|
|
89
|
-
bFound = true
|
|
88
|
+
if (msg.payload === true) {
|
|
89
|
+
bFound = true;
|
|
90
90
|
// Add the device to the array of alertedDevices
|
|
91
|
-
const oTrovato = node.alertedDevices.find(a => a.topic === rule.topic)
|
|
91
|
+
const oTrovato = node.alertedDevices.find(a => a.topic === rule.topic);
|
|
92
92
|
if (oTrovato === undefined) {
|
|
93
|
-
node.alertedDevices.unshift({ topic: rule.topic, devicename: rule.devicename, longdevicename: rule.longdevicename }) // Add to the begin of array
|
|
94
|
-
if (node.whentostart === 'ifnewalert') node.send([null, null, node.getThirdPinMSG()])
|
|
93
|
+
node.alertedDevices.unshift({ topic: rule.topic, devicename: rule.devicename, longdevicename: rule.longdevicename }); // Add to the begin of array
|
|
94
|
+
if (node.whentostart === 'ifnewalert') node.send([null, null, node.getThirdPinMSG()]);
|
|
95
95
|
}
|
|
96
|
-
node.setLocalStatus({ fill: 'red', shape: 'dot', text: 'Alert', payload: '', GA: msg.topic, dpt: '', devicename: rule.devicename })
|
|
96
|
+
node.setLocalStatus({ fill: 'red', shape: 'dot', text: 'Alert', payload: '', GA: msg.topic, dpt: '', devicename: rule.devicename });
|
|
97
97
|
} else {
|
|
98
98
|
// Remove the device from the array
|
|
99
|
-
node.alertedDevices = node.alertedDevices.filter(a => a.topic !== msg.topic)
|
|
100
|
-
node.setLocalStatus({ fill: 'green', shape: 'dot', text: 'Restore', payload: '', GA: msg.topic, dpt: '', devicename: rule.devicename })
|
|
99
|
+
node.alertedDevices = node.alertedDevices.filter(a => a.topic !== msg.topic);
|
|
100
|
+
node.setLocalStatus({ fill: 'green', shape: 'dot', text: 'Restore', payload: '', GA: msg.topic, dpt: '', devicename: rule.devicename });
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
103
|
}
|
|
@@ -105,182 +105,182 @@ module.exports = function (RED) {
|
|
|
105
105
|
// If there's some device to alert, stop current timer and restart
|
|
106
106
|
// This allow the last alerted device to be outputted immediately
|
|
107
107
|
if (bFound && node.whentostart === 'ifnewalert' && node.alertedDevices.length > 0) {
|
|
108
|
-
clearTimeout(node.timerSend)
|
|
108
|
+
clearTimeout(node.timerSend);
|
|
109
109
|
// Send directly the second and third message PIN
|
|
110
|
-
node.send([null, node.getSecondPinMSG(), null])
|
|
111
|
-
node.curIndexAlertedDevice = 0 // Restart form the beginning
|
|
112
|
-
node.startTimer()
|
|
110
|
+
node.send([null, node.getSecondPinMSG(), null]);
|
|
111
|
+
node.curIndexAlertedDevice = 0; // Restart form the beginning
|
|
112
|
+
node.startTimer();
|
|
113
113
|
}
|
|
114
|
-
}
|
|
114
|
+
};
|
|
115
115
|
|
|
116
116
|
// Get the msg to be outputted on second PIN
|
|
117
117
|
node.getSecondPinMSG = () => {
|
|
118
118
|
if (node.alertedDevices.length > 0) {
|
|
119
|
-
const msg = {}
|
|
120
|
-
let sRet = ''
|
|
121
|
-
let sRetLong = ''
|
|
122
|
-
let sTopic = ''
|
|
119
|
+
const msg = {};
|
|
120
|
+
let sRet = '';
|
|
121
|
+
let sRetLong = '';
|
|
122
|
+
let sTopic = '';
|
|
123
123
|
node.alertedDevices.forEach(function (item) {
|
|
124
|
-
sTopic += item.topic + ', '
|
|
125
|
-
if (item.devicename !== undefined && item.devicename !== '') sRet += item.devicename + ', '
|
|
126
|
-
if (item.longdevicename !== undefined && item.longdevicename !== '') sRetLong += item.longdevicename + ', '
|
|
127
|
-
})
|
|
128
|
-
sTopic = sTopic.slice(0, -2)
|
|
129
|
-
if (sRet.length > 2) sRet = sRet.slice(0, -2)
|
|
130
|
-
if (sRetLong.length > 2) sRetLong = sRetLong.slice(0, -2)
|
|
131
|
-
msg.topic = sTopic
|
|
132
|
-
msg.devicename = sRet
|
|
133
|
-
msg.longdevicename = sRetLong
|
|
134
|
-
msg.count = node.alertedDevices.length
|
|
135
|
-
msg.payload = true
|
|
136
|
-
return msg
|
|
124
|
+
sTopic += item.topic + ', ';
|
|
125
|
+
if (item.devicename !== undefined && item.devicename !== '') sRet += item.devicename + ', ';
|
|
126
|
+
if (item.longdevicename !== undefined && item.longdevicename !== '') sRetLong += item.longdevicename + ', ';
|
|
127
|
+
});
|
|
128
|
+
sTopic = sTopic.slice(0, -2);
|
|
129
|
+
if (sRet.length > 2) sRet = sRet.slice(0, -2);
|
|
130
|
+
if (sRetLong.length > 2) sRetLong = sRetLong.slice(0, -2);
|
|
131
|
+
msg.topic = sTopic;
|
|
132
|
+
msg.devicename = sRet;
|
|
133
|
+
msg.longdevicename = sRetLong;
|
|
134
|
+
msg.count = node.alertedDevices.length;
|
|
135
|
+
msg.payload = true;
|
|
136
|
+
return msg;
|
|
137
137
|
}
|
|
138
|
-
}
|
|
138
|
+
};
|
|
139
139
|
|
|
140
140
|
// Get the msg to be outputted on third PIN
|
|
141
141
|
node.getThirdPinMSG = () => {
|
|
142
142
|
if (node.alertedDevices.length > 0) {
|
|
143
|
-
const msg = {}
|
|
144
|
-
let sRet = ''
|
|
145
|
-
let sRetLong = ''
|
|
146
|
-
let sTopic = ''
|
|
147
|
-
const item = node.alertedDevices[0] // Pick the last alerted device
|
|
148
|
-
sTopic = item.topic
|
|
149
|
-
if (item.devicename !== undefined && item.devicename !== '') sRet = item.devicename
|
|
150
|
-
if (item.longdevicename !== undefined && item.longdevicename !== '') sRetLong = item.longdevicename
|
|
151
|
-
msg.topic = sTopic
|
|
152
|
-
msg.devicename = sRet
|
|
153
|
-
msg.longdevicename = sRetLong
|
|
154
|
-
msg.count = node.alertedDevices.length
|
|
155
|
-
msg.payload = true
|
|
156
|
-
return msg
|
|
143
|
+
const msg = {};
|
|
144
|
+
let sRet = '';
|
|
145
|
+
let sRetLong = '';
|
|
146
|
+
let sTopic = '';
|
|
147
|
+
const item = node.alertedDevices[0]; // Pick the last alerted device
|
|
148
|
+
sTopic = item.topic;
|
|
149
|
+
if (item.devicename !== undefined && item.devicename !== '') sRet = item.devicename;
|
|
150
|
+
if (item.longdevicename !== undefined && item.longdevicename !== '') sRetLong = item.longdevicename;
|
|
151
|
+
msg.topic = sTopic;
|
|
152
|
+
msg.devicename = sRet;
|
|
153
|
+
msg.longdevicename = sRetLong;
|
|
154
|
+
msg.count = node.alertedDevices.length;
|
|
155
|
+
msg.payload = true;
|
|
156
|
+
return msg;
|
|
157
157
|
}
|
|
158
|
-
}
|
|
158
|
+
};
|
|
159
159
|
|
|
160
160
|
// 24/04/2021 perform a read on all GA in the rule list. Called both from node.on("input") and knxUltimate-config
|
|
161
161
|
node.initialReadAllDevicesInRules = () => {
|
|
162
162
|
if (node.server) {
|
|
163
|
-
let grpaddr = ''
|
|
163
|
+
let grpaddr = '';
|
|
164
164
|
for (let i = 0; i < node.rules.length; i++) {
|
|
165
165
|
// rule is { topic: rowRuleTopic, devicename: rowRuleDeviceName, longdevicename: rowRuleLongDeviceName}
|
|
166
|
-
const rule = node.rules[i]
|
|
166
|
+
const rule = node.rules[i];
|
|
167
167
|
// READ: Send a Read request to the bus
|
|
168
|
-
grpaddr = rule.topic
|
|
168
|
+
grpaddr = rule.topic;
|
|
169
169
|
try {
|
|
170
170
|
// Check if it's a group address
|
|
171
171
|
// const ret = Address.KNXAddress.createFromString(grpaddr, Address.KNXAddress.TYPE_GROUP)
|
|
172
|
-
node.setLocalStatus({ fill: 'grey', shape: 'dot', text: 'Read', payload: '', GA: grpaddr, dpt: '', devicename: rule.devicename })
|
|
173
|
-
node.server.writeQueueAdd({ grpaddr, payload: '', dpt: '', outputtype: 'read', nodecallerid: node.id })
|
|
172
|
+
node.setLocalStatus({ fill: 'grey', shape: 'dot', text: 'Read', payload: '', GA: grpaddr, dpt: '', devicename: rule.devicename });
|
|
173
|
+
node.server.writeQueueAdd({ grpaddr, payload: '', dpt: '', outputtype: 'read', nodecallerid: node.id });
|
|
174
174
|
} catch (error) {
|
|
175
|
-
node.setLocalStatus({ fill: 'grey', shape: 'dot', text: 'Not a KNX GA ' + error.message, payload: '', GA: grpaddr, dpt: '', devicename: rule.devicename })
|
|
175
|
+
node.setLocalStatus({ fill: 'grey', shape: 'dot', text: 'Not a KNX GA ' + error.message, payload: '', GA: grpaddr, dpt: '', devicename: rule.devicename });
|
|
176
176
|
}
|
|
177
177
|
}
|
|
178
178
|
} else {
|
|
179
|
-
node.setLocalStatus({ fill: 'red', shape: 'ring', text: 'No gateway selected. Unable to read from KNX bus', payload: '', GA: '', dpt: '', devicename: '' })
|
|
179
|
+
node.setLocalStatus({ fill: 'red', shape: 'ring', text: 'No gateway selected. Unable to read from KNX bus', payload: '', GA: '', dpt: '', devicename: '' });
|
|
180
180
|
}
|
|
181
|
-
}
|
|
181
|
+
};
|
|
182
182
|
|
|
183
183
|
node.on('input', function (msg) {
|
|
184
|
-
if (typeof msg === 'undefined') return
|
|
184
|
+
if (typeof msg === 'undefined') return;
|
|
185
185
|
if (msg.hasOwnProperty('start')) {
|
|
186
|
-
clearTimeout(node.timerSend)
|
|
187
|
-
node.curIndexAlertedDevice = 0 // Restart form the beginning
|
|
186
|
+
clearTimeout(node.timerSend);
|
|
187
|
+
node.curIndexAlertedDevice = 0; // Restart form the beginning
|
|
188
188
|
if (node.alertedDevices.length > 0) {
|
|
189
|
-
node.send([null, node.getSecondPinMSG(), node.getThirdPinMSG()])
|
|
190
|
-
node.startTimer()
|
|
189
|
+
node.send([null, node.getSecondPinMSG(), node.getThirdPinMSG()]);
|
|
190
|
+
node.startTimer();
|
|
191
191
|
} else {
|
|
192
192
|
// Nothing more to output
|
|
193
|
-
node.sendNoMoreDevices()
|
|
193
|
+
node.sendNoMoreDevices();
|
|
194
194
|
}
|
|
195
|
-
return
|
|
195
|
+
return;
|
|
196
196
|
}
|
|
197
197
|
|
|
198
198
|
// 24/04/2021 if payload is read or the Telegram type is set to "read", do a read
|
|
199
199
|
if ((msg.hasOwnProperty('readstatus') && msg.readstatus === true)) {
|
|
200
|
-
node.initialReadAllDevicesInRules()
|
|
201
|
-
return
|
|
200
|
+
node.initialReadAllDevicesInRules();
|
|
201
|
+
return;
|
|
202
202
|
}
|
|
203
203
|
|
|
204
204
|
if (msg.topic === undefined) {
|
|
205
|
-
node.setLocalStatus({ fill: 'grey', shape: 'dot', text: 'ERROR: You must provide a msg.topic', payload: '', GA: '', dpt: '', devicename: '' })
|
|
206
|
-
return
|
|
205
|
+
node.setLocalStatus({ fill: 'grey', shape: 'dot', text: 'ERROR: You must provide a msg.topic', payload: '', GA: '', dpt: '', devicename: '' });
|
|
206
|
+
return;
|
|
207
207
|
}
|
|
208
208
|
if (msg.payload === undefined) {
|
|
209
|
-
node.setLocalStatus({ fill: 'grey', shape: 'dot', text: 'ERROR: You must provide payload (true/false)', payload: '', GA: '', dpt: '', devicename: '' })
|
|
210
|
-
return
|
|
209
|
+
node.setLocalStatus({ fill: 'grey', shape: 'dot', text: 'ERROR: You must provide payload (true/false)', payload: '', GA: '', dpt: '', devicename: '' });
|
|
210
|
+
return;
|
|
211
211
|
}
|
|
212
|
-
msg.knx = { dpt: '1.001' }
|
|
213
|
-
node.handleSend(msg)
|
|
214
|
-
})
|
|
212
|
+
msg.knx = { dpt: '1.001' };
|
|
213
|
+
node.handleSend(msg);
|
|
214
|
+
});
|
|
215
215
|
|
|
216
216
|
node.on('close', function (done) {
|
|
217
|
-
clearTimeout(node.timerSend)
|
|
217
|
+
clearTimeout(node.timerSend);
|
|
218
218
|
if (node.server) {
|
|
219
|
-
node.server.removeClient(node)
|
|
219
|
+
node.server.removeClient(node);
|
|
220
220
|
}
|
|
221
|
-
done()
|
|
222
|
-
})
|
|
221
|
+
done();
|
|
222
|
+
});
|
|
223
223
|
|
|
224
224
|
node.handleTimer = () => {
|
|
225
225
|
if (node.alertedDevices.length > 0) {
|
|
226
|
-
const count = node.alertedDevices.length
|
|
226
|
+
const count = node.alertedDevices.length;
|
|
227
227
|
if (node.curIndexAlertedDevice > count - 1) {
|
|
228
|
-
node.curIndexAlertedDevice = 0
|
|
228
|
+
node.curIndexAlertedDevice = 0;
|
|
229
229
|
if (node.whentostart === 'manualstart') {
|
|
230
|
-
node.curIndexAlertedDevice = 0 // Restart form the beginning
|
|
231
|
-
return
|
|
230
|
+
node.curIndexAlertedDevice = 0; // Restart form the beginning
|
|
231
|
+
return;
|
|
232
232
|
}
|
|
233
233
|
}
|
|
234
234
|
// Create output message
|
|
235
235
|
try {
|
|
236
|
-
const curDev = node.alertedDevices[node.curIndexAlertedDevice] // is { topic: rule.topic, devicename: rule.devicename }
|
|
237
|
-
const msg = {}
|
|
238
|
-
msg.topic = curDev.topic
|
|
239
|
-
msg.count = count
|
|
240
|
-
msg.devicename = curDev.devicename
|
|
241
|
-
msg.longdevicename = curDev.longdevicename
|
|
242
|
-
msg.payload = true
|
|
243
|
-
node.send([msg, null, null])
|
|
236
|
+
const curDev = node.alertedDevices[node.curIndexAlertedDevice]; // is { topic: rule.topic, devicename: rule.devicename }
|
|
237
|
+
const msg = {};
|
|
238
|
+
msg.topic = curDev.topic;
|
|
239
|
+
msg.count = count;
|
|
240
|
+
msg.devicename = curDev.devicename;
|
|
241
|
+
msg.longdevicename = curDev.longdevicename;
|
|
242
|
+
msg.payload = true;
|
|
243
|
+
node.send([msg, null, null]);
|
|
244
244
|
} catch (error) {
|
|
245
245
|
}
|
|
246
|
-
node.curIndexAlertedDevice += 1
|
|
246
|
+
node.curIndexAlertedDevice += 1;
|
|
247
247
|
// Restart timer
|
|
248
|
-
node.startTimer()
|
|
248
|
+
node.startTimer();
|
|
249
249
|
} else {
|
|
250
250
|
// Nothing more to output
|
|
251
|
-
node.sendNoMoreDevices()
|
|
251
|
+
node.sendNoMoreDevices();
|
|
252
252
|
}
|
|
253
|
-
}
|
|
253
|
+
};
|
|
254
254
|
|
|
255
255
|
// Start timer
|
|
256
256
|
node.startTimer = () => {
|
|
257
|
-
clearTimeout(node.timerSend)
|
|
257
|
+
clearTimeout(node.timerSend);
|
|
258
258
|
node.timerSend = setTimeout(() => {
|
|
259
|
-
node.handleTimer()
|
|
260
|
-
}, node.timerinterval * 1000)
|
|
261
|
-
}
|
|
259
|
+
node.handleTimer();
|
|
260
|
+
}, node.timerinterval * 1000);
|
|
261
|
+
};
|
|
262
262
|
|
|
263
263
|
// As soon as there no more devices..
|
|
264
264
|
node.sendNoMoreDevices = () => {
|
|
265
|
-
const msg = {}
|
|
266
|
-
msg.topic = ''
|
|
267
|
-
msg.count = 0
|
|
268
|
-
msg.devicename = ''
|
|
269
|
-
msg.longdevicename = ''
|
|
270
|
-
msg.payload = false
|
|
271
|
-
node.send([msg, msg, msg])
|
|
272
|
-
}
|
|
265
|
+
const msg = {};
|
|
266
|
+
msg.topic = '';
|
|
267
|
+
msg.count = 0;
|
|
268
|
+
msg.devicename = '';
|
|
269
|
+
msg.longdevicename = '';
|
|
270
|
+
msg.payload = false;
|
|
271
|
+
node.send([msg, msg, msg]);
|
|
272
|
+
};
|
|
273
273
|
|
|
274
274
|
// Init
|
|
275
|
-
node.sendNoMoreDevices()
|
|
275
|
+
node.sendNoMoreDevices();
|
|
276
276
|
|
|
277
277
|
// On each deploy, unsubscribe+resubscribe
|
|
278
278
|
if (node.server) {
|
|
279
|
-
node.server.removeClient(node)
|
|
279
|
+
node.server.removeClient(node);
|
|
280
280
|
if (node.topic !== '' || node.topicSave !== '') {
|
|
281
|
-
node.server.addClient(node)
|
|
281
|
+
node.server.addClient(node);
|
|
282
282
|
}
|
|
283
283
|
}
|
|
284
284
|
}
|
|
285
|
-
RED.nodes.registerType('knxUltimateAlerter', knxUltimateAlerter)
|
|
286
|
-
}
|
|
285
|
+
RED.nodes.registerType('knxUltimateAlerter', knxUltimateAlerter);
|
|
286
|
+
};
|
|
@@ -361,7 +361,7 @@
|
|
|
361
361
|
<input type="text" id="node-input-nameshort_release" style="width:200px;margin-left: 5px; text-align: left;">
|
|
362
362
|
</div>
|
|
363
363
|
<div class="form-row">
|
|
364
|
-
<label style="width:100px;"><i class="fa fa-
|
|
364
|
+
<label style="width:100px;"><i class="fa fa-question-circle"></i> Switch Status</label>
|
|
365
365
|
|
|
366
366
|
<label for="node-input-GAshort_releaseStatus" style="width:20px;">GA</label>
|
|
367
367
|
<input type="text" id="node-input-GAshort_releaseStatus" placeholder="Ex: 1/1/1" style="width:70px;margin-left: 5px; text-align: left;">
|
|
@@ -141,14 +141,14 @@
|
|
|
141
141
|
getDPT("232.600", "#node-input-dptLightColorState");
|
|
142
142
|
getGroupAddress("#node-input-GALightColorState", "#node-input-nameLightColorState", "#node-input-dptLightColorState", " 232.600");
|
|
143
143
|
|
|
144
|
-
getDPT("3.007", "#node-input-
|
|
145
|
-
getGroupAddress("#node-input-
|
|
144
|
+
getDPT("3.007", "#node-input-dptLightKelvinDIM");
|
|
145
|
+
getGroupAddress("#node-input-GALightKelvinDIM", "#node-input-nameLightKelvinDIM", "#node-input-dptLightKelvinDIM", " 3.007");
|
|
146
146
|
|
|
147
|
-
getDPT("5.001", "#node-input-
|
|
148
|
-
getGroupAddress("#node-input-
|
|
147
|
+
getDPT("5.001", "#node-input-dptLightKelvinPercentage");
|
|
148
|
+
getGroupAddress("#node-input-GALightKelvinPercentage", "#node-input-nameLightKelvinPercentage", "#node-input-dptLightKelvinPercentage", " 5.001");
|
|
149
149
|
|
|
150
|
-
getDPT("5.001", "#node-input-
|
|
151
|
-
getGroupAddress("#node-input-
|
|
150
|
+
getDPT("5.001", "#node-input-dptLightKelvinPercentageState");
|
|
151
|
+
getGroupAddress("#node-input-GALightKelvinPercentageState", "#node-input-nameLightKelvinPercentageState", "#node-input-dptLightKelvinPercentageState", " 5.001");
|
|
152
152
|
|
|
153
153
|
getDPT("1.", "#node-input-dptLightBlink");
|
|
154
154
|
getGroupAddress("#node-input-GALightBlink", "#node-input-nameLightBlink", "#node-input-dptLightBlink", " 1.");
|
|
@@ -173,15 +173,13 @@
|
|
|
173
173
|
|
|
174
174
|
|
|
175
175
|
// Get the HUE capabilities to enable/disable UI parts
|
|
176
|
+
var getJsonPromise;
|
|
176
177
|
if ($("#node-input-hueDevice").val() === '') {
|
|
177
178
|
$("#tabs").hide();
|
|
178
179
|
} else {
|
|
179
|
-
|
|
180
|
+
if (getJsonPromise !== undefined) getJsonPromise.abort();
|
|
181
|
+
getJsonPromise = $.getJSON("knxUltimateGetLightObject?id=" + $("#node-input-hueDevice").val().split("#")[0] + "&" + { _: new Date().getTime() }, (data) => {
|
|
180
182
|
let oLight = data;
|
|
181
|
-
if (JSON.stringify(data) === "{}") {
|
|
182
|
-
$("#mainWindow").hide();
|
|
183
|
-
RED.notify("Please close and reload this window in few seconds. Still querying the HUE Bridge.", { type: "warning", timeout: 8000 });
|
|
184
|
-
}
|
|
185
183
|
// Check if grouped, to hide/show the "Get current" buttons
|
|
186
184
|
if (oLight.type === "grouped_light") {
|
|
187
185
|
$("#tabs").tabs("enable", "#tabs-4");
|
|
@@ -310,6 +308,7 @@
|
|
|
310
308
|
$("#node-input-specifySwitchOnBrightness").val(node.specifySwitchOnBrightness).trigger('change');
|
|
311
309
|
$("#node-input-enableDayNightLighting").val(node.enableDayNightLighting).trigger('change');
|
|
312
310
|
});
|
|
311
|
+
setTimeout(function () { if (getJsonPromise !== undefined) getJsonPromise.abort(); }, 10000);
|
|
313
312
|
}
|
|
314
313
|
// Show/Hide the div of the color at swich on
|
|
315
314
|
if (node.specifySwitchOnBrightness === "yes") {
|
|
@@ -560,7 +559,7 @@
|
|
|
560
559
|
$("#node-input-hueDevice").val(ui.item.hueDevice + "#light");
|
|
561
560
|
}
|
|
562
561
|
onEditPrepare(node);
|
|
563
|
-
|
|
562
|
+
//$("#tabs").show();
|
|
564
563
|
},
|
|
565
564
|
});
|
|
566
565
|
}
|
|
@@ -594,17 +593,25 @@
|
|
|
594
593
|
GALightColorState: { value: "" },
|
|
595
594
|
dptLightColorState: { value: "" },
|
|
596
595
|
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
596
|
+
nameLightKelvinDIM: { value: "" },
|
|
597
|
+
GALightKelvinDIM: { value: "" },
|
|
598
|
+
dptLightKelvinDIM: { value: "" },
|
|
599
|
+
|
|
600
|
+
nameLightKelvinPercentage: { value: "" },
|
|
601
|
+
GALightKelvinPercentage: { value: "" },
|
|
602
|
+
dptLightKelvinPercentage: { value: "" },
|
|
600
603
|
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
+
nameLightKelvinPercentageState: { value: "" },
|
|
605
|
+
GALightKelvinPercentageState: { value: "" },
|
|
606
|
+
dptLightKelvinPercentageState: { value: "" },
|
|
604
607
|
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
+
nameLightKelvin: { value: "" },
|
|
609
|
+
GALightKelvin: { value: "" },
|
|
610
|
+
dptLightKelvin: { value: "" },
|
|
611
|
+
|
|
612
|
+
nameLightKelvinState: { value: "" },
|
|
613
|
+
GALightKelvinState: { value: "" },
|
|
614
|
+
dptLightKelvinState: { value: "" },
|
|
608
615
|
|
|
609
616
|
nameLightBrightness: { value: "" },
|
|
610
617
|
GALightBrightness: { value: "" },
|
|
@@ -626,14 +633,6 @@
|
|
|
626
633
|
GADaylightSensor: { value: "" },
|
|
627
634
|
dptDaylightSensor: { value: "" },
|
|
628
635
|
|
|
629
|
-
nameLightKelvin: { value: "" },
|
|
630
|
-
GALightKelvin: { value: "" },
|
|
631
|
-
dptLightKelvin: { value: "" },
|
|
632
|
-
|
|
633
|
-
nameLightKelvinState: { value: "" },
|
|
634
|
-
GALightKelvinState: { value: "" },
|
|
635
|
-
dptLightKelvinState: { value: "" },
|
|
636
|
-
|
|
637
636
|
specifySwitchOnBrightness: { value: "temperature" },
|
|
638
637
|
colorAtSwitchOnDayTime: { value: '{"kelvin":3000, "brightness":100 }' },
|
|
639
638
|
|
|
@@ -781,12 +780,12 @@
|
|
|
781
780
|
|
|
782
781
|
//#region color_temperature_mode
|
|
783
782
|
// ----------------------------
|
|
784
|
-
if ($("#node-input-
|
|
783
|
+
if ($("#node-input-GALightKelvinPercentage").val !== '') {
|
|
785
784
|
|
|
786
785
|
// color_temperature_mode: relative
|
|
787
786
|
sYaml += ' color_temperature_mode: relative' + '\n';
|
|
788
|
-
sYaml += ' color_temperature_address: "' + $("#node-input-
|
|
789
|
-
sYaml += $("#node-input-
|
|
787
|
+
sYaml += ' color_temperature_address: "' + $("#node-input-GALightKelvinPercentage").val() + '"\n';
|
|
788
|
+
sYaml += $("#node-input-GALightKelvinPercentageState").val() != '' ? ' color_temperature_state_address: "' + $("#node-input-GALightKelvinPercentageState").val() + '"' + "\n" : '';
|
|
790
789
|
sYaml += ' min_kelvin: 2200' + '\n';
|
|
791
790
|
sYaml += ' max_kelvin: 6500' + '\n';
|
|
792
791
|
|
|
@@ -899,7 +898,7 @@
|
|
|
899
898
|
<input type="text" id="node-input-nameLightSwitch" style="width:190px;margin-left: 5px; text-align: left;">
|
|
900
899
|
</div>
|
|
901
900
|
<div class="form-row">
|
|
902
|
-
<label for="node-input-nameLightState" style="width:110px;"><i class="fa fa-
|
|
901
|
+
<label for="node-input-nameLightState" style="width:110px;"><i class="fa fa-question-circle"></i> Status</label>
|
|
903
902
|
|
|
904
903
|
<label for="node-input-GALightState" style="width:20px;">GA</label>
|
|
905
904
|
<input type="text" id="node-input-GALightState" placeholder="Ex: 1/1/1"
|
|
@@ -947,8 +946,7 @@
|
|
|
947
946
|
</div>
|
|
948
947
|
|
|
949
948
|
<div class="form-row">
|
|
950
|
-
<label for="node-input-nameLightBrightnessState" style="width:110px;"><i class="fa fa-
|
|
951
|
-
Status</label>
|
|
949
|
+
<label for="node-input-nameLightBrightnessState" style="width:110px;"><i class="fa fa-question-circle"></i> Status</label>
|
|
952
950
|
|
|
953
951
|
<label for="node-input-GALightBrightnessState" style="width:20px;"><span
|
|
954
952
|
data-i18n="knxUltimateHueLight.node-input-GALightState"></span></label>
|
|
@@ -989,32 +987,32 @@
|
|
|
989
987
|
<div id="tabs-3">
|
|
990
988
|
<p>
|
|
991
989
|
<div class="form-row">
|
|
992
|
-
<label for="node-input-
|
|
990
|
+
<label for="node-input-nameLightKelvinDIM" style="width:110px;"><i class="fa fa-play-circle-o"></i> Control dim</label>
|
|
993
991
|
|
|
994
|
-
<label for="node-input-
|
|
995
|
-
<input type="text" id="node-input-
|
|
992
|
+
<label for="node-input-GALightKelvinDIM" style="width:20px;">GA</label>
|
|
993
|
+
<input type="text" id="node-input-GALightKelvinDIM" placeholder="Ex: 1/1/1"
|
|
996
994
|
style="width:70px;margin-left: 5px; text-align: left;">
|
|
997
995
|
|
|
998
|
-
<label for="node-input-
|
|
999
|
-
<select id="node-input-
|
|
996
|
+
<label for="node-input-dptLightKelvinDIM" style="width:40px; margin-left: 0px; text-align: right;">DPT</label>
|
|
997
|
+
<select id="node-input-dptLightKelvinDIM" style="width:140px;"></select>
|
|
1000
998
|
|
|
1001
|
-
<label for="node-input-
|
|
1002
|
-
<input type="text" id="node-input-
|
|
999
|
+
<label for="node-input-nameLightKelvinDIM" style="width:50px; margin-left: 0px; text-align: right;">Name</label>
|
|
1000
|
+
<input type="text" id="node-input-nameLightKelvinDIM" style="width:190px;margin-left: 5px; text-align: left;">
|
|
1003
1001
|
</div>
|
|
1004
1002
|
<div class="form-row">
|
|
1005
|
-
<label for="node-input-
|
|
1003
|
+
<label for="node-input-nameLightKelvinPercentage" style="width:110px;"><i class="fa fa-play-circle-o"></i> Control
|
|
1006
1004
|
%</label>
|
|
1007
1005
|
|
|
1008
|
-
<label for="node-input-
|
|
1009
|
-
<input type="text" id="node-input-
|
|
1006
|
+
<label for="node-input-GALightKelvinPercentage" style="width:20px;">GA</label>
|
|
1007
|
+
<input type="text" id="node-input-GALightKelvinPercentage" placeholder="Ex: 1/1/1"
|
|
1010
1008
|
style="width:70px;margin-left: 5px; text-align: left;">
|
|
1011
1009
|
|
|
1012
|
-
<label for="node-input-
|
|
1013
|
-
<select id="node-input-
|
|
1010
|
+
<label for="node-input-dptLightKelvinPercentage" style="width:40px; margin-left: 0px; text-align: right;">DPT</label>
|
|
1011
|
+
<select id="node-input-dptLightKelvinPercentage" style="width:140px;"></select>
|
|
1014
1012
|
|
|
1015
|
-
<label for="node-input-
|
|
1013
|
+
<label for="node-input-nameLightKelvinPercentage"
|
|
1016
1014
|
style="width:50px; margin-left: 0px; text-align: right;">Name</label>
|
|
1017
|
-
<input type="text" id="node-input-
|
|
1015
|
+
<input type="text" id="node-input-nameLightKelvinPercentage" style="width:190px;margin-left: 5px; text-align: left;">
|
|
1018
1016
|
</div>
|
|
1019
1017
|
<div class="form-row">
|
|
1020
1018
|
<label for="node-input-nameLightKelvin" style="width:110px;"><i class="fa fa-play-circle-o"></i> Control
|
|
@@ -1031,21 +1029,21 @@
|
|
|
1031
1029
|
<input type="text" id="node-input-nameLightKelvin" style="width:190px;margin-left: 5px; text-align: left;">
|
|
1032
1030
|
</div>
|
|
1033
1031
|
<div class="form-row">
|
|
1034
|
-
<label for="node-input-
|
|
1032
|
+
<label for="node-input-nameLightKelvinPercentageState" style="width:110px;"><i class="fa fa-question-circle"></i> Status
|
|
1035
1033
|
%</label>
|
|
1036
1034
|
|
|
1037
|
-
<label for="node-input-
|
|
1038
|
-
<input type="text" id="node-input-
|
|
1035
|
+
<label for="node-input-GALightKelvinPercentageState" style="width:20px;">GA</label>
|
|
1036
|
+
<input type="text" id="node-input-GALightKelvinPercentageState" placeholder="Ex: 1/1/1"
|
|
1039
1037
|
style="width:70px;margin-left: 5px; text-align: left;">
|
|
1040
1038
|
|
|
1041
|
-
<label for="node-input-
|
|
1042
|
-
<select id="node-input-
|
|
1039
|
+
<label for="node-input-dptLightKelvinPercentageState" style="width:40px; margin-left: 0px; text-align: right;">DPT</label>
|
|
1040
|
+
<select id="node-input-dptLightKelvinPercentageState" style="width:140px;"></select>
|
|
1043
1041
|
|
|
1044
|
-
<label for="node-input-
|
|
1045
|
-
<input type="text" id="node-input-
|
|
1042
|
+
<label for="node-input-nameLightKelvinPercentageState" style="width:50px; margin-left: 0px; text-align: right;">Name</label>
|
|
1043
|
+
<input type="text" id="node-input-nameLightKelvinPercentageState" style="width:190px;margin-left: 5px; text-align: left;">
|
|
1046
1044
|
</div>
|
|
1047
1045
|
<div class="form-row">
|
|
1048
|
-
<label for="node-input-nameLightKelvinState" style="width:110px;"><i class="fa fa-
|
|
1046
|
+
<label for="node-input-nameLightKelvinState" style="width:110px;"><i class="fa fa-question-circle"></i> Status
|
|
1049
1047
|
Kelvin</label>
|
|
1050
1048
|
|
|
1051
1049
|
<label for="node-input-GALightKelvinState" style="width:20px;">GA</label>
|
|
@@ -1085,8 +1083,7 @@
|
|
|
1085
1083
|
<input type="text" id="node-input-nameLightColor" style="width:190px;margin-left: 5px; text-align: left;">
|
|
1086
1084
|
</div>
|
|
1087
1085
|
<div class="form-row">
|
|
1088
|
-
<label for="node-input-nameLightColorState" style="width:110px;"><i class="fa fa-
|
|
1089
|
-
Status</label>
|
|
1086
|
+
<label for="node-input-nameLightColorState" style="width:110px;"><i class="fa fa-question-circle"></i> Status</label>
|
|
1090
1087
|
|
|
1091
1088
|
<label for="node-input-GALightColorState" style="width:20px;"><span
|
|
1092
1089
|
data-i18n="knxUltimateHueLight.node-input-GALightState"></span></label>
|
|
@@ -1142,7 +1139,7 @@
|
|
|
1142
1139
|
<div id="tabs-6">
|
|
1143
1140
|
<p>
|
|
1144
1141
|
<div class="form-row">
|
|
1145
|
-
<label style="width:260px;" for="node-input-readStatusAtStartup"><i class="fa fa-
|
|
1142
|
+
<label style="width:260px;" for="node-input-readStatusAtStartup"><i class="fa fa-question-circle"></i> Read status at
|
|
1146
1143
|
startup</label>
|
|
1147
1144
|
<select id="node-input-readStatusAtStartup">
|
|
1148
1145
|
<option value="no">No</option>
|
|
@@ -13,6 +13,22 @@ module.exports = function (RED) {
|
|
|
13
13
|
const node = this;
|
|
14
14
|
node.server = RED.nodes.getNode(config.server);
|
|
15
15
|
node.serverHue = RED.nodes.getNode(config.serverHue);
|
|
16
|
+
|
|
17
|
+
// Convert for backward compatibility
|
|
18
|
+
if (config.nameLightKelvinDIM === undefined) {
|
|
19
|
+
config.nameLightKelvinDIM = config.nameLightHSV;
|
|
20
|
+
config.GALightKelvinDIM = config.GALightHSV;
|
|
21
|
+
config.dptLightKelvinDIM = config.dptLightHSV;
|
|
22
|
+
|
|
23
|
+
config.nameLightKelvinPercentage = config.nameLightHSVPercentage;
|
|
24
|
+
config.GALightKelvinPercentage = config.GALightHSVPercentage;
|
|
25
|
+
config.dptLightKelvinPercentage = config.dptLightHSVPercentage;
|
|
26
|
+
|
|
27
|
+
config.nameLightKelvinPercentageState = config.nameLightHSVState;
|
|
28
|
+
config.GALightKelvinPercentageState = config.GALightHSVState;
|
|
29
|
+
config.dptLightKelvinPercentageState = config.dptLightHSVState;
|
|
30
|
+
}
|
|
31
|
+
|
|
16
32
|
node.topic = node.name;
|
|
17
33
|
node.name = config.name === undefined ? "Hue" : config.name;
|
|
18
34
|
node.outputtopic = node.name;
|
|
@@ -283,22 +299,22 @@ module.exports = function (RED) {
|
|
|
283
299
|
});
|
|
284
300
|
|
|
285
301
|
break;
|
|
286
|
-
case config.
|
|
287
|
-
if (config.
|
|
302
|
+
case config.GALightKelvinDIM:
|
|
303
|
+
if (config.dptLightKelvinDIM === "3.007") {
|
|
288
304
|
// MDT smartbutton will dim the color temperature
|
|
289
305
|
// { decr_incr: 1, data: 1 } : Start increasing until { decr_incr: 0, data: 0 } is received.
|
|
290
306
|
// { decr_incr: 0, data: 1 } : Start decreasing until { decr_incr: 0, data: 0 } is received.
|
|
291
|
-
msg.payload = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.
|
|
307
|
+
msg.payload = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptLightKelvinDIM));
|
|
292
308
|
node.hueDimmingTunableWhite(msg.payload.decr_incr, msg.payload.data, 5000);
|
|
293
309
|
node.setNodeStatusHue({
|
|
294
310
|
fill: "green", shape: "dot", text: "KNX->HUE", payload: JSON.stringify(msg.payload),
|
|
295
311
|
});
|
|
296
312
|
}
|
|
297
313
|
break;
|
|
298
|
-
case config.
|
|
299
|
-
if (config.
|
|
314
|
+
case config.GALightKelvinPercentage:
|
|
315
|
+
if (config.dptLightKelvinPercentage === "5.001") {
|
|
300
316
|
// 0-100% tunable white
|
|
301
|
-
msg.payload = 100 - dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.
|
|
317
|
+
msg.payload = 100 - dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptLightKelvinPercentage));
|
|
302
318
|
// msg.payload = msg.payload <= 0 ? 1 : msg.payload
|
|
303
319
|
const retMirek = hueColorConverter.ColorConverter.scale(msg.payload, [0, 100], [153, 500]);
|
|
304
320
|
msg.payload = retMirek;
|
|
@@ -456,9 +472,9 @@ module.exports = function (RED) {
|
|
|
456
472
|
ret = node.currentHUEDevice.color.xy;
|
|
457
473
|
if (ret !== undefined) node.updateKNXLightColorState(node.currentHUEDevice.color, "response");
|
|
458
474
|
break;
|
|
459
|
-
case config.
|
|
475
|
+
case config.GALightKelvinPercentageState:
|
|
460
476
|
ret = node.currentHUEDevice.color_temperature.mirek;
|
|
461
|
-
if (ret !== undefined) node.
|
|
477
|
+
if (ret !== undefined) node.updateKNXLightKelvinPercentageState(ret, "response");
|
|
462
478
|
break;
|
|
463
479
|
case config.GALightBrightnessState:
|
|
464
480
|
ret = node.currentHUEDevice.dimming.brightness;
|
|
@@ -591,7 +607,7 @@ module.exports = function (RED) {
|
|
|
591
607
|
// DIM UP
|
|
592
608
|
if (node.timerStepDimTunableWhite !== undefined) clearInterval(node.timerStepDimTunableWhite);
|
|
593
609
|
node.timerStepDimTunableWhite = setInterval(() => {
|
|
594
|
-
node.
|
|
610
|
+
node.updateKNXLightKelvinPercentageState(node.brightnessStepTunableWhite); // Unnecessary, but necessary to set the KNX Status in real time.
|
|
595
611
|
node.brightnessStepTunableWhite += numStepTunableWhite; // *2 to speed up the things
|
|
596
612
|
if (node.brightnessStepTunableWhite > maxDimLevelLightTunableWhite) node.brightnessStepTunableWhite = maxDimLevelLightTunableWhite;
|
|
597
613
|
const hueTelegram = { color_temperature: { mirek: node.brightnessStepTunableWhite }, dynamics: { duration: _dimSpeedInMillisecsTunableWhite } };
|
|
@@ -608,7 +624,7 @@ module.exports = function (RED) {
|
|
|
608
624
|
// DIM DOWN
|
|
609
625
|
if (node.timerStepDimTunableWhite !== undefined) clearInterval(node.timerStepDimTunableWhite);
|
|
610
626
|
node.timerStepDimTunableWhite = setInterval(() => {
|
|
611
|
-
node.
|
|
627
|
+
node.updateKNXLightKelvinPercentageState(node.brightnessStepTunableWhite); // Unnecessary, but necessary to set the KNX Status in real time.
|
|
612
628
|
node.brightnessStepTunableWhite -= numStepTunableWhite; // *2 to speed up the things
|
|
613
629
|
if (node.brightnessStepTunableWhite < minDimLevelLightTunableWhite) node.brightnessStepTunableWhite = minDimLevelLightTunableWhite;
|
|
614
630
|
const hueTelegram = { color_temperature: { mirek: node.brightnessStepTunableWhite }, dynamics: { duration: _dimSpeedInMillisecsTunableWhite } };
|
|
@@ -626,7 +642,8 @@ module.exports = function (RED) {
|
|
|
626
642
|
|
|
627
643
|
node.handleSendHUE = (_event) => {
|
|
628
644
|
try {
|
|
629
|
-
|
|
645
|
+
let deviceByRef = cloneDeep(_event);
|
|
646
|
+
if (deviceByRef.id === node.hueDevice) {
|
|
630
647
|
if (node.currentHUEDevice === undefined || node.serverHue === null || node.serverHue === undefined) {
|
|
631
648
|
node.setNodeStatusHue({
|
|
632
649
|
fill: "red",
|
|
@@ -638,7 +655,7 @@ module.exports = function (RED) {
|
|
|
638
655
|
}
|
|
639
656
|
|
|
640
657
|
// Output the msg to the flow
|
|
641
|
-
node.send(
|
|
658
|
+
node.send(deviceByRef);
|
|
642
659
|
|
|
643
660
|
// // DEBUG testing enable/disable HTML UI Tabs
|
|
644
661
|
//delete _event.dimming;
|
|
@@ -648,51 +665,51 @@ module.exports = function (RED) {
|
|
|
648
665
|
|
|
649
666
|
// As grouped_light doesn't contain all requested properties, i find the first light in the group, and use this below in the code
|
|
650
667
|
// If the event type is grouped light, and there are missing properties, i infer these missing properties from the first light in the group!
|
|
651
|
-
if ((
|
|
668
|
+
if ((deviceByRef.color !== undefined || deviceByRef.dimming !== undefined || deviceByRef.color_temperature !== undefined) && deviceByRef.type === 'grouped_light') {
|
|
652
669
|
try {
|
|
653
|
-
const firstLightInGroup = node.serverHue.getFirstLightInGroup(
|
|
670
|
+
const firstLightInGroup = node.serverHue.getFirstLightInGroup(deviceByRef.id);
|
|
654
671
|
if (firstLightInGroup !== null && firstLightInGroup !== undefined) {
|
|
655
|
-
if (
|
|
656
|
-
|
|
672
|
+
if (deviceByRef.color === undefined) {
|
|
673
|
+
deviceByRef.color = firstLightInGroup.color;
|
|
657
674
|
}
|
|
658
|
-
if (
|
|
659
|
-
|
|
675
|
+
if (deviceByRef.color_temperature === undefined) {
|
|
676
|
+
deviceByRef.color_temperature = firstLightInGroup.color_temperature;
|
|
660
677
|
}
|
|
661
678
|
}
|
|
662
679
|
} catch (error) { }
|
|
663
680
|
}
|
|
664
681
|
|
|
665
|
-
if (
|
|
666
|
-
node.updateKNXLightState(
|
|
682
|
+
if (deviceByRef.on !== undefined) {
|
|
683
|
+
node.updateKNXLightState(deviceByRef.on.on);
|
|
667
684
|
// In case of switch off, set the dim to zero
|
|
668
|
-
if (
|
|
685
|
+
if (deviceByRef.on.on === false && (config.updateKNXBrightnessStatusOnHUEOnOff === undefined || config.updateKNXBrightnessStatusOnHUEOnOff === "onhueoff")) {
|
|
669
686
|
node.updateKNXBrightnessState(0);
|
|
670
|
-
if (
|
|
671
|
-
} else if (
|
|
687
|
+
if (deviceByRef.dimming !== undefined) delete deviceByRef.dimming; // Remove event.dimming, because has beem handled by this function and i don't want the function below to take care of it.
|
|
688
|
+
} else if (deviceByRef.on.on === true && node.currentHUEDevice.on.on === false) {
|
|
672
689
|
// Turn on always update the dimming KNX Status value as well.
|
|
673
690
|
let brightVal = 50;
|
|
674
691
|
if (node.currentHUEDevice.dimming !== undefined && node.currentHUEDevice.dimming.brightness !== undefined) brightVal = node.currentHUEDevice.dimming.brightness;
|
|
675
692
|
node.updateKNXBrightnessState(brightVal);
|
|
676
693
|
}
|
|
677
|
-
node.currentHUEDevice.on.on =
|
|
694
|
+
node.currentHUEDevice.on.on = deviceByRef.on.on;
|
|
678
695
|
}
|
|
679
696
|
|
|
680
|
-
if (
|
|
681
|
-
node.updateKNXLightColorState(
|
|
682
|
-
node.currentHUEDevice.color =
|
|
697
|
+
if (deviceByRef.color !== undefined) { // fixed https://github.com/Supergiovane/node-red-contrib-knx-ultimate/issues/287
|
|
698
|
+
node.updateKNXLightColorState(deviceByRef.color);
|
|
699
|
+
node.currentHUEDevice.color = deviceByRef.color;
|
|
683
700
|
}
|
|
684
701
|
|
|
685
|
-
if (
|
|
702
|
+
if (deviceByRef.dimming !== undefined && deviceByRef.dimming.brightness !== undefined) {
|
|
686
703
|
// Once upon n a time, the light transmit the brightness value of 0.39.
|
|
687
704
|
// To avoid wrongly turn light state on, exit
|
|
688
|
-
if (
|
|
689
|
-
if (node.currentHUEDevice.on !== undefined && node.currentHUEDevice.on.on === false &&
|
|
705
|
+
if (deviceByRef.dimming.brightness < 1) deviceByRef.dimming.brightness = 0;
|
|
706
|
+
if (node.currentHUEDevice.on !== undefined && node.currentHUEDevice.on.on === false && deviceByRef.dimming.brightness === 0) {
|
|
690
707
|
// Do nothing, because the light is off and the dimming also is 0
|
|
691
708
|
} else {
|
|
692
|
-
if (node.currentHUEDevice.on !== undefined && node.currentHUEDevice.on.on === false && (
|
|
693
|
-
node.updateKNXBrightnessState(
|
|
709
|
+
if (node.currentHUEDevice.on !== undefined && node.currentHUEDevice.on.on === false && (deviceByRef.on === undefined || (deviceByRef.on !== undefined && deviceByRef.on.on === true))) node.updateKNXLightState(deviceByRef.dimming.brightness > 0);
|
|
710
|
+
node.updateKNXBrightnessState(deviceByRef.dimming.brightness);
|
|
694
711
|
// If the brightness reaches zero, the hue lamp "on" property must be set to zero as well
|
|
695
|
-
if (
|
|
712
|
+
if (deviceByRef.dimming.brightness === 0 && node.currentHUEDevice.on !== undefined && node.currentHUEDevice.on.on === true) {
|
|
696
713
|
node.serverHue.hueManager.writeHueQueueAdd(
|
|
697
714
|
node.hueDevice,
|
|
698
715
|
{ on: { on: false } },
|
|
@@ -700,13 +717,13 @@ module.exports = function (RED) {
|
|
|
700
717
|
);
|
|
701
718
|
node.currentHUEDevice.on.on = false;
|
|
702
719
|
}
|
|
703
|
-
node.currentHUEDevice.dimming.brightness =
|
|
720
|
+
node.currentHUEDevice.dimming.brightness = deviceByRef.dimming.brightness;
|
|
704
721
|
}
|
|
705
722
|
}
|
|
706
|
-
if (
|
|
707
|
-
node.
|
|
708
|
-
node.updateKNXLightKelvinState(
|
|
709
|
-
node.currentHUEDevice.color_temperature.mirek =
|
|
723
|
+
if (deviceByRef.color_temperature !== undefined && deviceByRef.color_temperature.mirek !== undefined) {
|
|
724
|
+
node.updateKNXLightKelvinPercentageState(deviceByRef.color_temperature.mirek);
|
|
725
|
+
node.updateKNXLightKelvinState(deviceByRef.color_temperature.mirek);
|
|
726
|
+
node.currentHUEDevice.color_temperature.mirek = deviceByRef.color_temperature.mirek;
|
|
710
727
|
}
|
|
711
728
|
}
|
|
712
729
|
} catch (error) {
|
|
@@ -780,12 +797,12 @@ module.exports = function (RED) {
|
|
|
780
797
|
}
|
|
781
798
|
};
|
|
782
799
|
|
|
783
|
-
node.
|
|
784
|
-
if (config.
|
|
800
|
+
node.updateKNXLightKelvinPercentageState = function updateKNXLightKelvinPercentageState(_value, _outputtype = "write") {
|
|
801
|
+
if (config.GALightKelvinPercentageState !== undefined && config.GALightKelvinPercentageState !== "") {
|
|
785
802
|
const knxMsgPayload = {};
|
|
786
|
-
knxMsgPayload.topic = config.
|
|
787
|
-
knxMsgPayload.dpt = config.
|
|
788
|
-
if (config.
|
|
803
|
+
knxMsgPayload.topic = config.GALightKelvinPercentageState;
|
|
804
|
+
knxMsgPayload.dpt = config.dptLightKelvinPercentageState;
|
|
805
|
+
if (config.dptLightKelvinPercentageState === "5.001") {
|
|
789
806
|
const retPercent = hueColorConverter.ColorConverter.scale(_value, [153, 500], [0, 100]);
|
|
790
807
|
knxMsgPayload.payload = 100 - retPercent;
|
|
791
808
|
}
|
|
@@ -804,7 +821,7 @@ module.exports = function (RED) {
|
|
|
804
821
|
node.setNodeStatusHue({
|
|
805
822
|
fill: "blue",
|
|
806
823
|
shape: "ring",
|
|
807
|
-
text: "HUE->KNX
|
|
824
|
+
text: "HUE->KNX Tunable White",
|
|
808
825
|
payload: knxMsgPayload.payload,
|
|
809
826
|
});
|
|
810
827
|
}
|
|
@@ -508,7 +508,7 @@
|
|
|
508
508
|
<select id="node-input-valscene" style="width:180px;margin-left: 5px; text-align: left;"></select>
|
|
509
509
|
</div>
|
|
510
510
|
<div class="form-row">
|
|
511
|
-
<label for="node-input-namesceneStatus" style="width:100px;"><i class="fa fa-
|
|
511
|
+
<label for="node-input-namesceneStatus" style="width:100px;"><i class="fa fa-question-circle"></i> Status</label>
|
|
512
512
|
|
|
513
513
|
<label for="node-input-GAsceneStatus" style="width:20px;">GA</label>
|
|
514
514
|
<input type="text" id="node-input-GAsceneStatus" placeholder="Ex: 1/1/1" style="width:70px;margin-left: 5px; text-align: left;">
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"engines": {
|
|
4
4
|
"node": ">=16.0.0"
|
|
5
5
|
},
|
|
6
|
-
"version": "2.3.
|
|
6
|
+
"version": "2.3.5",
|
|
7
7
|
"description": "Control your KNX intallation via Node-Red! A bunch of KNX nodes, with integrated Philips HUE control and ETS group address importer. Easy to use and highly configurable.",
|
|
8
8
|
"dependencies": {
|
|
9
9
|
"binary-parser": "2.2.1",
|