iobroker.alias-workbench 0.9.10 → 0.9.12
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 +66 -4
- package/admin/css/werkbank.css +122 -6
- package/admin/js/baum.js +8 -0
- package/admin/js/detail.js +63 -10
- package/admin/js/entwurf.js +416 -190
- package/admin/js/enums.js +2 -2
- package/admin/js/ergebnis.js +156 -23
- package/admin/js/erkennung.js +7 -0
- package/admin/js/mqtt.js +75 -5
- package/admin/js/musterwahl.js +196 -29
- package/admin/js/objekte.js +44 -0
- package/admin/js/pruefungen.js +37 -8
- package/admin/js/schreiben.js +150 -74
- package/admin/js/verwendung.js +478 -0
- package/admin/js/vorlagen.js +75 -6
- package/admin/js/vorlagenblatt.js +134 -17
- package/admin/js/vorschlagen.js +50 -17
- package/admin/js/werte.js +96 -6
- package/admin/js/zielleiste.js +8 -2
- package/admin/js/zuordnung.js +43 -13
- package/admin/js/zustand.js +3 -0
- package/admin/js/zustandsliste.js +56 -20
- package/admin/sprachen/de.json +64 -10
- package/admin/sprachen/en.json +64 -10
- package/admin/tab.html +44 -44
- package/admin/vorlagen/messpunkt.json +1 -0
- package/admin/vorlagen/tasmota-lampe.json +1 -0
- package/admin/vorlagen/tasmota-mehrfach.json +1 -0
- package/admin/vorlagen/tasmota-steckdose.json +1 -0
- package/io-package.json +38 -29
- package/package.json +1 -1
|
@@ -11,7 +11,7 @@ import { katalogFehlt } from './katalog.js';
|
|
|
11
11
|
import { funktionsAuswahl, kennungZuFunktion } from './aufzaehlungen.js';
|
|
12
12
|
import { musterVon, platzFuerRolle, typenFuerRolle } from './erkennung.js';
|
|
13
13
|
import { musterZeile, musterName } from './musternamen.js';
|
|
14
|
-
import { ladeVorlagen, aendereVorlagen, setzeMeta, pruefeVorlage, hinweisTaugt, INSTANZ_ID } from './vorlagen.js';
|
|
14
|
+
import { ladeVorlagen, aendereVorlagen, setzeMeta, pruefeVorlage, wendeAn, hinweisTaugt, INSTANZ_ID } from './vorlagen.js';
|
|
15
15
|
import { kindZustaende, hatPunkt, feldAusFormel } from './werte.js';
|
|
16
16
|
import { opt } from './entwurf.js';
|
|
17
17
|
import { zeichneErgebnis } from './ergebnis.js';
|
|
@@ -287,7 +287,17 @@ export function zeichneVorlagenBlatt(host) {
|
|
|
287
287
|
Musterschluessel. musterwahl.js loest es seit laengerem so. */
|
|
288
288
|
(D ? Object.keys(D.patterns).sort() : []).forEach(function (m) {
|
|
289
289
|
var typ = D.patterns[m].type || m;
|
|
290
|
-
|
|
290
|
+
/* Beschriftet wird nach dem TYP, nicht nach dem Musterschluessel.
|
|
291
|
+
|
|
292
|
+
Der Wert stand seit dem 11.09.2026 richtig da, die Beschriftung
|
|
293
|
+
nicht: `musterZeile(m)` schlug den Schluessel nach, und fuer die
|
|
294
|
+
drei Muster, die anders heissen als ihr Typ (`blinds`,
|
|
295
|
+
`mediaPlayer`, `levelSlider`), kennt der Admin keinen Beinamen.
|
|
296
|
+
Im Feld stand am Rollladen deshalb „blinds" statt „Jalousien ·
|
|
297
|
+
blind" - der Musterschluessel, den der Nutzer nirgends sonst
|
|
298
|
+
sieht. Die Musterwahl (musterwahl.js) geht seit jeher ueber den
|
|
299
|
+
Typ. Gefunden produktiv am 15.09.2026 (I37). */
|
|
300
|
+
var o = opt(typ, musterZeile(typ));
|
|
291
301
|
if (typ === v.geraetetyp) { o.selected = true; }
|
|
292
302
|
st.appendChild(o);
|
|
293
303
|
});
|
|
@@ -465,6 +475,18 @@ export function zeichneVorlagenBlatt(host) {
|
|
|
465
475
|
? probelauf(ohneUnterstrich(v), v._quelle === 'benutzer') : [];
|
|
466
476
|
var gew = treffer.filter(function (t) { return t.gewinnt; }).length;
|
|
467
477
|
ph2.appendChild(el('span', 'chip ' + (gew ? 'ok' : 'warn'), tr('tpls.hits', treffer.length, gew)));
|
|
478
|
+
/* „Getroffen" heisst nur, dass die Erkennung anspringt. Wie oft die
|
|
479
|
+
Vorlage danach gar nicht antreten kann, weil eine Pflichtzeile ihre
|
|
480
|
+
Quelle nicht findet, gehoert daneben — das ist die Zahl, an der man
|
|
481
|
+
beim Bauen einer Vorlage merkt, dass ihre Erkennung breiter greift
|
|
482
|
+
als ihre Zeilen. Neutral gefaerbt: eine Spezialvorlage SOLL auf die
|
|
483
|
+
meisten Geraete nicht passen. */
|
|
484
|
+
var passtNicht = treffer.filter(function (t) { return !t.greift; }).length;
|
|
485
|
+
if (passtNicht) {
|
|
486
|
+
var cl = el('span', 'chip mut', tr('tpls.fitsNot', passtNicht));
|
|
487
|
+
cl.title = tr('tpls.needsRowHint');
|
|
488
|
+
ph2.appendChild(cl);
|
|
489
|
+
}
|
|
468
490
|
pk.appendChild(ph2);
|
|
469
491
|
var pb2 = el('div');
|
|
470
492
|
pb2.style.padding = '9px 12px 11px';
|
|
@@ -473,14 +495,35 @@ export function zeichneVorlagenBlatt(host) {
|
|
|
473
495
|
} else {
|
|
474
496
|
treffer.slice(0, 40).forEach(function (t) {
|
|
475
497
|
var r = el('div', 'slot');
|
|
476
|
-
|
|
498
|
+
/* Die Kennung darf schrumpfen, die Marke rechts nicht: ihr Text
|
|
499
|
+
ist nicht umbrechbar (`.chip` steht auf nowrap) und wuerde in
|
|
500
|
+
einer festen Spalte abgeschnitten. */
|
|
501
|
+
r.style.gridTemplateColumns = 'minmax(0,1fr) 72px 64px auto';
|
|
477
502
|
var idl = el('span', 'sn', t.id);
|
|
478
503
|
idl.style.fontFamily = 'var(--mono)';
|
|
504
|
+
idl.style.wordBreak = 'break-all';
|
|
479
505
|
r.appendChild(idl);
|
|
480
506
|
r.appendChild(el('span', 'rx', tr('tpls.evidence', t.eigen.belege)));
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
507
|
+
/* Wie viele Zeilen dort wirklich entstehen. Die Belege sagen, wie
|
|
508
|
+
gut die Erkennung greift; erst die Zeilen sagen, was dabei
|
|
509
|
+
herauskommt. */
|
|
510
|
+
r.appendChild(el('span', 'rx',
|
|
511
|
+
t.zeilen === 1 ? tr('tpls.rowBuilt') : tr('tpls.rowsBuilt', t.zeilen)));
|
|
512
|
+
/* Drei Ausgaenge, nicht zwei: gewonnen und getragen, duenn
|
|
513
|
+
gewonnen, verloren. Der mittlere sah ohne eigene Marke aus wie
|
|
514
|
+
ein Erfolg. */
|
|
515
|
+
if (!t.greift) {
|
|
516
|
+
/* Passt hier nicht — und es steht dabei, woran es liegt. Ohne
|
|
517
|
+
den Namen der Zeile raet man, welches Feld am Geraet fehlt. */
|
|
518
|
+
var cn = el('span', 'chip bad', t.fehlendeZeile
|
|
519
|
+
? tr('tpls.needsRow', t.fehlendeZeile) : tr('tpls.buildsNone'));
|
|
520
|
+
cn.title = tr('tpls.needsRowHint');
|
|
521
|
+
r.appendChild(cn);
|
|
522
|
+
} else {
|
|
523
|
+
r.appendChild(el('span', 'chip ' + (t.gewinnt ? 'ok' : 'bad'),
|
|
524
|
+
t.gewinnt ? (t.gegner ? tr('tpls.beats', textIn(t.gegner.vorlage.name, sprache)) : tr('tpls.onlyOne'))
|
|
525
|
+
: tr('tpls.losesTo', textIn(t.gegner.vorlage.name, sprache))));
|
|
526
|
+
}
|
|
484
527
|
pb2.appendChild(r);
|
|
485
528
|
});
|
|
486
529
|
if (treffer.length > 40) {
|
|
@@ -1540,16 +1583,41 @@ var probeGeholt = {};
|
|
|
1540
1583
|
|
|
1541
1584
|
function probeWerteHolen(v, danach) {
|
|
1542
1585
|
var erk = v.erkennung || {};
|
|
1543
|
-
if (!erk.inhalt) { return false; }
|
|
1544
1586
|
var ph = v.mehrfach ? (v.mehrfach.platzhalter || '%N%') : null;
|
|
1587
|
+
|
|
1588
|
+
/* Nicht nur die Erkennungspunkte, sondern auch die, aus denen die
|
|
1589
|
+
ZEILEN lesen — samt ihrer Ersatzwege.
|
|
1590
|
+
|
|
1591
|
+
Sonst urteilt der Probelauf ueber Felder in Werten, die er gar nicht
|
|
1592
|
+
hat: am `Brenner` lag `tele.STATE` im Speicher und `tele.SENSOR`
|
|
1593
|
+
nicht, und derselbe Probelauf meldete je nachdem, was man vorher
|
|
1594
|
+
angeklickt hatte, „1 statt 11" oder „1 statt 20" (Ricardo,
|
|
1595
|
+
14.09.2026). Eine Zahl, die von der Vorgeschichte abhaengt, ist
|
|
1596
|
+
keine. Hier soll dasselbe herauskommen wie beim „Alias anlegen",
|
|
1597
|
+
und dort sind alle Werte des Geraets geladen. */
|
|
1598
|
+
var pfade = Object.keys(erk.inhalt || {});
|
|
1599
|
+
(v.zustaende || []).forEach(function (z) {
|
|
1600
|
+
if (z.absolut) { return; }
|
|
1601
|
+
if (z.lesen) { pfade.push(z.lesen); }
|
|
1602
|
+
var sonst = z.lesenSonst;
|
|
1603
|
+
if (!sonst) { return; }
|
|
1604
|
+
(Array.isArray(sonst) ? sonst : [sonst]).forEach(function (x) {
|
|
1605
|
+
var pf = (typeof x === 'string') ? x : x.punkt;
|
|
1606
|
+
if (pf) { pfade.push(pf); }
|
|
1607
|
+
});
|
|
1608
|
+
});
|
|
1609
|
+
pfade = pfade.filter(function (x, i, a) { return x && a.indexOf(x) === i; });
|
|
1610
|
+
if (!pfade.length) { return false; }
|
|
1611
|
+
|
|
1545
1612
|
var fehlt = [];
|
|
1546
1613
|
kandidatenFuer(v).forEach(function (kanal) {
|
|
1547
|
-
|
|
1614
|
+
pfade.forEach(function (p0) {
|
|
1548
1615
|
var pfad = ph ? String(p0).split(ph).join('1') : p0;
|
|
1549
1616
|
var id = hatPunkt(kanal, pfad);
|
|
1550
1617
|
if (id && S.werte[id] === undefined && !probeGeholt[id]) { fehlt.push(id); }
|
|
1551
1618
|
});
|
|
1552
1619
|
});
|
|
1620
|
+
fehlt = fehlt.filter(function (x, i, a) { return a.indexOf(x) === i; });
|
|
1553
1621
|
if (!fehlt.length) { return false; }
|
|
1554
1622
|
var offen = fehlt.length;
|
|
1555
1623
|
fehlt.forEach(function (id) {
|
|
@@ -1581,19 +1649,57 @@ export function probelauf(vorlage, alsEigene) {
|
|
|
1581
1649
|
|
|
1582
1650
|
/* Wer gewinnt hier heute, mit dem Bestand wie er ist? Beim
|
|
1583
1651
|
Aktualisieren ist das oft die Vorlage selbst — dann wechselt
|
|
1584
|
-
nichts, auch wenn sie den Vergleich gegen alle anderen gewinnt.
|
|
1585
|
-
|
|
1652
|
+
nichts, auch wenn sie den Vergleich gegen alle anderen gewinnt.
|
|
1653
|
+
|
|
1654
|
+
Die Punktbeste ist nicht zwingend die Gewinnerin: greift sie am
|
|
1655
|
+
Geraet nicht, ruecken die naechsten nach — genau wie in
|
|
1656
|
+
`vorschlag`. Vorher stand hier nur DIE eine punktbeste andere, und
|
|
1657
|
+
griff die nicht, erklaerte sich die gepruefte Vorlage zur
|
|
1658
|
+
Siegerin. An `PC_Bastelzimmer` meldete deshalb „Reiner Messpunkt"
|
|
1659
|
+
einen Sieg, obwohl `tasmota-steckdose` dazwischenliegt und dort
|
|
1660
|
+
wirklich greift (Ricardo, 14.09.2026). */
|
|
1661
|
+
var rang = [];
|
|
1586
1662
|
S.VORLAGEN.forEach(function (v) {
|
|
1587
1663
|
var x = pruefeVorlage(v, p);
|
|
1588
|
-
if (
|
|
1589
|
-
if (!jetzt || x.punkte > jetzt.punkte) { jetzt = x; }
|
|
1590
|
-
if (v.id !== neu.id && (!andere || x.punkte > andere.punkte)) { andere = x; }
|
|
1664
|
+
if (x.passt) { rang.push(x); }
|
|
1591
1665
|
});
|
|
1666
|
+
rang.sort(function (a, b) { return b.punkte - a.punkte; });
|
|
1667
|
+
var ersteGreifende = function (ausser) {
|
|
1668
|
+
for (var i = 0; i < rang.length; i++) {
|
|
1669
|
+
if (ausser && rang[i].vorlage.id === ausser) { continue; }
|
|
1670
|
+
var x = wendeAn(rang[i].vorlage, p, rang[i].gruende, rang[i].instanz, true);
|
|
1671
|
+
if (x && x.states.length && !x.pflichtFehlt) { return rang[i]; }
|
|
1672
|
+
}
|
|
1673
|
+
return null;
|
|
1674
|
+
};
|
|
1675
|
+
var jetzt = ersteGreifende(null);
|
|
1676
|
+
var andere = ersteGreifende(neu.id);
|
|
1677
|
+
/* Gewinnen ist nicht dasselbe wie greifen.
|
|
1678
|
+
|
|
1679
|
+
`pruefeVorlage` fragt, ob die genannten Punkte da sind; `wendeAn`
|
|
1680
|
+
braucht bestimmte Felder darin. Eine Vorlage, deren Zeilen
|
|
1681
|
+
`SML.Total_Summe` lesen, gewinnt an jeder Tasmota mit
|
|
1682
|
+
`tele.SENSOR` — und baut dort dann keine einzige Zeile.
|
|
1683
|
+
|
|
1684
|
+
Das laesst sich nicht vorhersagen, ohne zu raten. Also wird es
|
|
1685
|
+
gemessen: derselbe Aufruf, der beim Anlegen auch laeuft. Kostet
|
|
1686
|
+
nichts, weil die Kandidatenliste ohnehin schon durchgegangen
|
|
1687
|
+
wird (gefunden 14.09.2026, C3). */
|
|
1688
|
+
/* Mit `auchOhneTreffer`, damit auch der gescheiterte Fall zurueckkommt
|
|
1689
|
+
— sonst wuesste man nur „null", nicht warum. */
|
|
1690
|
+
var gebaut = wendeAn(neu, p, t.gruende, t.instanz, true);
|
|
1691
|
+
var greift = !!(gebaut && gebaut.states.length && !gebaut.pflichtFehlt);
|
|
1692
|
+
|
|
1693
|
+
/* Gewonnen hat nur, wer hier auch greift — und `andere` ist bereits
|
|
1694
|
+
die beste, die das tut. */
|
|
1695
|
+
var vorn = greift && (!andere || t.punkte > andere.punkte);
|
|
1696
|
+
|
|
1592
1697
|
raus.push({
|
|
1593
1698
|
id: p, eigen: t, gegner: andere,
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1699
|
+
greift: greift, fehlendeZeile: gebaut ? gebaut.pflichtFehlt : null,
|
|
1700
|
+
zeilen: gebaut ? gebaut.states.length : 0,
|
|
1701
|
+
gewinnt: vorn,
|
|
1702
|
+
wechsel: !!(jetzt && jetzt.vorlage.id !== neu.id && vorn)
|
|
1597
1703
|
});
|
|
1598
1704
|
});
|
|
1599
1705
|
return raus;
|
|
@@ -1738,7 +1844,17 @@ export function zeichneVorlagenDialog() {
|
|
|
1738
1844
|
"- keiner -", obwohl "blind" gesetzt war. */
|
|
1739
1845
|
(D ? Object.keys(D.patterns).sort() : []).forEach(function (m) {
|
|
1740
1846
|
var typ = D.patterns[m].type || m;
|
|
1741
|
-
|
|
1847
|
+
/* Beschriftet wird nach dem TYP, nicht nach dem Musterschluessel.
|
|
1848
|
+
|
|
1849
|
+
Der Wert stand seit dem 11.09.2026 richtig da, die Beschriftung
|
|
1850
|
+
nicht: `musterZeile(m)` schlug den Schluessel nach, und fuer die
|
|
1851
|
+
drei Muster, die anders heissen als ihr Typ (`blinds`,
|
|
1852
|
+
`mediaPlayer`, `levelSlider`), kennt der Admin keinen Beinamen.
|
|
1853
|
+
Im Feld stand am Rollladen deshalb „blinds" statt „Jalousien ·
|
|
1854
|
+
blind" - der Musterschluessel, den der Nutzer nirgends sonst
|
|
1855
|
+
sieht. Die Musterwahl (musterwahl.js) geht seit jeher ueber den
|
|
1856
|
+
Typ. Gefunden produktiv am 15.09.2026 (I37). */
|
|
1857
|
+
var o = opt(typ, musterZeile(typ));
|
|
1742
1858
|
if (typ === k.geraetetyp) { o.selected = true; }
|
|
1743
1859
|
st.appendChild(o);
|
|
1744
1860
|
});
|
|
@@ -2026,6 +2142,7 @@ export function zeichneVorlagenDialog() {
|
|
|
2026
2142
|
r.style.gridTemplateColumns = '1fr 150px 130px';
|
|
2027
2143
|
var idl = el('span', 'sn', t.id);
|
|
2028
2144
|
idl.style.fontFamily = 'var(--mono)';
|
|
2145
|
+
idl.style.wordBreak = 'break-all';
|
|
2029
2146
|
if (t.id === (z.e.kanal || '')) { idl.style.fontWeight = '700'; }
|
|
2030
2147
|
r.appendChild(idl);
|
|
2031
2148
|
r.appendChild(el('span', 'rx', tr('tpls.evidence', t.eigen.belege)));
|
package/admin/js/vorschlagen.js
CHANGED
|
@@ -96,7 +96,7 @@ function kernWoerter(name) {
|
|
|
96
96
|
ueberhaupt etwas anzubieten haette. */
|
|
97
97
|
function rateLage(e) {
|
|
98
98
|
var mu = musterVon(e.want);
|
|
99
|
-
if (!mu) { return
|
|
99
|
+
if (!mu) { return { grund: 'keinMuster', frei: [], handWeg: 0 }; }
|
|
100
100
|
|
|
101
101
|
/* 1. Was schon belegt ist, bleibt unangetastet - in beide
|
|
102
102
|
Richtungen: weder der Platz noch die Zeile. */
|
|
@@ -120,7 +120,11 @@ function rateLage(e) {
|
|
|
120
120
|
gesehen[pl.name] = true;
|
|
121
121
|
freiePlaetze.push(pl);
|
|
122
122
|
});
|
|
123
|
-
|
|
123
|
+
/* Die Namen der freien Plaetze werden auch dann gebraucht, wenn nichts
|
|
124
|
+
zu holen ist - die Meldung soll sagen, WAS frei bleibt, statt nur
|
|
125
|
+
„nichts vorzuschlagen". */
|
|
126
|
+
var freiNamen = freiePlaetze.map(function (pl) { return pl.name; });
|
|
127
|
+
if (!freiePlaetze.length) { return { grund: 'voll', frei: [], handWeg: 0 }; }
|
|
124
128
|
|
|
125
129
|
/* Ein Quellpunkt gehoert an genau eine Stelle.
|
|
126
130
|
|
|
@@ -142,30 +146,58 @@ function rateLage(e) {
|
|
|
142
146
|
if (st.srcW) { quelleBelegt[st.srcW] = true; }
|
|
143
147
|
});
|
|
144
148
|
|
|
149
|
+
/* Was von Hand entschieden ist, bleibt liegen (U15, Ricardo,
|
|
150
|
+
15.09.2026).
|
|
151
|
+
|
|
152
|
+
Vorher fand der zweite Druck den Musterplatz wieder frei - die Rolle
|
|
153
|
+
stand ja nicht mehr auf dem, was das Muster verlangt - und bog
|
|
154
|
+
DIESELBE Zeile zurueck: aus der Handentscheidung `level.speed` wurde
|
|
155
|
+
wieder das vermutete `level.mode.fan`, samt Marke. Die Zeile war
|
|
156
|
+
damit doppelt bezeichnet („geaendert" UND „vermutet"), was sich
|
|
157
|
+
ausschliesst: was frisch geraten ist, kann nicht vorher geaendert
|
|
158
|
+
worden sein.
|
|
159
|
+
|
|
160
|
+
Eine zweite Zeile daneben zu legen waere die Alternative gewesen -
|
|
161
|
+
dann haette derselbe Quellpunkt aber zwei Plaetze, und die achte
|
|
162
|
+
Pruefung („Kein Punkt doppelt") meldete es zu Recht. Also bleibt der
|
|
163
|
+
Platz frei. Die Meldung unter der Leiste sagt, dass und warum. */
|
|
164
|
+
var handWeg = 0;
|
|
145
165
|
var freieZeilen = e.states.filter(function (st) {
|
|
146
166
|
if (!st.n || !st.srcR || zeileBelegt[st.n]) { return false; }
|
|
147
167
|
if (quelleBelegt[st.srcR]) { return false; }
|
|
148
168
|
if (st.srcW && quelleBelegt[st.srcW]) { return false; }
|
|
169
|
+
if (st.geaendert) { handWeg++; return false; }
|
|
149
170
|
return true;
|
|
150
171
|
});
|
|
151
|
-
if (!freieZeilen.length) {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
/* Gibt es ueberhaupt etwas, das der Knopf vorschlagen koennte? Die
|
|
157
|
-
komplette Rechnung, nur ohne anzuwenden - eine grobe Vorpruefung
|
|
158
|
-
liess den Knopf stehen, obwohl die Schwelle hinterher alles verwarf
|
|
159
|
-
(Ricardos Thermostat: 3 Zeilen, 22 freie Plaetze, nichts passt).
|
|
160
|
-
Der Preis ist ein zusaetzlicher Bewertungslauf je Zeichnen; die
|
|
161
|
-
teuren Erkennungslaeufe macht das Zeichnen ohnehin mehrfach. */
|
|
162
|
-
export function rateMoeglich(e) {
|
|
163
|
-
return ratePlaetze(e, true) > 0;
|
|
172
|
+
if (!freieZeilen.length) {
|
|
173
|
+
return { grund: 'vergeben', frei: freiNamen, handWeg: handWeg };
|
|
174
|
+
}
|
|
175
|
+
return { grund: null, mu: mu, namenDa: namenDa, freiePlaetze: freiePlaetze,
|
|
176
|
+
freieZeilen: freieZeilen, frei: freiNamen, handWeg: handWeg };
|
|
164
177
|
}
|
|
165
178
|
|
|
179
|
+
/* Der Rateschritt - mit `trocken` rechnet er nur, ohne den Entwurf
|
|
180
|
+
anzufassen. Zurueck kommt, wie viele Plaetze belegt wurden (`anzahl`),
|
|
181
|
+
und wenn keiner: warum nicht.
|
|
182
|
+
|
|
183
|
+
`grund` ist einer von vier Faellen - `keinMuster`, `voll` (kein Platz
|
|
184
|
+
frei), `vergeben` (Plaetze frei, aber kein uebriger Punkt mehr da),
|
|
185
|
+
`keinTreffer` (beides da, nichts kam ueber die Schwelle). `frei` nennt
|
|
186
|
+
die Plaetze, die frei bleiben, `handWeg` die Zeilen, die als
|
|
187
|
+
Handentscheidung liegenblieben. Der Text dazu wird dort gebaut, wo die
|
|
188
|
+
Sprachdatei liegt (musterwahl.js); hier steht nur der Befund.
|
|
189
|
+
|
|
190
|
+
Der Trockenlauf rechnet alles bis zur Schwelle durch. Eine grobe
|
|
191
|
+
Vorpruefung genuegte nicht: sie liess den Knopf stehen, obwohl die
|
|
192
|
+
Schwelle hinterher alles verwarf (Ricardos Thermostat, 3 Zeilen, 22
|
|
193
|
+
freie Plaetze, nichts passt). Seit dem 15.09.2026 laeuft er nur noch
|
|
194
|
+
einmal je Zeichnen - vorher fragte erst `rateMoeglich`, ob der Knopf
|
|
195
|
+
ueberhaupt hingehoert, und der Klick rechnete dasselbe noch einmal. */
|
|
166
196
|
export function ratePlaetze(e, trocken) {
|
|
167
197
|
var lage = rateLage(e);
|
|
168
|
-
if (
|
|
198
|
+
if (lage.grund) {
|
|
199
|
+
return { anzahl: 0, grund: lage.grund, frei: lage.frei, handWeg: lage.handWeg };
|
|
200
|
+
}
|
|
169
201
|
var namenDa = lage.namenDa;
|
|
170
202
|
var freiePlaetze = lage.freiePlaetze, freieZeilen = lage.freieZeilen;
|
|
171
203
|
|
|
@@ -355,7 +387,8 @@ export function ratePlaetze(e, trocken) {
|
|
|
355
387
|
st.geratenPartner = partner.n;
|
|
356
388
|
}
|
|
357
389
|
});
|
|
358
|
-
return gesetzt
|
|
390
|
+
return { anzahl: gesetzt, grund: gesetzt ? null : 'keinTreffer',
|
|
391
|
+
frei: lage.frei, handWeg: lage.handWeg };
|
|
359
392
|
}
|
|
360
393
|
|
|
361
394
|
/* Eine einzelne Vermutung zuruecknehmen. */
|
package/admin/js/werte.js
CHANGED
|
@@ -166,7 +166,12 @@ export function zeileAusAlias(o, zeilenname) {
|
|
|
166
166
|
unit: c.unit || '',
|
|
167
167
|
states: c.states || undefined,
|
|
168
168
|
wr: !!c.write,
|
|
169
|
-
|
|
169
|
+
/* Ein Taster meldet nichts, und der Alias sagt es mit `read: false`.
|
|
170
|
+
Die Kennung steht trotzdem in `alias.id` — ioBroker verlangt sie —,
|
|
171
|
+
aber als Lesequelle gilt sie nicht: sonst zeigte „Alias bearbeiten"
|
|
172
|
+
eine Quelle, die „Alias anlegen" nie eingetragen hat, und beim
|
|
173
|
+
naechsten Aktualisieren kippte `read` still auf `true` zurueck. */
|
|
174
|
+
srcR: (c.read === false && q.einfach) ? '' : (q.read || ''),
|
|
170
175
|
srcW: schreibQuelle(o),
|
|
171
176
|
f: (typeof a.read === 'string') ? a.read : '',
|
|
172
177
|
fw: (typeof a.write === 'string') ? a.write : '',
|
|
@@ -280,7 +285,7 @@ export function auswerten(formel, roh) {
|
|
|
280
285
|
var gedauert = performance.now() - t0;
|
|
281
286
|
if (gedauert > GEDULD_MS) {
|
|
282
287
|
formelGesperrt[formel] = Math.round(gedauert);
|
|
283
|
-
console.warn('[alias-workbench]
|
|
288
|
+
console.warn('[alias-workbench] Formula blocked after ' + Math.round(gedauert) + ' ms: ' + formel);
|
|
284
289
|
return { ok: false, txt: tr('detail.formulaTooSlow', Math.round(gedauert)), fehler: true };
|
|
285
290
|
}
|
|
286
291
|
if (sch !== null) {
|
|
@@ -348,21 +353,58 @@ export function fmt(s) {
|
|
|
348
353
|
`JSON.parse(val)?.ENERGY?.Power ?? null`. Beide Schreibweisen muessen
|
|
349
354
|
als Feldzugriff durchgehen - sonst zeigt die Feldauswahl an einem
|
|
350
355
|
Punkt, den die Werkbank selbst gebaut hat, „eigene Formel". */
|
|
351
|
-
|
|
356
|
+
/* ---- Feldpfade: Anfang (die Vorpruefung schneidet diesen Block heraus) ---- */
|
|
357
|
+
|
|
358
|
+
/* Ein Schluessel darf nur dann hinter den Punkt, wenn er ein gueltiger
|
|
359
|
+
JS-Bezeichner ist. Sonst entsteht kein Zugriff, sondern etwas anderes:
|
|
360
|
+
aus `?.DS18B20-1` wird die Rechnung `?.DS18B20 - 1?.…` (ergibt NaN),
|
|
361
|
+
aus `?./dev/shm` und `?.1111740` wird ein Syntaxfehler. Gemessen an
|
|
362
|
+
einem Frigate-Telegramm: von 233 angebotenen Feldern liessen sich so
|
|
363
|
+
nur 27 benutzen. Deshalb Klammern, wo der Name keiner ist. */
|
|
364
|
+
var NAME_OK = /^[A-Za-z_$][\w$]*$/;
|
|
365
|
+
|
|
366
|
+
function feldSegment(name) {
|
|
367
|
+
if (NAME_OK.test(name)) { return '?.' + name; }
|
|
368
|
+
return "?.['" + String(name).replace(/\\/g, '\\\\').replace(/'/g, "\\'") + "']";
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/* Ein Wegstueck ist entweder `?.name` oder `?.['name']` — die zweite
|
|
372
|
+
Form auch mit doppelten Anfuehrungszeichen, falls sie jemand von Hand
|
|
373
|
+
so geschrieben hat. */
|
|
374
|
+
var FELDTEIL = "\\??\\.(?:[A-Za-z_$][\\w$]*|\\[(?:'(?:[^'\\\\]|\\\\.)*'|\"(?:[^\"\\\\]|\\\\.)*\")\\])";
|
|
375
|
+
var FELDPFAD = new RegExp('^JSON\\.parse\\(val\\)((?:' + FELDTEIL + ')+)(?:\\s*\\?\\?\\s*null)?$');
|
|
376
|
+
var FELDSTUECK = new RegExp(FELDTEIL, 'g');
|
|
352
377
|
|
|
353
378
|
export function feldAusFormel(f) {
|
|
354
379
|
var m = FELDPFAD.exec(f || '');
|
|
355
|
-
|
|
356
|
-
|
|
380
|
+
if (!m) { return ''; }
|
|
381
|
+
var teile = [];
|
|
382
|
+
var t;
|
|
383
|
+
FELDSTUECK.lastIndex = 0;
|
|
384
|
+
while ((t = FELDSTUECK.exec(m[1])) !== null) {
|
|
385
|
+
var s = t[0].slice(t[0].indexOf('.') + 1);
|
|
386
|
+
if (s.charAt(0) === '[') {
|
|
387
|
+
/* Nur die Fluchtzeichen zurueckdrehen, die feldSegment setzt. */
|
|
388
|
+
s = s.slice(2, -2).replace(/\\(.)/g, '$1');
|
|
389
|
+
/* Ein Punkt im Schluessel selbst laesst sich im Pfadformat von
|
|
390
|
+
`jsonFelder` nicht wieder auseinanderhalten — dann lieber
|
|
391
|
+
„eigene Formel" zeigen als einen Pfad, der nicht zurueckfuehrt. */
|
|
392
|
+
if (s.indexOf('.') > -1) { return ''; }
|
|
393
|
+
}
|
|
394
|
+
teile.push(s);
|
|
395
|
+
}
|
|
396
|
+
return teile.join('.');
|
|
357
397
|
}
|
|
358
398
|
|
|
359
399
|
/* Der abgesicherte Zugriff auf ein JSON-Feld. An einer Stelle, damit die
|
|
360
400
|
Vorlagen-Abkuerzung und die Feldauswahl im Detail dasselbe bauen. */
|
|
361
401
|
export function feldFormel(feld) {
|
|
362
402
|
if (!feld) { return ''; }
|
|
363
|
-
return 'JSON.parse(val)
|
|
403
|
+
return 'JSON.parse(val)' + String(feld).split('.').map(feldSegment).join('') + ' ?? null';
|
|
364
404
|
}
|
|
365
405
|
|
|
406
|
+
/* ---- Feldpfade: Ende ---- */
|
|
407
|
+
|
|
366
408
|
export function jsonFelder(id) {
|
|
367
409
|
var st = S.werte[id];
|
|
368
410
|
if (!st || typeof st.val !== 'string') { return null; }
|
|
@@ -380,3 +422,51 @@ export function jsonFelder(id) {
|
|
|
380
422
|
})(o, '', 0);
|
|
381
423
|
return raus;
|
|
382
424
|
}
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
/* ---- Was das Ziel kann ---------------------------------------------
|
|
428
|
+
|
|
429
|
+
Die Werkbank schreibt auf fremde Punkte und liest aus fremden Punkten.
|
|
430
|
+
Ob die koennen, was von ihnen verlangt wird, stand nirgends: weder
|
|
431
|
+
`common.read` noch `common.write` noch der Typ des Ziels wurden je
|
|
432
|
+
angesehen (Ricardo, 15.09.2026).
|
|
433
|
+
|
|
434
|
+
Drei Arten, wie es auseinandergeht, alle drei still:
|
|
435
|
+
|
|
436
|
+
`formelBlind` Die Schreibformel liefert Text, das Ziel nimmt keinen.
|
|
437
|
+
Der js-controller rechnet ihn auf den Zieltyp zurueck,
|
|
438
|
+
die Formel war umsonst. Aus `"ON"` wird wieder `true`.
|
|
439
|
+
`nichtSchreibbar` Das Ziel traegt `write: false`. ioBroker laesst den
|
|
440
|
+
Schreibvorgang trotzdem durch — gemessen —, der Adapter
|
|
441
|
+
dahinter reicht ihn aber nicht ans Geraet weiter.
|
|
442
|
+
Ein stiller Blindgaenger.
|
|
443
|
+
`meldetNichts` Die Lesequelle traegt `read: false`. Ein Tastendruck
|
|
444
|
+
hat keinen Zustand; gelesen wird dort nie etwas.
|
|
445
|
+
|
|
446
|
+
Gibt eine Liste zurueck, nicht ein Urteil — wer sie anzeigt, entscheidet
|
|
447
|
+
ueber die Schaerfe. */
|
|
448
|
+
export function zielKann(s) {
|
|
449
|
+
var raus = [];
|
|
450
|
+
if (!s) { return raus; }
|
|
451
|
+
|
|
452
|
+
if (s.srcW) {
|
|
453
|
+
var w = S.objects[s.srcW];
|
|
454
|
+
var wc = (w && w.common) || null;
|
|
455
|
+
if (wc) {
|
|
456
|
+
if (wc.write === false) { raus.push({ art: 'nichtSchreibbar', punkt: s.srcW }); }
|
|
457
|
+
/* Anfuehrungszeichen im Ausdruck sind der Beleg, dass die Formel Text
|
|
458
|
+
liefert; ohne sie rechnet sie mit Zahlen oder Wahrheitswerten und
|
|
459
|
+
passt zum Ziel. */
|
|
460
|
+
if (s.fw && /['"`]/.test(s.fw) && (wc.type === 'boolean' || wc.type === 'number')) {
|
|
461
|
+
raus.push({ art: 'formelBlind', punkt: s.srcW, typ: wc.type });
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
if (s.srcR) {
|
|
467
|
+
var r = S.objects[s.srcR];
|
|
468
|
+
var rc = (r && r.common) || null;
|
|
469
|
+
if (rc && rc.read === false) { raus.push({ art: 'meldetNichts', punkt: s.srcR }); }
|
|
470
|
+
}
|
|
471
|
+
return raus;
|
|
472
|
+
}
|
package/admin/js/zielleiste.js
CHANGED
|
@@ -11,7 +11,7 @@ import { el } from './basis.js';
|
|
|
11
11
|
import { tr } from './sprache.js';
|
|
12
12
|
import { enumListe } from './aufzaehlungen.js';
|
|
13
13
|
import { enumZeile, setzeZiel } from './zuordnung.js';
|
|
14
|
-
import { aliasFuer, ordnerUnterAlias, kennungtauglich, knotenDa } from './entwurf.js';
|
|
14
|
+
import { aliasFuer, ausgangsQuellen, ordnerUnterAlias, kennungtauglich, knotenDa } from './entwurf.js';
|
|
15
15
|
import { zeichneErgebnis, entwurfAngefasst, angebotFrisch } from './ergebnis.js';
|
|
16
16
|
import { zeigeVerlegen } from './schreiben.js';
|
|
17
17
|
|
|
@@ -381,7 +381,13 @@ export function baueZielleiste(host, e) {
|
|
|
381
381
|
irrt sie hier, irrt sie dort ebenso. Und der Hinweis haelt
|
|
382
382
|
niemanden auf. */
|
|
383
383
|
var zwilling = null;
|
|
384
|
-
|
|
384
|
+
/* Dieselbe Frage wie die Vorbelegung, also auch dieselbe Antwort:
|
|
385
|
+
am Mehrfachgeraet der Alias DIESES Ausgangs. Sonst nennt der
|
|
386
|
+
Hinweis einen anderen Alias als den, dessen Stand oben in der
|
|
387
|
+
Liste steht - zwei Aussagen auf einem Bildschirm, die sich
|
|
388
|
+
widersprechen (C13). */
|
|
389
|
+
var vorhAlias = aliasFuer(e.kanal, ausgangsQuellen(e)) ||
|
|
390
|
+
((e.instanz === null || e.instanz === undefined) ? aliasFuer(e.kanal) : null);
|
|
385
391
|
if (vorhAlias && S.objects[vorhAlias] && vorhAlias !== e.ziel) {
|
|
386
392
|
var zw = el('div', 'uebernahme zwilling');
|
|
387
393
|
zw.appendChild(el('span', 'zq', tr('target.twinExists', vorhAlias)));
|
package/admin/js/zuordnung.js
CHANGED
|
@@ -16,7 +16,8 @@ import { enumVorlagen, katalogFehlt, ikonCache, ikonErlaubt, ikonNachtrag,
|
|
|
16
16
|
ikonTaugt, vorlageZu, holeIkon } from './katalog.js';
|
|
17
17
|
import { kindZustaende, aliasQuellen, holeEinzelne,
|
|
18
18
|
zeileAusAlias } from './werte.js';
|
|
19
|
-
import { aliasFuer, zielId, knotenDa
|
|
19
|
+
import { aliasFuer, ausgangsQuellen, zielId, knotenDa,
|
|
20
|
+
aboErgaenzen, quellenVonEntwurf } from './entwurf.js';
|
|
20
21
|
import { zeichneErgebnis, entwurfAngefasst, angebotFrisch } from './ergebnis.js';
|
|
21
22
|
|
|
22
23
|
|
|
@@ -475,18 +476,35 @@ export function setzeZiel(e) {
|
|
|
475
476
|
/* Mehrere Ausgaenge: das Geraet wird zum Ordner, jeder Ausgang
|
|
476
477
|
ein Kanal darin. Ein folder darueber, kein device — sonst
|
|
477
478
|
rutscht die Erkennung hoch und macht aus dreien wieder eins. */
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
479
|
+
/* Gibt es den Alias fuer GENAU DIESEN Ausgang, ist er das Ziel —
|
|
480
|
+
mit seinem eigenen Namen, nicht mit dem der Ausgangsnummer.
|
|
481
|
+
|
|
482
|
+
Hier stand: „Wie der einzelne Ausgang heisst, kann die Werkbank
|
|
483
|
+
nicht wissen." Das galt, solange sie nur nach dem GERAET fragen
|
|
484
|
+
konnte. Seit `ausgangsQuellen` weiss sie es: Ricardos `RF1000`
|
|
485
|
+
liest aus `stat.POWER1` und ist damit Ausgang 1. Vorher schlug
|
|
486
|
+
die Zielzeile `…Steckdosenleiste.POWER1` vor — eine Kennung, die
|
|
487
|
+
es gar nicht gibt —, meldete „wird neu angelegt" und haette beim
|
|
488
|
+
Klick einen zweiten Alias neben `RF1000` gesetzt (C13,
|
|
489
|
+
16.09.2026). */
|
|
490
|
+
var vorhAus = aliasFuer(S.current, ausgangsQuellen(e));
|
|
491
|
+
if (vorhAus) {
|
|
492
|
+
var ta = vorhAus.split('.');
|
|
493
|
+
e.zielName = ta.pop();
|
|
494
|
+
e.zielOrdner = ta.join('.');
|
|
486
495
|
} else {
|
|
487
|
-
|
|
496
|
+
if (vorhanden) {
|
|
497
|
+
/* Kein Alias fuer diesen Ausgang, aber einer am Geraet: der gibt
|
|
498
|
+
wenigstens den Ordner vor. Der Name kommt aus der
|
|
499
|
+
Ausgangsnummer — hier entsteht wirklich etwas Neues. */
|
|
500
|
+
var tm = vorhanden.split('.');
|
|
501
|
+
tm.pop();
|
|
502
|
+
e.zielOrdner = tm.join('.');
|
|
503
|
+
} else {
|
|
504
|
+
e.zielOrdner = zt0.join('.') + '.' + geraet;
|
|
505
|
+
}
|
|
506
|
+
e.zielName = ausgangName(e, e.instanz);
|
|
488
507
|
}
|
|
489
|
-
e.zielName = ausgangName(e, e.instanz);
|
|
490
508
|
} else if (vorhanden) {
|
|
491
509
|
var te = vorhanden.split('.');
|
|
492
510
|
e.zielName = te.pop();
|
|
@@ -583,7 +601,10 @@ export function uebernehmeBestand(e) {
|
|
|
583
601
|
function vomAliasUebernehmen(z, o) {
|
|
584
602
|
var c = o.common || {}, q2 = aliasQuellen(o);
|
|
585
603
|
var ist = zeileAusAlias(o, z.n);
|
|
586
|
-
|
|
604
|
+
/* Dieselbe Frage wie in `bestandVorrang`: ohne Vorlage gibt es keinen
|
|
605
|
+
Vorlagenwert. Sonst verglich die Zeile gegen den Rohentwurf und
|
|
606
|
+
nannte das „weicht von der Vorlage ab" (17.09.2026). */
|
|
607
|
+
if (!z.vorlagenWert && e.vorschlag && e.vorlage) {
|
|
587
608
|
z.vorlagenWert = {
|
|
588
609
|
on: z.on, role: z.role, typ: z.typ, unit: z.unit, caption: z.caption,
|
|
589
610
|
f: z.f, fw: z.fw, srcR: z.srcR, srcW: z.srcW, states: z.states
|
|
@@ -610,7 +631,9 @@ export function uebernehmeBestand(e) {
|
|
|
610
631
|
Gleicht die Beschriftung dem Zeilennamen, bleibt sie leer - so
|
|
611
632
|
haelt es der Zweig darunter, der neue Zeilen anlegt, auch. */
|
|
612
633
|
z.caption = ist.caption;
|
|
613
|
-
|
|
634
|
+
/* Wie im Bestandsvorrang: `ist.srcR` kennt den Taster, `q2.read`
|
|
635
|
+
nicht. */
|
|
636
|
+
if (q2.read) { z.srcR = ist.srcR; }
|
|
614
637
|
/* Und dieselbe Schreibregel wie im Bestandsvorrang.
|
|
615
638
|
|
|
616
639
|
Hier stand die Schreibquelle nur fuer das getrennte Paar; sonst
|
|
@@ -766,6 +789,13 @@ export function uebernehmeBestand(e) {
|
|
|
766
789
|
});
|
|
767
790
|
}
|
|
768
791
|
}
|
|
792
|
+
/* Geholt ist nur der erste Wert - mitlaufen tut er erst mit einem Abo.
|
|
793
|
+
Die Zeilen aus dem Alias lesen oft aus einem anderen Knoten als dem
|
|
794
|
+
angeklickten (J9, 18.09.2026: `Stromzaehler` zeigte drei Zeilen „aus
|
|
795
|
+
Solar.Netz" stundenlang auf dem Stand vom Anklicken). Ergaenzt wird
|
|
796
|
+
nur, was noch nicht laeuft; bei jedem Zeichnen zu rufen kostet also
|
|
797
|
+
nichts. */
|
|
798
|
+
if (S.entwurf === e && e.kanal === S.current) { aboErgaenzen(quellenVonEntwurf(e)); }
|
|
769
799
|
}
|
|
770
800
|
|
|
771
801
|
export function unterschiede(e) {
|
package/admin/js/zustand.js
CHANGED
|
@@ -14,6 +14,9 @@ export var S = {
|
|
|
14
14
|
objects: {},
|
|
15
15
|
keysSorted: [],
|
|
16
16
|
kleinIndex: {},
|
|
17
|
+
/* Alias-Kanal -> Zahl der Punkte, deren Quelle es nicht (mehr) gibt.
|
|
18
|
+
Einmal beim Laden gerechnet; der Baum schlaegt nur nach. */
|
|
19
|
+
aliasKaputt: {},
|
|
17
20
|
werte: {},
|
|
18
21
|
current: null,
|
|
19
22
|
openRow: null,
|