iobroker.alias-workbench 0.9.11 → 0.10.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/README.md +106 -4
- package/admin/css/werkbank.css +50 -6
- package/admin/js/aufzaehlungen.js +24 -8
- package/admin/js/baum.js +13 -2
- package/admin/js/detail.js +310 -89
- package/admin/js/entwurf.js +443 -197
- package/admin/js/enums.js +9 -2
- package/admin/js/ergebnis.js +148 -28
- package/admin/js/erkennung.js +52 -0
- package/admin/js/mqtt.js +175 -65
- package/admin/js/musterwahl.js +103 -26
- package/admin/js/objekte.js +16 -0
- package/admin/js/pruefungen.js +10 -8
- package/admin/js/quellentausch.js +59 -12
- package/admin/js/schreiben.js +76 -43
- package/admin/js/vorlagen.js +28 -5
- package/admin/js/vorlagenblatt.js +49 -7
- package/admin/js/vorschlagen.js +60 -7
- package/admin/js/werte.js +99 -21
- package/admin/js/zielleiste.js +8 -2
- package/admin/js/zuordnung.js +79 -28
- package/admin/js/zustand.js +5 -1
- package/admin/js/zustandsliste.js +219 -42
- package/admin/sprachen/de.json +45 -18
- package/admin/sprachen/en.json +45 -18
- package/admin/tab.html +44 -44
- package/admin/vorlagen/hm-rollladen.json +3 -2
- package/admin/vorlagen/messpunkt.json +7 -2
- package/admin/vorlagen/tasmota-lampe.json +7 -2
- package/admin/vorlagen/tasmota-mehrfach.json +7 -2
- package/admin/vorlagen/tasmota-rgb.json +6 -2
- package/admin/vorlagen/tasmota-steckdose.json +7 -2
- package/io-package.json +38 -29
- package/package.json +1 -1
package/admin/js/detail.js
CHANGED
|
@@ -4,11 +4,11 @@ import { S } from './zustand.js';
|
|
|
4
4
|
import { socket } from './verbindung.js';
|
|
5
5
|
import { el } from './basis.js';
|
|
6
6
|
import { tr } from './sprache.js';
|
|
7
|
-
import { wertVon, jsonFelder, feldAusFormel, feldFormel, feldWert, jsonVon, zielKann } from './werte.js';
|
|
8
|
-
import { musterVon, erkenneEntwurf, platzFuerRolle, typenFuerRolle } from './erkennung.js';
|
|
7
|
+
import { wertVon, jsonFelder, feldAusFormel, feldFormel, feldWert, jsonVon, zielKann, knopfZeile } from './werte.js';
|
|
8
|
+
import { musterVon, erkenneEntwurf, platzFuerRolle, typenFuerRolle, rolleVonPlatz } from './erkennung.js';
|
|
9
9
|
import { musterName } from './musternamen.js';
|
|
10
10
|
import { rollenFeld } from './rollenwahl.js';
|
|
11
|
-
import { opt , quellenAuswahl, vorlagenAbweichung, bestandsAbweichung } from './entwurf.js';
|
|
11
|
+
import { opt , quellenAuswahl, vorlagenAbweichung, bestandsAbweichung, aboErgaenzen } from './entwurf.js';
|
|
12
12
|
import { zeichneErgebnis, entwurfAngefasst } from './ergebnis.js';
|
|
13
13
|
|
|
14
14
|
import { rateBehalten } from './vorschlagen.js';
|
|
@@ -26,7 +26,13 @@ export function detailZeile(e, s, idx) {
|
|
|
26
26
|
|
|
27
27
|
/* Wer die Zeile anfasst, hat entschieden - die Vermutung ist damit
|
|
28
28
|
keine mehr und faellt beim Zuruecknehmen nicht mehr weg. */
|
|
29
|
-
|
|
29
|
+
/* Eine von Hand umgestellte Quelle wird mit abonniert - sonst stuende
|
|
30
|
+
ihr Wert nach dem ersten Abholen still (J9b). */
|
|
31
|
+
function neu() {
|
|
32
|
+
s.geaendert = true; rateBehalten(s); entwurfAngefasst();
|
|
33
|
+
aboErgaenzen([s.srcR, s.srcW]);
|
|
34
|
+
zeichneErgebnis();
|
|
35
|
+
}
|
|
30
36
|
|
|
31
37
|
/* ---- Die Auswahl der Datenpunkte ---------------------------------
|
|
32
38
|
|
|
@@ -100,9 +106,7 @@ export function detailZeile(e, s, idx) {
|
|
|
100
106
|
if (!o || !o.common) { return null; }
|
|
101
107
|
var c = o.common;
|
|
102
108
|
|
|
103
|
-
var maengel = zielKann(s)
|
|
104
|
-
return seite === 'w' ? x.art !== 'meldetNichts' : x.art === 'meldetNichts';
|
|
105
|
-
});
|
|
109
|
+
var maengel = seite === 'w' ? zielKann(s) : [];
|
|
106
110
|
|
|
107
111
|
var text, hinweis;
|
|
108
112
|
if (seite === 'w') {
|
|
@@ -114,9 +118,13 @@ export function detailZeile(e, s, idx) {
|
|
|
114
118
|
: (bl ? tr('detail.targetTypeVoid', c.type) : tr('detail.targetTypeHint', c.type));
|
|
115
119
|
if (nw) { text = c.type + ' \u00b7 ' + tr('detail.notWritable'); }
|
|
116
120
|
} else {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
121
|
+
/* Nur noch der Knopf, und ruhig: kein Mangel, eine Auskunft -
|
|
122
|
+
was `read: false` am Alias bewirkt und warum die Lesequelle
|
|
123
|
+
trotzdem da ist. Die Marke „aendert sich nie" an jeder Quelle
|
|
124
|
+
mit `read: false` ist seit 19.09.2026 weg (siehe `zielKann`). */
|
|
125
|
+
if (!knopfZeile(s)) { return null; }
|
|
126
|
+
text = tr('detail.readIsButton');
|
|
127
|
+
hinweis = tr('detail.readIsButtonHint');
|
|
120
128
|
}
|
|
121
129
|
|
|
122
130
|
var m = el('span', 'chip ' + (maengel.length ? 'warn' : 'mut'), text);
|
|
@@ -243,6 +251,121 @@ export function detailZeile(e, s, idx) {
|
|
|
243
251
|
zeigeBestand(r, felder);
|
|
244
252
|
}
|
|
245
253
|
|
|
254
|
+
/* Welcher Platz ist schon vergeben, und von welcher Zeile? Das steht
|
|
255
|
+
als Vermerk am Gruppenkopf der Rollenliste. */
|
|
256
|
+
var belegtVon = {};
|
|
257
|
+
/* Eigener Platz direkt aus dem Befund — ueber `belegtVon` allein fand
|
|
258
|
+
ihn nur die LETZTE Zeile eines mehrfach vergebenen Platzes (ACTUAL
|
|
259
|
+
bei `info`), alle anderen standen auf „kein Platz“. */
|
|
260
|
+
var eigenerPlatz = null;
|
|
261
|
+
if (e.want) {
|
|
262
|
+
var fundR = erkenneEntwurf(e, e.want);
|
|
263
|
+
if (fundR.length) {
|
|
264
|
+
fundR[0].states.forEach(function (x) {
|
|
265
|
+
if (!x.id) { return; }
|
|
266
|
+
var nx = x.id.slice(e.kanal.length + 1);
|
|
267
|
+
belegtVon[x.name] = nx;
|
|
268
|
+
if (nx === s.n) { eigenerPlatz = x.name; }
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
/* --- Platz im Muster ---
|
|
273
|
+
|
|
274
|
+
Umgekehrt zur Rolle: erst den Platz waehlen, und die Werkbank traegt
|
|
275
|
+
ein, was das Muster dafuer vorgibt — Rolle, Typ, Einheit, Werteliste.
|
|
276
|
+
Wer eine Zeile auf SET setzen wollte, musste bisher wissen, dass SET
|
|
277
|
+
`switch` heisst (Ricardo, 19.09.2026). Datenpunkt und Name bleiben;
|
|
278
|
+
dieselbe Vorgabe nimmt der Klick auf einen freien Platz unten, nur
|
|
279
|
+
dass der eine neue Zeile anlegt. */
|
|
280
|
+
var platzBox = null;
|
|
281
|
+
var mPl = e.want && musterVon(e.want);
|
|
282
|
+
if (mPl) {
|
|
283
|
+
var meinPlatz = eigenerPlatz;
|
|
284
|
+
var pb = el('div');
|
|
285
|
+
var selP = el('select', 'tx');
|
|
286
|
+
selP.style.width = '240px';
|
|
287
|
+
var oKein = opt('', tr('detail.slotNone'));
|
|
288
|
+
if (meinPlatz) { oKein.disabled = true; }
|
|
289
|
+
selP.appendChild(oKein);
|
|
290
|
+
var gesehen = {};
|
|
291
|
+
mPl.states.forEach(function (st) {
|
|
292
|
+
if (gesehen[st.name]) { return; }
|
|
293
|
+
gesehen[st.name] = 1;
|
|
294
|
+
var fremd = belegtVon[st.name] && belegtVon[st.name] !== s.n && !st.multiple;
|
|
295
|
+
var txt = st.name + (st.required ? ' · ' + tr('detail.slotRequired') : '') +
|
|
296
|
+
(fremd ? ' · ' + tr('roles.slotTaken', belegtVon[st.name]) : '');
|
|
297
|
+
var o = opt(st.name, txt);
|
|
298
|
+
if (fremd) { o.disabled = true; }
|
|
299
|
+
selP.appendChild(o);
|
|
300
|
+
});
|
|
301
|
+
/* Gewaehlt, aber nicht bekommen (etwa weil die Schreibrichtung nicht
|
|
302
|
+
passt): die Wahl bleibt im Feld stehen, darunter steht warum. */
|
|
303
|
+
var gewuenscht = !meinPlatz && s.platzWahl && gesehen[s.platzWahl] ? s.platzWahl : '';
|
|
304
|
+
selP.value = meinPlatz || gewuenscht;
|
|
305
|
+
selP.addEventListener('click', function (ev) { ev.stopPropagation(); });
|
|
306
|
+
selP.addEventListener('change', function () {
|
|
307
|
+
var st = null;
|
|
308
|
+
mPl.states.forEach(function (x) { if (!st && x.name === selP.value) { st = x; } });
|
|
309
|
+
if (!st) { return; }
|
|
310
|
+
s.platzWahl = st.name;
|
|
311
|
+
s.role = rolleVonPlatz(st);
|
|
312
|
+
var ty = Array.isArray(st.type) ? st.type[0] : st.type;
|
|
313
|
+
if (ty) { s.typ = ty; }
|
|
314
|
+
if (st.defaultUnit) { s.unit = st.defaultUnit; }
|
|
315
|
+
/* Ohne Werteliste bleibt so ein Platz leer, egal wie gut Rolle und
|
|
316
|
+
Typ passen — wie beim Klick auf einen freien Platz. */
|
|
317
|
+
if (st.statesDefined && !s.states) { s.states = st.defaultStates || { 0: 'None' }; }
|
|
318
|
+
/* Ein Platz, den das Muster nur lesen laesst (ELECTRIC_POWER,
|
|
319
|
+
ACTUAL …), bekommt kein Schreibziel. mqtt-client legt jeden Punkt
|
|
320
|
+
beschreibbar an, und die Werkbank trug deshalb die Quelle auch
|
|
321
|
+
bei „schreibt auf" ein — am 3D-Drucker schrieb die Leistung auf
|
|
322
|
+
`tele.SENSOR` (Ricardo, 19.09.2026). Weggenommen wird nur, nie
|
|
323
|
+
geraten: ein fehlendes Schreibziel bei SET setzt das Feld nicht. */
|
|
324
|
+
if (st.write === false) { s.srcW = ''; s.wr = false; }
|
|
325
|
+
neu();
|
|
326
|
+
});
|
|
327
|
+
pb.appendChild(selP);
|
|
328
|
+
/* Neben dem Namen ist kein Platz fuer einen Satz darunter — er steht
|
|
329
|
+
als Tooltip am Feld. */
|
|
330
|
+
selP.title = tr('detail.slotHint');
|
|
331
|
+
/* Gewaehlt, aber nicht bekommen — warum?
|
|
332
|
+
|
|
333
|
+
Die Schreibrichtung ist es fast nie: traegt ein Punkt genau die
|
|
334
|
+
Vorgaberolle des Platzes, prueft der type-detector `read`/`write`
|
|
335
|
+
gar nicht (ChannelDetector: „When the default role is assigned …
|
|
336
|
+
we can be a bit more laxe"). Und das Feld setzt immer die
|
|
337
|
+
Vorgaberolle. Der erste Hinweis an dieser Stelle behauptete
|
|
338
|
+
deshalb etwas Falsches (Ricardo, 19.09.2026: an tele.SENSOR mit
|
|
339
|
+
Schreibziel blieb ELECTRIC_POWER vergeben, ohne Hinweis — zu
|
|
340
|
+
Recht). Der haeufige Grund ist ein anderer: das Muster greift als
|
|
341
|
+
Ganzes nicht, weil ein Pflichtplatz fehlt — dann vergibt der
|
|
342
|
+
Detektor gar keinen Platz, auch diesen nicht. */
|
|
343
|
+
/* Greift das Muster als Ganzes nicht, steht das in der Legende ueber
|
|
344
|
+
der Liste — hier waere es an der falschen Zeile. */
|
|
345
|
+
if (gewuenscht && Object.keys(belegtVon).length > 0) {
|
|
346
|
+
pb.appendChild(el('div', 'aside w', tr('detail.slotNotGiven', gewuenscht)));
|
|
347
|
+
}
|
|
348
|
+
platzBox = pb;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/* Der Platz steht rechts neben dem Namen, mit seiner Bezeichnung links
|
|
352
|
+
daneben wie eine Zeilenbeschriftung: man waehlt ihn am Anfang, und
|
|
353
|
+
weiter unten musste man ihn suchen (Ricardo, 19.09.2026). */
|
|
354
|
+
function mitPlatz(inhalt) {
|
|
355
|
+
if (!platzBox) { return inhalt; }
|
|
356
|
+
var w = el('div', 'fields');
|
|
357
|
+
w.style.alignItems = 'flex-start';
|
|
358
|
+
w.appendChild(inhalt);
|
|
359
|
+
var pf = el('div');
|
|
360
|
+
pf.style.display = 'flex';
|
|
361
|
+
pf.style.gap = '8px';
|
|
362
|
+
pf.style.alignItems = 'flex-start';
|
|
363
|
+
pf.appendChild(el('div', 'k', tr('detail.slot')));
|
|
364
|
+
pf.appendChild(platzBox);
|
|
365
|
+
w.appendChild(pf);
|
|
366
|
+
return w;
|
|
367
|
+
}
|
|
368
|
+
|
|
246
369
|
/* --- Name, nur bei selbst angelegten --- */
|
|
247
370
|
if (s.manuell) {
|
|
248
371
|
var iN = el('input', 'tx w');
|
|
@@ -255,24 +378,76 @@ export function detailZeile(e, s, idx) {
|
|
|
255
378
|
s.n = iN.value.trim().toUpperCase().replace(/\s+/g, '_');
|
|
256
379
|
neu();
|
|
257
380
|
});
|
|
258
|
-
zeile(tr('detail.name'), iN);
|
|
381
|
+
zeile(tr('detail.name'), mitPlatz(iN));
|
|
259
382
|
} else if (s.urId) {
|
|
260
|
-
zeile(tr('detail.objectId'), s.urId);
|
|
383
|
+
zeile(tr('detail.objectId'), mitPlatz(el('div', null, s.urId)));
|
|
261
384
|
} else {
|
|
262
|
-
|
|
385
|
+
/* Der letzte Teil ist ein Feld: der Name bestimmt nur die Kennung,
|
|
386
|
+
nicht was der Punkt tut — das macht die Rolle. Wer `cmnd_POWER`
|
|
387
|
+
lieber `POWER` oder `SET` nennen will, konnte das bisher nirgends
|
|
388
|
+
(Ricardo, 19.09.2026). Nur hier, beim Neuanlegen: an einem
|
|
389
|
+
bestehenden Punkt (`urId`, oben) haengen Skripte und vis am Namen.
|
|
390
|
+
|
|
391
|
+
Bereinigt wie die Zielkennung (zielleiste.js), aber ohne Punkt —
|
|
392
|
+
ein Punkt machte aus dem Namen einen Unterordner. Gross- und
|
|
393
|
+
Kleinschreibung bleiben, wie sie getippt wurden. */
|
|
394
|
+
/* Aufgebaut wie „liest aus": oben das Feld, darunter klein die
|
|
395
|
+
volle Kennung, die daraus entsteht (Ricardo, 19.09.2026). */
|
|
396
|
+
var vorspann = (e.ziel || e.kanal) + '.';
|
|
397
|
+
var iNa = el('input', 'tx');
|
|
398
|
+
iNa.type = 'text';
|
|
399
|
+
iNa.value = s.n;
|
|
400
|
+
iNa.style.width = '260px';
|
|
401
|
+
iNa.style.maxWidth = '100%';
|
|
402
|
+
iNa.style.fontFamily = 'var(--mono)';
|
|
403
|
+
var nameFelder = el('div', 'fields');
|
|
404
|
+
nameFelder.appendChild(iNa);
|
|
405
|
+
var nameStapel = mitVollId(nameFelder, vorspann + s.n);
|
|
406
|
+
var nameVoll = nameStapel.querySelector('.vollid');
|
|
407
|
+
var bereinigt = function (v) {
|
|
408
|
+
return v.trim().replace(/\s+/g, '_').replace(/[^\w\-äöüÄÖÜß]/g, '_');
|
|
409
|
+
};
|
|
410
|
+
iNa.addEventListener('click', function (ev) { ev.stopPropagation(); });
|
|
411
|
+
/* Beim Tippen zieht die Kennung darunter mit — man sieht, was
|
|
412
|
+
entsteht, bevor man das Feld verlaesst. */
|
|
413
|
+
iNa.addEventListener('input', function () {
|
|
414
|
+
var v = bereinigt(iNa.value) || s.n;
|
|
415
|
+
nameVoll.textContent = vorspann + v;
|
|
416
|
+
nameVoll.title = vorspann + v;
|
|
417
|
+
});
|
|
418
|
+
iNa.addEventListener('change', function () {
|
|
419
|
+
var neuN = bereinigt(iNa.value);
|
|
420
|
+
if (!neuN) { iNa.value = s.n; return; }
|
|
421
|
+
if (neuN !== s.n && e.states.some(function (x) { return x !== s && x.n === neuN; })) {
|
|
422
|
+
iNa.value = s.n;
|
|
423
|
+
iNa.title = tr('detail.nameTaken', neuN);
|
|
424
|
+
iNa.classList.add('miss');
|
|
425
|
+
nameVoll.textContent = vorspann + s.n;
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
s.n = neuN;
|
|
429
|
+
neu();
|
|
430
|
+
});
|
|
431
|
+
zeile(tr('detail.name'), mitPlatz(nameStapel));
|
|
263
432
|
}
|
|
264
433
|
|
|
265
434
|
/* --- Quelle lesen + JSON-Feld --- */
|
|
266
435
|
var qf = el('div', 'fields');
|
|
267
436
|
|
|
268
437
|
var selR = el('select', 'tx');
|
|
269
|
-
|
|
438
|
+
/* 420 statt 260 px: gekuerzt wird hinten, und dort steht, was den
|
|
439
|
+
Punkt unterscheidet — `…3D_Drucker.` ohne `cmnd.POWER` sagte nichts
|
|
440
|
+
(Ricardo, 19.09.2026). `maxWidth` haelt es in schmalen Fenstern. */
|
|
441
|
+
selR.style.width = '420px';
|
|
442
|
+
selR.style.maxWidth = '100%';
|
|
270
443
|
selR.appendChild(opt('', quellen.length ? tr('detail.pickOne') : tr('detail.noSource')));
|
|
271
444
|
quellenIn(selR, quellen);
|
|
272
445
|
if (s.srcR && quellen.indexOf(s.srcR) === -1) { selR.appendChild(opt(s.srcR, s.srcR + ' ' + tr('detail.elsewhere'))); }
|
|
273
446
|
selR.appendChild(opt('__frei__', tr('detail.otherObject')));
|
|
274
447
|
selR.value = s.srcR || '';
|
|
275
|
-
|
|
448
|
+
/* Ein Knopf darf ohne Lesequelle sein - dann ist das leere Feld kein
|
|
449
|
+
Fehler und wird nicht rot (Ricardo, 19.09.2026). */
|
|
450
|
+
if (!s.srcR && !knopfZeile(s)) { selR.className += ' miss'; }
|
|
276
451
|
selR.addEventListener('click', function (ev) { ev.stopPropagation(); });
|
|
277
452
|
selR.addEventListener('change', function () {
|
|
278
453
|
if (selR.value === '__frei__') { s.frei = true; s.srcR = ''; }
|
|
@@ -293,7 +468,6 @@ export function detailZeile(e, s, idx) {
|
|
|
293
468
|
neu();
|
|
294
469
|
});
|
|
295
470
|
var lR = el('label', 'fld');
|
|
296
|
-
lR.appendChild(el('span', null, tr('detail.datapoint')));
|
|
297
471
|
lR.appendChild(selR);
|
|
298
472
|
qf.appendChild(lR);
|
|
299
473
|
|
|
@@ -338,10 +512,31 @@ export function detailZeile(e, s, idx) {
|
|
|
338
512
|
zeile('Objekt-ID', iFrei);
|
|
339
513
|
}
|
|
340
514
|
|
|
515
|
+
/* --- Leseformel ---
|
|
516
|
+
|
|
517
|
+
Direkt unter „liest aus“, wie die Schreibformel unter „schreibt auf“:
|
|
518
|
+
jede Formel steht bei der Quelle, fuer die sie rechnet (Ricardo,
|
|
519
|
+
19.09.2026). */
|
|
520
|
+
var fb = el('div');
|
|
521
|
+
var iF = el('input', 'tx w');
|
|
522
|
+
iF.type = 'text';
|
|
523
|
+
iF.value = s.f || '';
|
|
524
|
+
iF.placeholder = tr('detail.formulaPlaceholder');
|
|
525
|
+
iF.addEventListener('click', function (ev) { ev.stopPropagation(); });
|
|
526
|
+
iF.addEventListener('change', function () { s.f = iF.value.trim(); neu(); });
|
|
527
|
+
fb.appendChild(iF);
|
|
528
|
+
/* Der Hinweis gilt nur fuer Schaltpunkte. Bei einem Spannungswert
|
|
529
|
+
stand er auch — und ergab dort keinen Sinn. */
|
|
530
|
+
if ((s.typ || '') === 'boolean') {
|
|
531
|
+
fb.appendChild(el('div', 'sugg', tr('detail.formulaHint')));
|
|
532
|
+
}
|
|
533
|
+
zeigeBeide(zeile(tr('detail.formula'), fb), ['f']);
|
|
534
|
+
|
|
341
535
|
/* --- Quelle schreiben --- */
|
|
342
536
|
var wf = el('div', 'fields');
|
|
343
537
|
var selW = el('select', 'tx');
|
|
344
|
-
selW.style.width = '
|
|
538
|
+
selW.style.width = '420px';
|
|
539
|
+
selW.style.maxWidth = '100%';
|
|
345
540
|
/* Leer heisst „schreibt nicht" - und muss auch so heissen. Frueher
|
|
346
541
|
stand hier „dieselbe wie lesen", also das Gegenteil dessen, was
|
|
347
542
|
passierte (Ricardo, 25.08.2026). Wer wirklich auf die Lesequelle
|
|
@@ -355,34 +550,40 @@ export function detailZeile(e, s, idx) {
|
|
|
355
550
|
selW.addEventListener('click', function (ev) { ev.stopPropagation(); });
|
|
356
551
|
selW.addEventListener('change', function () {
|
|
357
552
|
s.srcW = (selW.value === '=lesen') ? (s.srcR || '') : selW.value;
|
|
553
|
+
/* Wer hier waehlt, hat die Schreibrichtung entschieden. `wr` stammt
|
|
554
|
+
sonst vom Quellobjekt (`common.write`) und hielt die Zeile fuer den
|
|
555
|
+
Detektor beschreibbar, auch nach „nicht schreiben" — angelegt wird
|
|
556
|
+
aber nach `srcW` (`write: !!s.srcW`). Die Vorschau sagte also
|
|
557
|
+
„Platz bleibt frei", der fertige Alias haette ihn bekommen
|
|
558
|
+
(Ricardo, 19.09.2026, an 3D_Drucker tele.SENSOR). */
|
|
559
|
+
s.wr = !!s.srcW;
|
|
358
560
|
neu();
|
|
359
561
|
});
|
|
360
562
|
var lW = el('label', 'fld');
|
|
361
|
-
lW.appendChild(el('span', null, 'Datenpunkt'));
|
|
362
563
|
lW.appendChild(selW);
|
|
363
564
|
wf.appendChild(lW);
|
|
364
565
|
/* Der Hinweis gilt nur, wenn die beiden Quellen wirklich
|
|
365
566
|
auseinandergehen - bei gleicher Quelle ist nichts getrennt. */
|
|
366
|
-
|
|
367
|
-
|
|
567
|
+
/* Unter die Kennung, nicht daneben: seit die Auswahl 420 px breit ist,
|
|
568
|
+
brach der Satz um und schob sich mit einer Luecke zwischen Auswahl und
|
|
569
|
+
Kennung (Ricardo, 19.09.2026). */
|
|
570
|
+
var wStapel = mitVollId(wf, s.srcW, zielMarke(s, 'w'));
|
|
571
|
+
/* Nur-lesen-Platz mit Schreibziel: der Detektor sieht es bei der
|
|
572
|
+
Vorgaberolle nicht so streng, gemeint ist es aber so — ein Messwert,
|
|
573
|
+
den jemand setzt, geht ans Geraet. */
|
|
574
|
+
var platzDef = null;
|
|
575
|
+
if (mPl && eigenerPlatz) {
|
|
576
|
+
mPl.states.forEach(function (x) { if (!platzDef && x.name === eigenerPlatz) { platzDef = x; } });
|
|
368
577
|
}
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
/* --- Leseformel --- */
|
|
372
|
-
var fb = el('div');
|
|
373
|
-
var iF = el('input', 'tx w');
|
|
374
|
-
iF.type = 'text';
|
|
375
|
-
iF.value = s.f || '';
|
|
376
|
-
iF.placeholder = tr('detail.formulaPlaceholder');
|
|
377
|
-
iF.addEventListener('click', function (ev) { ev.stopPropagation(); });
|
|
378
|
-
iF.addEventListener('change', function () { s.f = iF.value.trim(); neu(); });
|
|
379
|
-
fb.appendChild(iF);
|
|
380
|
-
/* Der Hinweis gilt nur fuer Schaltpunkte. Bei einem Spannungswert
|
|
381
|
-
stand er auch — und ergab dort keinen Sinn. */
|
|
382
|
-
if ((s.typ || '') === 'boolean') {
|
|
383
|
-
fb.appendChild(el('div', 'sugg', tr('detail.formulaHint')));
|
|
578
|
+
if (platzDef && platzDef.write === false && s.srcW) {
|
|
579
|
+
wStapel.appendChild(el('div', 'aside w', tr('detail.readOnlySlotWrites', platzDef.name)));
|
|
384
580
|
}
|
|
385
|
-
|
|
581
|
+
/* Nur, wo wirklich woanders gelesen wird - ohne Lesequelle liest die
|
|
582
|
+
Zeile nirgends, und der Satz stimmte nicht. */
|
|
583
|
+
if (s.srcR && s.srcW && s.srcW !== s.srcR) {
|
|
584
|
+
wStapel.appendChild(el('div', 'sugg', tr('detail.separateHint')));
|
|
585
|
+
}
|
|
586
|
+
zeigeBeide(zeile(tr('detail.writesTo'), wStapel), ['srcW']);
|
|
386
587
|
|
|
387
588
|
if (s.srcW) {
|
|
388
589
|
var wb = el('div');
|
|
@@ -399,17 +600,7 @@ export function detailZeile(e, s, idx) {
|
|
|
399
600
|
|
|
400
601
|
/* --- Rolle, mit Begründung --- */
|
|
401
602
|
var rb = el('div');
|
|
402
|
-
|
|
403
|
-
als Vermerk am Gruppenkopf der Rollenliste. */
|
|
404
|
-
var belegtVon = {};
|
|
405
|
-
if (e.want) {
|
|
406
|
-
var fundR = erkenneEntwurf(e, e.want);
|
|
407
|
-
if (fundR.length) {
|
|
408
|
-
fundR[0].states.forEach(function (x) {
|
|
409
|
-
if (x.id) { belegtVon[x.name] = x.id.slice(e.kanal.length + 1); }
|
|
410
|
-
});
|
|
411
|
-
}
|
|
412
|
-
}
|
|
603
|
+
|
|
413
604
|
rb.appendChild(rollenFeld({
|
|
414
605
|
wert: s.role,
|
|
415
606
|
muster: e.want,
|
|
@@ -422,6 +613,9 @@ export function detailZeile(e, s, idx) {
|
|
|
422
613
|
die Rolle erneut wechselt. */
|
|
423
614
|
beiWahl: function (r) {
|
|
424
615
|
s.role = r;
|
|
616
|
+
/* Eine Rolle von Hand ersetzt eine fruehere Platzwahl — sonst
|
|
617
|
+
stuende ein Hinweis zu einem Platz da, den keiner mehr will. */
|
|
618
|
+
delete s.platzWahl;
|
|
425
619
|
/* Nur wo die Rolle im Muster genau einen Datentyp zulaesst, gibt
|
|
426
620
|
es etwas vorzugeben. Trifft sie mehrere Plaetze mit
|
|
427
621
|
verschiedenen Typen - wie die Fahrtrichtung boolean und number -
|
|
@@ -437,22 +631,41 @@ export function detailZeile(e, s, idx) {
|
|
|
437
631
|
var treffer = platzFuerRolle(e.want, s.role, s.typ);
|
|
438
632
|
var erlaubt = typenFuerRolle(e.want, s.role);
|
|
439
633
|
var hin = el('div', 'sugg');
|
|
634
|
+
/* Hat den Platz schon eine ANDERE Zeile, hilft weder der Typ noch die
|
|
635
|
+
Rolle — es gibt ihn nur einmal. Am KeinJson-Testgeraet hielt
|
|
636
|
+
`cmnd_POWER` SET, und unter `stat_POWER` (Rolle `switch` von
|
|
637
|
+
mqtt-client, Typ string) stand „SET verlangt boolean … so faellt der
|
|
638
|
+
Punkt aus dem Muster" — als muesste man nur den Typ stellen. Richtig
|
|
639
|
+
waere ACTUAL gewesen (Ricardo, 19.09.2026). */
|
|
640
|
+
var besetztVon = treffer && !treffer.multiple && belegtVon[treffer.name] &&
|
|
641
|
+
belegtVon[treffer.name] !== s.n ? belegtVon[treffer.name] : null;
|
|
440
642
|
if (treffer) {
|
|
441
643
|
hin.appendChild(document.createTextNode(tr('pattern.fitsOn')));
|
|
442
644
|
hin.appendChild(el('b', null, String(treffer.role)));
|
|
443
645
|
hin.appendChild(document.createTextNode(tr('pattern.toSlot', treffer.name, musterName(e.want) || e.want)));
|
|
444
|
-
if (erlaubt.length) {
|
|
646
|
+
if (!besetztVon && erlaubt.length) {
|
|
445
647
|
hin.appendChild(document.createTextNode(
|
|
446
648
|
tr('pattern.expectsType', erlaubt.join(tr('pattern.typeOr')))));
|
|
447
649
|
}
|
|
448
|
-
} else {
|
|
650
|
+
} else if (!eigenerPlatz) {
|
|
449
651
|
hin.textContent = tr('pattern.noPlaceLong', musterName(e.want) || e.want);
|
|
450
652
|
}
|
|
451
|
-
|
|
653
|
+
/* Findet die Rollenliste nichts, hat die Zeile aber doch einen Platz,
|
|
654
|
+
steht hier nichts: das sagt schon „Platz im Muster“ oben. Der Fall
|
|
655
|
+
ist der Auffangplatz ACTUAL bei `info` — er verlangt keine Rolle und
|
|
656
|
+
steht deshalb in keiner Rollenliste. Hier stand vorher „kein Platz im
|
|
657
|
+
Information-Muster … Alexa & Co. zählen ihn separat“, direkt unter
|
|
658
|
+
einem Feld, das ACTUAL zeigte (Ricardo, 19.09.2026). */
|
|
659
|
+
if (hin.childNodes.length) { rb.appendChild(hin); }
|
|
452
660
|
/* Passt der Typ nicht zum Platz, faellt der Punkt aus dem Muster -
|
|
453
661
|
lautlos, denn die Rolle stimmt ja. Genau das ist am 11.09.2026
|
|
454
662
|
an einem Tasmota-Stromzaehler passiert (I33). */
|
|
455
|
-
|
|
663
|
+
/* Gelb wie die Typ-Warnung: in beiden Faellen bekommt die Zeile keinen
|
|
664
|
+
Platz. Als grauer Nachsatz ging es unter (Ricardo, 19.09.2026). */
|
|
665
|
+
if (besetztVon) {
|
|
666
|
+
rb.appendChild(el('div', 'aside w', tr('pattern.slotHeldBy', treffer.name, besetztVon)));
|
|
667
|
+
}
|
|
668
|
+
if (treffer && !besetztVon && s.typ && erlaubt.length && erlaubt.indexOf(s.typ) === -1) {
|
|
456
669
|
rb.appendChild(el('div', 'aside w',
|
|
457
670
|
tr('pattern.typeMismatch', treffer.name, erlaubt.join(tr('pattern.typeOr')), s.typ)));
|
|
458
671
|
}
|
|
@@ -523,48 +736,56 @@ export function detailZeile(e, s, idx) {
|
|
|
523
736
|
|
|
524
737
|
/* --- Rohwert und Ergebnis --- */
|
|
525
738
|
var a = wertVon(s);
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
wbox.appendChild(el('span', 'chip bad', tr('detail.sourceGone')));
|
|
532
|
-
zeile(tr('detail.attention'), wbox);
|
|
739
|
+
/* Ein Knopf ohne Lesequelle hat nichts anzuzeigen - das ist gewollt
|
|
740
|
+
und kein „liefert nichts" in Rot. */
|
|
741
|
+
if (!s.srcR && knopfZeile(s)) {
|
|
742
|
+
zeile(tr('detail.yields'), el('span', 'chip mut', tr('detail.buttonNoRead')));
|
|
743
|
+
a = null;
|
|
533
744
|
}
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
if (a.ok) {
|
|
543
|
-
var t = typeof a.val;
|
|
544
|
-
var passt = !s.typ || s.typ === t || s.typ === 'mixed';
|
|
545
|
-
box.appendChild(el('span', 'chip ' + (passt ? 'ok' : 'warn'),
|
|
546
|
-
(passt ? '✓ ' : '≠ ') + (t === 'number' ? 'Zahl' : (t === 'boolean' ? 'boolean' : t))));
|
|
547
|
-
if (a.gewandelt) {
|
|
548
|
-
box.appendChild(document.createTextNode(' '));
|
|
549
|
-
box.appendChild(el('span', 'chip mut', tr('detail.converted')));
|
|
550
|
-
}
|
|
551
|
-
if (!passt) {
|
|
552
|
-
box.appendChild(document.createTextNode(' '));
|
|
553
|
-
box.appendChild(el('span', 'chip warn', tr('detail.expects', s.typ)));
|
|
745
|
+
if (a) {
|
|
746
|
+
zeile(tr('detail.rawValue'), a.roh === undefined ? tr('detail.rawNothing')
|
|
747
|
+
: (typeof a.roh === 'object' ? JSON.stringify(a.roh) : String(a.roh)), 'raw');
|
|
748
|
+
|
|
749
|
+
if (s.srcR && !S.objects[s.srcR]) {
|
|
750
|
+
var wbox = el('div');
|
|
751
|
+
wbox.appendChild(el('span', 'chip bad', tr('detail.sourceGone')));
|
|
752
|
+
zeile(tr('detail.attention'), wbox);
|
|
554
753
|
}
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
754
|
+
|
|
755
|
+
var box = el('div');
|
|
756
|
+
var rv = el('span', 'res', a.ok
|
|
757
|
+
? (typeof a.val === 'object' ? JSON.stringify(a.val) : String(a.val)) + (s.unit ? ' ' + s.unit : '')
|
|
758
|
+
: a.txt);
|
|
759
|
+
if (!a.ok) { rv.style.color = 'var(--bad)'; }
|
|
760
|
+
box.appendChild(rv);
|
|
761
|
+
box.appendChild(document.createTextNode(' '));
|
|
762
|
+
if (a.ok) {
|
|
763
|
+
var t = typeof a.val;
|
|
764
|
+
var passt = !s.typ || s.typ === t || s.typ === 'mixed';
|
|
765
|
+
box.appendChild(el('span', 'chip ' + (passt ? 'ok' : 'warn'),
|
|
766
|
+
(passt ? '✓ ' : '≠ ') + (t === 'number' ? tr('detail.typeNumber') : t)));
|
|
767
|
+
if (a.gewandelt) {
|
|
768
|
+
box.appendChild(document.createTextNode(' '));
|
|
769
|
+
box.appendChild(el('span', 'chip mut', tr('detail.converted')));
|
|
770
|
+
}
|
|
771
|
+
if (!passt) {
|
|
772
|
+
box.appendChild(document.createTextNode(' '));
|
|
773
|
+
box.appendChild(el('span', 'chip warn', tr('detail.expects', s.typ)));
|
|
774
|
+
}
|
|
775
|
+
var ist = s.urId ? S.werte[s.urId] : null;
|
|
776
|
+
if (ist && ist.val !== undefined && ist.val !== null) {
|
|
777
|
+
var gleich = String(ist.val) === String(a.val);
|
|
778
|
+
box.appendChild(document.createTextNode(' '));
|
|
779
|
+
box.appendChild(el('span', 'chip ' + (gleich ? 'ok' : 'warn'),
|
|
780
|
+
gleich ? tr('detail.matchesState') : tr('detail.stateSays', String(ist.val))));
|
|
781
|
+
}
|
|
782
|
+
} else if (a.fehler) {
|
|
783
|
+
box.appendChild(el('span', 'chip bad', tr('detail.formulaBroken')));
|
|
784
|
+
} else {
|
|
785
|
+
box.appendChild(el('span', 'chip bad', tr('detail.yieldsNothing')));
|
|
561
786
|
}
|
|
562
|
-
|
|
563
|
-
box.appendChild(el('span', 'chip bad', tr('detail.formulaBroken')));
|
|
564
|
-
} else {
|
|
565
|
-
box.appendChild(el('span', 'chip bad', tr('detail.yieldsNothing')));
|
|
787
|
+
zeile(tr('detail.yields'), box);
|
|
566
788
|
}
|
|
567
|
-
zeile(tr('detail.yields'), box);
|
|
568
789
|
|
|
569
790
|
if (s.manuell) {
|
|
570
791
|
var db = el('div');
|