node-red-contrib-knx-ultimate 2.3.3 → 2.3.4
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 +3 -0
- package/nodes/hue-config.js +2 -2
- package/nodes/knxUltimateAlerter.html +7 -7
- package/nodes/knxUltimateAlerter.js +154 -154
- package/nodes/knxUltimateHueLight.js +29 -28
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
|
|
7
7
|
# CHANGELOG
|
|
8
8
|
|
|
9
|
+
**Version 2.3.4** - Jan 2024<br/>
|
|
10
|
+
- HUE Light: fixex tab "DIM/Brightness" inaccessible, when "KNX Brightness Status" was set to use the default knx behaviour.<br/>
|
|
11
|
+
|
|
9
12
|
**Version 2.3.3** - Jan 2024<br/>
|
|
10
13
|
- HUE Light: added a warning if you double click a node, with the node still querying the HUE Bridge for the device.<br/>
|
|
11
14
|
|
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
|
+
};
|
|
@@ -626,7 +626,8 @@ module.exports = function (RED) {
|
|
|
626
626
|
|
|
627
627
|
node.handleSendHUE = (_event) => {
|
|
628
628
|
try {
|
|
629
|
-
|
|
629
|
+
let deviceByRef = cloneDeep(_event);
|
|
630
|
+
if (deviceByRef.id === node.hueDevice) {
|
|
630
631
|
if (node.currentHUEDevice === undefined || node.serverHue === null || node.serverHue === undefined) {
|
|
631
632
|
node.setNodeStatusHue({
|
|
632
633
|
fill: "red",
|
|
@@ -638,7 +639,7 @@ module.exports = function (RED) {
|
|
|
638
639
|
}
|
|
639
640
|
|
|
640
641
|
// Output the msg to the flow
|
|
641
|
-
node.send(
|
|
642
|
+
node.send(deviceByRef);
|
|
642
643
|
|
|
643
644
|
// // DEBUG testing enable/disable HTML UI Tabs
|
|
644
645
|
//delete _event.dimming;
|
|
@@ -648,51 +649,51 @@ module.exports = function (RED) {
|
|
|
648
649
|
|
|
649
650
|
// 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
651
|
// 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 ((
|
|
652
|
+
if ((deviceByRef.color !== undefined || deviceByRef.dimming !== undefined || deviceByRef.color_temperature !== undefined) && deviceByRef.type === 'grouped_light') {
|
|
652
653
|
try {
|
|
653
|
-
const firstLightInGroup = node.serverHue.getFirstLightInGroup(
|
|
654
|
+
const firstLightInGroup = node.serverHue.getFirstLightInGroup(deviceByRef.id);
|
|
654
655
|
if (firstLightInGroup !== null && firstLightInGroup !== undefined) {
|
|
655
|
-
if (
|
|
656
|
-
|
|
656
|
+
if (deviceByRef.color === undefined) {
|
|
657
|
+
deviceByRef.color = firstLightInGroup.color;
|
|
657
658
|
}
|
|
658
|
-
if (
|
|
659
|
-
|
|
659
|
+
if (deviceByRef.color_temperature === undefined) {
|
|
660
|
+
deviceByRef.color_temperature = firstLightInGroup.color_temperature;
|
|
660
661
|
}
|
|
661
662
|
}
|
|
662
663
|
} catch (error) { }
|
|
663
664
|
}
|
|
664
665
|
|
|
665
|
-
if (
|
|
666
|
-
node.updateKNXLightState(
|
|
666
|
+
if (deviceByRef.on !== undefined) {
|
|
667
|
+
node.updateKNXLightState(deviceByRef.on.on);
|
|
667
668
|
// In case of switch off, set the dim to zero
|
|
668
|
-
if (
|
|
669
|
+
if (deviceByRef.on.on === false && (config.updateKNXBrightnessStatusOnHUEOnOff === undefined || config.updateKNXBrightnessStatusOnHUEOnOff === "onhueoff")) {
|
|
669
670
|
node.updateKNXBrightnessState(0);
|
|
670
|
-
if (
|
|
671
|
-
} else if (
|
|
671
|
+
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.
|
|
672
|
+
} else if (deviceByRef.on.on === true && node.currentHUEDevice.on.on === false) {
|
|
672
673
|
// Turn on always update the dimming KNX Status value as well.
|
|
673
674
|
let brightVal = 50;
|
|
674
675
|
if (node.currentHUEDevice.dimming !== undefined && node.currentHUEDevice.dimming.brightness !== undefined) brightVal = node.currentHUEDevice.dimming.brightness;
|
|
675
676
|
node.updateKNXBrightnessState(brightVal);
|
|
676
677
|
}
|
|
677
|
-
node.currentHUEDevice.on.on =
|
|
678
|
+
node.currentHUEDevice.on.on = deviceByRef.on.on;
|
|
678
679
|
}
|
|
679
680
|
|
|
680
|
-
if (
|
|
681
|
-
node.updateKNXLightColorState(
|
|
682
|
-
node.currentHUEDevice.color =
|
|
681
|
+
if (deviceByRef.color !== undefined) { // fixed https://github.com/Supergiovane/node-red-contrib-knx-ultimate/issues/287
|
|
682
|
+
node.updateKNXLightColorState(deviceByRef.color);
|
|
683
|
+
node.currentHUEDevice.color = deviceByRef.color;
|
|
683
684
|
}
|
|
684
685
|
|
|
685
|
-
if (
|
|
686
|
+
if (deviceByRef.dimming !== undefined && deviceByRef.dimming.brightness !== undefined) {
|
|
686
687
|
// Once upon n a time, the light transmit the brightness value of 0.39.
|
|
687
688
|
// To avoid wrongly turn light state on, exit
|
|
688
|
-
if (
|
|
689
|
-
if (node.currentHUEDevice.on !== undefined && node.currentHUEDevice.on.on === false &&
|
|
689
|
+
if (deviceByRef.dimming.brightness < 1) deviceByRef.dimming.brightness = 0;
|
|
690
|
+
if (node.currentHUEDevice.on !== undefined && node.currentHUEDevice.on.on === false && deviceByRef.dimming.brightness === 0) {
|
|
690
691
|
// Do nothing, because the light is off and the dimming also is 0
|
|
691
692
|
} else {
|
|
692
|
-
if (node.currentHUEDevice.on !== undefined && node.currentHUEDevice.on.on === false && (
|
|
693
|
-
node.updateKNXBrightnessState(
|
|
693
|
+
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);
|
|
694
|
+
node.updateKNXBrightnessState(deviceByRef.dimming.brightness);
|
|
694
695
|
// If the brightness reaches zero, the hue lamp "on" property must be set to zero as well
|
|
695
|
-
if (
|
|
696
|
+
if (deviceByRef.dimming.brightness === 0 && node.currentHUEDevice.on !== undefined && node.currentHUEDevice.on.on === true) {
|
|
696
697
|
node.serverHue.hueManager.writeHueQueueAdd(
|
|
697
698
|
node.hueDevice,
|
|
698
699
|
{ on: { on: false } },
|
|
@@ -700,13 +701,13 @@ module.exports = function (RED) {
|
|
|
700
701
|
);
|
|
701
702
|
node.currentHUEDevice.on.on = false;
|
|
702
703
|
}
|
|
703
|
-
node.currentHUEDevice.dimming.brightness =
|
|
704
|
+
node.currentHUEDevice.dimming.brightness = deviceByRef.dimming.brightness;
|
|
704
705
|
}
|
|
705
706
|
}
|
|
706
|
-
if (
|
|
707
|
-
node.updateKNXLightHSVState(
|
|
708
|
-
node.updateKNXLightKelvinState(
|
|
709
|
-
node.currentHUEDevice.color_temperature.mirek =
|
|
707
|
+
if (deviceByRef.color_temperature !== undefined && deviceByRef.color_temperature.mirek !== undefined) {
|
|
708
|
+
node.updateKNXLightHSVState(deviceByRef.color_temperature.mirek);
|
|
709
|
+
node.updateKNXLightKelvinState(deviceByRef.color_temperature.mirek);
|
|
710
|
+
node.currentHUEDevice.color_temperature.mirek = deviceByRef.color_temperature.mirek;
|
|
710
711
|
}
|
|
711
712
|
}
|
|
712
713
|
} catch (error) {
|
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.4",
|
|
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",
|