Skip to content

Commit be466f3

Browse files
committed
.
1 parent bbc275d commit be466f3

28 files changed

+421
-77
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66

77
# CHANGELOG
88

9+
**Version 4.0.10** - October 2025<br/>
10+
- KNX Config node: you can now choose wether to display only the errors in the node statuses only errors.<br/>
11+
912
**Version 4.0.9** - September 2025<br/>
1013
- KNX Config node: now the ethernet interface is automatically selected, based on the KNX Gateway's IP subnet.<br/>
1114
- Added the details of keyring in clear text, when the loglevel is set to "debug".<br/>

nodes/knxUltimate-config.html

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@
2727
tunnelInterfaceIndividualAddress: { value: "" },
2828
tunnelUserPassword: { value: "" },
2929
tunnelUserId: { value: "" },
30-
autoReconnect: { value: "yes" }
30+
autoReconnect: { value: "yes" },
31+
statusDisplayPolicy: { value: "all" }
3132
},
3233
credentials: {
3334
keyringFilePassword: { type: "password" }
@@ -1145,6 +1146,17 @@
11451146
<!-- <// DEBUG LEVELS: success < debug < info < warn < error < disable
11461147
</div> -->
11471148
</div>
1149+
1150+
<div class="form-row">
1151+
<label for="node-config-input-statusDisplayPolicy">
1152+
<i class="fa fa-eye"></i>
1153+
<span data-i18n="knxUltimate-config.advanced.status_display_policy"></span>
1154+
</label>
1155+
<select id="node-config-input-statusDisplayPolicy" style="width:40%;">
1156+
<option value="all" data-i18n="knxUltimate-config.advanced.status_display_all"></option>
1157+
<option value="errors" data-i18n="knxUltimate-config.advanced.status_display_errors"></option>
1158+
</select>
1159+
</div>
11481160
</p>
11491161
</div>
11501162
<div id="tabs-3">

nodes/knxUltimate-config.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ const loggerClass = require('./utils/sysLogger')
2424
const payloadRounder = require("./utils/payloadManipulation");
2525
const utils = require('./utils/utils');
2626

27+
const STATUS_DISPLAY_ALLOWED_COLORS = new Set(['red', 'yellow']);
28+
2729
// DATAPONT MANIPULATION HELPERS
2830
// ####################
2931
const sortBy = (field) => (a, b) => {
@@ -218,6 +220,13 @@ module.exports = (RED) => {
218220
node.autoReconnect = true;
219221
}
220222
node.ignoreTelegramsWithRepeatedFlag = config.ignoreTelegramsWithRepeatedFlag === undefined ? false : config.ignoreTelegramsWithRepeatedFlag;
223+
const policyFromConfig = typeof config.statusDisplayPolicy === "string" ? config.statusDisplayPolicy : "all";
224+
node.statusDisplayPolicy = ['all', 'errors'].includes(policyFromConfig) ? policyFromConfig : "all";
225+
node.shouldDisplayStatus = (fill) => {
226+
if (node.statusDisplayPolicy !== 'errors') return true;
227+
const normalizedFill = (typeof fill === 'string' ? fill : '').toLowerCase();
228+
return STATUS_DISPLAY_ALLOWED_COLORS.has(normalizedFill);
229+
};
221230
// 24/07/2021 KNX Secure checks...
222231
node.keyringFileXML = typeof config.keyringFileXML === "undefined" || config.keyringFileXML.trim() === "" ? "" : config.keyringFileXML;
223232
node.knxSecureSelected = typeof config.knxSecureSelected === "undefined" ? false : config.knxSecureSelected;

nodes/knxUltimate.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,13 @@ module.exports = function (RED) {
3030
devicename = devicename || '';
3131
dpt = (typeof dpt === 'undefined' || dpt == '') ? '' : ` DPT${dpt}`;
3232
payload = typeof payload === 'object' ? JSON.stringify(payload) : payload;
33-
node.status({ fill, shape, text: `${GA + payload + (node.listenallga === true ? ` ${devicename}` : '')} (day ${dDate.getDate()}, ${dDate.toLocaleTimeString()}) ${text}` });
33+
const statusText = `${GA + payload + (node.listenallga === true ? ` ${devicename}` : '')} (day ${dDate.getDate()}, ${dDate.toLocaleTimeString()}) ${text}`;
34+
const shouldUpdateStatus = (node.serverKNX && typeof node.serverKNX.shouldDisplayStatus === 'function')
35+
? node.serverKNX.shouldDisplayStatus(fill)
36+
: true;
37+
if (shouldUpdateStatus) {
38+
node.status({ fill, shape, text: statusText });
39+
}
3440
// 16/02/2020 signal errors to the server
3541
if (fill.toUpperCase() === 'RED') {
3642
if (node.serverKNX) {

nodes/knxUltimateAlerter.js

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,14 @@ module.exports = function (RED) {
3333
node.whentostart = config.whentostart === undefined ? 'ifnewalert' : config.whentostart;
3434
node.timerinterval = (config.timerinterval === undefined || config.timerinterval == '') ? '2' : config.timerinterval;
3535

36+
const shouldDisplayStatus = (color) => {
37+
const provider = node.serverKNX;
38+
if (provider && typeof provider.shouldDisplayStatus === 'function') {
39+
return provider.shouldDisplayStatus(color);
40+
}
41+
return true;
42+
};
43+
3644
if (config.initialreadGAInRules === undefined) {
3745
node.initialread = 1;
3846
} else {
@@ -53,7 +61,9 @@ module.exports = function (RED) {
5361
devicename = devicename || '';
5462
dpt = (typeof dpt === 'undefined' || dpt == '') ? '' : ' DPT' + dpt;
5563
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 });
64+
if (shouldDisplayStatus(fill)) {
65+
node.status({ fill, shape, text: GA + payload + (node.listenallga === true ? ' ' + devicename : '') + ' (' + dDate.getDate() + ', ' + dDate.toLocaleTimeString() + ' ' + text });
66+
}
5767
} catch (error) {
5868
}
5969
};
@@ -66,7 +76,9 @@ module.exports = function (RED) {
6676
devicename = devicename || '';
6777
dpt = (typeof dpt === 'undefined' || dpt == '') ? '' : ' DPT' + dpt;
6878
try {
69-
node.status({ fill, shape, text: GA + payload + (node.listenallga === true ? ' ' + devicename : '') + ' (' + dDate.getDate() + ', ' + dDate.toLocaleTimeString() + ' ' + text });
79+
if (shouldDisplayStatus(fill)) {
80+
node.status({ fill, shape, text: GA + payload + (node.listenallga === true ? ' ' + devicename : '') + ' (' + dDate.getDate() + ', ' + dDate.toLocaleTimeString() + ' ' + text });
81+
}
7082
} catch (error) {
7183
}
7284
};

nodes/knxUltimateAutoResponder.js

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,22 @@ module.exports = function (RED) {
5151
node.exposedGAs = [];
5252
node.commandText = []; // Raw list Respond To
5353
node.timerSaveExposedGAs = null;
54-
if (node.serverKNX === null) { node.status({ fill: 'red', shape: 'dot', text: '[NO GATEWAY SELECTED]' }); return; }
54+
55+
const shouldDisplayStatus = (color) => {
56+
const provider = node.serverKNX;
57+
if (provider && typeof provider.shouldDisplayStatus === 'function') {
58+
return provider.shouldDisplayStatus(color);
59+
}
60+
return true;
61+
};
62+
63+
const updateStatus = (status) => {
64+
if (!status) return;
65+
if (shouldDisplayStatus(status.fill)) {
66+
node.status(status);
67+
}
68+
};
69+
if (node.serverKNX === null) { updateStatus({ fill: 'red', shape: 'dot', text: '[NO GATEWAY SELECTED]' }); return; }
5570

5671
try {
5772
const baseLogLevel = (node.serverKNX && node.serverKNX.loglevel) ? node.serverKNX.loglevel : 'error';
@@ -109,7 +124,7 @@ module.exports = function (RED) {
109124

110125
// Add the ETS CSV file list to exposedGAs
111126
if (node.serverKNX.csv === undefined || node.serverKNX.csv === '' || node.serverKNX.csv.length === 0) {
112-
node.status({ fill: 'grey', shape: 'ring', text: 'No ETS file imported', payload: '', dpt: '', devicename: '' });
127+
updateStatus({ fill: 'grey', shape: 'ring', text: 'No ETS file imported', payload: '', dpt: '', devicename: '' });
113128
//return;
114129
} else {
115130
node.serverKNX.csv.forEach(element => {
@@ -120,14 +135,14 @@ module.exports = function (RED) {
120135
curGa.enabled = false;
121136
}
122137
})
123-
node.status({ fill: 'green', shape: 'ring', text: 'ETS file loaded', payload: '', dpt: '', devicename: '' });
138+
updateStatus({ fill: 'green', shape: 'ring', text: 'ETS file loaded', payload: '', dpt: '', devicename: '' });
124139
}
125140

126141
// Fill the filter list
127142
try {
128143
node.commandText = JSON.parse(config.commandText);
129144
} catch (error) {
130-
node.status({ fill: 'red', shape: 'dot', text: 'JSON error: ' + error.message, payload: '', dpt: '', devicename: '' });
145+
updateStatus({ fill: 'red', shape: 'dot', text: 'JSON error: ' + error.message, payload: '', dpt: '', devicename: '' });
131146
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.error(`knxUltimateAutoResponder: node.commandText = JSON.parse(config.commandText) ${error.stack}`);
132147
return;
133148
}
@@ -164,10 +179,10 @@ module.exports = function (RED) {
164179
// Delete all not wanted GAs, that aren't in the node.commandText directive list.
165180
node.exposedGAs = node.exposedGAs.filter(a => (a.enabled !== undefined && a.enabled === true));
166181

167-
node.status({ fill: 'green', shape: 'ring', text: 'JSON parsed: ' + node.commandText.length + " directive(s).", payload: '', dpt: '', devicename: '' });
182+
updateStatus({ fill: 'green', shape: 'ring', text: 'JSON parsed: ' + node.commandText.length + " directive(s).", payload: '', dpt: '', devicename: '' });
168183
} else {
169184
// Error
170-
node.status({ fill: 'red', shape: 'dot', text: 'JSON error: ga or default keys not set. Abort.', payload: '', dpt: '', devicename: '' });
185+
updateStatus({ fill: 'red', shape: 'dot', text: 'JSON error: ga or default keys not set. Abort.', payload: '', dpt: '', devicename: '' });
171186
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.error(`knxUltimateAutoResponder: node.commandText.forEach(element.. JSON error: ga or default keys not set. Abort.`);
172187
return;
173188
}
@@ -191,7 +206,7 @@ module.exports = function (RED) {
191206
// Take only RAW data and decode it with the dpt specified by the commandText directive
192207
decodedPayload = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(oGa.dpt));
193208
} catch (error) {
194-
node.status({ fill: 'red', shape: 'dot', text: 'const decodedPayload = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(oGa.dpt)); ' + error.message, payload: '', dpt: '', devicename: '' });
209+
updateStatus({ fill: 'red', shape: 'dot', text: 'const decodedPayload = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(oGa.dpt)); ' + error.message, payload: '', dpt: '', devicename: '' });
195210
if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.error(`knxUltimateAutoResponder: const decodedPayload = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(oGa.dpt)); ${error.stack}`);
196211
}
197212
oGa.payload = decodedPayload
@@ -211,9 +226,9 @@ module.exports = function (RED) {
211226
const dDate = new Date()
212227
if (oFoundGA.address !== undefined && oFoundGA.dpt !== undefined && retVal !== undefined) {
213228
node.serverKNX.sendKNXTelegramToKNXEngine({ grpaddr: oFoundGA.address, payload: retVal, dpt: oFoundGA.dpt, outputtype: 'response', nodecallerid: node.id });
214-
node.status({ fill: 'blue', shape: 'dot', text: 'Respond ' + oFoundGA.address + ' => ' + retVal + ' (' + dDate.getDate() + ', ' + dDate.toLocaleTimeString() + ')' })
229+
updateStatus({ fill: 'blue', shape: 'dot', text: 'Respond ' + oFoundGA.address + ' => ' + retVal + ' (' + dDate.getDate() + ', ' + dDate.toLocaleTimeString() + ')' })
215230
} else {
216-
node.status({ fill: 'yellow', shape: 'ring', text: 'Issue responding ' + oFoundGA.address + ' => ' + retVal + ' (' + dDate.getDate() + ', ' + dDate.toLocaleTimeString() + ')' })
231+
updateStatus({ fill: 'yellow', shape: 'ring', text: 'Issue responding ' + oFoundGA.address + ' => ' + retVal + ' (' + dDate.getDate() + ', ' + dDate.toLocaleTimeString() + ')' })
217232
}
218233
}
219234
} catch (error) {

nodes/knxUltimateGlobalContext.js

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,17 +55,32 @@ module.exports = function (RED) {
5555
node.contextStorage = config.contextStorage !== undefined ? config.contextStorage : ''
5656
node.exposeAsVariable = config.exposeAsVariable !== undefined ? config.exposeAsVariable : 'exposeAsVariableREADONLY' // Should expose the Group Addresses to the Global Context?
5757
node.exposedGAs = []
58-
node.timerExposedGAs = null
58+
node.timerExposedGAs = null
59+
60+
const shouldDisplayStatus = (color) => {
61+
const provider = node.serverKNX;
62+
if (provider && typeof provider.shouldDisplayStatus === 'function') {
63+
return provider.shouldDisplayStatus(color);
64+
}
65+
return true;
66+
};
67+
68+
const updateStatus = (status) => {
69+
if (!status) return;
70+
if (shouldDisplayStatus(status.fill)) {
71+
node.status(status);
72+
}
73+
};
5974

6075
// Used to call the status update from the config node.
6176
node.setNodeStatus = ({ fill, shape, text, payload, GA, dpt, devicename }) => {
6277
try {
63-
if (node.serverKNX === null) { node.status({ fill: 'red', shape: 'dot', text: '[NO GATEWAY SELECTED]' }); return }
78+
if (node.serverKNX === null) { updateStatus({ fill: 'red', shape: 'dot', text: '[NO GATEWAY SELECTED]' }); return }
6479
GA = GA === undefined ? '' : GA
6580
payload = payload === undefined ? '' : payload
6681
payload = typeof payload === 'object' ? JSON.stringify(payload) : payload
6782
const dDate = new Date()
68-
node.status({ fill, shape, text: GA + ' ' + payload + ' ' + text + ' (' + dDate.getDate() + ', ' + dDate.toLocaleTimeString() + ')' })
83+
updateStatus({ fill, shape, text: GA + ' ' + payload + ' ' + text + ' (' + dDate.getDate() + ', ' + dDate.toLocaleTimeString() + ')' })
6984
} catch (error) {
7085
}
7186
}

nodes/knxUltimateHueBattery.js

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,22 @@ module.exports = function (RED) {
2424
node.formatdecimalsvalue = 2;
2525
node.hueDevice = config.hueDevice;
2626
node.initializingAtStart = (config.readStatusAtStartup === undefined || config.readStatusAtStartup === "yes");
27-
node.currentDeviceValue = 0;
27+
node.currentDeviceValue = 0;
28+
29+
const shouldDisplayStatus = (color) => {
30+
const provider = node.serverKNX;
31+
if (provider && typeof provider.shouldDisplayStatus === 'function') {
32+
return provider.shouldDisplayStatus(color);
33+
}
34+
return true;
35+
};
36+
37+
const updateStatus = (status) => {
38+
if (!status) return;
39+
if (shouldDisplayStatus(status.fill)) {
40+
node.status(status);
41+
}
42+
};
2843

2944
// Used to call the status update from the config node.
3045
node.setNodeStatus = ({
@@ -35,7 +50,9 @@ module.exports = function (RED) {
3550
const dDate = new Date();
3651
payload = typeof payload === "object" ? JSON.stringify(payload) : payload.toString();
3752
node.sKNXNodeStatusText = `|KNX: ${text} ${payload} (${dDate.getDate()}, ${dDate.toLocaleTimeString()})`;
38-
node.status({ fill, shape, text: (node.sHUENodeStatusText || '') + ' ' + (node.sKNXNodeStatusText || '') });
53+
if (shouldDisplayStatus(fill)) {
54+
node.status({ fill, shape, text: (node.sHUENodeStatusText || '') + ' ' + (node.sKNXNodeStatusText || '') });
55+
}
3956
} catch (error) { }
4057
};
4158
// Used to call the status update from the HUE config node.
@@ -45,7 +62,9 @@ module.exports = function (RED) {
4562
const dDate = new Date();
4663
payload = typeof payload === "object" ? JSON.stringify(payload) : payload.toString();
4764
node.sHUENodeStatusText = `|HUE: ${text} ${payload} (${dDate.getDate()}, ${dDate.toLocaleTimeString()})`;
48-
node.status({ fill, shape, text: node.sHUENodeStatusText + ' ' + (node.sKNXNodeStatusText || '') });
65+
if (shouldDisplayStatus(fill)) {
66+
node.status({ fill, shape, text: node.sHUENodeStatusText + ' ' + (node.sKNXNodeStatusText || '') });
67+
}
4968
} catch (error) { }
5069
};
5170

@@ -94,7 +113,7 @@ module.exports = function (RED) {
94113
});
95114
}
96115
} catch (error) {
97-
node.status({ fill: 'red', shape: 'dot', text: `HUE->KNX error ${error.message} (${new Date().getDate()}, ${new Date().toLocaleTimeString()})` });
116+
updateStatus({ fill: 'red', shape: 'dot', text: `HUE->KNX error ${error.message} (${new Date().getDate()}, ${new Date().toLocaleTimeString()})` });
98117
}
99118
};
100119

nodes/knxUltimateHueButton.js

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,21 @@ module.exports = function (RED) {
3737
if (node.dimSend === 'down') node.dimSend = { decr_incr: 0, data: 3 };
3838
if (node.dimSend === 'stop') node.dimSend = { decr_incr: 0, data: 0 };
3939

40+
const shouldDisplayStatus = (color) => {
41+
const provider = node.serverKNX;
42+
if (provider && typeof provider.shouldDisplayStatus === 'function') {
43+
return provider.shouldDisplayStatus(color);
44+
}
45+
return true;
46+
};
47+
48+
const updateStatus = (status) => {
49+
if (!status) return;
50+
if (shouldDisplayStatus(status.fill)) {
51+
node.status(status);
52+
}
53+
};
54+
4055
// Used to call the status update from the config node.
4156
node.setNodeStatus = ({
4257
fill, shape, text, payload,
@@ -46,7 +61,7 @@ module.exports = function (RED) {
4661
const dDate = new Date();
4762
payload = typeof payload === "object" ? JSON.stringify(payload) : payload.toString();
4863
node.sKNXNodeStatusText = `|KNX: ${text} ${payload} (${dDate.getDate()}, ${dDate.toLocaleTimeString()})`;
49-
node.status({ fill, shape, text: (node.sHUENodeStatusText || '') + ' ' + (node.sKNXNodeStatusText || '') });
64+
updateStatus({ fill, shape, text: (node.sHUENodeStatusText || '') + ' ' + (node.sKNXNodeStatusText || '') });
5065
} catch (error) { }
5166
};
5267
// Used to call the status update from the HUE config node.
@@ -56,7 +71,7 @@ module.exports = function (RED) {
5671
const dDate = new Date();
5772
payload = typeof payload === "object" ? JSON.stringify(payload) : payload.toString();
5873
node.sHUENodeStatusText = `|HUE: ${text} ${payload} (${dDate.getDate()}, ${dDate.toLocaleTimeString()})`;
59-
node.status({ fill, shape, text: node.sHUENodeStatusText + ' ' + (node.sKNXNodeStatusText || '') });
74+
updateStatus({ fill, shape, text: node.sHUENodeStatusText + ' ' + (node.sKNXNodeStatusText || '') });
6075
} catch (error) { }
6176
};
6277

0 commit comments

Comments
 (0)