node-red-contrib-ccu 3.4.1 → 4.0.0
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/.prettierignore +10 -0
- package/.prettierrc +14 -0
- package/AGENTS.md +96 -0
- package/CHANGELOG.md +66 -0
- package/README.md +17 -21
- package/ROADMAP.md +402 -0
- package/eslint.config.js +45 -0
- package/examples/rpc-events.json +65 -0
- package/examples/switch-datapoint.json +86 -0
- package/examples/system-variable.json +60 -0
- package/nodes/ccu-alexa.html +12 -7
- package/nodes/ccu-alexa.js +43 -15
- package/nodes/ccu-connection.html +9 -1
- package/nodes/ccu-connection.js +602 -337
- package/nodes/ccu-display.html +11 -4
- package/nodes/ccu-display.js +26 -9
- package/nodes/ccu-get-value.html +23 -14
- package/nodes/ccu-get-value.js +10 -5
- package/nodes/ccu-mqtt.html +9 -10
- package/nodes/ccu-mqtt.js +55 -29
- package/nodes/ccu-program.html +1 -1
- package/nodes/ccu-program.js +9 -7
- package/nodes/ccu-rpc-event.html +12 -7
- package/nodes/ccu-rpc-event.js +4 -5
- package/nodes/ccu-rpc.html +12 -5
- package/nodes/ccu-rpc.js +8 -6
- package/nodes/ccu-script.html +1 -1
- package/nodes/ccu-script.js +4 -3
- package/nodes/ccu-set-value.html +11 -7
- package/nodes/ccu-set-value.js +47 -16
- package/nodes/ccu-signal.html +11 -5
- package/nodes/ccu-signal.js +34 -23
- package/nodes/ccu-switch.html +23 -13
- package/nodes/ccu-sysvar.html +1 -1
- package/nodes/ccu-sysvar.js +11 -7
- package/nodes/ccu-value.html +12 -7
- package/nodes/ccu-value.js +36 -13
- package/nodes/lib/base62.js +22 -0
- package/nodes/lib/discover.js +127 -0
- package/nodes/lib/nextport.js +30 -0
- package/nodes/lib/similarity.js +51 -0
- package/nodes/lib/status.js +1 -1
- package/package.json +105 -144
- package/roadmap-archive/README.md +20 -0
- package/roadmap-archive/phase-1.md +48 -0
- package/roadmap-archive/todo-history.md +39 -0
- package/TODO.md +0 -41
- package/ccu_rega_localhost.json +0 -1
- package/ccu_values_localhost.json +0 -1
package/nodes/ccu-rpc-event.html
CHANGED
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
change: {value: false},
|
|
30
30
|
working: {value: false},
|
|
31
31
|
cache: {value: false},
|
|
32
|
-
topic: {value: '${CCU}/${Interface}/${channelName}/${datapoint}'}
|
|
32
|
+
topic: {value: '${CCU}/${Interface}/${channelName}/${datapoint}'}
|
|
33
33
|
},
|
|
34
34
|
inputs: 0,
|
|
35
35
|
outputs: 1,
|
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
|
|
52
52
|
let ifacesLoaded = false;
|
|
53
53
|
let ifacesPending = false;
|
|
54
|
+
let ifaceLoadRetries = 5;
|
|
54
55
|
|
|
55
56
|
$('.filter').each(function () {
|
|
56
57
|
const id = $(this).prop('id');
|
|
@@ -96,7 +97,6 @@
|
|
|
96
97
|
}
|
|
97
98
|
|
|
98
99
|
ifacesPending = true;
|
|
99
|
-
console.log('loadIfaces()');
|
|
100
100
|
$nodeInputIface.html('<option value="">*</option>');
|
|
101
101
|
const nodeId = $nodeInputCcuConfig.val();
|
|
102
102
|
if (nodeId === '_ADD_') {
|
|
@@ -116,6 +116,12 @@
|
|
|
116
116
|
cb();
|
|
117
117
|
ifacesPending = false;
|
|
118
118
|
}
|
|
119
|
+
}).fail(() => {
|
|
120
|
+
// config node not (yet) available in the runtime - allow retrying
|
|
121
|
+
ifacesPending = false;
|
|
122
|
+
if (ifaceLoadRetries-- > 0) {
|
|
123
|
+
setTimeout(() => loadIfaces(iface, cb), 1000);
|
|
124
|
+
}
|
|
119
125
|
});
|
|
120
126
|
}
|
|
121
127
|
}
|
|
@@ -128,15 +134,17 @@
|
|
|
128
134
|
});
|
|
129
135
|
});
|
|
130
136
|
|
|
137
|
+
// Node-RED 4.x does not reliably fire the change event on dialog
|
|
138
|
+
// open - trigger the initial load explicitly (see ROADMAP.md 8.1)
|
|
139
|
+
$nodeInputCcuConfig.trigger('change');
|
|
140
|
+
|
|
131
141
|
function loadConfig() {
|
|
132
142
|
if (ifacesLoaded) {
|
|
133
|
-
console.log('loadConfig');
|
|
134
143
|
const nodeId = $nodeInputCcuConfig.val();
|
|
135
144
|
const url = 'ccu?config=' + nodeId;
|
|
136
145
|
$.getJSON(url, d => {
|
|
137
146
|
data = d;
|
|
138
147
|
processChannelAssignments();
|
|
139
|
-
console.log('data', data);
|
|
140
148
|
autocomplete();
|
|
141
149
|
});
|
|
142
150
|
}
|
|
@@ -150,7 +158,6 @@
|
|
|
150
158
|
data.nameChannels = {};
|
|
151
159
|
data.nameDevices = {};
|
|
152
160
|
|
|
153
|
-
console.log(data);
|
|
154
161
|
|
|
155
162
|
Object.keys(data.channelRooms).forEach(channel => {
|
|
156
163
|
const room = data.channelRooms[channel];
|
|
@@ -235,7 +242,6 @@
|
|
|
235
242
|
const channelNames = $('#node-input-channelName').val() ? [$('#node-input-channelName').val()] : [];
|
|
236
243
|
const channelTypes = $('#node-input-channelType').val() ? [$('#node-input-channelType').val()] : [];
|
|
237
244
|
//const channelIndexes = $('#node-input-channelIndex').val() ? [$('#node-input-channelIndex').val()] : [];
|
|
238
|
-
const datapoints = $('#node-input-datapoint').val() ? [$('#node-input-datapoint').val()] : [];
|
|
239
245
|
|
|
240
246
|
const lists = {
|
|
241
247
|
device: [],
|
|
@@ -291,7 +297,6 @@
|
|
|
291
297
|
}
|
|
292
298
|
}
|
|
293
299
|
|
|
294
|
-
console.log('autocomplete', {iface, rooms, funcs, devices, deviceNames, deviceTypes, channels, channelNames, channelTypes, datapoints});
|
|
295
300
|
|
|
296
301
|
function checkDatapoint(datapoint) {
|
|
297
302
|
let deviceCheck = false;
|
package/nodes/ccu-rpc-event.js
CHANGED
|
@@ -18,7 +18,7 @@ module.exports = function (RED) {
|
|
|
18
18
|
cache: config.cache,
|
|
19
19
|
change: config.change,
|
|
20
20
|
stable: config.working,
|
|
21
|
-
iface: config.iface
|
|
21
|
+
iface: config.iface,
|
|
22
22
|
};
|
|
23
23
|
|
|
24
24
|
this.ccu.register(this);
|
|
@@ -33,9 +33,8 @@ module.exports = function (RED) {
|
|
|
33
33
|
'channelName',
|
|
34
34
|
'channelType',
|
|
35
35
|
'channelIndex',
|
|
36
|
-
'datapoint'
|
|
37
|
-
|
|
38
|
-
].forEach(attr => {
|
|
36
|
+
'datapoint',
|
|
37
|
+
].forEach((attr) => {
|
|
39
38
|
if (!config[attr]) {
|
|
40
39
|
return;
|
|
41
40
|
}
|
|
@@ -46,7 +45,7 @@ module.exports = function (RED) {
|
|
|
46
45
|
filter[attr] = config[attr];
|
|
47
46
|
}
|
|
48
47
|
});
|
|
49
|
-
this.idSubscription = this.ccu.subscribe(filter, message => {
|
|
48
|
+
this.idSubscription = this.ccu.subscribe(filter, (message) => {
|
|
50
49
|
message.topic = this.ccu.topicReplace(config.topic, message);
|
|
51
50
|
this.send(message);
|
|
52
51
|
});
|
package/nodes/ccu-rpc.html
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
return false;
|
|
15
15
|
}
|
|
16
16
|
}},
|
|
17
|
-
topic: {value: '${CCU}/${Interface}/${Method}'}
|
|
17
|
+
topic: {value: '${CCU}/${Interface}/${Method}'}
|
|
18
18
|
},
|
|
19
19
|
inputs: 1,
|
|
20
20
|
outputs: 1,
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
|
|
35
35
|
let ifacesLoaded = false;
|
|
36
36
|
let ifacesPending = false;
|
|
37
|
+
let ifaceLoadRetries = 5;
|
|
37
38
|
|
|
38
39
|
function loadIfaces(iface, cb) {
|
|
39
40
|
if (ifacesPending) {
|
|
@@ -41,7 +42,6 @@
|
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
ifacesPending = true;
|
|
44
|
-
console.log('loadIfaces()');
|
|
45
45
|
$nodeInputIface.html('<option></option>');
|
|
46
46
|
const nodeId = $nodeInputCcuConfig.val();
|
|
47
47
|
if (nodeId === '_ADD_') {
|
|
@@ -59,12 +59,17 @@
|
|
|
59
59
|
cb();
|
|
60
60
|
ifacesPending = false;
|
|
61
61
|
}
|
|
62
|
+
}).fail(() => {
|
|
63
|
+
// config node not (yet) available in the runtime - allow retrying
|
|
64
|
+
ifacesPending = false;
|
|
65
|
+
if (ifaceLoadRetries-- > 0) {
|
|
66
|
+
setTimeout(() => loadIfaces(iface, cb), 1000);
|
|
67
|
+
}
|
|
62
68
|
});
|
|
63
69
|
}
|
|
64
70
|
}
|
|
65
71
|
|
|
66
72
|
$nodeInputCcuConfig.change(() => {
|
|
67
|
-
console.log('$nodeInputCcuConfig change');
|
|
68
73
|
loadIfaces(this.iface, () => {
|
|
69
74
|
ifacesLoaded = true;
|
|
70
75
|
$nodeInputIface.removeAttr('disabled');
|
|
@@ -72,6 +77,10 @@
|
|
|
72
77
|
});
|
|
73
78
|
});
|
|
74
79
|
|
|
80
|
+
// Node-RED 4.x does not reliably fire the change event on dialog
|
|
81
|
+
// open - trigger the initial load explicitly (see ROADMAP.md 8.1)
|
|
82
|
+
$nodeInputCcuConfig.trigger('change');
|
|
83
|
+
|
|
75
84
|
$('#node-input-method').autocomplete({
|
|
76
85
|
source: [],
|
|
77
86
|
close: () => {
|
|
@@ -83,7 +92,6 @@
|
|
|
83
92
|
|
|
84
93
|
$('#node-input-iface').change(() => {
|
|
85
94
|
if (ifacesLoaded) {
|
|
86
|
-
console.log('#node-input-iface change');
|
|
87
95
|
autocompleteMethods();
|
|
88
96
|
}
|
|
89
97
|
});
|
|
@@ -116,7 +124,6 @@
|
|
|
116
124
|
methods = [];
|
|
117
125
|
}
|
|
118
126
|
|
|
119
|
-
console.log('autocompleteMethods', iface, methods.length);
|
|
120
127
|
$('#node-input-method').autocomplete('option', 'source', methods);
|
|
121
128
|
}
|
|
122
129
|
}
|
package/nodes/ccu-rpc.js
CHANGED
|
@@ -37,27 +37,29 @@ module.exports = function (RED) {
|
|
|
37
37
|
} else if (method === 'putParamset') {
|
|
38
38
|
let [address, paramset, values] = parameters;
|
|
39
39
|
values = values || {};
|
|
40
|
-
Object.keys(values).forEach(parameter => {
|
|
40
|
+
Object.keys(values).forEach((parameter) => {
|
|
41
41
|
values[parameter] = this.ccu.paramCast(iface, address, paramset, parameter, values[parameter]);
|
|
42
42
|
});
|
|
43
43
|
parameters[2] = values;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
this.ccu
|
|
47
|
-
.
|
|
46
|
+
this.ccu
|
|
47
|
+
.methodCall(iface, method, parameters)
|
|
48
|
+
.then((res) => {
|
|
48
49
|
const message = {
|
|
49
50
|
ccu: this.ccu.host,
|
|
50
51
|
iface,
|
|
51
52
|
topic: config.topic,
|
|
52
53
|
payload: res,
|
|
53
|
-
ts:
|
|
54
|
-
method
|
|
54
|
+
ts: new Date().getTime(),
|
|
55
|
+
method,
|
|
55
56
|
};
|
|
56
57
|
message.topic = this.ccu.topicReplace(config.topic, message);
|
|
57
58
|
send(message);
|
|
58
59
|
|
|
59
60
|
done();
|
|
60
|
-
})
|
|
61
|
+
})
|
|
62
|
+
.catch((error) => {
|
|
61
63
|
done(error);
|
|
62
64
|
});
|
|
63
65
|
});
|
package/nodes/ccu-script.html
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
name: {value: ''},
|
|
7
7
|
script: {value: ''},
|
|
8
8
|
ccuConfig: {value: 'localhost', type: 'ccu-connection', required: true},
|
|
9
|
-
topic: {value: '${CCU}/${Interface}'}
|
|
9
|
+
topic: {value: '${CCU}/${Interface}'}
|
|
10
10
|
},
|
|
11
11
|
inputs: 1,
|
|
12
12
|
outputs: 1,
|
package/nodes/ccu-script.js
CHANGED
|
@@ -21,8 +21,9 @@ module.exports = function (RED) {
|
|
|
21
21
|
_input(message, send, done) {
|
|
22
22
|
let script = this.script || message.payload;
|
|
23
23
|
script += '\n\nvar nr_script_call_success = true;\n';
|
|
24
|
-
this.ccu
|
|
25
|
-
.
|
|
24
|
+
this.ccu
|
|
25
|
+
.script(script)
|
|
26
|
+
.then((message) => {
|
|
26
27
|
message.iface = this.iface;
|
|
27
28
|
message.ccu = this.ccu.host;
|
|
28
29
|
message.topic = this.ccu.topicReplace(this.topic, message);
|
|
@@ -36,7 +37,7 @@ module.exports = function (RED) {
|
|
|
36
37
|
done(new Error('Script call failed'));
|
|
37
38
|
}
|
|
38
39
|
})
|
|
39
|
-
.catch(error => {
|
|
40
|
+
.catch((error) => {
|
|
40
41
|
this.status({fill: 'red', shape: 'dot', text: 'error'});
|
|
41
42
|
done(error);
|
|
42
43
|
});
|
package/nodes/ccu-set-value.html
CHANGED
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
|
|
50
50
|
let ifacesLoaded = false;
|
|
51
51
|
let ifacesPending = false;
|
|
52
|
+
let ifaceLoadRetries = 5;
|
|
52
53
|
|
|
53
54
|
$('#node-input-force-dropdown').val(String(Boolean(this.force)));
|
|
54
55
|
|
|
@@ -96,7 +97,6 @@
|
|
|
96
97
|
}
|
|
97
98
|
|
|
98
99
|
ifacesPending = true;
|
|
99
|
-
console.log('loadIfaces()');
|
|
100
100
|
$nodeInputIface.html('<option value="">*</option>');
|
|
101
101
|
const nodeId = $nodeInputCcuConfig.val();
|
|
102
102
|
if (nodeId === '_ADD_') {
|
|
@@ -116,6 +116,12 @@
|
|
|
116
116
|
cb();
|
|
117
117
|
ifacesPending = false;
|
|
118
118
|
}
|
|
119
|
+
}).fail(() => {
|
|
120
|
+
// config node not (yet) available in the runtime - allow retrying
|
|
121
|
+
ifacesPending = false;
|
|
122
|
+
if (ifaceLoadRetries-- > 0) {
|
|
123
|
+
setTimeout(() => loadIfaces(iface, cb), 1000);
|
|
124
|
+
}
|
|
119
125
|
});
|
|
120
126
|
}
|
|
121
127
|
}
|
|
@@ -128,15 +134,17 @@
|
|
|
128
134
|
});
|
|
129
135
|
});
|
|
130
136
|
|
|
137
|
+
// Node-RED 4.x does not reliably fire the change event on dialog
|
|
138
|
+
// open - trigger the initial load explicitly (see ROADMAP.md 8.1)
|
|
139
|
+
$nodeInputCcuConfig.trigger('change');
|
|
140
|
+
|
|
131
141
|
function loadConfig() {
|
|
132
142
|
if (ifacesLoaded) {
|
|
133
|
-
console.log('loadConfig');
|
|
134
143
|
const nodeId = $nodeInputCcuConfig.val();
|
|
135
144
|
const url = 'ccu?config=' + nodeId;
|
|
136
145
|
$.getJSON(url, d => {
|
|
137
146
|
data = d;
|
|
138
147
|
processChannelAssignments();
|
|
139
|
-
console.log('data', data);
|
|
140
148
|
autocomplete();
|
|
141
149
|
});
|
|
142
150
|
}
|
|
@@ -150,7 +158,6 @@
|
|
|
150
158
|
data.nameChannels = {};
|
|
151
159
|
data.nameDevices = {};
|
|
152
160
|
|
|
153
|
-
console.log(data);
|
|
154
161
|
|
|
155
162
|
Object.keys(data.channelRooms).forEach(channel => {
|
|
156
163
|
const room = data.channelRooms[channel];
|
|
@@ -236,7 +243,6 @@
|
|
|
236
243
|
const channelNames = $('#node-input-channelName').val() ? [$('#node-input-channelName').val()] : [];
|
|
237
244
|
const channelTypes = $('#node-input-channelType').val() ? [$('#node-input-channelType').val()] : [];
|
|
238
245
|
//const channelIndexes = $('#node-input-channelIndex').val() ? [$('#node-input-channelIndex').val()] : [];
|
|
239
|
-
const datapoints = $('#node-input-datapoint').val() ? [$('#node-input-datapoint').val()] : [];
|
|
240
246
|
|
|
241
247
|
const lists = {
|
|
242
248
|
device: [],
|
|
@@ -292,7 +298,6 @@
|
|
|
292
298
|
}
|
|
293
299
|
}
|
|
294
300
|
|
|
295
|
-
console.log('autocomplete', {iface, rooms, funcs, devices, deviceNames, deviceTypes, channels, channelNames, channelTypes, datapoints});
|
|
296
301
|
|
|
297
302
|
function checkDatapoint(datapoint) {
|
|
298
303
|
let deviceCheck = false;
|
|
@@ -476,7 +481,6 @@
|
|
|
476
481
|
});
|
|
477
482
|
}
|
|
478
483
|
|
|
479
|
-
console.log('lists', lists);
|
|
480
484
|
|
|
481
485
|
Object.keys(lists).forEach(key => {
|
|
482
486
|
if (key === 'channelIndex') {
|
package/nodes/ccu-set-value.js
CHANGED
|
@@ -37,13 +37,13 @@ module.exports = function (RED) {
|
|
|
37
37
|
channelIndexRx: config.channelIndexRx,
|
|
38
38
|
channelNameRx: config.channelNameRx,
|
|
39
39
|
datapointRx: config.datapointRx,
|
|
40
|
-
force: config.force
|
|
40
|
+
force: config.force,
|
|
41
41
|
};
|
|
42
42
|
|
|
43
43
|
this.blacklist = new Set();
|
|
44
44
|
this.whitelist = new Set();
|
|
45
45
|
|
|
46
|
-
this.on('input', message => {
|
|
46
|
+
this.on('input', (message) => {
|
|
47
47
|
this.setValues(message);
|
|
48
48
|
});
|
|
49
49
|
|
|
@@ -65,21 +65,28 @@ module.exports = function (RED) {
|
|
|
65
65
|
|
|
66
66
|
setValues(message) {
|
|
67
67
|
const {config} = this;
|
|
68
|
-
|
|
68
|
+
let dynamicConfig = false;
|
|
69
|
+
Object.keys(config).forEach((key) => {
|
|
69
70
|
if (!config[key]) {
|
|
70
71
|
if (key in message) {
|
|
72
|
+
dynamicConfig = true;
|
|
71
73
|
config[key] = message[key];
|
|
72
74
|
}
|
|
73
75
|
}
|
|
74
76
|
});
|
|
75
77
|
|
|
78
|
+
if (dynamicConfig) {
|
|
79
|
+
this.whitelist.clear();
|
|
80
|
+
this.blacklist.clear();
|
|
81
|
+
}
|
|
82
|
+
|
|
76
83
|
let count = 0;
|
|
77
|
-
Object.keys(this.ccu.metadata.devices).forEach(iface => {
|
|
84
|
+
Object.keys(this.ccu.metadata.devices).forEach((iface) => {
|
|
78
85
|
if (config.iface && iface !== config.iface) {
|
|
79
86
|
return;
|
|
80
87
|
}
|
|
81
88
|
|
|
82
|
-
Object.keys(this.ccu.metadata.devices[iface]).forEach(address => {
|
|
89
|
+
Object.keys(this.ccu.metadata.devices[iface]).forEach((address) => {
|
|
83
90
|
if (this.blacklist.has(address)) {
|
|
84
91
|
return;
|
|
85
92
|
}
|
|
@@ -123,12 +130,18 @@ module.exports = function (RED) {
|
|
|
123
130
|
return;
|
|
124
131
|
}
|
|
125
132
|
|
|
126
|
-
if (
|
|
133
|
+
if (
|
|
134
|
+
config.deviceNameRx === 'str' &&
|
|
135
|
+
this.ccu.channelNames[channel.PARENT] !== config.deviceName
|
|
136
|
+
) {
|
|
127
137
|
this.blacklist.add(address);
|
|
128
138
|
return;
|
|
129
139
|
}
|
|
130
140
|
|
|
131
|
-
if (
|
|
141
|
+
if (
|
|
142
|
+
config.deviceNameRx === 're' &&
|
|
143
|
+
!this.ccu.channelNames[channel.PARENT].match(new RegExp(config.deviceName))
|
|
144
|
+
) {
|
|
132
145
|
this.blacklist.add(address);
|
|
133
146
|
return;
|
|
134
147
|
}
|
|
@@ -164,7 +177,10 @@ module.exports = function (RED) {
|
|
|
164
177
|
return;
|
|
165
178
|
}
|
|
166
179
|
|
|
167
|
-
if (
|
|
180
|
+
if (
|
|
181
|
+
config.channelIndexRx === 're' &&
|
|
182
|
+
!address.split(':')[1].match(new RegExp(String(config.channelIndex)))
|
|
183
|
+
) {
|
|
168
184
|
this.blacklist.add(address);
|
|
169
185
|
return;
|
|
170
186
|
}
|
|
@@ -176,12 +192,18 @@ module.exports = function (RED) {
|
|
|
176
192
|
return;
|
|
177
193
|
}
|
|
178
194
|
|
|
179
|
-
if (
|
|
195
|
+
if (
|
|
196
|
+
config.channelNameRx === 'str' &&
|
|
197
|
+
this.ccu.channelNames[address] !== config.channelName
|
|
198
|
+
) {
|
|
180
199
|
this.blacklist.add(address);
|
|
181
200
|
return;
|
|
182
201
|
}
|
|
183
202
|
|
|
184
|
-
if (
|
|
203
|
+
if (
|
|
204
|
+
config.channelNameRx === 're' &&
|
|
205
|
+
!this.ccu.channelNames[address].match(new RegExp(config.channelName))
|
|
206
|
+
) {
|
|
185
207
|
this.blacklist.add(address);
|
|
186
208
|
return;
|
|
187
209
|
}
|
|
@@ -200,7 +222,7 @@ module.exports = function (RED) {
|
|
|
200
222
|
|
|
201
223
|
if (config.roomsRx === 're') {
|
|
202
224
|
let match = false;
|
|
203
|
-
this.ccu.channelRooms[address].forEach(room => {
|
|
225
|
+
this.ccu.channelRooms[address].forEach((room) => {
|
|
204
226
|
if (room.match(new RegExp(config.rooms))) {
|
|
205
227
|
match = true;
|
|
206
228
|
}
|
|
@@ -218,14 +240,17 @@ module.exports = function (RED) {
|
|
|
218
240
|
return;
|
|
219
241
|
}
|
|
220
242
|
|
|
221
|
-
if (
|
|
243
|
+
if (
|
|
244
|
+
config.functionsRx === 'str' &&
|
|
245
|
+
!this.ccu.channelFunctions[address].includes(config.functions)
|
|
246
|
+
) {
|
|
222
247
|
this.blacklist.add(address);
|
|
223
248
|
return;
|
|
224
249
|
}
|
|
225
250
|
|
|
226
251
|
if (config.functionsRx === 're') {
|
|
227
252
|
let match = false;
|
|
228
|
-
this.ccu.channelFunctions[address].forEach(func => {
|
|
253
|
+
this.ccu.channelFunctions[address].forEach((func) => {
|
|
229
254
|
if (func.match(new RegExp(config.functions))) {
|
|
230
255
|
match = true;
|
|
231
256
|
}
|
|
@@ -243,7 +268,7 @@ module.exports = function (RED) {
|
|
|
243
268
|
const psKey = this.ccu.paramsetName(iface, channel, 'VALUES');
|
|
244
269
|
if (this.ccu.paramsetDescriptions[psKey]) {
|
|
245
270
|
const rx = new RegExp(config.datapoint);
|
|
246
|
-
Object.keys(this.ccu.paramsetDescriptions[psKey]).forEach(dp => {
|
|
271
|
+
Object.keys(this.ccu.paramsetDescriptions[psKey]).forEach((dp) => {
|
|
247
272
|
if (config.datapointRx === 'str' && dp !== config.datapoint) {
|
|
248
273
|
return;
|
|
249
274
|
}
|
|
@@ -255,8 +280,14 @@ module.exports = function (RED) {
|
|
|
255
280
|
const datapointName = iface + '.' + address + '.' + dp;
|
|
256
281
|
const currentValue = this.ccu.values[datapointName] && this.ccu.values[datapointName].value;
|
|
257
282
|
count += 1;
|
|
258
|
-
if (
|
|
259
|
-
|
|
283
|
+
if (
|
|
284
|
+
dp.startsWith('PRESS_') ||
|
|
285
|
+
typeof currentValue === 'undefined' ||
|
|
286
|
+
currentValue !== message.payload
|
|
287
|
+
) {
|
|
288
|
+
this.ccu
|
|
289
|
+
.setValueQueued(iface, address, dp, message.payload, false, config.force)
|
|
290
|
+
.catch(() => {});
|
|
260
291
|
}
|
|
261
292
|
});
|
|
262
293
|
}
|
package/nodes/ccu-signal.html
CHANGED
|
@@ -151,6 +151,7 @@
|
|
|
151
151
|
|
|
152
152
|
let ifacesLoaded = false;
|
|
153
153
|
let ifacesPending = false;
|
|
154
|
+
let ifaceLoadRetries = 5;
|
|
154
155
|
|
|
155
156
|
function loadIfaces(iface, cb) {
|
|
156
157
|
if (ifacesPending) {
|
|
@@ -158,7 +159,6 @@
|
|
|
158
159
|
}
|
|
159
160
|
|
|
160
161
|
ifacesPending = true;
|
|
161
|
-
console.log('loadIfaces()');
|
|
162
162
|
$nodeInputIface.html('');
|
|
163
163
|
const nodeId = $nodeInputCcuConfig.val();
|
|
164
164
|
if (nodeId === '_ADD_') {
|
|
@@ -178,6 +178,12 @@
|
|
|
178
178
|
cb();
|
|
179
179
|
ifacesPending = false;
|
|
180
180
|
}
|
|
181
|
+
}).fail(() => {
|
|
182
|
+
// config node not (yet) available in the runtime - allow retrying
|
|
183
|
+
ifacesPending = false;
|
|
184
|
+
if (ifaceLoadRetries-- > 0) {
|
|
185
|
+
setTimeout(() => loadIfaces(iface, cb), 1000);
|
|
186
|
+
}
|
|
181
187
|
});
|
|
182
188
|
}
|
|
183
189
|
}
|
|
@@ -186,7 +192,6 @@
|
|
|
186
192
|
if (ifacesLoaded) {
|
|
187
193
|
const nodeId = $nodeInputCcuConfig.val();
|
|
188
194
|
if (nodeId && nodeId !== '__ADD__') {
|
|
189
|
-
console.log('loadConfig()');
|
|
190
195
|
$.getJSON('ccu?type=signal&config=' + nodeId + '&iface=' + $nodeInputIface.val(), d => {
|
|
191
196
|
data = d;
|
|
192
197
|
autoCompleteChannel();
|
|
@@ -198,7 +203,6 @@
|
|
|
198
203
|
}
|
|
199
204
|
|
|
200
205
|
$nodeInputCcuConfig.change(() => {
|
|
201
|
-
console.log('$nodeInputCcuConfig change');
|
|
202
206
|
loadIfaces(this.iface, () => {
|
|
203
207
|
ifacesLoaded = true;
|
|
204
208
|
$nodeInputIface.removeAttr('disabled');
|
|
@@ -206,6 +210,10 @@
|
|
|
206
210
|
});
|
|
207
211
|
});
|
|
208
212
|
|
|
213
|
+
// Node-RED 4.x does not reliably fire the change event on dialog
|
|
214
|
+
// open - trigger the initial load explicitly (see ROADMAP.md 8.1)
|
|
215
|
+
$nodeInputCcuConfig.trigger('change');
|
|
216
|
+
|
|
209
217
|
$nodeInputIface.change(() => {
|
|
210
218
|
if (ifacesLoaded) {
|
|
211
219
|
loadConfig();
|
|
@@ -258,7 +266,6 @@
|
|
|
258
266
|
});
|
|
259
267
|
}
|
|
260
268
|
|
|
261
|
-
console.log('autoCompleteChannel()', arr.length);
|
|
262
269
|
$nodeInputChannel.autocomplete('option', 'source', arr);
|
|
263
270
|
|
|
264
271
|
if (!data[$nodeInputChannel.val().split(' ')[0]]) {
|
|
@@ -417,7 +424,6 @@
|
|
|
417
424
|
$('#node-input-dimmer-container').editableList('items').each(function () {
|
|
418
425
|
dimmerList.push({color: $(this).find('select.color').val(), ontime: $(this).find('select.ontime').val()});
|
|
419
426
|
});
|
|
420
|
-
console.log(dimmerList);
|
|
421
427
|
this.dimmerList = dimmerList;
|
|
422
428
|
break;
|
|
423
429
|
}
|
package/nodes/ccu-signal.js
CHANGED
|
@@ -29,16 +29,19 @@ module.exports = function (RED) {
|
|
|
29
29
|
.then(() => this.getValue(values, 'repeat', message))
|
|
30
30
|
.then(() => this.getValue(values, 'volume', message))
|
|
31
31
|
.then(() => this.getValue(values, 'soundLevel', message))
|
|
32
|
-
.catch(error => {
|
|
32
|
+
.catch((error) => {
|
|
33
33
|
this.error(error.message);
|
|
34
34
|
})
|
|
35
35
|
.then(() => {
|
|
36
|
-
this.sendCommand({...this.config, ...values})
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
36
|
+
this.sendCommand({...this.config, ...values})
|
|
37
|
+
.then(() => {
|
|
38
|
+
done();
|
|
39
|
+
})
|
|
40
|
+
.catch((error) => {
|
|
41
|
+
done(error);
|
|
42
|
+
});
|
|
43
|
+
})
|
|
44
|
+
.catch(() => {});
|
|
42
45
|
});
|
|
43
46
|
}
|
|
44
47
|
|
|
@@ -56,12 +59,16 @@ module.exports = function (RED) {
|
|
|
56
59
|
payload = ['1', config.repeat, 108000, ...config.led.split(',')];
|
|
57
60
|
return this.ccu.setValue(config.iface, config.channel, 'SUBMIT', payload);
|
|
58
61
|
case 'ALARM_SWITCH_VIRTUAL_RECEIVER':
|
|
59
|
-
return this.ccu.methodCall(config.iface, 'putParamset', [
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
62
|
+
return this.ccu.methodCall(config.iface, 'putParamset', [
|
|
63
|
+
config.channel,
|
|
64
|
+
'VALUES',
|
|
65
|
+
{
|
|
66
|
+
ACOUSTIC_ALARM_SELECTION: config.acousticAlarmSelection,
|
|
67
|
+
DURATION_UNIT: config.durationUnit,
|
|
68
|
+
DURATION_VALUE: Number.parseInt(config.durationValue, 10) || 0,
|
|
69
|
+
OPTICAL_ALARM_SELECTION: config.opticalAlarmSelection,
|
|
70
|
+
},
|
|
71
|
+
]);
|
|
65
72
|
case 'DIMMER_VIRTUAL_RECEIVER': {
|
|
66
73
|
const parameters = {
|
|
67
74
|
LEVEL: config.dimmerLevel / 100,
|
|
@@ -70,7 +77,7 @@ module.exports = function (RED) {
|
|
|
70
77
|
DURATION_UNIT: config.durationUnit,
|
|
71
78
|
DURATION_VALUE: Number.parseInt(config.durationValue, 10) || 0,
|
|
72
79
|
REPETITIONS: Number(config.repetitions),
|
|
73
|
-
OUTPUT_SELECT_SIZE: config.dimmerList.length
|
|
80
|
+
OUTPUT_SELECT_SIZE: config.dimmerList.length,
|
|
74
81
|
};
|
|
75
82
|
config.dimmerList.forEach((item, i) => {
|
|
76
83
|
const index = i + 1;
|
|
@@ -81,14 +88,18 @@ module.exports = function (RED) {
|
|
|
81
88
|
}
|
|
82
89
|
|
|
83
90
|
case 'BSL_DIMMER_VIRTUAL_RECEIVER': {
|
|
84
|
-
return this.ccu.methodCall(config.iface, 'putParamset', [
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
91
|
+
return this.ccu.methodCall(config.iface, 'putParamset', [
|
|
92
|
+
config.channel,
|
|
93
|
+
'VALUES',
|
|
94
|
+
{
|
|
95
|
+
LEVEL: config.dimmerLevel / 100,
|
|
96
|
+
RAMP_TIME_UNIT: config.rampTimeUnit,
|
|
97
|
+
RAMP_TIME_VALUE: Number(config.rampTimeValue),
|
|
98
|
+
DURATION_UNIT: config.durationUnit,
|
|
99
|
+
DURATION_VALUE: Number.parseInt(config.durationValue, 10) || 0,
|
|
100
|
+
COLOR: Number(config.dimmerColor),
|
|
101
|
+
},
|
|
102
|
+
]);
|
|
92
103
|
}
|
|
93
104
|
|
|
94
105
|
case 'ACOUSTIC_SIGNAL_VIRTUAL_RECEIVER': {
|
|
@@ -99,7 +110,7 @@ module.exports = function (RED) {
|
|
|
99
110
|
DURATION_UNIT: config.durationUnit,
|
|
100
111
|
DURATION_VALUE: Number.parseInt(config.durationValue, 10) || 0,
|
|
101
112
|
REPETITIONS: Number(config.repetitions),
|
|
102
|
-
OUTPUT_SELECT_SIZE: config.soundList.length
|
|
113
|
+
OUTPUT_SELECT_SIZE: config.soundList.length,
|
|
103
114
|
};
|
|
104
115
|
config.soundList.forEach((item, i) => {
|
|
105
116
|
const index = i + 1;
|