node-red-contrib-knx-ultimate 6.1.0 → 6.2.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/CHANGELOG.md +16 -0
- package/examples/KNX AI - Conversational Control with Confirmation.json +374 -0
- package/examples/KNX AI - Telegrambot Direct Chat.json +221 -0
- package/examples/Matter Controller - Semantic Flow Input.json +248 -0
- package/nodes/commonFunctions.js +0 -174
- package/nodes/knxUltimateAI.html +254 -90
- package/nodes/knxUltimateAI.js +1286 -61
- package/nodes/knxUltimateMatterBridge.html +144 -11
- package/nodes/knxUltimateMatterControllerDevice.html +80 -55
- package/nodes/locales/de/knxUltimateAI.html +38 -2
- package/nodes/locales/de/knxUltimateAI.json +16 -3
- package/nodes/locales/de/knxUltimateMatterBridge.html +1 -1
- package/nodes/locales/de/knxUltimateMatterBridge.json +14 -0
- package/nodes/locales/de/knxUltimateMatterControllerDevice.html +2 -2
- package/nodes/locales/de/knxUltimateMatterControllerDevice.json +2 -1
- package/nodes/locales/en/knxUltimateAI.html +38 -2
- package/nodes/locales/en/knxUltimateAI.json +16 -3
- package/nodes/locales/en/knxUltimateMatterBridge.html +1 -1
- package/nodes/locales/en/knxUltimateMatterBridge.json +14 -0
- package/nodes/locales/en/knxUltimateMatterControllerDevice.html +2 -2
- package/nodes/locales/en/knxUltimateMatterControllerDevice.json +2 -1
- package/nodes/locales/es/knxUltimateAI.html +38 -2
- package/nodes/locales/es/knxUltimateAI.json +16 -3
- package/nodes/locales/es/knxUltimateMatterBridge.html +1 -1
- package/nodes/locales/es/knxUltimateMatterBridge.json +14 -0
- package/nodes/locales/es/knxUltimateMatterControllerDevice.html +2 -2
- package/nodes/locales/es/knxUltimateMatterControllerDevice.json +2 -1
- package/nodes/locales/fr/knxUltimateAI.html +38 -2
- package/nodes/locales/fr/knxUltimateAI.json +16 -3
- package/nodes/locales/fr/knxUltimateMatterBridge.html +1 -1
- package/nodes/locales/fr/knxUltimateMatterBridge.json +14 -0
- package/nodes/locales/fr/knxUltimateMatterControllerDevice.html +2 -2
- package/nodes/locales/fr/knxUltimateMatterControllerDevice.json +2 -1
- package/nodes/locales/it/knxUltimateAI.html +38 -2
- package/nodes/locales/it/knxUltimateAI.json +16 -3
- package/nodes/locales/it/knxUltimateMatterBridge.html +1 -1
- package/nodes/locales/it/knxUltimateMatterBridge.json +14 -0
- package/nodes/locales/it/knxUltimateMatterControllerDevice.html +2 -2
- package/nodes/locales/it/knxUltimateMatterControllerDevice.json +2 -1
- package/nodes/locales/zh-CN/knxUltimateAI.html +38 -2
- package/nodes/locales/zh-CN/knxUltimateAI.json +16 -3
- package/nodes/locales/zh-CN/knxUltimateMatterBridge.html +1 -1
- package/nodes/locales/zh-CN/knxUltimateMatterBridge.json +14 -0
- package/nodes/locales/zh-CN/knxUltimateMatterControllerDevice.html +2 -2
- package/nodes/locales/zh-CN/knxUltimateMatterControllerDevice.json +2 -1
- package/nodes/utils/sysLogger.js +0 -109
- package/package.json +1 -2
- package/resources/KNXAIChatAdapterMappings.js +87 -0
- package/nodes/plugins/knxUltimateMonitor-sidebar-plugin.html +0 -922
|
@@ -67,6 +67,28 @@
|
|
|
67
67
|
robotvacuum: [] // Flow-only: no GA fields, controlled via the node PINs
|
|
68
68
|
};
|
|
69
69
|
const TYPE_ORDER = ['onofflight', 'dimmablelight', 'rgblight', 'colortemperaturelight', 'onoffplug', 'windowcovering', 'thermostat', 'fan', 'temperaturesensor', 'humiditysensor', 'lightsensor', 'occupancysensor', 'contactsensor', 'smokecoalarm', 'waterleakdetector', 'airqualitysensor', 'robotvacuum'];
|
|
70
|
+
const FLOW_HELP = {
|
|
71
|
+
onofflight: { input: [['onoff', true]], output: [['onoff', true]] },
|
|
72
|
+
onoffplug: { input: [['onoff', true]], output: [['onoff', true]] },
|
|
73
|
+
dimmablelight: { input: [['onoff', true], ['level', 50]], output: [['onoff', true], ['level', 50]] },
|
|
74
|
+
rgblight: { input: [['onoff', true], ['level', 50], ['rgb', { red: 255, green: 80, blue: 20 }]], output: [['onoff', true], ['level', 50], ['rgb', { red: 255, green: 80, blue: 20 }]] },
|
|
75
|
+
colortemperaturelight: { input: [['onoff', true], ['level', 50], ['colortemp', 3000]], output: [['onoff', true], ['level', 50], ['colortemp', 3000]] },
|
|
76
|
+
temperaturesensor: { input: [['temperature', 21.5]], output: [] },
|
|
77
|
+
humiditysensor: { input: [['humidity', 45]], output: [] },
|
|
78
|
+
lightsensor: { input: [['illuminance', 500]], output: [] },
|
|
79
|
+
occupancysensor: { input: [['occupancy', true]], output: [] },
|
|
80
|
+
contactsensor: { input: [['contact', true]], output: [] },
|
|
81
|
+
windowcovering: { input: [['position', 35]], output: [['updown', false], ['position', 35], ['stop', true]] },
|
|
82
|
+
thermostat: { input: [['currenttemp', 21.5], ['setpoint', 21], ['coolingsetpoint', 24]], output: [['setpoint', 21], ['coolingsetpoint', 24]] },
|
|
83
|
+
smokecoalarm: { input: [['smoke', true], ['co', true]], output: [] },
|
|
84
|
+
waterleakdetector: { input: [['leak', true]], output: [] },
|
|
85
|
+
airqualitysensor: { input: [['co2', 850]], output: [] },
|
|
86
|
+
fan: { input: [['fanspeed', 60]], output: [['fanspeed', 60]] },
|
|
87
|
+
robotvacuum: {
|
|
88
|
+
input: [['rvcstate', 'charging'], ['rvcmode', 'idle']],
|
|
89
|
+
output: [['rvccommand', 'gohome'], ['rvccommand', 'pause'], ['rvccommand', 'resume'], ['rvcmode', 'cleaning']]
|
|
90
|
+
}
|
|
91
|
+
};
|
|
70
92
|
|
|
71
93
|
// Every GA/DPT key across all types, used to declare the node defaults.
|
|
72
94
|
const ALL_KEYS = [];
|
|
@@ -167,6 +189,25 @@
|
|
|
167
189
|
border-top: 1px solid #ccc;
|
|
168
190
|
margin: 8px 0;
|
|
169
191
|
}
|
|
192
|
+
.matter-bridge-flow-help-table {
|
|
193
|
+
width: 100%;
|
|
194
|
+
border-collapse: collapse;
|
|
195
|
+
margin-bottom: 12px;
|
|
196
|
+
}
|
|
197
|
+
.matter-bridge-flow-help-table th,
|
|
198
|
+
.matter-bridge-flow-help-table td {
|
|
199
|
+
border-bottom: 1px solid #ddd;
|
|
200
|
+
padding: 7px 6px;
|
|
201
|
+
text-align: left;
|
|
202
|
+
vertical-align: top;
|
|
203
|
+
}
|
|
204
|
+
.matter-bridge-flow-help-table code {
|
|
205
|
+
white-space: pre-wrap;
|
|
206
|
+
word-break: break-word;
|
|
207
|
+
}
|
|
208
|
+
.matter-bridge-flow-help-copy {
|
|
209
|
+
white-space: nowrap;
|
|
210
|
+
}
|
|
170
211
|
</style>`);
|
|
171
212
|
}
|
|
172
213
|
const $tabs = $('#mb-tabs');
|
|
@@ -217,6 +258,9 @@
|
|
|
217
258
|
const $commandAdvancedRows = $('.mb-command-advanced-row');
|
|
218
259
|
const $dimmerAdvancedRows = $('.mb-dimmer-advanced-row');
|
|
219
260
|
const $coverAdvancedRows = $('.mb-cover-advanced-row');
|
|
261
|
+
const $pinSelect = $('#node-input-enableNodePINS');
|
|
262
|
+
const $flowHelp = $('#matter-bridge-flow-help');
|
|
263
|
+
const $nameInput = $('#node-input-name');
|
|
220
264
|
const gaInputs = {};
|
|
221
265
|
|
|
222
266
|
function createLabeledField(row, element, options = {}) {
|
|
@@ -272,6 +316,51 @@
|
|
|
272
316
|
gaInputs[key] = { wrapper, $ga, $dpt, $labelSpan };
|
|
273
317
|
});
|
|
274
318
|
|
|
319
|
+
const inputMessageCode = (fn, value) => `msg.payload = ${JSON.stringify({ function: fn, value }, null, 2)};\nreturn msg;`;
|
|
320
|
+
const outputMessageCode = (fn, value) => JSON.stringify({
|
|
321
|
+
topic: ($nameInput.val() || node.name || 'Matter device').toString(),
|
|
322
|
+
payload: value,
|
|
323
|
+
matter: { fn, value }
|
|
324
|
+
}, null, 2);
|
|
325
|
+
const appendFlowHelpRow = ($body, fn, code) => {
|
|
326
|
+
const $row = $('<tr></tr>');
|
|
327
|
+
$('<td><code></code></td>').find('code').text(fn).end().appendTo($row);
|
|
328
|
+
$('<td><code></code></td>').find('code').text(code).end().appendTo($row);
|
|
329
|
+
$('<td></td>').append(
|
|
330
|
+
$('<button type="button" class="red-ui-button matter-bridge-flow-help-copy"><i class="fa fa-copy"></i></button>')
|
|
331
|
+
.attr('title', node._('knxUltimateMatterBridge.flow_help.copy') || 'Copy')
|
|
332
|
+
.data('copy-text', code)
|
|
333
|
+
).appendTo($row);
|
|
334
|
+
$body.append($row);
|
|
335
|
+
};
|
|
336
|
+
const copyFlowHelpText = (value) => {
|
|
337
|
+
const copied = () => RED.notify(node._('knxUltimateMatterBridge.flow_help.copied') || 'Message copied', 'success');
|
|
338
|
+
if (navigator.clipboard && typeof navigator.clipboard.writeText === 'function') {
|
|
339
|
+
navigator.clipboard.writeText(value).then(copied).catch(() => {
|
|
340
|
+
const $temporary = $('<textarea></textarea>').val(value).appendTo('body').select();
|
|
341
|
+
try { document.execCommand('copy'); copied(); } finally { $temporary.remove(); }
|
|
342
|
+
});
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
const $temporary = $('<textarea></textarea>').val(value).appendTo('body').select();
|
|
346
|
+
try { document.execCommand('copy'); copied(); } finally { $temporary.remove(); }
|
|
347
|
+
};
|
|
348
|
+
const renderFlowHelp = () => {
|
|
349
|
+
const examples = FLOW_HELP[$type.val()] || { input: [], output: [] };
|
|
350
|
+
const $inputBody = $('#matter-bridge-flow-help-input-body').empty();
|
|
351
|
+
const $outputBody = $('#matter-bridge-flow-help-output-body').empty();
|
|
352
|
+
examples.input.forEach(([fn, value]) => appendFlowHelpRow($inputBody, fn, inputMessageCode(fn, value)));
|
|
353
|
+
examples.output.forEach(([fn, value]) => appendFlowHelpRow($outputBody, fn, outputMessageCode(fn, value)));
|
|
354
|
+
$('#matter-bridge-flow-help-input-empty').toggle(examples.input.length === 0);
|
|
355
|
+
$('#matter-bridge-flow-help-output-empty').toggle(examples.output.length === 0);
|
|
356
|
+
$('#matter-bridge-flow-help-output-table').toggle(examples.output.length > 0);
|
|
357
|
+
};
|
|
358
|
+
const updateFlowHelpVisibility = () => {
|
|
359
|
+
const visible = $pinSelect.val() === 'yes';
|
|
360
|
+
$flowHelp.toggle(visible);
|
|
361
|
+
if (visible) renderFlowHelp();
|
|
362
|
+
};
|
|
363
|
+
|
|
275
364
|
function updateTypePresentation() {
|
|
276
365
|
const selected = $type.val();
|
|
277
366
|
const active = TYPE_FIELDS[selected] || [];
|
|
@@ -295,6 +384,7 @@
|
|
|
295
384
|
$commandAdvancedRows.toggle(hasCommand);
|
|
296
385
|
$dimmerAdvancedRows.toggle(hasLevel);
|
|
297
386
|
$coverAdvancedRows.toggle(isCover);
|
|
387
|
+
updateFlowHelpVisibility();
|
|
298
388
|
}
|
|
299
389
|
$type.on('change', updateTypePresentation);
|
|
300
390
|
updateTypePresentation();
|
|
@@ -312,7 +402,15 @@
|
|
|
312
402
|
$('#node-input-coverUpdateMode').val(node.coverUpdateMode || 'optimistic');
|
|
313
403
|
$('#node-input-coverStatusTimeoutMs').val(node.coverStatusTimeoutMs === undefined ? 3000 : node.coverStatusTimeoutMs);
|
|
314
404
|
$('#node-input-readStatusAtStartup').val(node.readStatusAtStartup || 'yes');
|
|
315
|
-
$
|
|
405
|
+
$pinSelect.val(node.enableNodePINS || 'no');
|
|
406
|
+
$pinSelect.on('change.knxUltimateMatterBridge', updateFlowHelpVisibility);
|
|
407
|
+
$nameInput.on('change.knxUltimateMatterBridge input.knxUltimateMatterBridge', () => {
|
|
408
|
+
if ($flowHelp.is(':visible')) renderFlowHelp();
|
|
409
|
+
});
|
|
410
|
+
$flowHelp.off('click.knxUltimateMatterBridge').on('click.knxUltimateMatterBridge', '.matter-bridge-flow-help-copy', function () {
|
|
411
|
+
copyFlowHelpText($(this).data('copy-text') || '');
|
|
412
|
+
});
|
|
413
|
+
updateFlowHelpVisibility();
|
|
316
414
|
},
|
|
317
415
|
oneditsave() {
|
|
318
416
|
try { RED.sidebar.show('info'); } catch (error) { /* empty */ }
|
|
@@ -479,18 +577,52 @@
|
|
|
479
577
|
</select>
|
|
480
578
|
</div>
|
|
481
579
|
|
|
482
|
-
<div class="form-row">
|
|
483
|
-
<label for="node-input-enableNodePINS" style="width:260px;">
|
|
484
|
-
<i class="fa fa-code"></i> <span data-i18n="knxUltimateMatterBridge.node_pins"></span>
|
|
485
|
-
</label>
|
|
486
|
-
<select id="node-input-enableNodePINS" style="width:220px;">
|
|
487
|
-
<option value="no" data-i18n="knxUltimateMatterBridge.node_pins_hide"></option>
|
|
488
|
-
<option value="yes" data-i18n="knxUltimateMatterBridge.node_pins_show"></option>
|
|
489
|
-
</select>
|
|
490
|
-
</div>
|
|
491
580
|
</div>
|
|
492
581
|
</div>
|
|
493
582
|
|
|
583
|
+
<div class="form-row" style="margin-top:12px;">
|
|
584
|
+
<label for="node-input-enableNodePINS" style="width:260px;">
|
|
585
|
+
<i class="fa fa-code"></i> <span data-i18n="knxUltimateMatterBridge.node_pins"></span>
|
|
586
|
+
</label>
|
|
587
|
+
<select id="node-input-enableNodePINS" style="width:220px;">
|
|
588
|
+
<option value="no" data-i18n="knxUltimateMatterBridge.node_pins_hide"></option>
|
|
589
|
+
<option value="yes" data-i18n="knxUltimateMatterBridge.node_pins_show"></option>
|
|
590
|
+
</select>
|
|
591
|
+
</div>
|
|
592
|
+
|
|
593
|
+
<div id="matter-bridge-flow-help" style="display:none; width:900px; margin-top:12px;">
|
|
594
|
+
<h4><i class="fa fa-code"></i> <span data-i18n="knxUltimateMatterBridge.flow_help.title"></span></h4>
|
|
595
|
+
<p data-i18n="knxUltimateMatterBridge.flow_help.intro"></p>
|
|
596
|
+
|
|
597
|
+
<h4 data-i18n="knxUltimateMatterBridge.flow_help.input_title"></h4>
|
|
598
|
+
<p data-i18n="knxUltimateMatterBridge.flow_help.input_hint"></p>
|
|
599
|
+
<table class="matter-bridge-flow-help-table">
|
|
600
|
+
<thead>
|
|
601
|
+
<tr>
|
|
602
|
+
<th data-i18n="knxUltimateMatterBridge.flow_help.function"></th>
|
|
603
|
+
<th data-i18n="knxUltimateMatterBridge.flow_help.message"></th>
|
|
604
|
+
<th></th>
|
|
605
|
+
</tr>
|
|
606
|
+
</thead>
|
|
607
|
+
<tbody id="matter-bridge-flow-help-input-body"></tbody>
|
|
608
|
+
</table>
|
|
609
|
+
<p id="matter-bridge-flow-help-input-empty" style="display:none;" data-i18n="knxUltimateMatterBridge.flow_help.no_input"></p>
|
|
610
|
+
|
|
611
|
+
<h4 data-i18n="knxUltimateMatterBridge.flow_help.output_title"></h4>
|
|
612
|
+
<p data-i18n="knxUltimateMatterBridge.flow_help.output_hint"></p>
|
|
613
|
+
<table id="matter-bridge-flow-help-output-table" class="matter-bridge-flow-help-table">
|
|
614
|
+
<thead>
|
|
615
|
+
<tr>
|
|
616
|
+
<th data-i18n="knxUltimateMatterBridge.flow_help.function"></th>
|
|
617
|
+
<th data-i18n="knxUltimateMatterBridge.flow_help.message"></th>
|
|
618
|
+
<th></th>
|
|
619
|
+
</tr>
|
|
620
|
+
</thead>
|
|
621
|
+
<tbody id="matter-bridge-flow-help-output-body"></tbody>
|
|
622
|
+
</table>
|
|
623
|
+
<p id="matter-bridge-flow-help-output-empty" style="display:none;" data-i18n="knxUltimateMatterBridge.flow_help.no_output"></p>
|
|
624
|
+
</div>
|
|
625
|
+
|
|
494
626
|
<br/>
|
|
495
627
|
<br/>
|
|
496
628
|
<br/>
|
|
@@ -518,7 +650,8 @@ The advanced compatibility options are intentionally type-specific:
|
|
|
518
650
|
|
|
519
651
|
The **KNX gateway is optional**: without it, enable the node PINs and drive the device from the flow.
|
|
520
652
|
|
|
521
|
-
|
|
653
|
+
The PIN selector sits outside the tabs. Enabling it reveals a contextual **Flow input/output**
|
|
654
|
+
section directly below, with copyable examples filtered to the selected device type:
|
|
522
655
|
|
|
523
656
|
- input: `msg.payload = { function: "onoff", value: true }` updates the Matter state without the KNX bus
|
|
524
657
|
- output: every command from a Matter controller is forwarded as `msg` (topic = device name, payload = value, `msg.matter` = raw command)
|
|
@@ -274,7 +274,7 @@
|
|
|
274
274
|
const $refreshDevicesButton = $(".hue-refresh-devices");
|
|
275
275
|
const $locateDeviceButton = $(".hue-locate-device");
|
|
276
276
|
const $hueDevicesLoading = $(".hue-devices-loading");
|
|
277
|
-
const $inputHelpPanel = $("#
|
|
277
|
+
const $inputHelpPanel = $("#matter-flow-input-help");
|
|
278
278
|
const UNIVERSAL_NODE_ID = '__UNIVERSAL__';
|
|
279
279
|
const isUniversalMode = () => $matterControllerModeInput.val() === 'universal';
|
|
280
280
|
$matterControllerModeInput.val(node.matterControllerMode || 'single');
|
|
@@ -921,9 +921,25 @@
|
|
|
921
921
|
const caps = getEffectiveMatterCapabilities();
|
|
922
922
|
const structure = inputStructureFor(caps);
|
|
923
923
|
const endpointId = structure ? structure.endpointId : Number($matterEndpointInput.val() || node.matterEndpointId || 1);
|
|
924
|
+
const isMatterLight = !caps.profile;
|
|
924
925
|
$('#matter-input-help-endpoint').text(String(endpointId));
|
|
925
926
|
const $simpleBody = $('#matter-input-help-simple-body').empty();
|
|
926
|
-
if (
|
|
927
|
+
if (isMatterLight) {
|
|
928
|
+
const writeLabel = node._('knxUltimateMatterControllerDevice.input_help_write') || 'Write';
|
|
929
|
+
if (caps.onOff !== false) {
|
|
930
|
+
appendInputHelpRow($simpleBody, writeLabel, node._('knxUltimateMatterControllerDevice.input_functions.on') || 'On', 'msg.on = { on: true };\nreturn msg;');
|
|
931
|
+
appendInputHelpRow($simpleBody, writeLabel, node._('knxUltimateMatterControllerDevice.input_functions.off') || 'Off', 'msg.on = { on: false };\nreturn msg;');
|
|
932
|
+
}
|
|
933
|
+
if (caps.level !== false) {
|
|
934
|
+
appendInputHelpRow($simpleBody, writeLabel, node._('knxUltimateMatterControllerDevice.input_functions.brightness') || 'Brightness', 'msg.dimming = { brightness: 50 };\nreturn msg;');
|
|
935
|
+
}
|
|
936
|
+
if (caps.colorTemperature === true) {
|
|
937
|
+
appendInputHelpRow($simpleBody, writeLabel, node._('knxUltimateMatterControllerDevice.input_functions.color_temperature') || 'Colour temperature', 'msg.color_temperature = { mirek: 370 };\nreturn msg;');
|
|
938
|
+
}
|
|
939
|
+
if (caps.color === true) {
|
|
940
|
+
appendInputHelpRow($simpleBody, writeLabel, node._('knxUltimateMatterControllerDevice.input_functions.xy_color') || 'XY colour', 'msg.color = { xy: { x: 0.675, y: 0.322 } };\nreturn msg;');
|
|
941
|
+
}
|
|
942
|
+
} else if (caps.profile === 'doorLock') {
|
|
927
943
|
if (caps.lockDoor !== false) appendInputHelpRow($simpleBody, node._('knxUltimateMatterControllerDevice.input_help_write') || 'Write', node._('knxUltimateMatterControllerDevice.input_functions.lock') || 'Lock', functionCode({ function: 'lock', value: true }));
|
|
928
944
|
if (caps.unlockDoor !== false) appendInputHelpRow($simpleBody, node._('knxUltimateMatterControllerDevice.input_help_write') || 'Write', node._('knxUltimateMatterControllerDevice.input_functions.unlock') || 'Unlock', functionCode({ function: 'lock', value: false }));
|
|
929
945
|
} else {
|
|
@@ -949,9 +965,19 @@
|
|
|
949
965
|
});
|
|
950
966
|
}
|
|
951
967
|
$('#matter-input-help-simple-empty').toggle($simpleBody.children().length === 0);
|
|
968
|
+
$('#matter-input-help-simple-hint').attr(
|
|
969
|
+
'data-i18n',
|
|
970
|
+
isMatterLight
|
|
971
|
+
? 'knxUltimateMatterControllerDevice.input_help_light_hint'
|
|
972
|
+
: 'knxUltimateMatterControllerDevice.input_help_simple_hint'
|
|
973
|
+
).text(node._(
|
|
974
|
+
isMatterLight
|
|
975
|
+
? 'knxUltimateMatterControllerDevice.input_help_light_hint'
|
|
976
|
+
: 'knxUltimateMatterControllerDevice.input_help_simple_hint'
|
|
977
|
+
) || '');
|
|
952
978
|
|
|
953
979
|
const $advancedBody = $('#matter-input-help-advanced-body').empty();
|
|
954
|
-
if (structure) {
|
|
980
|
+
if (structure && !isMatterLight) {
|
|
955
981
|
structure.clusters.forEach((cluster) => {
|
|
956
982
|
(cluster.attributes || []).forEach((attribute) => {
|
|
957
983
|
const prefix = `${cluster.name || 'Cluster'} (${cluster.id}) · ${attribute.name} (${attribute.id})`;
|
|
@@ -967,6 +993,7 @@
|
|
|
967
993
|
});
|
|
968
994
|
}
|
|
969
995
|
$('#matter-input-help-advanced-empty').toggle($advancedBody.children().length === 0);
|
|
996
|
+
$('#matter-input-help-advanced-details').toggle(!isMatterLight);
|
|
970
997
|
};
|
|
971
998
|
const copyInputHelpText = (text) => {
|
|
972
999
|
const copied = () => RED.notify(node._('knxUltimateMatterControllerDevice.input_help_copied') || 'Message copied', 'success');
|
|
@@ -985,11 +1012,6 @@
|
|
|
985
1012
|
});
|
|
986
1013
|
$tabs.addClass('hue-vertical-tabs');
|
|
987
1014
|
$tabs.tabs(); // Tabs gestione KNX
|
|
988
|
-
$tabs.off('tabsactivate.matterInputHelp').on('tabsactivate.matterInputHelp', (event, ui) => {
|
|
989
|
-
if (ui && ui.newPanel && ui.newPanel.attr('id') === 'tabs-input') {
|
|
990
|
-
renderInputHelp();
|
|
991
|
-
}
|
|
992
|
-
});
|
|
993
1015
|
$tabs.find('ul').addClass('ui-tabs-nav');
|
|
994
1016
|
$tabs.find('li').removeClass('ui-corner-top').addClass('ui-corner-left');
|
|
995
1017
|
$universalTabs.addClass('hue-vertical-tabs');
|
|
@@ -1300,7 +1322,6 @@
|
|
|
1300
1322
|
setMatterTabVisible('tabs-3', !isUniversal && knxSelected && supportsTemperature);
|
|
1301
1323
|
setMatterTabVisible('tabs-4', !isUniversal && knxSelected && supportsColor);
|
|
1302
1324
|
setMatterTabVisible('tabs-mapped', !isUniversal && knxSelected && isMapped);
|
|
1303
|
-
setMatterTabVisible('tabs-input', !isUniversal && hasHueDevice && (isMapped || isDoorLock));
|
|
1304
1325
|
setMatterTabVisible('tabs-6', !isUniversal && knxSelected && hasHueDevice);
|
|
1305
1326
|
$('#matter-universal-description').toggle(isUniversal);
|
|
1306
1327
|
$pinSelect.val(isUniversal ? 'yes' : $pinSelect.val()).prop('disabled', isUniversal);
|
|
@@ -1316,7 +1337,9 @@
|
|
|
1316
1337
|
$('#matter-door-lock-pin-required').toggle(isDoorLock && caps.requirePinForRemoteOperation === true);
|
|
1317
1338
|
$('#matter-mapped-profile').toggle(isMapped);
|
|
1318
1339
|
if (isMapped) renderMappedProfile(caps.mappedTargets || []);
|
|
1319
|
-
|
|
1340
|
+
const showInputHelp = !isUniversal && hasHueDevice && $pinSelect.val() === 'yes';
|
|
1341
|
+
$inputHelpPanel.toggle(showInputHelp);
|
|
1342
|
+
if (showInputHelp) renderInputHelp();
|
|
1320
1343
|
$('#matter-selected-device-type-value').text(deviceTypeDisplay);
|
|
1321
1344
|
$('#matter-selected-device-type').toggle(hasHueDevice && deviceTypeDisplay !== '');
|
|
1322
1345
|
try { $tabs.tabs('refresh'); } catch (error) { /* empty */ }
|
|
@@ -1440,6 +1463,7 @@
|
|
|
1440
1463
|
});
|
|
1441
1464
|
|
|
1442
1465
|
$hueDeviceInput.on('change.knxUltimateMatterControllerDevice input.knxUltimateMatterControllerDevice', updateTabsVisibility);
|
|
1466
|
+
$pinSelect.on('change.knxUltimateMatterControllerDevice', updateTabsVisibility);
|
|
1443
1467
|
|
|
1444
1468
|
if (($deviceNameInput.val() || '').trim() !== '') {
|
|
1445
1469
|
applyHueDevicesPlaceholder(true);
|
|
@@ -2134,7 +2158,6 @@
|
|
|
2134
2158
|
<li><a href="#tabs-3"><i class="fa-solid fa-temperature-quarter"></i> <span data-i18n="knxUltimateMatterControllerDevice.tabs.tunable_white"></span></a></li>
|
|
2135
2159
|
<li><a href="#tabs-4"><i class="fa-solid fa-palette"></i> <span data-i18n="knxUltimateMatterControllerDevice.tabs.rgb_hsv"></span></a></li>
|
|
2136
2160
|
<li><a href="#tabs-mapped"><i class="fa-solid fa-list-check"></i> <span data-i18n="knxUltimateMatterControllerDevice.tabs.mappings"></span></a></li>
|
|
2137
|
-
<li><a href="#tabs-input"><i class="fa-solid fa-code"></i> <span data-i18n="knxUltimateMatterControllerDevice.input_help_label"></span></a></li>
|
|
2138
2161
|
<li><a href="#tabs-6"><i class="fa-solid fa-code-merge"></i> <span data-i18n="knxUltimateMatterControllerDevice.tabs.behaviour"></span></a></li>
|
|
2139
2162
|
</ul>
|
|
2140
2163
|
<div id="tabs-mapped">
|
|
@@ -2142,46 +2165,6 @@
|
|
|
2142
2165
|
<div id="matter-mapped-profile-body"></div>
|
|
2143
2166
|
</div>
|
|
2144
2167
|
</div>
|
|
2145
|
-
<div id="tabs-input">
|
|
2146
|
-
<p class="form-tips" data-i18n="knxUltimateMatterControllerDevice.input_help_pin_hint"></p>
|
|
2147
|
-
<p>
|
|
2148
|
-
<strong data-i18n="knxUltimateMatterControllerDevice.input_help_endpoint"></strong>:
|
|
2149
|
-
<code id="matter-input-help-endpoint"></code>.
|
|
2150
|
-
<span data-i18n="knxUltimateMatterControllerDevice.input_help_endpoint_hint"></span>
|
|
2151
|
-
</p>
|
|
2152
|
-
<h4 data-i18n="knxUltimateMatterControllerDevice.input_help_simple_title"></h4>
|
|
2153
|
-
<p data-i18n="knxUltimateMatterControllerDevice.input_help_simple_hint"></p>
|
|
2154
|
-
<table class="matter-input-help-table">
|
|
2155
|
-
<thead>
|
|
2156
|
-
<tr>
|
|
2157
|
-
<th data-i18n="knxUltimateMatterControllerDevice.input_help_operation"></th>
|
|
2158
|
-
<th data-i18n="knxUltimateMatterControllerDevice.input_help_function"></th>
|
|
2159
|
-
<th data-i18n="knxUltimateMatterControllerDevice.input_help_message"></th>
|
|
2160
|
-
<th></th>
|
|
2161
|
-
</tr>
|
|
2162
|
-
</thead>
|
|
2163
|
-
<tbody id="matter-input-help-simple-body"></tbody>
|
|
2164
|
-
</table>
|
|
2165
|
-
<p id="matter-input-help-simple-empty" style="display:none;" data-i18n="knxUltimateMatterControllerDevice.input_help_no_simple"></p>
|
|
2166
|
-
<details>
|
|
2167
|
-
<summary><strong data-i18n="knxUltimateMatterControllerDevice.input_help_advanced_title"></strong></summary>
|
|
2168
|
-
<p data-i18n="knxUltimateMatterControllerDevice.input_help_advanced_hint"></p>
|
|
2169
|
-
<div class="matter-input-help-advanced">
|
|
2170
|
-
<table class="matter-input-help-table">
|
|
2171
|
-
<thead>
|
|
2172
|
-
<tr>
|
|
2173
|
-
<th data-i18n="knxUltimateMatterControllerDevice.input_help_operation"></th>
|
|
2174
|
-
<th data-i18n="knxUltimateMatterControllerDevice.input_help_target"></th>
|
|
2175
|
-
<th data-i18n="knxUltimateMatterControllerDevice.input_help_message"></th>
|
|
2176
|
-
<th></th>
|
|
2177
|
-
</tr>
|
|
2178
|
-
</thead>
|
|
2179
|
-
<tbody id="matter-input-help-advanced-body"></tbody>
|
|
2180
|
-
</table>
|
|
2181
|
-
<p id="matter-input-help-advanced-empty" style="display:none;" data-i18n="knxUltimateMatterControllerDevice.input_help_no_structure"></p>
|
|
2182
|
-
</div>
|
|
2183
|
-
</details>
|
|
2184
|
-
</div>
|
|
2185
2168
|
<div id="tabs-1">
|
|
2186
2169
|
<p>
|
|
2187
2170
|
<div class="form-row">
|
|
@@ -2623,6 +2606,46 @@
|
|
|
2623
2606
|
<option value="yes" data-i18n="knxUltimateMatterControllerDevice.node_pins_show"></option>
|
|
2624
2607
|
</select>
|
|
2625
2608
|
</div>
|
|
2609
|
+
<div id="matter-flow-input-help" style="display:none; width:900px; margin-top:12px;">
|
|
2610
|
+
<h4><i class="fa-solid fa-code"></i> <span data-i18n="knxUltimateMatterControllerDevice.input_help_label"></span></h4>
|
|
2611
|
+
<p>
|
|
2612
|
+
<strong data-i18n="knxUltimateMatterControllerDevice.input_help_endpoint"></strong>:
|
|
2613
|
+
<code id="matter-input-help-endpoint"></code>.
|
|
2614
|
+
<span data-i18n="knxUltimateMatterControllerDevice.input_help_endpoint_hint"></span>
|
|
2615
|
+
</p>
|
|
2616
|
+
<h4 data-i18n="knxUltimateMatterControllerDevice.input_help_simple_title"></h4>
|
|
2617
|
+
<p id="matter-input-help-simple-hint" data-i18n="knxUltimateMatterControllerDevice.input_help_simple_hint"></p>
|
|
2618
|
+
<table class="matter-input-help-table">
|
|
2619
|
+
<thead>
|
|
2620
|
+
<tr>
|
|
2621
|
+
<th data-i18n="knxUltimateMatterControllerDevice.input_help_operation"></th>
|
|
2622
|
+
<th data-i18n="knxUltimateMatterControllerDevice.input_help_function"></th>
|
|
2623
|
+
<th data-i18n="knxUltimateMatterControllerDevice.input_help_message"></th>
|
|
2624
|
+
<th></th>
|
|
2625
|
+
</tr>
|
|
2626
|
+
</thead>
|
|
2627
|
+
<tbody id="matter-input-help-simple-body"></tbody>
|
|
2628
|
+
</table>
|
|
2629
|
+
<p id="matter-input-help-simple-empty" style="display:none;" data-i18n="knxUltimateMatterControllerDevice.input_help_no_simple"></p>
|
|
2630
|
+
<details id="matter-input-help-advanced-details">
|
|
2631
|
+
<summary><strong data-i18n="knxUltimateMatterControllerDevice.input_help_advanced_title"></strong></summary>
|
|
2632
|
+
<p data-i18n="knxUltimateMatterControllerDevice.input_help_advanced_hint"></p>
|
|
2633
|
+
<div class="matter-input-help-advanced">
|
|
2634
|
+
<table class="matter-input-help-table">
|
|
2635
|
+
<thead>
|
|
2636
|
+
<tr>
|
|
2637
|
+
<th data-i18n="knxUltimateMatterControllerDevice.input_help_operation"></th>
|
|
2638
|
+
<th data-i18n="knxUltimateMatterControllerDevice.input_help_target"></th>
|
|
2639
|
+
<th data-i18n="knxUltimateMatterControllerDevice.input_help_message"></th>
|
|
2640
|
+
<th></th>
|
|
2641
|
+
</tr>
|
|
2642
|
+
</thead>
|
|
2643
|
+
<tbody id="matter-input-help-advanced-body"></tbody>
|
|
2644
|
+
</table>
|
|
2645
|
+
<p id="matter-input-help-advanced-empty" style="display:none;" data-i18n="knxUltimateMatterControllerDevice.input_help_no_structure"></p>
|
|
2646
|
+
</div>
|
|
2647
|
+
</details>
|
|
2648
|
+
</div>
|
|
2626
2649
|
</div>
|
|
2627
2650
|
|
|
2628
2651
|
<br/>
|
|
@@ -2647,9 +2670,11 @@ optional flow output; simpler endpoints continue through the generic mapped fall
|
|
|
2647
2670
|
|
|
2648
2671
|
**Flow input**
|
|
2649
2672
|
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2673
|
+
Enable **Node Input/Output PINs** to reveal the **Flow input** section directly below
|
|
2674
|
+
the selector. Lights show copyable examples for supported top-level properties such as
|
|
2675
|
+
`msg.on`, `msg.dimming`, `msg.color_temperature` and `msg.color`. For non-light
|
|
2676
|
+
endpoints the section shows the selected endpoint, all advertised readable/writable
|
|
2677
|
+
attributes and all accepted commands.
|
|
2653
2678
|
|
|
2654
2679
|
Use `msg.payload = {function:"position", value:35}` for a simple write. Omit `value`
|
|
2655
2680
|
to read a supported state, for example `{function:"temperature"}`. Values use human
|
|
@@ -2779,7 +2804,7 @@ _Basic Matter effects_
|
|
|
2779
2804
|
| Read status at startup | Read the Matter endpoint status at Node-RED startup or full deploy and send it to the KNX bus. |
|
|
2780
2805
|
| KNX Brightness Status | Updates the KNX brightness status whenever the Matter light switches ON/OFF. It can send 0% while OFF and restore the previous value when ON, or leave the brightness status unchanged. |
|
|
2781
2806
|
| Update local cached Matter state from KNX bus writes | When enabled, KNX writes immediately update the local Matter-state cache without waiting for the endpoint report. Disable it if the cache should follow only real Matter reports. |
|
|
2782
|
-
| Node Input/Output PINs |
|
|
2807
|
+
| Node Input/Output PINs | Reveals the **Flow input** section below the selector. Lights use the supported top-level state properties shown there (`msg.on`, `msg.dimming`, `msg.color_temperature`, `msg.color`). Mapped endpoints use `msg.payload = {function,value}` or the advanced Matter selectors. Door Lock accepts both `msg.payload = true/false` and `{function:"lock",value:true/false}`. |
|
|
2783
2808
|
| Switch on behaviour | It sets the behaviour of your lights when switched on. You can choose from differents behaviours.<br/> **Select color: ** the light will be switched on with the color of your choice. To change color, just CLICK on the color selector (under the _Select color_ control).<br/>**Select temperature and brightness: ** the light will be switched on with the temperature (Kelvin) and brightness (0-100) of your choice.<br/>**None:** the light will retain its last status. In case you've enable the night lighting, after the night time ends, the lamp will resume the color/temperature/brightness state set at day time. |
|
|
2784
2809
|
| Night Lighting | It allows to set a particular light color/brightness at nighttime. The options are the same as the daytime. You could select either a temperature/brightness or color. A cozy temperature of 2700 Kelvin, with a brightness of 10% or 20%, is a good choice for bathroom's night light.|
|
|
2785
2810
|
| Day/Night | Select the group address used to set the day/night behaviour. The group address value is _true_ if daytime, _false_ if nighttime. |
|
|
@@ -1,18 +1,48 @@
|
|
|
1
1
|
<script type="text/markdown" data-help-name="knxUltimateAI">
|
|
2
2
|
Dieser Node überwacht **alle KNX-Telegramme** des ausgewählten KNX-Ultimate-Gateways, erstellt Verkehrsstatistiken, erkennt Anomalien und kann optional ein LLM befragen.
|
|
3
3
|
|
|
4
|
+
Die Editor-Bereiche verwenden dieselben vertikalen Tabs auf der linken Seite wie die Matter-Nodes. **Schnelleinrichtung** enthält nur die üblichen KI-Einstellungen (Aktivierung, Anbieter, Zugangsdaten, Modell, KNX-Zustandsabfrage/Aktorsteuerung und Bestätigung); technische Parameter sind thematisch in den übrigen Tabs gruppiert.
|
|
5
|
+
|
|
4
6
|
## Ausgänge
|
|
5
7
|
1. **Zusammenfassung/Statistik** (`msg.payload` JSON)
|
|
6
8
|
2. **Anomalien** (`msg.payload` JSON)
|
|
7
9
|
3. **KI-Assistent** (`msg.payload` Text, mit `msg.summary`)
|
|
10
|
+
4. **KNX-Operationen** (eine Universal-Mode-Nachricht je validierter Lese- oder Schreiboperation)
|
|
11
|
+
|
|
12
|
+
Jede an den Ausgängen 3 und 4 ausgegebene Nachricht enthält außerdem eine Kopie der ursprünglichen Eingangsnachricht in `msg.inputMessage`. Dadurch bleiben der ursprüngliche Payload, das Topic, die Chat-Metadaten und alle weiteren Eingangseigenschaften für nachfolgende Nodes verfügbar. Fehler beim Klonen oder Ausgeben werden abgefangen und gemeldet, statt in die Node-RED-Laufzeit zu gelangen.
|
|
8
13
|
|
|
9
14
|
## Befehle (Eingang)
|
|
10
15
|
Sende `msg.topic`:
|
|
11
16
|
- `summary` (oder leer): Summary sofort senden
|
|
12
17
|
- `reset`: internen Verlauf/Zähler zurücksetzen
|
|
13
18
|
- `ask`: Frage an das konfigurierte LLM senden
|
|
19
|
+
- `confirm` / `cancel`: ausstehende KNX-Befehle ohne erneuten LLM-Aufruf bestätigen oder abbrechen
|
|
20
|
+
- `clear_chat`: Gesprächsspeicher der aktuellen Sitzung löschen
|
|
21
|
+
|
|
22
|
+
Für `ask` die Frage in `msg.prompt` (empfohlen), `msg.payload` (String) oder den üblichen Telegram-Feldern `msg.payload.content` / `msg.payload.text` übergeben.
|
|
23
|
+
|
|
24
|
+
Bei aktiviertem KNX-Steuern werden die letzten Gesprächsschritte im RAM nach `msg.knxAi.sessionId`, `msg.sessionId` oder erkannter Telegram-Chat-ID getrennt. Ausgang 3 mit dem Chat-Sender und Ausgang 4 mit einem KNX-Ultimate-Node im **Universalmodus** verbinden. Bei aktiver Bestätigung zeigt die erste Antwort GA, DPT und Payload, ohne Schreiboperationen auszugeben; dieselbe Sitzung muss innerhalb von 5 Minuten `BESTÄTIGEN` oder `ABBRECHEN` antworten. Eine neue Anfrage ersetzt einen älteren Plan. Jeder bestätigte Befehl enthält `msg.destination`, `msg.dpt`, `msg.payload` und `msg.event = "GroupValue_Write"`.
|
|
25
|
+
Bei DPT-1.xxx-Schreibvorgängen werden die sicheren KI-Entsprechungen `true`/`false`, `1`/`0` und `on`/`off` vor der lokalen Validierung und Ausgabe in echte Boolesche Werte normalisiert.
|
|
26
|
+
|
|
27
|
+
### Aktuelle KNX-Lesewerte
|
|
28
|
+
Wenn der Benutzer ausdrücklich einen aktuellen oder aktualisierten Zustand anfordert, kann die KI exakte Objekte aus dem importierten ETS-Katalog abfragen, einschließlich Status- und anderer schreibgeschützter Objekte. Ausgang 4 gibt `msg.destination`, `msg.dpt`, `msg.event = "GroupValue_Read"` und `msg.readstatus = true` aus. Der Node wartet bis zu 6 Sekunden auf jede `GroupValue_Response` oder ein aktuelles Write-Telegramm, gibt anschließend die dekodierten Werte an Ausgang 3 zurück und stellt Details in `msg.knxAi.readResults` bereit. Leseoperationen erfordern keine Bestätigung und werden niemals in Schreiboperationen umgewandelt.
|
|
29
|
+
|
|
30
|
+
### Bestätigungsanfrage für Chat-Schaltflächen
|
|
31
|
+
Solange ein Plan aussteht, enthält Ausgang 3 `msg.knxAi.confirmationRequest`. Das Objekt enthält `required`, `status`, `sessionId`, `expiresAt`, `commandCount` und zwei Einträge in `actions`. Verwenden Sie `action.label` als Text der Telegram-Schaltfläche, `action.callbackData` als Callback und senden Sie `action.message` an KNX AI zurück, um ohne Texteingabe zu bestätigen oder abzubrechen.
|
|
32
|
+
|
|
33
|
+
### Chat-Adapter-Vorlagen
|
|
34
|
+
Der Tab **Chat-Adapter** lädt seine auswählbaren Zuordnungen aus `resources/KNXAIChatAdapterMappings.js`. Die Auswahl einer Vorlage fügt zwei bearbeitbare synchrone JavaScript-Zuordnungen in Textfeldern über die volle Breite ein: eine vor der Verarbeitung des Eingangs durch KNX AI und eine vor der Ausgabe an Ausgang 3. Geben Sie `msg` zurück, um fortzufahren, oder keinen Wert, um die Nachricht zu verwerfen. Syntax- und Laufzeitfehler werden abgefangen und gemeldet, ohne Node-RED anzuhalten.
|
|
35
|
+
|
|
36
|
+
Die enthaltene Vorlage **windkh/node-red-contrib-telegrambot** folgt dem Receiver-/Sender-Vertrag des Pakets. Verbinden Sie einen `telegram receiver` direkt mit KNX AI und Ausgang 3 direkt mit einem `telegram sender`. Für Inline-Bestätigungsschaltflächen verbinden Sie zusätzlich einen als `callback_query` konfigurierten `telegram event` mit demselben KNX-AI-Eingang. Die Eingangszuordnung liest `msg.payload.content`, `msg.payload.chatId` und die Telegram-Sprache. Die Ausgangszuordnung erstellt `msg.payload.chatId`, `type` und `content` und ergänzt bei ausstehender Schreibbestätigung `options.reply_markup` aus `msg.knxAi.confirmationRequest`. Das Telegram-Paket bleibt eine separate optionale Abhängigkeit.
|
|
14
37
|
|
|
15
|
-
|
|
38
|
+
## Kurzer Ablauf: KNX-Steuerung
|
|
39
|
+
1. Importieren Sie die ETS-CSV in das Gateway und konfigurieren Sie LLM-Anbieter, Modell und Zugangsdaten.
|
|
40
|
+
2. Aktivieren Sie **LLM-Assistent** und **KNX-Zustände lesen und Aktoren steuern**; lassen Sie die Bestätigung aktiviert.
|
|
41
|
+
3. Verbinden Sie den Chat-Eingang mit KNX AI und behalten Sie eine stabile Sitzungs-/Chat-ID bei.
|
|
42
|
+
4. Verbinden Sie Ausgang 3 mit der Chat-Antwort und Ausgang 4 mit KNX Ultimate im **Universalmodus**.
|
|
43
|
+
5. Der Benutzer sendet eine Anfrage; aktuelle Zustandsabfragen werden sofort gelesen, während Schreiboperationen zuerst GA, DPT und Wert ohne Bus-Schreibzugriff anzeigen.
|
|
44
|
+
6. Innerhalb von 5 Minuten antwortet derselbe Chat exakt mit `BESTÄTIGEN` oder `ABBRECHEN`.
|
|
45
|
+
7. Nur `BESTÄTIGEN` validiert erneut und gibt Befehle an Ausgang 4 aus; prüfen Sie die Ausführung über eine KNX-Status-GA.
|
|
16
46
|
|
|
17
47
|
## Konfigurationsfelder
|
|
18
48
|
Hier sind alle Felder aufgeführt, wie sie im KNX-AI-Editor sichtbar sind.
|
|
@@ -53,7 +83,13 @@ Hier sind alle Felder aufgeführt, wie sie im KNX-AI-Editor sichtbar sind.
|
|
|
53
83
|
- **Endpoint URL**: URL des Chat/Completions-Endpunkts.
|
|
54
84
|
- **API key**: API-Schlüssel (für lokales Ollama nicht erforderlich).
|
|
55
85
|
- **Model**: Modell-ID/Name.
|
|
86
|
+
- **Chatmodell-Kompatibilität**: Das ausgewählte Modell muss den konfigurierten Chat-Completions-Endpunkt unterstützen. Ältere reine Completions-Modelle wie `gpt-3.5-turbo-instruct` werden beim Aktualisieren der Modellliste ausgeschlossen. Lehnt der Anbieter einen benutzerdefinierten Temperaturwert oder den Token-Limit-Parameter ab, wiederholt KNX AI die Anfrage und entfernt oder ersetzt nur das inkompatible Feld.
|
|
56
87
|
- **System prompt**: Globale Instruktion für KNX-Analyse (Advanced).
|
|
88
|
+
- **KI darf KNX-Zustände lesen und Aktoren steuern**: Aktiviert Ausgang 4 und ist standardmäßig aus. Exakte ETS-Katalogobjekte dürfen gelesen werden; Schreiboperationen werden ausschließlich für Objekte mit Rolle `command` akzeptiert. Unbekannte, DPT-falsche, ungültige oder überzählige Operationen sowie Schreiboperationen auf Status-/Neutralobjekte werden lokal abgewiesen.
|
|
89
|
+
- **Vor dem Senden von KNX-Befehlen bestätigen lassen**: Standardmäßig aktiv. Zeigt zuerst die validierten Änderungen und sendet nichts, bis dieselbe Chat-Sitzung bestätigt. Wenn Befehle auf Bestätigung warten, fügt die Antwort immer die genauen Anweisungen zum Bestätigen oder Abbrechen in der Sprache der aktuellen Anfrage hinzu. Vor der Ausgabe werden die Befehle erneut validiert.
|
|
90
|
+
- **Adapter-Vorlage**: Lädt ein Paar aus Ein- und Ausgangszuordnung aus der mitgelieferten Chat-Adapter-Datei. Die Auswahl ersetzt bewusst beide Textfelder; der Code bleibt danach bearbeitbar.
|
|
91
|
+
- **Eingangszuordnung (Chat → KNX AI)**: Synchrones JavaScript vor der Verarbeitung des Eingangsbefehls.
|
|
92
|
+
- **Ausgangszuordnung (KNX AI → Chat)**: Synchrones JavaScript ausschließlich für Nachrichten an Ausgang 3.
|
|
57
93
|
- Wenn das Festplattenarchiv aktiv ist, nutzt **Ask** standardmäßig dieses Archiv: explizite Datumsangaben/Zeitbereiche werden beachtet, sonst durchsucht der Assistent die letzten 24 Stunden plus aktuelle RAM-Events.
|
|
58
94
|
- **Include raw payload hex**: Rohe Hex-Payload im Prompt einfügen.
|
|
59
95
|
- **Node-RED-Projektinventar einbeziehen**: Nimmt das gesamte Node-RED-Projektinventar in den Prompt auf, einschließlich KNX-Nodes und anderer hilfreicher Nodes wie function/change/inject/template, wenn sie KNX-Logik oder Gruppenadressen enthalten.
|
|
@@ -84,5 +120,5 @@ Hier sind alle Felder aufgeführt, wie sie im KNX-AI-Editor sichtbar sind.
|
|
|
84
120
|
- Wenn Node-RED in Docker läuft, im Endpoint `host.docker.internal` statt `localhost` verwenden.
|
|
85
121
|
|
|
86
122
|
## Sicherheitshinweis
|
|
87
|
-
Bei aktiviertem LLM kann KNX-Traffic-Kontext an den konfigurierten Endpoint gesendet werden. Für striktes On-Premise lokale Provider verwenden.
|
|
123
|
+
Bei aktiviertem LLM kann KNX-Traffic-Kontext an den konfigurierten Endpoint gesendet werden. Für striktes On-Premise lokale Provider verwenden. Ein Befehl an Ausgang 4 hat die lokale Validierung bestanden und wurde an den Flow weitergegeben; dies bestätigt nicht die Ausführung durch den Aktor. Dafür eine KNX-Status-GA verwenden.
|
|
88
124
|
</script>
|
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
"knxUltimateAI": {
|
|
3
3
|
"title": "KNX AI (Traffic Analyzer)",
|
|
4
4
|
"sections": {
|
|
5
|
+
"quickSetup": "Schnelleinrichtung",
|
|
5
6
|
"capture": "Capture",
|
|
6
7
|
"storage": "Speicher & Zusammenfassung",
|
|
7
8
|
"detection": "Erkennung & Warnungen",
|
|
8
9
|
"llmConnection": "KI-Assistent-Verbindung",
|
|
9
10
|
"llmContext": "KI-Assistent-Kontext",
|
|
10
|
-
"
|
|
11
|
+
"chatAdapter": "Chat-Adapter",
|
|
12
|
+
"advanced": "Erweiterte KI"
|
|
11
13
|
},
|
|
12
14
|
"properties": {
|
|
13
15
|
"server": "Gateway",
|
|
@@ -39,19 +41,28 @@
|
|
|
39
41
|
"llmSystemPrompt": "System prompt",
|
|
40
42
|
"llmIncludeRaw": "Include raw payload hex",
|
|
41
43
|
"llmIncludeFlowContext": "Node-RED-Projektinventar einbeziehen",
|
|
44
|
+
"llmAllowKnxCommands": "KI darf KNX-Zustände lesen und Aktoren steuern",
|
|
45
|
+
"llmRequireCommandConfirmation": "Vor dem Senden von KNX-Befehlen bestätigen lassen",
|
|
46
|
+
"chatAdapterPreset": "Adapter-Vorlage",
|
|
47
|
+
"chatInputCode": "Eingangszuordnung (Chat → KNX AI)",
|
|
48
|
+
"chatOutputCode": "Ausgangszuordnung (KNX AI → Chat)",
|
|
42
49
|
"llmIncludeDocsSnippets": "Include documentation snippets (help/README/examples)",
|
|
43
50
|
"llmDocsLanguage": "Docs language"
|
|
44
51
|
},
|
|
45
52
|
"outputs": {
|
|
46
53
|
"summary": "Zusammenfassung/Statistik",
|
|
47
54
|
"anomalies": "Anomalien",
|
|
48
|
-
"assistant": "KI-Assistent"
|
|
55
|
+
"assistant": "KI-Assistent",
|
|
56
|
+
"knxCommands": "KNX-Operationen"
|
|
49
57
|
},
|
|
50
58
|
"selectlists": {
|
|
51
59
|
"llmProvider": {
|
|
52
60
|
"openai_compat": "OpenAI-compatible (chat/completions)",
|
|
53
61
|
"anthropic": "Anthropic (Claude)",
|
|
54
62
|
"ollama": "Ollama (local, beta)"
|
|
63
|
+
},
|
|
64
|
+
"chatAdapter": {
|
|
65
|
+
"none": "Kein Adapter"
|
|
55
66
|
}
|
|
56
67
|
},
|
|
57
68
|
"placeholder": {
|
|
@@ -67,7 +78,9 @@
|
|
|
67
78
|
"installedOllamaModel": "Ollama model installed",
|
|
68
79
|
"installOllamaModelFailed": "Failed to install Ollama model",
|
|
69
80
|
"ollamaInstallSteps": "1) Open the model library and copy the model name (for example llama3.1). 2) Put the name in the Model field and click Install it.",
|
|
70
|
-
"ollamaStartedAuto": "Ollama server started automatically."
|
|
81
|
+
"ollamaStartedAuto": "Ollama server started automatically.",
|
|
82
|
+
"chatAdapterIntro": "Wählen Sie eine Vorlage, um den Code für Ein- und Ausgang einzufügen. Die Liste wird aus der mitgelieferten Chat-Adapter-Datei geladen; der erzeugte Code bleibt bearbeitbar.",
|
|
83
|
+
"chatAdapterCodeHelp": "Zuordnungen laufen synchron. Geben Sie msg zurück, um fortzufahren, oder keinen Wert, um die Nachricht zu verwerfen. Fehler werden abgefangen und gemeldet, ohne Node-RED anzuhalten."
|
|
71
84
|
},
|
|
72
85
|
"sidebar": {
|
|
73
86
|
"ui": {
|
|
@@ -45,7 +45,7 @@ Diese Optionen werden nur angezeigt, wenn sie zum gewählten Typ passen. Dimmbar
|
|
|
45
45
|
|
|
46
46
|
## Node-PINs
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
Der PIN-Selektor befindet sich außerhalb der Editor-Tabs. Nach dem Aktivieren erscheint direkt darunter ein kontextbezogener Bereich **Flow-Eingang/-Ausgang** mit kopierbaren Beispielen für den ausgewählten Gerätetyp:
|
|
49
49
|
|
|
50
50
|
- **Input**: aktualisiere den Matter-Zustand aus dem Flow, ohne den KNX-Bus: `msg.payload = { function: "onoff", value: true }` (`function` ist eine von `onoff`, `level`, `rgb`, `colortemp`, `position`, `temperature`, `humidity`, `illuminance`, `occupancy`, `contact`, `currenttemp`, `setpoint`, `fanspeed`, `smoke`, `co`, `leak`, `co2`, `rvcstate`, `rvcmode`). Nützlich, um im Flow berechnete Werte (z.B. einen virtuellen Sensor) für Alexa & Co. bereitzustellen.
|
|
51
51
|
- **Output**: Jeder von einem Matter-Controller empfangene Befehl wird an den Flow weitergeleitet: `msg.topic` = Gerätename, `msg.payload` = Wert, `msg.matter` = der rohe Befehl. Ein Gerät ohne Befehls-GAs wird zum **Nur-Flow-Gerät**.
|
|
@@ -36,6 +36,20 @@
|
|
|
36
36
|
"mappings": "KNX-Zuordnungen",
|
|
37
37
|
"advanced": "Erweiterte Optionen"
|
|
38
38
|
},
|
|
39
|
+
"flow_help": {
|
|
40
|
+
"title": "Flow-Eingang/-Ausgang",
|
|
41
|
+
"intro": "Diese Beispiele entsprechen dem ausgewählten Gerätetyp und sind verfügbar, solange die Ein-/Ausgangs-PINs aktiviert sind.",
|
|
42
|
+
"input_title": "Flow → Matter (Node-Eingang)",
|
|
43
|
+
"input_hint": "Sende eine dieser Nachrichten, um den bereitgestellten Matter-Zustand ohne KNX-Schreibvorgang zu aktualisieren.",
|
|
44
|
+
"output_title": "Matter → Flow (Node-Ausgang)",
|
|
45
|
+
"output_hint": "Befehle eines Matter-Controllers werden mit dem Wert in msg.payload und den Rohdetails in msg.matter ausgegeben.",
|
|
46
|
+
"function": "Funktion",
|
|
47
|
+
"message": "Beispielnachricht",
|
|
48
|
+
"copy": "Kopieren",
|
|
49
|
+
"copied": "Nachricht kopiert",
|
|
50
|
+
"no_input": "Für diesen Gerätetyp gibt es keine Beispiele für Flow-Zustandsupdates.",
|
|
51
|
+
"no_output": "Dieser Gerätetyp gibt keine Controller-Befehle aus; sein Ausgang bleibt inaktiv."
|
|
52
|
+
},
|
|
39
53
|
"functions": {
|
|
40
54
|
"fn_onoff_cmd": "Ein/Aus Befehls-GA",
|
|
41
55
|
"fn_onoff_status": "Ein/Aus Status-GA",
|
|
@@ -19,11 +19,11 @@ Er ersetzt die unveröffentlichten getrennten Matter-Controller-Nodes und behäl
|
|
|
19
19
|
| Sensoren | Sensor-Endpunkte zeigen ihre Mess-/Status-GA nur bei Unterstützung: Temperatur, Feuchte, Helligkeit, Präsenz, Kontakt und Batterie. |
|
|
20
20
|
| Read at startup | Veröffentlicht den gecachten Matter-Wert beim Deploy/Start oder wenn sich das Gerät erneut verbindet. |
|
|
21
21
|
| Update local state from KNX write | Aktualisiert den lokalen Matter/KNX-Cache, wenn ein Telegramm auf eine konfigurierte KNX-GA geschrieben wird. |
|
|
22
|
-
| Node Input/Output PINs | Zeigt Node-RED-Eingangs-/Ausgangspins
|
|
22
|
+
| Node Input/Output PINs | Zeigt Node-RED-Eingangs-/Ausgangspins und direkt darunter den Bereich **Flow-Eingang**. Für Leuchten werden die unterstützten Lichtzustands-Nachrichten auf oberster Ebene angezeigt; andere Endpunkte zeigen das einfache `{function,value}`-Format und die erweiterten Matter-Felder. |
|
|
23
23
|
|
|
24
24
|
## Flow-Eingang
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
Aktiviere **Node Input/Output PINs**, um den Bereich **Flow-Eingang** direkt unter dem Auswahlfeld einzublenden. Für Leuchten zeigt er kopierbare Beispiele der unterstützten Eigenschaften auf oberster Ebene, etwa `msg.on`, `msg.dimming`, `msg.color_temperature` und `msg.color`. Für andere Endpunkte wird er aus der angekündigten Struktur aufgebaut und zeigt Endpoint ID sowie alle lesbaren/schreibbaren Attribute und akzeptierten Befehle. Er bleibt auch ohne KNX-Gateway verfügbar.
|
|
27
27
|
|
|
28
28
|
Einfache Schreibvorgänge verwenden `msg.payload = {function:"position",value:35}`. Ohne `value` wird ein unterstützter Zustand gelesen, zum Beispiel `{function:"temperature"}`; die Ausgabe verwendet verständliche Einheiten und enthält Rohdetails in `msg.matter`. Funktionen wie `onoff`, `level`, `position`, `open`, `close`, `stop`, Sollwerte, Lüfter und Sensoren erscheinen nur bei Unterstützung. Türschlösser akzeptieren `{function:"lock",value:true|false}`.
|
|
29
29
|
|