iobroker.alias-workbench 0.9.11 → 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/admin/js/enums.js CHANGED
@@ -40,8 +40,8 @@ export function ladeEnums(danach) {
40
40
  /* Den alten Stand behalten. Er ist vielleicht veraltet, aber
41
41
  veraltet ist naeher an der Wahrheit als leer. */
42
42
  unbekannt = true;
43
- console.warn('[alias-workbench] Aufzaehlungen nicht geladen: ' +
44
- (err || 'keine Antwort'));
43
+ console.warn('[alias-workbench] Enumerations not loaded: ' +
44
+ (err || 'no answer'));
45
45
  if (versuch < 3) {
46
46
  versuch++;
47
47
  wiederTimer = setTimeout(function () {
@@ -1,7 +1,7 @@
1
1
  import { S } from './zustand.js';
2
2
  import './start.js';
3
3
  import { socket } from './verbindung.js';
4
- import { D, $, el, klappZeichen } from './basis.js';
4
+ import { D, $, el } from './basis.js';
5
5
  import { tr, sprachtext } from './sprache.js';
6
6
  import './einstellungen.js';
7
7
  import './enums.js';
@@ -27,13 +27,17 @@ import {
27
27
  mitNachfrage,
28
28
  geraeteDarunter,
29
29
  aliasFuer,
30
+ ausgangsQuellen,
30
31
  quellenVerteilung,
31
32
  quelleHier,
32
33
  tipptGerade,
33
34
  opt,
34
35
  vorlagenAbweichung,
35
36
  vorlageAnwenden,
36
- bestandVorrang
37
+ bestandVorrang,
38
+ kanalGruppen,
39
+ aufteilBehaelter,
40
+ rohEntwurf
37
41
  } from './entwurf.js';
38
42
  import { setzeEnums, setzeZiel, unterschiede } from './zuordnung.js';
39
43
  import './detail.js';
@@ -512,15 +516,21 @@ var warumAuf = false;
512
516
  sg.style.borderColor = 'var(--line-strong)';
513
517
  sg.style.background = 'var(--surface-2)';
514
518
  }
515
- var hd = el('div', 'hd', e.vorschlag
519
+ var hd = el('div', 'hd');
520
+ /* Der Name steht in einer eigenen Spanne, damit er sich kuerzen
521
+ laesst. Die Kopfzeile bricht nicht mehr um: vorher nahm sich der
522
+ Hinweis rechts eine eigene Zeile, sobald Marken dazukamen und der
523
+ Name lang wurde („Homematic Rollladenaktor" + von Hand gewaehlt +
524
+ erzwungen). Jetzt weicht stattdessen der Name zurueck — vollstaendig
525
+ steht er im Tooltip und im Auswahlfeld darunter (Ricardo, 17.09.2026). */
526
+ var hdText = e.vorschlag
516
527
  ? tr('tpl.identifiedAs', e.tplName)
517
- : tr('tpl.noneDetected'));
528
+ : tr('tpl.noneDetected');
529
+ var hdName = el('span', 'tname', hdText);
530
+ hdName.title = hdText;
531
+ hd.appendChild(hdName);
518
532
  if (e.vorlageVersion) {
519
- var vv2 = el('span', null, ' · ' + e.vorlage + ' v' + e.vorlageVersion);
520
- vv2.style.fontFamily = 'var(--mono)';
521
- vv2.style.fontSize = '10.5px';
522
- vv2.style.fontWeight = '400';
523
- vv2.style.color = 'var(--ink-3)';
533
+ var vv2 = el('span', 'tid', ' · ' + e.vorlage + ' v' + e.vorlageVersion);
524
534
  hd.appendChild(vv2);
525
535
  }
526
536
  if (e.vonHandGewaehlt) {
@@ -571,13 +581,22 @@ var warumAuf = false;
571
581
  Breite, die eine Liste braucht. */
572
582
  var warumKopf = null, warumLeib = null;
573
583
  if (gruende.length || weggelassen.length) {
574
- warumKopf = el('div', 'wkopf');
575
584
  /* Ohne Vorlage waere „Warum diese Vorlage" die falsche Frage —
576
585
  dann steht hier, warum keine greift. */
577
- warumKopf.appendChild(el('span', null, weggelassen.length
578
- ? tr('tpl.skippedHead', weggelassen.length)
579
- : tr(e.vorschlag ? 'tpl.whyThis' : 'tpl.whyNone')));
580
- warumKopf.appendChild(klappZeichen(warumAuf));
586
+ var warumText = tr(e.vorschlag ? 'tpl.whyThis' : 'tpl.whyNone');
587
+ warumKopf = el('div', 'wkopf' + (warumAuf ? ' auf' : ''));
588
+ /* Statt „Warum diese Vorlage +" nur ein Fragezeichen: der Satz
589
+ steht im Tooltip und spart die halbe Kopfzeile. Was weggelassen
590
+ wurde, bleibt als kurze Marke stehen — dass Punkte fehlen, soll
591
+ niemand erst suchen muessen (Ricardo, 17.09.2026). */
592
+ if (weggelassen.length) {
593
+ warumKopf.appendChild(el('span', 'chip mut',
594
+ tr('tpl.skippedChip', weggelassen.length)));
595
+ warumText = tr('tpl.skippedHead', weggelassen.length) + ' — ' + warumText;
596
+ }
597
+ warumKopf.appendChild(el('span', 'fragez', '?'));
598
+ warumKopf.title = warumText;
599
+ warumKopf.setAttribute('aria-label', warumText);
581
600
  warumKopf.tabIndex = 0;
582
601
  var um2 = function () { warumAuf = !warumAuf; zeichneErgebnis(); };
583
602
  warumKopf.addEventListener('click', um2);
@@ -634,6 +653,25 @@ var warumAuf = false;
634
653
  v2.zielOrdner = e.zielOrdner;
635
654
  v2.zielName = ausgangName(v2, selA.value);
636
655
  v2.ziel = v2.zielOrdner + '.' + v2.zielName;
656
+ /* Dieser Weg fragte bisher gar nicht nach dem Bestand -
657
+ deshalb sah derselbe Ausgang je nach Weg anders aus:
658
+ ueber den Baum kamen die Haken eines fremden Alias, hier
659
+ die Vorgaben der Vorlage. Jetzt stellen alle drei Wege
660
+ dieselbe Frage (C13, 16.09.2026).
661
+
662
+ Und die Zielzeile folgt derselben Antwort. Sie wird hier von
663
+ Hand zusammengesetzt und laeuft nicht durch `setzeZiel`;
664
+ ohne diese Zeilen zeigte sie beim Umschalten weiter auf
665
+ `…POWER2` und meldete „wird neu angelegt", waehrend
666
+ `SUNLU` daneben stand. */
667
+ var vorhA2 = aliasFuer(S.current, ausgangsQuellen(v2));
668
+ if (vorhA2) {
669
+ var t2 = vorhA2.split('.');
670
+ v2.zielName = t2.pop();
671
+ v2.zielOrdner = t2.join('.');
672
+ v2.ziel = vorhA2;
673
+ }
674
+ if (vorhA2 && S.objects[vorhA2]) { bestandVorrang(v2, vorhA2); }
637
675
  S.entwurf = v2; S.openRow = null; zeichneErgebnis();
638
676
  }
639
677
  });
@@ -650,7 +688,10 @@ var warumAuf = false;
650
688
  das Ausgangsfeld daneben blieb ungestylt in der Browserschrift. */
651
689
  var selV = el('select', 'tx');
652
690
 
653
- selV.appendChild(opt('', e.vorschlag ? tr('tpl.switch') : tr('tpl.choose')));
691
+ /* Greift eine Vorlage, ist der erste Eintrag kein Titel, sondern
692
+ eine Wahl: sie wieder loswerden. „— Vorlage wechseln —" stand
693
+ wie eine Handlung da, tat aber nichts (Ricardo, 17.09.2026). */
694
+ selV.appendChild(opt('', e.vorschlag ? tr('tpl.none') : tr('tpl.choose')));
654
695
  S.VORLAGEN.forEach(function (v) {
655
696
  var t = pruefeVorlage(v, S.current);
656
697
  /* Auch nicht passende bleiben waehlbar — mit Grund daneben. Sonst
@@ -663,7 +704,35 @@ var warumAuf = false;
663
704
  selV.appendChild(o);
664
705
  });
665
706
  selV.addEventListener('change', function () {
666
- if (!selV.value) { return; }
707
+ if (!selV.value) {
708
+ /* „keine Vorlage": den Rohentwurf nehmen. Ziel, Raum und
709
+ Funktion bleiben — die hat der Mensch eingestellt, nicht die
710
+ Vorlage. `angefasst` verhindert, dass der naechste
711
+ eintreffende Messwert die Vorlage wieder heranzieht. */
712
+ if (!e.vorschlag) { return; }
713
+ var r = rohEntwurf(S.current);
714
+ if (!r) { selV.value = e.vorlage || ''; return; }
715
+ r.ziel = e.ziel; r.zielOrdner = e.zielOrdner; r.zielName = e.zielName;
716
+ r.raum = e.raum; r.funktion = e.funktion;
717
+ r.raumHer = e.raumHer; r.funktionHer = e.funktionHer;
718
+ /* Der Ausgang wird NICHT uebernommen. Er ist keine Eigenschaft
719
+ des Geraets, sondern des `mehrfach`-Blocks der Vorlage:
720
+ `instanzenVon` gibt ohne ihn `[null]` zurueck. Wurde die
721
+ Vorlage abgewaehlt, gibt es keinen Platzhalter mehr und damit
722
+ nichts zu waehlen. Bis 17.09.2026 erbte der Rohentwurf die
723
+ Ausgaenge der alten Vorlage: der Kasten blieb stehen, und wer
724
+ ihn benutzte, holte die abgewaehlte Vorlage kommentarlos
725
+ zurueck — der Ausgang-Zweig ruft `vorschlag` mit leerem
726
+ `e.vorlage` und laesst neu erkennen (gemessen an
727
+ `hm.ZWEIKANAL`: nach dem Umschalten auf `Licht_Esstisch`
728
+ stand `hm-schaltaktor-mehrfach` wieder im Feld). Die beiden
729
+ Kanaele laufen ohne Vorlage ueber „Unterordner je Kanal". */
730
+ S.entwurf = r;
731
+ S.openRow = null;
732
+ entwurfAngefasst();
733
+ zeichneErgebnis();
734
+ return;
735
+ }
667
736
  var v = vorschlag(S.current, selV.value);
668
737
  if (v) {
669
738
  v.ziel = e.ziel;
@@ -751,7 +820,11 @@ var warumAuf = false;
751
820
  neuE.zielName = e.zielName;
752
821
  neuE.ordnerVonHand = e.ordnerVonHand;
753
822
  if (fest) { neuE.vonHandGewaehlt = false; neuE.ausBestand = true; }
754
- if (vorh && S.objects[vorh]) { bestandVorrang(neuE, vorh); }
823
+ /* Wie beim ersten Aufbau: am Mehrfachgeraet zaehlt der Alias
824
+ DIESES Ausgangs (C13). */
825
+ var vorhW = aliasFuer(S.current, ausgangsQuellen(neuE)) ||
826
+ ((neuE.instanz === null || neuE.instanz === undefined) ? vorh : null);
827
+ if (vorhW && S.objects[vorhW]) { bestandVorrang(neuE, vorhW); }
755
828
  S.entwurf = neuE; S.openRow = null; zeichneErgebnis();
756
829
  });
757
830
  /* An der Quelle bleibt die Musterwahl im Vorlagenkasten, wo sie
@@ -844,7 +917,22 @@ var warumAuf = false;
844
917
  var bd = $('#btn-dry');
845
918
  var ba = $('#btn-dry-alle');
846
919
  var mehr = e.instanzen && e.instanzen.length > 1;
847
- var mehrK = !mehr && (e.kanalGeraete || []).length > 1;
920
+ /* Welche Unterordner entstuenden — nach den Haken, dieselbe Rechnung
921
+ wie der Schreibweg (C51). */
922
+ var grp = mehr ? [] : kanalGruppen(e).filter(function (g) { return !g.eigen; });
923
+ /* Zwei Kanaele mit Haken genuegen — auch wenn einer davon der
924
+ Wartungskanal ist.
925
+
926
+ Zwischenzeitlich zaehlte `.0` hier nicht mit, damit der Knopf nicht
927
+ an jedem Fuehler steht. Das ergab aber eine Unstimmigkeit, die beim
928
+ Benutzen auffaellt: fuer den INHALT zaehlte der Kanal (er bekommt
929
+ seinen Ordner), fuer den ANLASS nicht — man hakt etwas an und
930
+ nichts passiert. Ricardo am 17.09.2026: „zählt ist ja eher ne
931
+ Expertenfunktion, da weiss man schon was man macht." Also eine
932
+ Regel statt zweier. */
933
+ var typVonK = {};
934
+ (e.kanalGeraete || []).forEach(function (k) { typVonK[k.id] = k.typ; });
935
+ var mehrK = !mehr && grp.length > 1;
848
936
  /* Die Knoepfe gehoeren zum Modus, nicht nur zum Objekt: Im Modus
849
937
  „Alias anlegen" wird angelegt, nicht verwaltet — Verlegen, Quelle
850
938
  tauschen und Entfernen haben dort nichts zu suchen. Im Modus
@@ -891,13 +979,25 @@ var warumAuf = false;
891
979
  return (e.zielOrdner || '') + '.' + nm;
892
980
  }).join(String.fromCharCode(10));
893
981
  } else if (mehrK) {
894
- ba.textContent = tr('write.createPerChannel', e.kanalGeraete.length);
982
+ ba.textContent = tr('write.createPerChannel', grp.length);
895
983
  /* Im Hinweis stehen die Namen, die entstehen wuerden — sonst
896
984
  klickt man auf eine Zahl und weiss nicht, was danach dasteht. */
897
- ba.title = e.kanalGeraete.map(function (k) {
985
+ /* Dieselbe Rechnung wie der Schreibweg (schreiben.js, `ordner`):
986
+ ein Behaelter fuer das Geraet, darunter je Kanal einer. Hier
987
+ stand nur `zielOrdner` — also eine Ebene zu wenig. Am
988
+ Geschirrspueler versprach der Hinweis `alias.0.Befehle`,
989
+ entstanden waere `alias.0.Geschirrspueler.Befehle`; am
990
+ Wohnzimmer_Dimmer las man `alias.0.1`, `alias.0.2`, `alias.0.3`
991
+ — drei Aliase, nach blanken Kanalnummern benannt, direkt unter
992
+ alias.0. Gefunden im Lauf vom 16.09.2026 (C15). */
993
+ var ordnerK = aufteilBehaelter(e);
994
+ ba.title = grp.map(function (g) {
898
995
  /* Die Kennung, nicht den Anzeigenamen - so heisst das Objekt
899
- hinterher wirklich (G31). */
900
- return (e.zielOrdner || '') + '.' + (k.kennung || k.name) + ' (' + k.typ + ')';
996
+ hinterher wirklich (G31). Dahinter, was hineinkommt: die
997
+ Zahl der angehakten Zeilen — daran sieht man vor dem Klick,
998
+ dass nichts Erfundenes dabei ist. */
999
+ return ordnerK + '.' + (g.kennung || g.name)
1000
+ + ' (' + g.zeilen.length + (typVonK[g.id] ? ', ' + typVonK[g.id] : '') + ')';
901
1001
  }).join(String.fromCharCode(10));
902
1002
  }
903
1003
  ba.disabled = false;
@@ -226,6 +226,13 @@ export function musterVon(typ) {
226
226
  statt als `electricity` nur noch als `info` erkannt. Am Bildschirm
227
227
  stand dazu nichts weiter als „passt nicht".
228
228
 
229
+ Zur Rolle selbst: `value.power.consumption` ist in der Rollenliste von
230
+ ioBroker durchgestrichen, der type-detector 6.0.x erkennt CONSUMPTION
231
+ aber NUR mit genau dieser Rolle (typePatterns.js, Muster `consumption`).
232
+ Die mitgelieferten Vorlagen behalten sie deshalb bewusst; jede traegt
233
+ den Grund im Feld `_rolle`. Role note for reviewers: the deprecated role
234
+ is kept on purpose because the type-detector requires it.
235
+
229
236
  Dieselbe Suche stand vorher zweimal im Code (detail.js und
230
237
  vorlagenblatt.js), beide Male ohne den Typ anzusehen. */
231
238
  export function plaetzeFuerRolle(musterName, rolle) {
package/admin/js/mqtt.js CHANGED
@@ -149,6 +149,38 @@ export function befehlsWissen(name) {
149
149
  return null;
150
150
  }
151
151
 
152
+ /* Welche Zeilen haengen ueberhaupt am Telemetrietakt?
153
+
154
+ Betroffen ist nur die eine Bauform: aus `tele/...` lesen und nach
155
+ `cmnd/...` schreiben. Wer aus `stat/POWER1` liest, bekommt die Antwort
156
+ bei jedem Befehl sofort — SetOption59 aendert daran nichts.
157
+
158
+ Ricardos Steckdosenleiste hat keine einzige solche Zeile: seine Aliase
159
+ lesen alle aus `stat.POWERn`. Trotzdem stand der SetOption59-Block an
160
+ der Karte und die Pruefung meldete "alle in Ordnung", als waere etwas
161
+ geprueft worden. Beides ist jetzt an diese Frage gebunden
162
+ (Ricardo, 16.09.2026). */
163
+ export function telegesteuert(e) {
164
+ if (!e || !e.states) { return []; }
165
+ return e.states.filter(function (x) {
166
+ if (!x.on || !x.srcW || !x.srcR) { return false; }
167
+ return /\.tele\./.test(x.srcR) && /\.cmnd\./.test(x.srcW);
168
+ });
169
+ }
170
+
171
+ /* Wie oft meldet das Geraet von sich aus? Steht in tele/STATE.
172
+
173
+ Ohne diese Zahl stand im Hinweis "minutenlang" — das unterstellt die
174
+ Tasmota-Vorgabe von 300 Sekunden. An Ricardos Leiste sind es zehn
175
+ (gemessen 16.09.2026: tele/STATE im Zehnsekundentakt). Wer eine Zahl
176
+ nennt, soll die richtige nennen. */
177
+ export function telePeriode(kanal) {
178
+ var id = hatPunkt(kanal, 'tele.STATE');
179
+ var j = id ? jsonVon(id) : null;
180
+ var n = j && Number(j.TelePeriod);
181
+ return (n && isFinite(n)) ? n : null;
182
+ }
183
+
152
184
  /* Meldet das Geraet Aenderungen sofort? Das haengt an SetOption59.
153
185
 
154
186
  Die Bitmaske aus StatusLOG zu entziffern waere moeglich, ist aber
@@ -281,7 +313,12 @@ export function mqttKarte(host, kanal) {
281
313
  ch.appendChild(th);
282
314
  if (l.fehlen.length) { ch.appendChild(el('span', 'chip warn', tr('mq.missing', l.fehlen.length))); }
283
315
  if (l.stumm.length) { ch.appendChild(el('span', 'chip bad', tr('mq.mute', l.stumm.length))); }
284
- if (l.sofort === false) { ch.appendChild(el('span', 'chip warn', tr('mq.instantOffShort'))); }
316
+ /* Nur warnen, wo es jemanden trifft: Ohne eine Zeile, die aus tele
317
+ liest und nach cmnd schreibt, ist SetOption59 folgenlos. */
318
+ var telZ = telegesteuert(S.entwurf);
319
+ if (l.sofort === false && telZ.length) {
320
+ ch.appendChild(el('span', 'chip warn', tr('mq.instantOffShort')));
321
+ }
285
322
  ch.insertBefore(klappZeichen(mqttAuf), ch.firstChild);
286
323
  ch.classList.add('klappbar');
287
324
  ch.addEventListener('click', function () { mqttAuf = !mqttAuf; zeichneErgebnis(); });
@@ -379,7 +416,13 @@ export function mqttKarte(host, kanal) {
379
416
  }
380
417
  b.appendChild(leiste);
381
418
 
382
- /* Sofortige Rueckmeldung — Zustand und, wenn noetig, der Schalter. */
419
+ /* Sofortige Rueckmeldung — Zustand und, wenn noetig, der Schalter.
420
+
421
+ Nur, wenn ueberhaupt eine Zeile am Telemetrietakt haengt. An einem
422
+ Geraet, dessen Aliase alle aus `stat/...` lesen, ist SetOption59
423
+ folgenlos, und ein Block darueber ist bloss Betrieb. */
424
+ if (!telZ.length) { host.appendChild(k); return; }
425
+
383
426
  var sk = el('div');
384
427
  sk.style.marginTop = '11px';
385
428
  sk.style.paddingTop = '10px';
@@ -423,8 +466,14 @@ export function mqttKarte(host, kanal) {
423
466
  if (lauf) { bp.disabled = true; be.disabled = true; }
424
467
  }
425
468
  sk.appendChild(sl);
469
+ /* Der Hinweis nennt die betroffenen Zeilen und den wirklichen Takt,
470
+ statt pauschal "minutenlang" zu behaupten. */
471
+ var telNamen = telZ.map(function (x) { return x.n; }).join(', ');
472
+ var takt = telePeriode(kanal);
426
473
  var hinweis = l.sofort === true ? tr('mq.instantHintOn')
427
- : (l.sofort === false ? tr('mq.instantHintOff') : tr('mq.instantHintUnknown'));
474
+ : (l.sofort === false
475
+ ? (takt ? tr('mq.instantHintOffTakt', telNamen, takt) : tr('mq.instantHintOff', telNamen))
476
+ : tr('mq.instantHintUnknown'));
428
477
  /* Woher der Befund stammt, gehoert dazu — sonst weiss niemand, ob er
429
478
  von eben ist oder von vorgestern. */
430
479
  var alter = (l.sofort === null) ? '' : so59Alter(kanal);
@@ -151,6 +151,19 @@ export function horcheAufObjekte() {
151
151
  return;
152
152
  }
153
153
 
154
+ /* Nur die vier Arten, aus denen der Baum besteht.
155
+
156
+ Beim Laden holt `ladeObjekte` gezielt `ARTEN`, beim Nachziehen
157
+ wurde dagegen alles aufgenommen: eine geaenderte Instanz, ein
158
+ Adapter-Update, `system.config` nach dem Umstellen der Sprache.
159
+ Der Zaehler in der Kopfzeile ging dadurch von 3145 auf 3146, und
160
+ mit jeder Stunde offenem Reiter wurden es mehr (A4, 17.09.2026).
161
+ Wechselt ein Objekt seine Art, faellt es hier ebenfalls heraus. */
162
+ if (obj && ARTEN.indexOf(obj.type) === -1) {
163
+ if (S.objects[id] === undefined) { return; }
164
+ obj = null;
165
+ }
166
+
154
167
  /* Alles Uebrige: nur was zaehlt, nicht jeder Schreibvorgang. */
155
168
  var vorher = kern(S.objects[id]);
156
169
  if (vorher === nachher) { return; }
@@ -5,7 +5,7 @@ import { $, el } from './basis.js';
5
5
  import { tr } from './sprache.js';
6
6
  import { wertVon, zielKann } from './werte.js';
7
7
  import './erkennung.js';
8
- import { sofortRueckmeldung, mqttEinstellung } from './mqtt.js';
8
+ import { sofortRueckmeldung, mqttEinstellung, telegesteuert } from './mqtt.js';
9
9
 
10
10
 
11
11
  /* Kommt die Rueckmeldung sofort oder erst mit der naechsten Telemetrie?
@@ -21,11 +21,14 @@ import { sofortRueckmeldung, mqttEinstellung } from './mqtt.js';
21
21
  da, tele/STATE kam erst nach der naechsten Telemetrie — bei
22
22
  TelePeriod 300 also bis zu fuenf Minuten spaeter. */
23
23
  export function pruefungRueckmeldung(e) {
24
- var lahm = e.states.filter(function (x) {
25
- if (!x.on || !x.srcW || !x.srcR) { return false; }
26
- return /\.tele\./.test(x.srcR) && /\.cmnd\./.test(x.srcW);
27
- });
28
- if (!lahm.length) { return { s: 'ok', t: tr('check.feedback'), d: tr('check.feedbackFine') }; }
24
+ /* Dieselbe Frage wie die MQTT-Karte, damit beide nicht auseinander
25
+ laufen (Ricardo, 16.09.2026). */
26
+ var lahm = telegesteuert(e);
27
+ /* Kein Haken, sondern ein Punkt: Hier wurde nichts geprueft, weil es
28
+ nichts zu pruefen gab. „alle in Ordnung" las sich, als haette die
29
+ Werkbank etwas nachgesehen - an Ricardos Steckdosenleiste lesen alle
30
+ Zeilen aus stat/..., SetOption59 ist dort folgenlos. */
31
+ if (!lahm.length) { return { s: 'mut', t: tr('check.feedback'), d: tr('check.feedbackNone') }; }
29
32
  /* Nur warnen, wenn es wirklich langsam ist. Mit SetOption59 meldet
30
33
  das Geraet jede Aenderung sofort — dann ist die Bauform zwar
31
34
  dieselbe, aber kein Problem. Gemessen: drei Sekunden. */
@@ -16,9 +16,10 @@ import { ausgangName, vorschlag } from './vorlagen.js';
16
16
  import {
17
17
  waehle,
18
18
  ordnerUnterAlias,
19
- gemeinsameKanaele,
20
19
  kennungtauglich,
21
- entwurfFuer,
20
+ kanalGruppen,
21
+ teilEntwurf,
22
+ aufteilBehaelter,
22
23
  aliasFuer,
23
24
  knotenDa,
24
25
  aboLoesen
@@ -434,8 +435,8 @@ export function zuSchreiben(alleAusgaenge) {
434
435
  /* Raum und Funktion gehoeren an jeden Kanal, nicht nur an den
435
436
  gezeigten.
436
437
 
437
- Die Teil-Entwuerfe entstehen frisch aus `entwurfFuer` bzw.
438
- `vorschlag` und trugen keins von beidem. `enumAenderungen` baute
438
+ Die Teil-Entwuerfe entstehen aus `teilEntwurf` bzw. `vorschlag` und
439
+ trugen keins von beidem. `enumAenderungen` baute
439
440
  deshalb fuer keinen einzigen Kanal ein Enum-Objekt: Wer oben einen
440
441
  Raum einstellte und „Alle erzeugen" drueckte, bekam Kanaele ohne
441
442
  Raum — der Einzelweg setzte ihn, der Sammelweg nicht.
@@ -461,25 +462,46 @@ export function zuSchreiben(alleAusgaenge) {
461
462
  return v;
462
463
  };
463
464
 
464
- if (alleAusgaenge && S.entwurf.kanalGeraete && S.entwurf.kanalGeraete.length > 1) {
465
- /* Ein Ordner fuer das Geraet, darunter je Kanal ein Kanal — dieselbe
466
- Form, die die Tasmota-Mehrfachvorlage seit jeher erzeugt. */
467
- var ordner = (S.entwurf.zielOrdner || 'alias.0') + '.' + S.entwurf.zielName;
468
- var gem = gemeinsameKanaele(S.entwurf.kanal, S.entwurf.kanalGeraete);
469
- return S.entwurf.kanalGeraete.map(function (k) {
470
- var v = entwurfFuer(k.id, gem, S.entwurf.gemeinsamMitPlatz);
471
- if (!v) { return null; }
472
- v.zielOrdner = ordner;
473
- /* Die Kennung, nicht der Anzeigename. „Licht Bar, oben" ergab eine
474
- Kennung mit Leerzeichen und Komma, „Kueche.Decke" sogar eine
475
- zusaetzliche Ebene samt Zwischenordner. Und der js-controller
476
- lehnt so etwas nicht ab, er saeubert still: aus
477
- `alias.0.wb[pruef]` wurde `alias.0.wb_pruef_`, der Rueckruf
478
- meldete `null`. Das Objekt entstuende also unter einer anderen
479
- Kennung als angezeigt (gemessen 08.09.2026). Der Name bleibt
480
- davon unberuehrt — er steht als Beschriftung im Objekt. */
481
- v.zielName = k.kennung || k.name;
482
- v.ziel = ordner + '.' + v.zielName;
465
+ /* Greift eine Mehrfachvorlage, gilt IHR Weg — die Ausgaenge stehen im
466
+ `%N%` der Vorlage, nicht in den Kanaelen des Geraets. Ohne diese
467
+ Ausnahme kaperte die Kanal-Aufteilung den Vorlagenweg: „Alle 2
468
+ erzeugen" lieferte am NEQ1660737 `…Licht_Bar_Esstisch.Licht_Bar.
469
+ Licht_Bar.STATE` und dazu den Wartungskanal mitten hinein
470
+ (17.09.2026). Die Beschriftung hatte den Fall ausgenommen, der
471
+ Schreibweg nicht — zwei Stellen, eine Bedingung. */
472
+ var mehrfach = S.entwurf.instanzen && S.entwurf.instanzen.length > 1;
473
+ var gruppen = (alleAusgaenge && !mehrfach) ? kanalGruppen(S.entwurf) : [];
474
+ if (gruppen.filter(function (g) { return !g.eigen; }).length > 1) {
475
+ /* Ein Behaelter fuer das Geraet, darunter je Kanal einer — die
476
+ Struktur der Quelle, abgebildet nach den Haken (C51).
477
+
478
+ Die Teil-Entwuerfe kamen bis 17.09.2026 frisch aus `entwurfFuer`:
479
+ sie hakten an, was das Muster des Kanals hergab, und lasen den
480
+ Entwurf oben gar nicht. Damit entstand, was niemand gewaehlt
481
+ hatte, und was jemand gewaehlt hatte, fiel weg. */
482
+ var ordner = aufteilBehaelter(S.entwurf);
483
+ return gruppen.map(function (g) {
484
+ var v = teilEntwurf(S.entwurf, g);
485
+ if (!v || !v.states.length) { return null; }
486
+ if (g.eigen) {
487
+ /* Punkte, die direkt am Geraet haengen, bleiben im Behaelter —
488
+ sonst verschwaenden sie beim Aufteilen. */
489
+ v.zielOrdner = S.entwurf.zielOrdner;
490
+ v.zielName = S.entwurf.zielName;
491
+ v.ziel = ordner;
492
+ } else {
493
+ v.zielOrdner = ordner;
494
+ /* Die Kennung, nicht der Anzeigename. „Licht Bar, oben" ergab eine
495
+ Kennung mit Leerzeichen und Komma, „Kueche.Decke" sogar eine
496
+ zusaetzliche Ebene samt Zwischenordner. Und der js-controller
497
+ lehnt so etwas nicht ab, er saeubert still: aus
498
+ `alias.0.wb[pruef]` wurde `alias.0.wb_pruef_`, der Rueckruf
499
+ meldete `null`. Das Objekt entstuende also unter einer anderen
500
+ Kennung als angezeigt (gemessen 08.09.2026). Der Name bleibt
501
+ davon unberuehrt — er steht als Beschriftung im Objekt. */
502
+ v.zielName = g.kennung || g.name;
503
+ v.ziel = ordner + '.' + v.zielName;
504
+ }
483
505
  uebernehmeBestand(v);
484
506
  return enumsUebertragen(v);
485
507
  }).filter(Boolean);
@@ -1307,7 +1329,14 @@ export function zeigeVerlegen(altVorgabe, zielVorgabe) {
1307
1329
  Zielleiste getippt wurde (`zielVorgabe`). */
1308
1330
  var alt = (typeof altVorgabe === 'string' && altVorgabe) ||
1309
1331
  (S.entwurf && (S.entwurf.ziel || S.entwurf.kanal)) || S.current;
1310
- if (!alt || alt.indexOf('alias.') !== 0 || !S.objects[alt]) { return; }
1332
+ /* `knotenDa`, nicht `S.objects[alt]`: ein Alias muss kein eigenes
1333
+ Kanalobjekt haben (E31). Der Knopf stand an so einem Alias schon
1334
+ da - `ergebnis.js` fragt `knotenDa` -, der Dialog stieg hier aber
1335
+ still aus: kein Fenster, keine Meldung. Produktiv betraf das
1336
+ `alias.0.Solar.Einstellungen.Bilanz` und `alias.0.NSPanel.1.Helligkeit`
1337
+ (T25, 18.09.2026). Verlegt wird ohnehin alles unter dem Pfad; ein
1338
+ fehlender Kanal fehlt danach am neuen Ort genauso. */
1339
+ if (!alt || alt.indexOf('alias.') !== 0 || !knotenDa(alt)) { return; }
1311
1340
  S.verlegeZiel = alt;
1312
1341
 
1313
1342
  var vorbelegt = (typeof zielVorgabe === 'string' && zielVorgabe &&
@@ -1739,7 +1768,7 @@ export function verlegeAlias(alt, neu) {
1739
1768
  markiert hatte. Wer danach nochmal verlegen wollte, fand keinen
1740
1769
  Knopf und musste den Alias erst wieder anklicken. `waehle` baut
1741
1770
  den Entwurf neu auf, und damit steht auch der Knopf wieder da. */
1742
- if (zielId && S.objects[zielId]) { waehle(zielId); }
1771
+ if (zielId && knotenDa(zielId)) { waehle(zielId); }
1743
1772
  else { zeichneErgebnis(); }
1744
1773
  });
1745
1774
  }
package/admin/js/werte.js CHANGED
@@ -285,7 +285,7 @@ export function auswerten(formel, roh) {
285
285
  var gedauert = performance.now() - t0;
286
286
  if (gedauert > GEDULD_MS) {
287
287
  formelGesperrt[formel] = Math.round(gedauert);
288
- console.warn('[alias-workbench] Formel gesperrt nach ' + Math.round(gedauert) + ' ms: ' + formel);
288
+ console.warn('[alias-workbench] Formula blocked after ' + Math.round(gedauert) + ' ms: ' + formel);
289
289
  return { ok: false, txt: tr('detail.formulaTooSlow', Math.round(gedauert)), fehler: true };
290
290
  }
291
291
  if (sch !== null) {
@@ -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
- var vorhAlias = aliasFuer(e.kanal);
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)));
@@ -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 } from './entwurf.js';
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
- if (vorhanden) {
479
- /* Der gefundene Kanal ist einer der Ausgaenge; der Ordner liegt
480
- darueber. Wie der einzelne Ausgang heisst, kann die Werkbank
481
- nicht wissen — wer sie umbenannt hat, sieht den Vorschlag in
482
- der Zielzeile und kann ihn anpassen. */
483
- var tm = vorhanden.split('.');
484
- tm.pop();
485
- e.zielOrdner = tm.join('.');
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
- e.zielOrdner = zt0.join('.') + '.' + geraet;
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
- if (!z.vorlagenWert) {
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
@@ -768,6 +789,13 @@ export function uebernehmeBestand(e) {
768
789
  });
769
790
  }
770
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)); }
771
799
  }
772
800
 
773
801
  export function unterschiede(e) {