iobroker.alias-workbench 0.9.9 → 0.9.10
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/LICENSE +1 -1
- package/README.md +15 -0
- package/admin/css/werkbank.css +8 -3
- package/admin/js/entwurf.js +47 -17
- package/admin/js/ergebnis.js +49 -4
- package/admin/js/erkennung.js +46 -2
- package/admin/js/musterwahl.js +12 -2
- package/admin/js/quellentausch.js +179 -19
- package/admin/js/schreiben.js +98 -3
- package/admin/js/sprache.js +6 -1
- package/admin/js/vorlagen.js +12 -2
- package/admin/js/vorlagenblatt.js +65 -3
- package/admin/js/werte.js +63 -1
- package/admin/js/zuordnung.js +29 -35
- package/admin/js/zustandsliste.js +37 -4
- package/admin/sprachen/de.json +13 -0
- package/admin/sprachen/en.json +13 -0
- package/admin/vorlagen/messpunkt.json +2 -1
- package/admin/vorlagen/tasmota-lampe.json +2 -1
- package/admin/vorlagen/tasmota-mehrfach.json +2 -1
- package/admin/vorlagen/tasmota-rgb.json +2 -1
- package/admin/vorlagen/tasmota-steckdose.json +2 -1
- package/io-package.json +14 -14
- package/package.json +1 -1
package/admin/js/schreiben.js
CHANGED
|
@@ -445,12 +445,31 @@ export function zuSchreiben(alleAusgaenge) {
|
|
|
445
445
|
}).filter(Boolean);
|
|
446
446
|
}
|
|
447
447
|
if (!alleAusgaenge || !S.entwurf.instanzen || S.entwurf.instanzen.length < 2) { return [S.entwurf]; }
|
|
448
|
+
/* Der Kanalname wird zur Kennung - und zwei koennen dabei gleich werden.
|
|
449
|
+
|
|
450
|
+
`ausgangName` saeubert seit dem 12.09.2026 selbst (G31), und damit
|
|
451
|
+
ergeben „Licht Bar" und „Licht, Bar" beide `Licht_Bar`: der zweite
|
|
452
|
+
Kanal ueberschriebe den ersten, still und ohne Meldung. Deshalb
|
|
453
|
+
wird hier mitgezaehlt, was schon vergeben ist - der zweite bekommt
|
|
454
|
+
`_2`, der dritte `_3`. Der Anzeigename bleibt der des Kanals. */
|
|
455
|
+
var vergeben = {};
|
|
456
|
+
var eindeutig = function (name) {
|
|
457
|
+
if (!vergeben[name]) { vergeben[name] = 1; return name; }
|
|
458
|
+
var z = 1, n2;
|
|
459
|
+
do { z++; n2 = name + '_' + z; } while (vergeben[n2]);
|
|
460
|
+
vergeben[n2] = 1;
|
|
461
|
+
return n2;
|
|
462
|
+
};
|
|
448
463
|
return S.entwurf.instanzen.map(function (n) {
|
|
449
|
-
if (String(n) === String(S.entwurf.instanz)) {
|
|
464
|
+
if (String(n) === String(S.entwurf.instanz)) {
|
|
465
|
+
S.entwurf.zielName = eindeutig(S.entwurf.zielName);
|
|
466
|
+
S.entwurf.ziel = S.entwurf.zielOrdner + '.' + S.entwurf.zielName;
|
|
467
|
+
return S.entwurf;
|
|
468
|
+
}
|
|
450
469
|
var v = vorschlag(S.current, S.entwurf.vorlage, n);
|
|
451
470
|
if (v) {
|
|
452
471
|
v.zielOrdner = S.entwurf.zielOrdner;
|
|
453
|
-
v.zielName = ausgangName(v, n);
|
|
472
|
+
v.zielName = eindeutig(ausgangName(v, n));
|
|
454
473
|
v.ziel = v.zielOrdner + '.' + v.zielName;
|
|
455
474
|
enumsUebertragen(v);
|
|
456
475
|
}
|
|
@@ -487,6 +506,35 @@ function sammle(liste, o) {
|
|
|
487
506
|
vorh.neu = vorh.neu && o.neu;
|
|
488
507
|
}
|
|
489
508
|
|
|
509
|
+
/* Eine selbst angelegte Aufzaehlung, die durch diesen Schreibvorgang
|
|
510
|
+
leer wird, geht mit.
|
|
511
|
+
|
|
512
|
+
Beim LOESCHEN eines Alias tut das `enumsDieLeerWuerden` seit jeher.
|
|
513
|
+
Beim blossen WECHSEL der Funktion nicht: wer „Stehlampe" waehlte,
|
|
514
|
+
schrieb, dann auf „Licht" umstellte, hinterliess ein leeres
|
|
515
|
+
`enum.functions.floor_lamp` - von der Werkbank angelegt, von der
|
|
516
|
+
Werkbank geleert, und trotzdem stehengeblieben (gemessen 12.09.2026).
|
|
517
|
+
|
|
518
|
+
Dieselbe Regel wie dort: nur was `angelegtVon` traegt, und nur wenn
|
|
519
|
+
hinterher kein Mitglied uebrig ist. Der Eintrag wandert aus der
|
|
520
|
+
Schreibliste in die Loeschliste - sonst schriebe ihn `setObject`
|
|
521
|
+
parallel zum `delObject` wieder hin.
|
|
522
|
+
|
|
523
|
+
Gerufen von `zeigeTrockenlauf` UND `schreibeObjekte`: beide bauen ihre
|
|
524
|
+
Liste selbst, und was der Trockenlauf zeigt, muss das Schreiben tun. */
|
|
525
|
+
function leereEigeneEnums(liste, weg) {
|
|
526
|
+
liste.slice().forEach(function (x) {
|
|
527
|
+
if (!x.istEnum || !x.weg) { return; }
|
|
528
|
+
var o = enums[x.id];
|
|
529
|
+
if (!o || !o.native || o.native.angelegtVon !== 'alias-workbench') { return; }
|
|
530
|
+
if (((x.obj.common || {}).members || []).length) { return; }
|
|
531
|
+
liste.splice(liste.indexOf(x), 1);
|
|
532
|
+
if (!weg.some(function (y) { return y.id === x.id; })) {
|
|
533
|
+
weg.push({ id: x.id, vorgabe: true, grund: tr('enums.nowEmpty') });
|
|
534
|
+
}
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
|
|
490
538
|
export function zeigeTrockenlauf(alleAusgaenge) {
|
|
491
539
|
if (!S.entwurf) { return; }
|
|
492
540
|
S.trockenAlle = !!alleAusgaenge;
|
|
@@ -532,6 +580,7 @@ export function zeigeTrockenlauf(alleAusgaenge) {
|
|
|
532
580
|
if (!weg.some(function (x) { return x.id === v.id; })) { weg.push(v); }
|
|
533
581
|
});
|
|
534
582
|
});
|
|
583
|
+
leereEigeneEnums(liste, weg);
|
|
535
584
|
S.loeschListe = weg;
|
|
536
585
|
var body = $('#dry-body');
|
|
537
586
|
body.textContent = '';
|
|
@@ -600,6 +649,20 @@ export function zeigeTrockenlauf(alleAusgaenge) {
|
|
|
600
649
|
body.appendChild(zwK);
|
|
601
650
|
}
|
|
602
651
|
|
|
652
|
+
/* Was beim letzten Anlegen der Sendepunkte schiefging - hier oben, wo
|
|
653
|
+
man es sieht, und nicht als Tooltip an einem Knopf, den derselbe
|
|
654
|
+
Rueckruf gleich wieder wegwirft (H17). */
|
|
655
|
+
if (S.mqFehler && S.mqFehler.length) {
|
|
656
|
+
var mk = el('div', 'aside w');
|
|
657
|
+
mk.style.borderLeftColor = 'var(--bad)';
|
|
658
|
+
mk.style.marginBottom = '11px';
|
|
659
|
+
mk.appendChild(el('b', null, tr('write.failed', S.mqFehler.length)));
|
|
660
|
+
var mul = el('ul');
|
|
661
|
+
S.mqFehler.forEach(function (x) { mul.appendChild(el('li', null, x)); });
|
|
662
|
+
mk.appendChild(mul);
|
|
663
|
+
body.appendChild(mk);
|
|
664
|
+
}
|
|
665
|
+
|
|
603
666
|
/* Die Aufzaehlungen sind gerade nicht bekannt — dann wird auch keine
|
|
604
667
|
geschrieben. Das gehoert hierher und nicht in eine Sperre: der Alias
|
|
605
668
|
selbst braucht sie nicht, und aus einem stillen Datenverlust eine
|
|
@@ -618,7 +681,12 @@ export function zeigeTrockenlauf(alleAusgaenge) {
|
|
|
618
681
|
var wch = el('div', 'ch');
|
|
619
682
|
wch.appendChild(el('span', 'typ', tr('write.remove')));
|
|
620
683
|
wch.appendChild(el('span', 'chip bad', tr('write.removeCount', weg.length)));
|
|
621
|
-
|
|
684
|
+
/* Der Satz gilt fuer Datenpunkte. Steht eine leer gewordene
|
|
685
|
+
Aufzaehlung mit in der Liste, waere er dort falsch - die liegt
|
|
686
|
+
nicht im Alias und stand nie im Entwurf (12.09.2026). */
|
|
687
|
+
if (weg.some(function (v) { return String(v.id).indexOf('enum.') !== 0; })) {
|
|
688
|
+
wch.appendChild(el('span', 'chip mut', tr('write.removeHint')));
|
|
689
|
+
}
|
|
622
690
|
wk.appendChild(wch);
|
|
623
691
|
weg.forEach(function (v, i) {
|
|
624
692
|
var r = el('div', 'slot');
|
|
@@ -685,6 +753,19 @@ export function zeigeTrockenlauf(alleAusgaenge) {
|
|
|
685
753
|
kn2.disabled = false;
|
|
686
754
|
kn2.textContent = tr('write.retry');
|
|
687
755
|
kn2.title = mqFehler.join(' | ');
|
|
756
|
+
/* Und zwar so, dass es den Neuaufbau ueberlebt.
|
|
757
|
+
|
|
758
|
+
Drei Zeilen weiter unten wird der Entwurf neu gebaut
|
|
759
|
+
und der Trockenlauf noch einmal gerechnet - dabei
|
|
760
|
+
verschwindet dieser Knopf samt seiner Begruendung aus
|
|
761
|
+
dem DOM. Gemessen 12.09.2026: nach rund 120 ms war er
|
|
762
|
+
weg. Der neue Dialog sperrte wieder und sagte, welcher
|
|
763
|
+
Sendepunkt fehlt, aber nicht mehr, WARUM er nicht
|
|
764
|
+
entstanden ist (H17). Am Zustand gemerkt, zeichnet
|
|
765
|
+
`zeigeTrockenlauf` ihn oben wieder hin. */
|
|
766
|
+
S.mqFehler = mqFehler.slice();
|
|
767
|
+
} else {
|
|
768
|
+
S.mqFehler = null;
|
|
688
769
|
}
|
|
689
770
|
/* Erst den Entwurf neu aufbauen lassen, dann den
|
|
690
771
|
Trockenlauf noch einmal rechnen. Sofort geoeffnet zeigte
|
|
@@ -1024,6 +1105,16 @@ export function loescheAlias() {
|
|
|
1024
1105
|
m.style.fontSize = '13px';
|
|
1025
1106
|
if (fehler.length) {
|
|
1026
1107
|
m.appendChild(el('b', null, tr('write.failed', fehler.length)));
|
|
1108
|
+
/* Und WAS schiefging, nicht nur wie viel.
|
|
1109
|
+
|
|
1110
|
+
Beim Schreiben steht die Liste seit jeher da (`melden` beim
|
|
1111
|
+
Verlegen, der Trockenlauf-Dialog beim Anlegen); beim Loeschen
|
|
1112
|
+
fehlte sie, und zurueck blieb die blosse Zahl. Wer wissen
|
|
1113
|
+
will, welcher Punkt haengt — etwa weil ein Skript ihn sperrt
|
|
1114
|
+
—, erfuhr es nicht (G44, gemessen 12.09.2026). */
|
|
1115
|
+
var ul = el('ul');
|
|
1116
|
+
fehler.forEach(function (x) { ul.appendChild(el('li', null, x)); });
|
|
1117
|
+
m.appendChild(ul);
|
|
1027
1118
|
} else {
|
|
1028
1119
|
m.appendChild(el('b', null, tr('del.done', alle.length)));
|
|
1029
1120
|
}
|
|
@@ -1674,6 +1765,10 @@ export function schreibeObjekte() {
|
|
|
1674
1765
|
schlecht += pruefeSchreiben(en).length;
|
|
1675
1766
|
});
|
|
1676
1767
|
if (schlecht) { return; }
|
|
1768
|
+
/* Dieselbe Bereinigung wie im Trockenlauf - diese Liste entsteht neu,
|
|
1769
|
+
und ohne den Griff hier schriebe `setObject` die leere Aufzaehlung
|
|
1770
|
+
parallel zum `delObject` wieder hin. */
|
|
1771
|
+
leereEigeneEnums(liste, S.loeschListe);
|
|
1677
1772
|
|
|
1678
1773
|
var body = $('#dry-body');
|
|
1679
1774
|
var offen = liste.length;
|
package/admin/js/sprache.js
CHANGED
|
@@ -56,7 +56,12 @@ export function ladeSprache(fertig) {
|
|
|
56
56
|
|
|
57
57
|
var beantwortet = false;
|
|
58
58
|
socket.emit('getObject', 'system.config', function (err, obj) {
|
|
59
|
-
if (beantwortet) { return; }
|
|
59
|
+
/* Hier stand `if (beantwortet) { return; }` - der Waechter aus der
|
|
60
|
+
Zeit vor dem Nachziehen. Er stieg genau im Fall aus, fuer den der
|
|
61
|
+
Zweig weiter unten gebaut wurde, und machte ihn zu totem Text
|
|
62
|
+
(gemessen 12.09.2026: mit `expertMode: true` am System und einer
|
|
63
|
+
um 4 s verzoegerten Antwort blieb `expertenVorgabe` auch nach
|
|
64
|
+
zehn Sekunden auf `false`). */
|
|
60
65
|
/* Auch eine spaete Antwort gilt noch.
|
|
61
66
|
|
|
62
67
|
Nach 2,5 Sekunden geht es ohne weiter — richtig, sonst haengt der
|
package/admin/js/vorlagen.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { S } from './zustand.js';
|
|
8
8
|
import { D, $, el } from './basis.js';
|
|
9
9
|
import { hatPunkt, jsonVon, feldWert, kindZustaende, feldFormel } from './werte.js';
|
|
10
|
-
import { eigenerKanalName, merkeHakenVorgabe } from './entwurf.js';
|
|
10
|
+
import { eigenerKanalName, merkeHakenVorgabe, kennungtauglich } from './entwurf.js';
|
|
11
11
|
import { tasmotaBefehle } from './mqtt.js';
|
|
12
12
|
import { socket } from './verbindung.js';
|
|
13
13
|
|
|
@@ -271,7 +271,17 @@ export function setzeInstanz(text, v, n) {
|
|
|
271
271
|
export function ausgangName(e, n) {
|
|
272
272
|
if (e && e.kanal) {
|
|
273
273
|
var eigen = eigenerKanalName(e.kanal + '.' + n);
|
|
274
|
-
|
|
274
|
+
/* Der Kanalname wird hier zur KENNUNG - also durch dieselbe
|
|
275
|
+
Saeuberung wie ueberall sonst.
|
|
276
|
+
|
|
277
|
+
Vorher stand hier nur `replace(/\./g, '_')`. Leerzeichen und
|
|
278
|
+
Kommas blieben damit stehen, und der js-controller lehnt so etwas
|
|
279
|
+
nicht ab, er saeubert still: aus dem Kanalnamen „Licht, Bar" wurde
|
|
280
|
+
`alias.0.…Licht_ Bar`, waehrend die Werkbank „Licht, Bar" anzeigte
|
|
281
|
+
und in die Aufzaehlungen eintrug - eine Kennung, die es nie gab
|
|
282
|
+
(G31, gemessen 12.09.2026). Der Name selbst bleibt unberuehrt: er
|
|
283
|
+
steht als Beschriftung im Objekt. */
|
|
284
|
+
if (eigen) { return kennungtauglich(eigen) || eigen.replace(/\./g, '_'); }
|
|
275
285
|
}
|
|
276
286
|
if (!e || !e.kanalnameRoh) { return 'POWER' + n; }
|
|
277
287
|
return String(e.kanalnameRoh).split(e.platzhalter || '%N%').join(n);
|
|
@@ -2058,19 +2058,81 @@ export function zeichneVorlagenDialog() {
|
|
|
2058
2058
|
|
|
2059
2059
|
var b = $('#btn-tpl-save');
|
|
2060
2060
|
if (b) {
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2061
|
+
/* Fuenf Gruende sperren den Knopf - und jeder von ihnen gehoert
|
|
2062
|
+
hingeschrieben.
|
|
2063
|
+
|
|
2064
|
+
Bisher wurde nur `disabled` gesetzt, `title` blieb leer. Bei
|
|
2065
|
+
fehlenden Pflichtpunkten stand die Erklaerung weiter unten in der
|
|
2066
|
+
Probelauf-Karte, fuer die man scrollen musste; bei den anderen
|
|
2067
|
+
vier stand sie nirgends. Ricardo stiess darauf, als er fuer den
|
|
2068
|
+
Objektdump eine eigene Vorlage anlegen wollte - grauer Knopf,
|
|
2069
|
+
kein Wort dazu (I32, 11.09.2026). Ein Knopf, der nicht sagt warum,
|
|
2070
|
+
ist derselbe Fehler wie eine Sperre ohne Begruendung (U28). */
|
|
2071
|
+
var gruende = sperrgruende(z);
|
|
2072
|
+
b.disabled = gruende.length > 0;
|
|
2073
|
+
b.title = gruende.join(' · ');
|
|
2064
2074
|
b.textContent = k.modus === 'update' ? tr('tpls.saveUpdate') : tr('tpls.saveNew');
|
|
2075
|
+
if (gruende.length) {
|
|
2076
|
+
var sk = el('div', 'aside w');
|
|
2077
|
+
sk.style.borderLeftColor = 'var(--bad)';
|
|
2078
|
+
sk.appendChild(el('b', null, tr('tpls.lockedWhy')));
|
|
2079
|
+
var sul = el('ul');
|
|
2080
|
+
gruende.forEach(function (x) { sul.appendChild(el('li', null, x)); });
|
|
2081
|
+
sk.appendChild(sul);
|
|
2082
|
+
body.appendChild(sk);
|
|
2083
|
+
}
|
|
2065
2084
|
}
|
|
2066
2085
|
var tt = $('#tpl-titel');
|
|
2067
2086
|
if (tt) { tt.textContent = tr('tpls.title', z.e.kanal.split('.').pop()); }
|
|
2068
2087
|
}
|
|
2069
2088
|
|
|
2089
|
+
/* Die fuenf Gruende, die das Speichern sperren - an einer Stelle.
|
|
2090
|
+
|
|
2091
|
+
Gebraucht werden sie zweimal: beim Zeichnen, um Knopf und Kasten zu
|
|
2092
|
+
beschriften, und beim Speichern selbst. Das zweite ist keine
|
|
2093
|
+
Doppelung, sondern die eigentliche Sicherung: Der Knopf wird beim
|
|
2094
|
+
VERLASSEN eines Feldes neu bewertet, nicht beim Tippen. Wer eine
|
|
2095
|
+
offene Klammer in den Namenshinweis schreibt und ohne Umweg auf
|
|
2096
|
+
„Vorlage speichern" klickt, hat einen blauen Knopf vor sich - und die
|
|
2097
|
+
Vorlage wurde mit `namenshinweis: "("` gespeichert (Ricardo,
|
|
2098
|
+
12.09.2026, am Testsystem nachgestellt; `hinweisTrifft` faengt den
|
|
2099
|
+
kaputten Ausdruck zwar ab, aber gespeichert gehoert er trotzdem
|
|
2100
|
+
nicht). */
|
|
2101
|
+
export function sperrgruende(z) {
|
|
2102
|
+
if (!z) { return []; }
|
|
2103
|
+
var k = z.kopf;
|
|
2104
|
+
var neu = baueVorlage(z);
|
|
2105
|
+
var gruende = [];
|
|
2106
|
+
if (!neu.erkennung.erforderlich.length) { gruende.push(tr('tpls.lockNoRequired')); }
|
|
2107
|
+
if (!k.id) { gruende.push(tr('tpls.lockNoId')); }
|
|
2108
|
+
var fremd = fremdeMitId(k);
|
|
2109
|
+
if (fremd) { gruende.push(tr('tpls.lockIdTaken', fremd.id)); }
|
|
2110
|
+
if (!hinweisTaugt(k.namenshinweis)) { gruende.push(tr('tpls.lockBadHint')); }
|
|
2111
|
+
if (!z.zeilen.filter(function (r) { return !r.weg; }).length) { gruende.push(tr('tpls.lockNoRows')); }
|
|
2112
|
+
return gruende;
|
|
2113
|
+
}
|
|
2114
|
+
|
|
2070
2115
|
export function speichereVorlageAusDialog() {
|
|
2071
2116
|
if (!S.tplZ) { return; }
|
|
2072
2117
|
var neu = baueVorlage(S.tplZ);
|
|
2073
2118
|
|
|
2119
|
+
/* Letzte Instanz, unabhaengig vom Zustand des Knopfes. */
|
|
2120
|
+
var gesperrt = sperrgruende(S.tplZ);
|
|
2121
|
+
if (gesperrt.length) {
|
|
2122
|
+
var bodyS = $('#tpl-body');
|
|
2123
|
+
var altS = bodyS.querySelector('.sperrgrund-kasten');
|
|
2124
|
+
if (altS) { altS.remove(); }
|
|
2125
|
+
var wS = el('div', 'aside w sperrgrund-kasten');
|
|
2126
|
+
wS.style.borderLeftColor = 'var(--bad)';
|
|
2127
|
+
wS.appendChild(el('b', null, tr('tpls.lockedWhy')));
|
|
2128
|
+
var ulS = el('ul');
|
|
2129
|
+
gesperrt.forEach(function (x) { ulS.appendChild(el('li', null, x)); });
|
|
2130
|
+
wS.appendChild(ulS);
|
|
2131
|
+
bodyS.insertBefore(wS, bodyS.firstChild);
|
|
2132
|
+
bodyS.scrollTop = 0;
|
|
2133
|
+
return;
|
|
2134
|
+
}
|
|
2135
|
+
|
|
2074
2136
|
/* Widerspricht sie sich, wird nicht gespeichert — eine Vorlage, die
|
|
2075
2137
|
auf nichts passen kann, hilft niemandem und ist spaeter schwer zu
|
|
2076
2138
|
durchschauen. */
|
package/admin/js/werte.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import { S } from './zustand.js';
|
|
8
8
|
import { socket } from './verbindung.js';
|
|
9
|
-
import { tr } from './sprache.js';
|
|
9
|
+
import { tr, txt } from './sprache.js';
|
|
10
10
|
import './entwurf.js';
|
|
11
11
|
|
|
12
12
|
/* Direkt nachschlagen — die Vorlagen nennen den Pfad ja vollstaendig
|
|
@@ -76,10 +76,23 @@ export function mitVorspann(pre) {
|
|
|
76
76
|
return raus;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
+
/* Wie der Baum: POWER2 vor POWER10.
|
|
80
|
+
|
|
81
|
+
`keysSorted` MUSS byteweise sortiert bleiben - die Binaersuche in
|
|
82
|
+
`mitVorspann` haengt daran. Fuer die Anzeige ist das die falsche
|
|
83
|
+
Ordnung: an einer Mehrfachsteckdose stand `POWER10` vor `POWER2`,
|
|
84
|
+
waehrend der Baum daneben richtig zaehlte (er nimmt `Intl.Collator`
|
|
85
|
+
mit `numeric: true`). Also wird hier nachsortiert, wo die Liste
|
|
86
|
+
entsteht - je Kanal ein paar Dutzend Eintraege, nicht der
|
|
87
|
+
Gesamtindex (gemessen 12.09.2026). */
|
|
88
|
+
var ZEILEN_VERGLEICH = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' });
|
|
89
|
+
|
|
79
90
|
export function kindZustaende(kanal) {
|
|
80
91
|
var pre = kanal + '.';
|
|
81
92
|
return mitVorspann(pre).filter(function (k) {
|
|
82
93
|
return S.objects[k] && S.objects[k].type === 'state';
|
|
94
|
+
}).sort(function (a, b) {
|
|
95
|
+
return ZEILEN_VERGLEICH.compare(a.slice(pre.length), b.slice(pre.length));
|
|
83
96
|
});
|
|
84
97
|
}
|
|
85
98
|
|
|
@@ -112,6 +125,55 @@ export function schreibQuelle(o) {
|
|
|
112
125
|
return (c.write === true || typeof a.write === 'string') ? (q.write || '') : '';
|
|
113
126
|
}
|
|
114
127
|
|
|
128
|
+
/* Aus einem Alias-Objekt die Felder einer Entwurfszeile.
|
|
129
|
+
|
|
130
|
+
Dieselbe Umrechnung stand fuenfmal ausprogrammiert - dreimal in
|
|
131
|
+
`entwurf.js`, zweimal in `zuordnung.js` -, jedes Mal mit derselben
|
|
132
|
+
Schreibquellenregel und leicht anderem Drumherum. Die Kommentare an
|
|
133
|
+
jenen Stellen dokumentieren vier Fehler, die genau daraus entstanden
|
|
134
|
+
sind: eine verlorene Schreibquelle am Rollladen, ein schaltender Punkt
|
|
135
|
+
der zum lesenden wurde, eine fehlende Beschriftung an Solar.Netz und
|
|
136
|
+
eine Abweichung, die die Werkbank selbst erzeugt hatte (Y5).
|
|
137
|
+
|
|
138
|
+
Was hier steht, ist die ABLEITUNG - nicht, was damit geschieht. Die
|
|
139
|
+
eine Stelle vergleicht nur, die zweite legt eine Zeile an, die beiden
|
|
140
|
+
anderen ueberschreiben eine bestehende; das bleibt bei den Aufrufern.
|
|
141
|
+
|
|
142
|
+
`baueEntwurf` ruft sie bewusst NICHT: die Funktion baut Zeilen fuer
|
|
143
|
+
zwei Faelle, Alias und rohe Quelle, und drei der Felder haengen dort
|
|
144
|
+
an dieser Unterscheidung. Sie mit Schaltern hereinzuholen waere
|
|
145
|
+
schlechter als zwei ehrliche Stellen.
|
|
146
|
+
|
|
147
|
+
Die Beschriftung folgt der Regel „leer, wenn sie dem Zeilennamen
|
|
148
|
+
gleicht" - so halten es `vomAliasUebernehmen` und der Zweig, der neue
|
|
149
|
+
Zeilen anlegt. `bestandVorrang` will sie anders (uebernehmen, sobald
|
|
150
|
+
`common.name` gesetzt ist) und behaelt dafuer eine eigene Zeile.
|
|
151
|
+
|
|
152
|
+
Und `role`/`typ` bleiben bei den beiden Stellen, die eine BESTEHENDE
|
|
153
|
+
Zeile ueberschreiben (`bestandVorrang`, `vomAliasUebernehmen`), an
|
|
154
|
+
ihrer `!== undefined`-Frage: dort steht schon der Vorlagenwert, und
|
|
155
|
+
ein Alias, der gar keine Rolle nennt, soll ihn behalten statt ihn
|
|
156
|
+
gegen den Leerstring zu tauschen, den diese Funktion liefert. Wer das
|
|
157
|
+
wegraeumt, nimmt jedem vorlagenbasierten Alias seine Rollen. */
|
|
158
|
+
export function zeileAusAlias(o, zeilenname) {
|
|
159
|
+
var c = (o && o.common) || {};
|
|
160
|
+
var a = c.alias || {};
|
|
161
|
+
var q = aliasQuellen(o);
|
|
162
|
+
var nm = txt(c.name);
|
|
163
|
+
return {
|
|
164
|
+
role: c.role || '',
|
|
165
|
+
typ: c.type || '',
|
|
166
|
+
unit: c.unit || '',
|
|
167
|
+
states: c.states || undefined,
|
|
168
|
+
wr: !!c.write,
|
|
169
|
+
srcR: q.read || '',
|
|
170
|
+
srcW: schreibQuelle(o),
|
|
171
|
+
f: (typeof a.read === 'string') ? a.read : '',
|
|
172
|
+
fw: (typeof a.write === 'string') ? a.write : '',
|
|
173
|
+
caption: (nm && nm !== zeilenname) ? nm : ''
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
115
177
|
export function aliasQuellen(obj) {
|
|
116
178
|
var a = obj && obj.common && obj.common.alias;
|
|
117
179
|
if (!a) { return {}; }
|
package/admin/js/zuordnung.js
CHANGED
|
@@ -14,7 +14,8 @@ import {
|
|
|
14
14
|
} from './aufzaehlungen.js';
|
|
15
15
|
import { enumVorlagen, katalogFehlt, ikonCache, ikonErlaubt, ikonNachtrag,
|
|
16
16
|
ikonTaugt, vorlageZu, holeIkon } from './katalog.js';
|
|
17
|
-
import { kindZustaende, aliasQuellen, holeEinzelne,
|
|
17
|
+
import { kindZustaende, aliasQuellen, holeEinzelne,
|
|
18
|
+
zeileAusAlias } from './werte.js';
|
|
18
19
|
import { aliasFuer, zielId, knotenDa } from './entwurf.js';
|
|
19
20
|
import { zeichneErgebnis, entwurfAngefasst, angebotFrisch } from './ergebnis.js';
|
|
20
21
|
|
|
@@ -580,7 +581,8 @@ export function uebernehmeBestand(e) {
|
|
|
580
581
|
hiesse, dem Vorlagenknopf den Wert zu nehmen, den er zurueckholen
|
|
581
582
|
soll. */
|
|
582
583
|
function vomAliasUebernehmen(z, o) {
|
|
583
|
-
var c = o.common || {},
|
|
584
|
+
var c = o.common || {}, q2 = aliasQuellen(o);
|
|
585
|
+
var ist = zeileAusAlias(o, z.n);
|
|
584
586
|
if (!z.vorlagenWert) {
|
|
585
587
|
z.vorlagenWert = {
|
|
586
588
|
on: z.on, role: z.role, typ: z.typ, unit: z.unit, caption: z.caption,
|
|
@@ -593,10 +595,10 @@ export function uebernehmeBestand(e) {
|
|
|
593
595
|
stellen, kann der Stand nicht wieder auseinanderlaufen. */
|
|
594
596
|
if (c.role !== undefined) { z.role = c.role; }
|
|
595
597
|
if (c.type !== undefined) { z.typ = c.type; }
|
|
596
|
-
z.unit =
|
|
597
|
-
z.states =
|
|
598
|
-
z.f =
|
|
599
|
-
z.fw =
|
|
598
|
+
z.unit = ist.unit;
|
|
599
|
+
z.states = ist.states;
|
|
600
|
+
z.f = ist.f;
|
|
601
|
+
z.fw = ist.fw;
|
|
600
602
|
/* Die Beschriftung steht im Alias und gehoert dem Nutzer.
|
|
601
603
|
|
|
602
604
|
Sie fehlte hier als einzige - `bestandVorrang` holt sie laengst
|
|
@@ -607,8 +609,7 @@ export function uebernehmeBestand(e) {
|
|
|
607
609
|
„Bezug" geworden (Ricardo, 07.09.2026, an alias.0.Solar.Netz).
|
|
608
610
|
Gleicht die Beschriftung dem Zeilennamen, bleibt sie leer - so
|
|
609
611
|
haelt es der Zweig darunter, der neue Zeilen anlegt, auch. */
|
|
610
|
-
|
|
611
|
-
z.caption = (nm2 && nm2 !== z.n) ? nm2 : '';
|
|
612
|
+
z.caption = ist.caption;
|
|
612
613
|
if (q2.read) { z.srcR = q2.read; }
|
|
613
614
|
/* Und dieselbe Schreibregel wie im Bestandsvorrang.
|
|
614
615
|
|
|
@@ -622,7 +623,7 @@ export function uebernehmeBestand(e) {
|
|
|
622
623
|
Belegt ist das Schreiben durch `common.write` oder durch eine
|
|
623
624
|
hinterlegte Schreibformel; bei getrennten Quellen zaehlt, was in
|
|
624
625
|
`alias.id.write` steht. */
|
|
625
|
-
z.srcW =
|
|
626
|
+
z.srcW = ist.srcW;
|
|
626
627
|
if (q2.write && !q2.einfach && q2.write !== q2.read) {
|
|
627
628
|
/* Die Gegenzeile eines Paars ist keine eigene mehr - ihre Quelle
|
|
628
629
|
steckt jetzt als Schreibquelle in dieser Zeile. */
|
|
@@ -663,7 +664,7 @@ export function uebernehmeBestand(e) {
|
|
|
663
664
|
kindZustaende(e.ziel).forEach(function (id) {
|
|
664
665
|
var n = id.slice(e.ziel.length + 1);
|
|
665
666
|
if (n.indexOf('.') !== -1 || kennt[n]) { return; }
|
|
666
|
-
var o = S.objects[id],
|
|
667
|
+
var o = S.objects[id], q = aliasQuellen(o);
|
|
667
668
|
|
|
668
669
|
/* Derselbe Punkt unter anderem Namen? Dann ist es kein zweiter,
|
|
669
670
|
sondern derselbe — und der Name, der schon im Alias steht, gilt.
|
|
@@ -699,31 +700,24 @@ export function uebernehmeBestand(e) {
|
|
|
699
700
|
kennt[n] = vorhanden;
|
|
700
701
|
return;
|
|
701
702
|
}
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
gemacht (Ricardo, 08.09.2026). */
|
|
721
|
-
srcW: schreibQuelle(o),
|
|
722
|
-
f: (typeof a.read === 'string') ? a.read : '',
|
|
723
|
-
fw: (typeof a.write === 'string') ? a.write : '',
|
|
724
|
-
caption: (nm && nm !== n) ? nm : '',
|
|
725
|
-
manuell: false
|
|
726
|
-
});
|
|
703
|
+
/* Rolle, Typ, Einheit, Zustaende, Formeln, Quellen und Beschriftung
|
|
704
|
+
kommen geschlossen aus `zeileAusAlias` - dieselbe Ablesung wie in
|
|
705
|
+
`bestandVorrang`, `bestandsAbweichung` und `vomAliasUebernehmen`.
|
|
706
|
+
|
|
707
|
+
Dazu gehoert die Schreibregel: Hier stand `q.einfach ? '' :
|
|
708
|
+
(q.write || '')` - bei schlichtem `alias.id` also immer leer, ohne
|
|
709
|
+
`common.write` ueberhaupt anzusehen. Es trifft jeden Alias, bei dem
|
|
710
|
+
MEHRERE Punkte auf denselben Quellpunkt zeigen: die eine
|
|
711
|
+
Vorschlagszeile bekommt der erste von ihnen, alle weiteren landen
|
|
712
|
+
hier. Am `alias.0.Badezimmer.Rollladen` zeigen OPEN, CLOSE, SET und
|
|
713
|
+
pct alle auf `…Bad.level`; CLOSE lief oben durch und behielt seine
|
|
714
|
+
Schreibquelle, OPEN, SET und pct verloren sie - ein Aktualisieren
|
|
715
|
+
haette den Rollladen ueber den Alias unfahrbar gemacht (Ricardo,
|
|
716
|
+
08.09.2026). Seit dem Zusammenlegen der vier Stellen (Y5) gilt die
|
|
717
|
+
Regel fuer alle zugleich. */
|
|
718
|
+
e.states.push(Object.assign(zeileAusAlias(o, n), {
|
|
719
|
+
n: n, on: true, ausBestand: true, nurImAlias: true, manuell: false
|
|
720
|
+
}));
|
|
727
721
|
});
|
|
728
722
|
|
|
729
723
|
/* Einmal nachzeichnen.
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import { S } from './zustand.js';
|
|
10
10
|
import { el, $ } from './basis.js';
|
|
11
11
|
import { tr } from './sprache.js';
|
|
12
|
-
import { erkenneEntwurf, musterVon, rolleVonPlatz } from './erkennung.js';
|
|
12
|
+
import { erkenneEntwurf, musterVon, rolleVonPlatz, typKonflikte } from './erkennung.js';
|
|
13
13
|
import { kindZustaende, wertVon, fmt } from './werte.js';
|
|
14
14
|
import { steuerKanaele, vorlagenAbweichung, bestandsAbweichung, waehle, quelleHier, quellenVerteilung } from './entwurf.js';
|
|
15
15
|
import { musterName } from './musternamen.js';
|
|
@@ -367,6 +367,11 @@ export function baueListe(host, e, pl, rateKnopf, musterBlock) {
|
|
|
367
367
|
var legendenPlatz = el('div');
|
|
368
368
|
card.appendChild(legendenPlatz);
|
|
369
369
|
|
|
370
|
+
/* Kippt das ganze Muster an einem einzelnen Typkonflikt? Einmal
|
|
371
|
+
gerechnet: die Zeilenschleife braucht es, die Legende darunter
|
|
372
|
+
ebenso, und beide sollen dieselbe Antwort geben. */
|
|
373
|
+
var kipptAnTyp = typKonflikte(e.states, e.want).filter(function (x) { return x.pflicht; })[0] || null;
|
|
374
|
+
|
|
370
375
|
/* --- Zustandsliste --- */
|
|
371
376
|
e.states.forEach(function (s, i) {
|
|
372
377
|
var pl = platzVon[s.n];
|
|
@@ -378,12 +383,27 @@ export function baueListe(host, e, pl, rateKnopf, musterBlock) {
|
|
|
378
383
|
06.09.2026). Jetzt traegt die Zeile eine Marke, der Satz steht
|
|
379
384
|
einmal als Legende ueber der Liste, und der lange Text haengt als
|
|
380
385
|
Hinweis an der Zeile. */
|
|
381
|
-
|
|
386
|
+
/* Kippt das Muster an einem Typkonflikt, wird nur der Verursacher
|
|
387
|
+
getoent - nicht die ganze Liste.
|
|
388
|
+
|
|
389
|
+
Dass danach keine Zeile einen Platz hat, stimmt zwar, taugt aber
|
|
390
|
+
nicht als Auskunft: Die Toenung liest sich als „mit dieser Zeile
|
|
391
|
+
stimmt etwas nicht", und das trifft auf sieben von acht nicht zu.
|
|
392
|
+
Sie zeigte die Folge und nicht die Ursache (Ricardo, 12.09.2026).
|
|
393
|
+
Die Legende darueber nennt den Grund; markiert wird die Zeile, an
|
|
394
|
+
der er haengt. */
|
|
395
|
+
var ohnePlatz = (s.on && !unfertig && !pl &&
|
|
396
|
+
(!kipptAnTyp || kipptAnTyp.n === s.n));
|
|
382
397
|
if (ohnePlatz) { ohnePlatzZahl++; ohnePlatzNamen.push(s.n); }
|
|
383
398
|
var row = el('div', 'erow' + (s.on ? '' : ' skip') + (S.openRow === i ? ' open' : '') +
|
|
384
399
|
(ohnePlatz ? ' ohneplatz' : ''));
|
|
385
400
|
row.tabIndex = 0;
|
|
386
|
-
if (ohnePlatz) {
|
|
401
|
+
if (ohnePlatz) {
|
|
402
|
+
row.title = kipptAnTyp
|
|
403
|
+
? tr('pattern.noPlaceBecauseType', musterName(e.want) || e.want || '?',
|
|
404
|
+
kipptAnTyp.platz, kipptAnTyp.erwartet.join(tr('pattern.typeOr')), kipptAnTyp.typ)
|
|
405
|
+
: tr('pattern.noPlaceLong', musterName(e.want) || e.want || '?');
|
|
406
|
+
}
|
|
387
407
|
|
|
388
408
|
var cbw = el('span');
|
|
389
409
|
var cb = document.createElement('input');
|
|
@@ -670,7 +690,20 @@ export function baueListe(host, e, pl, rateKnopf, musterBlock) {
|
|
|
670
690
|
if (ohnePlatzZahl) {
|
|
671
691
|
var mn = musterName(e.want) || e.want || '?';
|
|
672
692
|
var lg = el('div', 'legendenzeile');
|
|
673
|
-
|
|
693
|
+
/* Steht die ganze Liste ohne Platz da, weil EINE Zeile im falschen
|
|
694
|
+
Datentyp ist, sagt die Legende das - statt acht Zeilen pauschal zu
|
|
695
|
+
beschuldigen.
|
|
696
|
+
|
|
697
|
+
Der Ablauf: der Punkt faellt wegen des Typs aus dem Muster, damit
|
|
698
|
+
ist ein Pflichtplatz leer, damit greift das Muster nicht mehr, und
|
|
699
|
+
damit hat KEINE Zeile mehr einen Platz. Am Bastelzimmer_Licht
|
|
700
|
+
reichte ein Wechsel an SET von `boolean` auf `mixed`: acht von acht
|
|
701
|
+
Zeilen getoent, Musterfeld „✕ SET fehlt", und die Ursache stand
|
|
702
|
+
nur in der aufgeklappten SET-Zeile (Ricardo, 12.09.2026). */
|
|
703
|
+
lg.appendChild(document.createTextNode(kipptAnTyp
|
|
704
|
+
? tr('pattern.noPlaceBecauseType', mn, kipptAnTyp.platz,
|
|
705
|
+
kipptAnTyp.erwartet.join(tr('pattern.typeOr')), kipptAnTyp.typ)
|
|
706
|
+
: tr('pattern.noPlaceLegend', mn)));
|
|
674
707
|
|
|
675
708
|
var ib = el('button', 'infoknopf', 'i');
|
|
676
709
|
ib.type = 'button';
|
package/admin/sprachen/de.json
CHANGED
|
@@ -130,6 +130,7 @@
|
|
|
130
130
|
"enums.members": "{0} drin",
|
|
131
131
|
"enums.newSuffix": "neu",
|
|
132
132
|
"enums.noMatch": "nichts gefunden zu „{0}“",
|
|
133
|
+
"enums.nowEmpty": "ist danach leer und wird entfernt",
|
|
133
134
|
"enums.placeholder": "leer = keine Zuordnung",
|
|
134
135
|
"enums.removeFrom": "wird herausgenommen",
|
|
135
136
|
"enums.room": "Raum",
|
|
@@ -288,6 +289,7 @@
|
|
|
288
289
|
"pattern.manualInstead": "von Hand statt {0}",
|
|
289
290
|
"pattern.missing": "{0} fehlt",
|
|
290
291
|
"pattern.noMatch": "kein Muster passt zu „{0}“",
|
|
292
|
+
"pattern.noPlaceBecauseType": "Das {0}-Muster greift gerade nicht: {1} verlangt {2}, eingestellt ist {3}. Deshalb hat keine Zeile einen Platz.",
|
|
291
293
|
"pattern.noPlaceLegend": "So hinterlegte Zeilen haben keinen Platz im {0}-Muster",
|
|
292
294
|
"pattern.noPlaceLong": "kein Platz im {0}-Muster — der Punkt wird trotzdem angelegt und ist voll benutzbar, nur Alexa & Co. zählen ihn separat",
|
|
293
295
|
"pattern.notDetected": "nicht erkannt",
|
|
@@ -326,14 +328,19 @@
|
|
|
326
328
|
"roles.slot": "Platz {0} im {1}-Muster",
|
|
327
329
|
"roles.slotFree": "frei",
|
|
328
330
|
"roles.slotTaken": "belegt von {0}",
|
|
331
|
+
"swap.blocked": "Erst entscheiden — diese Punkte können nicht mitziehen: {0}. Entweder beim Tauschen entfernen oder den Tausch lassen; ein Punkt, der schreiben soll, braucht ein Schreibziel.",
|
|
329
332
|
"swap.button": "Quelle tauschen …",
|
|
330
333
|
"swap.diffRange": "Wertebereich {0} → {1}",
|
|
331
334
|
"swap.diffType": "Typ {0} → {1}",
|
|
332
335
|
"swap.diffUnit": "Einheit {0} → {1}",
|
|
333
336
|
"swap.done": "Getauscht. Der Alias liest jetzt aus {0}.",
|
|
334
337
|
"swap.from": "Liest heute aus:",
|
|
338
|
+
"swap.goHigher": "eine Ebene höher nehmen",
|
|
335
339
|
"swap.gone": "Dieses Gerät gibt es nicht mehr — genau dafür ist dieser Weg gedacht.",
|
|
336
340
|
"swap.guessHint": "Die mit „vermutet“ gekennzeichneten Zeilen sind über Namen oder Rolle geraten — die bitte einzeln prüfen.",
|
|
341
|
+
"swap.guessedByName": "Über den Punktnamen geraten: unter der neuen Quelle heißt ebenfalls etwas „{0}\". Das kann weit danebenliegen — bitte prüfen.",
|
|
342
|
+
"swap.guessedByRole": "Über die Rolle geraten: unter der neuen Quelle trägt ein Punkt dieselbe Rolle „{0}\". Bitte prüfen.",
|
|
343
|
+
"swap.higherLevel": "Eine Ebene höher ({0}) finden {1} von {2} Punkten ein Ziel, hier nur {3}.",
|
|
337
344
|
"swap.keeps": "Der Alias behält seine Kennung, seine Namen, Rollen und Formeln, die Aufzeichnung in der Datenbank sowie Raum und Funktion. Geändert wird nur, woher jeder Punkt liest und wohin er schreibt.",
|
|
338
345
|
"swap.mapping": "Zuordnung",
|
|
339
346
|
"swap.mind": "Darauf achten:",
|
|
@@ -432,6 +439,12 @@
|
|
|
432
439
|
"tpls.json": "Das wird gespeichert",
|
|
433
440
|
"tpls.loadFailed": "eigene Vorlagen nicht geladen",
|
|
434
441
|
"tpls.loadFailedHint": "Die Instanzkonfiguration hat nicht geantwortet oder einen Fehler gemeldet. Deine eigenen Vorlagen sind nicht verschwunden — sie fehlen nur in dieser Ansicht. Solange dieser Hinweis steht, kann ein Speichern eine vorhandene Vorlage überschreiben: erst neu laden.",
|
|
442
|
+
"tpls.lockBadHint": "Der Namenshinweis ist kein brauchbarer Ausdruck",
|
|
443
|
+
"tpls.lockIdTaken": "Die Kennung gehört schon „{0}“",
|
|
444
|
+
"tpls.lockNoId": "Die Kennung fehlt",
|
|
445
|
+
"tpls.lockNoRequired": "Kein Punkt ist als Pflicht angehakt — so passt die Vorlage auf gar nichts",
|
|
446
|
+
"tpls.lockNoRows": "Es ist keine Zeile übrig",
|
|
447
|
+
"tpls.lockedWhy": "Speichern ist gesperrt:",
|
|
435
448
|
"tpls.losesTo": "verliert gegen {0}",
|
|
436
449
|
"tpls.multiple": "Dieses Gerät hat mehrere Ausgänge",
|
|
437
450
|
"tpls.multipleHelp": "Die {0} in den Pfaden wird durch %N% ersetzt — nur am Ende eines Abschnitts, damit ENERGY.Power heil bleibt.",
|