iobroker.alias-workbench 0.9.12 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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, typKonflikte } from './erkennung.js';
12
+ import { erkenneEntwurf, musterVon, rolleVonPlatz, typKonflikte, plaetzeFuerRolle, typPasstZuPlatz, fehlendePflicht } 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';
@@ -18,6 +18,7 @@ import { rateAnzahl, rateZurueck, rateZurueckEine, rateBehalten } from './vorsch
18
18
  import { mqttEinzeln } from './mqtt.js';
19
19
  import { zeichneErgebnis, entwurfAngefasst } from './ergebnis.js';
20
20
  import { taster } from './schreiben.js';
21
+ import { rateUndMelde } from './musterwahl.js';
21
22
 
22
23
  export function berechnePlaetze(e, haupt) {
23
24
  /* --- welcher Punkt sitzt auf welchem Platz --- */
@@ -105,8 +106,16 @@ export function berechnePlaetze(e, haupt) {
105
106
  stillschweigend weg. Ein Alias, dem heimlich ein Licht fehlt, ist
106
107
  schlimmer als einer mit einem Punkt zu viel. Also kommen die
107
108
  Pflichtpunkte der anderen Kanaele mit dazu; dass sie keinen Platz
108
- finden, steht als Hinweis darueber. */
109
+ finden, steht als Hinweis darueber.
110
+
111
+ Nur Kanaele mit einer Funktion. Ein Kanal, den der Detektor bloss
112
+ als `info` erkennt — der Wartungskanal 0 bei Homematic —, hat
113
+ keinen Ausgang, der verloren gehen koennte; bei `info` ist aber
114
+ JEDER Punkt Pflicht, und so kamen AES_KEY, DEVICE_IN_BOOTLOADER,
115
+ DUTYCYCLE und RSSI_DEVICE angehakt in jeden HM-Aktor ohne Vorlage
116
+ (Ricardo, 19.09.2026, an hm-rpc.2.LEQ0605614). */
109
117
  (e.kanalGeraete || []).forEach(function (k) {
118
+ if (k.typ === 'info') { return; }
110
119
  (k.pflicht || []).forEach(function (pid) {
111
120
  var kurz = pid.slice(S.current.length + 1).replace(/\./g, '_');
112
121
  e.states.forEach(function (st) { if (st.n === kurz) { st.on = true; } });
@@ -117,6 +126,30 @@ export function berechnePlaetze(e, haupt) {
117
126
  return { platzVon: platzVon, platzAnzahl: platzAnzahl, imInfo: imInfo };
118
127
  }
119
128
 
129
+ /* Angehakt wird genau, wer im Muster `e.want` einen Platz bekommt. Alle
130
+ Zeilen treten dazu an, unabhaengig vom Haken — sonst haette die
131
+ Auswahl ein Gedaechtnis: nach „alle" lieferte sie mehr als direkt nach
132
+ dem Oeffnen (Ricardo, 25.08.2026).
133
+
134
+ `geaenderteBehalten`: Zeilen, an denen jemand etwas eingestellt hat
135
+ (Rolle, Typ, Einheit, Formel, Beschriftung), bleiben angehakt. Das
136
+ braucht der Musterwechsel (C54) — wer eine Zeile von Hand hergerichtet
137
+ hat, will sie nicht verlieren, nur weil das neue Muster keinen Platz
138
+ fuer sie hat. Der Knopf „nur was ins Muster passt" behaelt sie nicht:
139
+ er sagt woertlich, was er tut. */
140
+ export function nurWasInsMusterPasst(e, geaenderteBehalten) {
141
+ var kopie = JSON.parse(JSON.stringify({ kanal: e.kanal, states: e.states }));
142
+ kopie.states.forEach(function (s) { s.on = true; });
143
+ var f = erkenneEntwurf(kopie, e.want);
144
+ var drin = {};
145
+ if (f.length) {
146
+ f[0].states.forEach(function (x) {
147
+ if (x.id) { drin[x.id.slice(e.kanal.length + 1)] = true; }
148
+ });
149
+ }
150
+ e.states.forEach(function (s) { s.on = !!drin[s.n] || !!(geaenderteBehalten && s.geaendert); });
151
+ }
152
+
120
153
  export function baueListe(host, e, pl, rateKnopf, musterBlock) {
121
154
  /* Bezugspunkt der Marken: die Quelle, an der ich gerade STEHE.
122
155
 
@@ -173,6 +206,7 @@ export function baueListe(host, e, pl, rateKnopf, musterBlock) {
173
206
  keinen Platz hat? Genau die, die eben dazugekommen sind. */
174
207
  var ohnePlatz = 0;
175
208
  e.kanalGeraete.forEach(function (k) {
209
+ if (k.typ === 'info') { return; } /* wie die Vorbelegung oben */
176
210
  (k.pflicht || []).forEach(function (pid) {
177
211
  var kurz = pid.slice(S.current.length + 1).replace(/\./g, '_');
178
212
  e.states.forEach(function (st) {
@@ -234,7 +268,10 @@ export function baueListe(host, e, pl, rateKnopf, musterBlock) {
234
268
  bar.appendChild(document.createTextNode(tr('list.ofDatapoints')));
235
269
  bar.appendChild(el('span', 'sp2'));
236
270
  /* Mittig zwischen der Zahl und den drei Filtern - zwischen zwei
237
- Dehnfugen steht er von selbst in der Mitte. */
271
+ Dehnfugen steht er von selbst in der Mitte. An einer Quelle ist das
272
+ seit 19.09.2026 „Alle Datenpunkte des Geraets laden" (aus dem
273
+ Vorlagenkasten hierher getauscht), im Aliasmodus weiter „Freie
274
+ Plaetze vorschlagen" - dort gibt es keinen Kasten. */
238
275
  if (rateKnopf) { bar.appendChild(rateKnopf); rateKnopf = null; }
239
276
  bar.appendChild(el('span', 'sp2'));
240
277
  /* Die vier Auswahlknoepfe nur an einer Quelle. Am fertigen Alias sind
@@ -273,16 +310,7 @@ export function baueListe(host, e, pl, rateKnopf, musterBlock) {
273
310
  direkt nach dem Oeffnen (Ricardo, 25.08.2026). Jetzt treten
274
311
  alle Zeilen zur Platzvergabe an, unabhaengig vom Haken, und
275
312
  angehakt wird genau, wer einen Platz bekommt. */
276
- var kopie = JSON.parse(JSON.stringify({ kanal: e.kanal, states: e.states }));
277
- kopie.states.forEach(function (s) { s.on = true; });
278
- var f = erkenneEntwurf(kopie, e.want);
279
- var drin = {};
280
- if (f.length) {
281
- f[0].states.forEach(function (x) {
282
- if (x.id) { drin[x.id.slice(e.kanal.length + 1)] = true; }
283
- });
284
- }
285
- e.states.forEach(function (s) { s.on = !!drin[s.n]; });
313
+ nurWasInsMusterPasst(e, false);
286
314
  }, tr('list.onlyPatternHint')]])
287
315
  .filter(function (pr) { return mitVorlage || pr[0] !== tr('list.tpl'); })
288
316
  .forEach(function (pr) {
@@ -337,7 +365,13 @@ export function baueListe(host, e, pl, rateKnopf, musterBlock) {
337
365
  rl.appendChild(bWeg);
338
366
  card.appendChild(rl);
339
367
  }
340
- if (e.rateMeldung) {
368
+ /* Greift das Muster gar nicht, erklaert die orange Legende ueber der
369
+ Liste, was fehlt und welche Zeile hilft. Die Meldung des Vorschlagens
370
+ („Kein uebriger Datenpunkt passt dazu …") stand dann direkt darueber
371
+ und widersprach ihr — sie laesst Zeilen, an denen jemand etwas
372
+ eingestellt hat, absichtlich in Ruhe (Ricardo, 19.09.2026). */
373
+ var musterGreift = !e.want || Object.keys(pl.platzVon || {}).length > 0;
374
+ if (e.rateMeldung && musterGreift) {
341
375
  var rm = el('div', 'aside');
342
376
  rm.style.margin = '8px 15px';
343
377
  rm.textContent = e.rateMeldung;
@@ -636,24 +670,62 @@ export function baueListe(host, e, pl, rateKnopf, musterBlock) {
636
670
  var belegteSlots = {};
637
671
  Object.keys(platzVon).forEach(function (n) { belegteSlots[platzVon[n]] = 1; });
638
672
  var muster = e.want && musterVon(e.want);
639
- var freie = muster ? muster.states.filter(function (st) {
640
- return !belegteSlots[st.name] && !st.multiple;
641
- }) : [];
673
+ /* Ein Platzname, ein Eintrag (E6, 19.09.2026).
674
+
675
+ Manche Muster fuehren denselben Platz mehrfach - als Alternativen,
676
+ von denen der Detektor genau eine nimmt („one of the following“ im
677
+ type-detector). Mediaplayer: COVER als `media.cover` und als
678
+ Auffangnetz `media.cover.…` ohne Standardrolle; Warnung: START als
679
+ `date.start` und `date`; Klimaanlage, Luftreiniger, Ventilator:
680
+ SWING als `level.mode.swing` oder `switch.mode.swing`; Saugroboter:
681
+ BATTERY zweimal gleich. Einzeln gezeigt stand COVER zweimal da, die
682
+ zweite Zeile mit dem rohen Ausdruck statt einer Rolle, und jeder
683
+ Klick darauf legte einen Punkt ohne Rolle an. Jetzt steht der Platz
684
+ einmal da, mit der ersten Alternative, die eine Rolle hat; die
685
+ anderen Rollen stehen daneben. */
686
+ var platzGruppen = [], gruppeVon = {};
687
+ (muster ? muster.states : []).forEach(function (st) {
688
+ if (!st.name || st.multiple) { return; }
689
+ var g = gruppeVon[st.name];
690
+ if (!g) { g = gruppeVon[st.name] = { st: st, rollen: [] }; platzGruppen.push(g); }
691
+ else if (!rolleVonPlatz(g.st) && rolleVonPlatz(st)) { g.st = st; }
692
+ if (st.required) { g.pflicht = true; }
693
+ var r0 = rolleVonPlatz(st);
694
+ if (r0 && g.rollen.indexOf(r0) === -1) { g.rollen.push(r0); }
695
+ });
696
+ /* Belegt ist ein Platz auch, wenn es seine Zeile schon gibt - unter
697
+ seinem Namen oder aus ihm angelegt -, selbst wenn der Detektor sie
698
+ (noch) nicht einordnet. Greift das Muster gar nicht, ordnet er keine
699
+ ein, und die Liste bot jeden Platz weiter an: ein zweiter Klick legte
700
+ eine zweite Zeile gleichen Namens an, die der Alias nicht haben kann
701
+ (gemessen am Mediaplayer ohne STATE, 19.09.2026). */
702
+ var zeileDa = {};
703
+ e.states.forEach(function (s0) {
704
+ if (s0.n) { zeileDa[s0.n] = 1; }
705
+ if (s0.ausPlatz) { zeileDa[s0.ausPlatz] = 1; }
706
+ });
707
+ var freie = platzGruppen.filter(function (g) {
708
+ return !belegteSlots[g.st.name] && !zeileDa[g.st.name];
709
+ });
642
710
 
643
711
  if (freie.length) {
644
712
  var fk = el('div', 'freikopf');
645
713
  fk.appendChild(el('span', null, tr('pattern.freeSlots', musterName(e.want) || e.want)));
646
- fk.appendChild(el('span', 'cnt2', tr('list.countOf', freie.length, muster.states.length)));
714
+ fk.appendChild(el('span', 'cnt2', tr('list.countOf', freie.length, platzGruppen.length)));
647
715
  card.appendChild(fk);
648
716
 
649
- freie.forEach(function (st) {
650
- var fr = el('div', 'freirow' + (st.required ? ' pflicht' : ''));
717
+ freie.forEach(function (g) {
718
+ var st = g.st;
719
+ var rolle = rolleVonPlatz(st);
720
+ var andere = g.rollen.filter(function (r0) { return r0 !== rolle; });
721
+ var fr = el('div', 'freirow' + (g.pflicht ? ' pflicht' : ''));
651
722
  fr.tabIndex = 0;
652
723
  fr.setAttribute('role', 'button');
653
- fr.appendChild(el('span', 'mk3', st.required ? '✕' : '+'));
724
+ fr.appendChild(el('span', 'mk3', g.pflicht ? '✕' : '+'));
654
725
  fr.appendChild(el('span', 'sn2', st.name));
655
- fr.appendChild(el('span', 'rl2', rolleVonPlatz(st) || String(st.role || '')));
656
- fr.appendChild(el('span', 'rx2', st.required ? tr('pattern.required') : String(st.role || '')));
726
+ fr.appendChild(el('span', 'rl2', rolle || String(st.role || '')));
727
+ fr.appendChild(el('span', 'rx2', g.pflicht ? tr('pattern.required')
728
+ : (andere.length ? tr('pattern.orRole', andere.join(', ')) : String(st.role || ''))));
657
729
 
658
730
  var nimm = function () {
659
731
  e.states.push({
@@ -723,7 +795,60 @@ export function baueListe(host, e, pl, rateKnopf, musterBlock) {
723
795
  hoch und legte sich ueber die Liste, die man gerade lesen wollte
724
796
  (Ricardo, 08.09.2026). Ein Klick auf das „i" oeffnet sie stattdessen
725
797
  als Dialog — dort darf sie so lang sein, wie sie ist. */
726
- if (ohnePlatzZahl) {
798
+ /* Greift das Muster gar nicht, weil ein Pflichtplatz fehlt, sagt die
799
+ Legende welcher — und ob eine NICHT angehakte Zeile ihn bekaeme.
800
+
801
+ Vorher stand das nur als „✕ SET fehlt" im Musterfeld, und ob
802
+ `cmnd_POWER` mit seiner Rolle `switch` passen wuerde, sah man nicht:
803
+ nicht angehakte Zeilen bekommt der Detektor gar nicht zu sehen. Ein
804
+ Hinweis in der Detailzeile („… wird vergeben, sobald das Muster
805
+ greift") stand an der falschen Stelle — die Zeile, die ihn trug, war
806
+ in Ordnung (Ricardo, 19.09.2026). Deshalb steht es hier, auch wenn
807
+ gar nichts angehakt ist. */
808
+ var fehlt = [];
809
+ var muL = e.want && musterVon(e.want);
810
+ if (muL && !kipptAnTyp && !Object.keys(platzVon).length) {
811
+ var passtAuf = function (st, s, auchTyp) {
812
+ return plaetzeFuerRolle(e.want, s.role).some(function (p) {
813
+ return p.name === st.name && (!auchTyp || !s.typ || typPasstZuPlatz(p, s.typ));
814
+ });
815
+ };
816
+ var gesehenL = {};
817
+ muL.states.forEach(function (st) {
818
+ if (!st.required || gesehenL[st.name]) { return; }
819
+ gesehenL[st.name] = 1;
820
+ if (e.states.some(function (s) { return s.on && passtAuf(st, s, true); })) { return; }
821
+ /* Kandidat ist jede nicht angehakte Zeile, deren ROLLE passt — auch
822
+ wenn der Typ nicht passt. Am 3D-Drucker bringt `cmnd.POWER` die
823
+ Rolle `switch` selbst mit, aber den Typ `mixed`; nur nach
824
+ passendem Typ gefragt, fiel er stillschweigend heraus, und die
825
+ Legende nannte niemanden (Ricardo, 19.09.2026). Den Typ stellt
826
+ der Knopf gleich mit. Passende Typen zuerst. */
827
+ var aus = e.states.filter(function (s) { return !s.on && s.n && passtAuf(st, s, false); });
828
+ aus.sort(function (a, b) { return (passtAuf(st, b, true) ? 1 : 0) - (passtAuf(st, a, true) ? 1 : 0); });
829
+ var soll = Array.isArray(st.type) ? st.type[0] : st.type;
830
+ fehlt.push({ platz: st.name, aus: aus, soll: soll,
831
+ typFalsch: aus.length ? !passtAuf(st, aus[0], true) : false });
832
+ });
833
+ }
834
+
835
+ /* Was wirklich fehlt, samt der Gruppen (`requiredOneOf`): an der
836
+ Klimaanlage ist das der Sollwert, nicht MODE (U13). Die Kandidaten
837
+ oben kennen nur `required`; der Text nimmt die genauere Liste. */
838
+ var fehltNamen = (muL && !kipptAnTyp && !Object.keys(platzVon).length)
839
+ ? fehlendePflicht(e.states, e.want) : [];
840
+ if (!fehltNamen.length) { fehltNamen = fehlt.map(function (f) { return f.platz; }); }
841
+
842
+ /* Greift das Muster erst nach dem Vorschlag, sagt die Legende das und
843
+ bietet den Knopf gleich hier an. Sonst liest man „greift gerade
844
+ nicht" und haelt das Muster fuer falsch gewaehlt, obwohl ein Klick
845
+ fehlt (Ricardo, 19.09.2026). Die Aussicht hat die Musterwahl schon
846
+ berechnet. */
847
+ var ausR = e.rateAussicht;
848
+ var greiftNachRaten = !!(fehltNamen.length && ausR && ausR.want === e.want &&
849
+ ausR.selbst && ausR.selbst.passt && !rateAnzahl(e));
850
+
851
+ if (ohnePlatzZahl || fehltNamen.length) {
727
852
  var mn = musterName(e.want) || e.want || '?';
728
853
  var lg = el('div', 'legendenzeile');
729
854
  /* Steht die ganze Liste ohne Platz da, weil EINE Zeile im falschen
@@ -739,8 +864,43 @@ export function baueListe(host, e, pl, rateKnopf, musterBlock) {
739
864
  lg.appendChild(document.createTextNode(kipptAnTyp
740
865
  ? tr('pattern.noPlaceBecauseType', mn, kipptAnTyp.platz,
741
866
  kipptAnTyp.erwartet.join(tr('pattern.typeOr')), kipptAnTyp.typ)
742
- : tr('pattern.noPlaceLegend', mn)));
867
+ : (fehltNamen.length
868
+ ? tr('pattern.offMissing', mn, fehltNamen.join(', '))
869
+ : tr('pattern.noPlaceLegend', mn))));
870
+ if (greiftNachRaten && !kipptAnTyp) {
871
+ lg.appendChild(document.createTextNode(' ' + tr('pattern.offGuess', tr('guess.button'))));
872
+ var bR = el('button', 'btn schmal', tr('guess.button'));
873
+ bR.type = 'button';
874
+ bR.title = tr('guess.hint');
875
+ bR.style.marginLeft = '6px';
876
+ bR.addEventListener('click', function (ev) {
877
+ ev.stopPropagation();
878
+ rateUndMelde(e);
879
+ entwurfAngefasst(); S.openRow = null; zeichneErgebnis();
880
+ });
881
+ lg.appendChild(bR);
882
+ }
883
+ var mitKandidat = fehlt.filter(function (f) { return f.aus.length; })[0];
884
+ if (mitKandidat) {
885
+ var kand = mitKandidat.aus[0];
886
+ var mitTyp = mitKandidat.typFalsch && mitKandidat.soll;
887
+ lg.appendChild(document.createTextNode(' ' + (mitTyp
888
+ ? tr('pattern.offCandidateType', kand.n, mitKandidat.platz, kand.typ, mitKandidat.soll)
889
+ : tr('pattern.offCandidate', kand.n, mitKandidat.platz))));
890
+ var bk = el('button', 'btn schmal', mitTyp
891
+ ? tr('pattern.tickItType', mitKandidat.soll) : tr('pattern.tickIt'));
892
+ bk.type = 'button';
893
+ bk.style.marginLeft = '6px';
894
+ bk.addEventListener('click', function (ev) {
895
+ ev.stopPropagation();
896
+ kand.on = true;
897
+ if (mitTyp) { kand.typ = mitKandidat.soll; kand.geaendert = true; }
898
+ entwurfAngefasst(); zeichneErgebnis();
899
+ });
900
+ lg.appendChild(bk);
901
+ }
743
902
 
903
+ if (!ohnePlatzZahl) { legendenPlatz.appendChild(lg); host.appendChild(card); return; }
744
904
  var ib = el('button', 'infoknopf', 'i');
745
905
  ib.type = 'button';
746
906
  ib.title = tr('info.more');
@@ -39,7 +39,6 @@
39
39
  "check.sourceHasValue": "Quelle hat einen Wert",
40
40
  "check.targetCan": "Ziel kann, was verlangt wird",
41
41
  "check.targetCanFine": "alle in Ordnung",
42
- "check.targetNoRead": "{0}: die Lesequelle ändert sich nie",
43
42
  "check.targetNoWrite": "{0}: das Ziel ist nicht beschreibbar",
44
43
  "check.targetVoidFormula": "{0}: Schreibformel läuft ins Leere, das Ziel ist {1}",
45
44
  "check.title": "Prüfungen — laufen immer mit, fassen nichts an",
@@ -70,7 +69,7 @@
70
69
  "detail.display": "Anzeige",
71
70
  "detail.elsewhere": "(anderswo)",
72
71
  "detail.expects": "erwartet {0}",
73
- "detail.formula": "Formel",
72
+ "detail.formula": "Leseformel",
74
73
  "detail.formulaBroken": "Formel fehlerhaft",
75
74
  "detail.formulaHint": "\"ON\"/\"OFF\" → boolean macht der Alias von selbst, dafür braucht es keine Formel.",
76
75
  "detail.formulaPlaceholder": "leer = Wert unverändert übernehmen",
@@ -78,6 +77,7 @@
78
77
  "detail.formulaTooSlow": "Diese Formel hat {0} ms gebraucht und wird nicht mehr ausgeführt. Nach dem Neuladen des Reiters wird sie einmal neu versucht.",
79
78
  "detail.jsonField": "Feld im JSON",
80
79
  "detail.matchesState": "✓ deckt sich mit dem Zustand",
80
+ "detail.nameTaken": "Den Namen {0} trägt schon eine andere Zeile.",
81
81
  "detail.name": "Name",
82
82
  "detail.namePlaceholder": "z. B. UNREACH",
83
83
  "detail.noSource": "— keine Quelle bekannt —",
@@ -90,17 +90,24 @@
90
90
  "detail.pickOne": "— bitte wählen —",
91
91
  "detail.rawNothing": "— Quelle liefert nichts —",
92
92
  "detail.rawValue": "Rohwert",
93
+ "detail.readOnlySlotWrites": "{0} ist im Muster nur zum Lesen da — „schreibt auf“ sollte „— nicht schreiben —“ sein, sonst geht ein geschriebener Wert ans Gerät.",
93
94
  "detail.readsFrom": "liest aus",
94
95
  "detail.removePoint": "Diesen Punkt entfernen",
95
- "detail.reportsNothing": "ändert sich nie",
96
+ "detail.readIsButton": "Taster",
97
+ "detail.slot": "Platz im Muster",
98
+ "detail.slotNone": "— kein Platz —",
99
+ "detail.slotNotGiven": "{0} ist gewählt, aber nicht vergeben — unter „Rolle“ steht, was nicht passt (meist der Typ).",
100
+ "detail.slotRequired": "Pflicht",
101
+ "detail.slotHint": "Setzt Rolle, Typ, Einheit und Werteliste so, wie das Muster sie für diesen Platz vorgibt. Datenpunkt und Name bleiben.",
96
102
  "detail.role": "Rolle",
97
103
  "detail.sameAsRead": "— dieselbe wie lesen —",
98
- "detail.separateHint": "getrennt — der Schalter zeigt den Zustand, nicht den Befehl",
104
+ "detail.separateHint": "Liest woanders, als es schreibt: angezeigt wird, was das Gerät zurückmeldet — nicht der zuletzt gesendete Befehl.",
99
105
  "detail.sourceGone": "Quelle existiert nicht",
100
106
  "detail.stateSays": "Zustand sagt {0}",
101
107
  "detail.takeFromAlias": "Diesen einen Wert aus dem gespeicherten Alias zurückholen",
102
108
  "detail.takeOne": "Diesen einen Wert aus der Vorlage übernehmen",
103
- "detail.targetNoRead": "Die Lesequelle trägt read: false — dort kommt nie eine Aktualisierung an. Ein Wert kann trotzdem darin stehen; es ist der, den zuletzt jemand hineingeschrieben hat, oft der Anfangswert des Adapters. Der Alias zeigt dann für immer diesen einen Wert. Bei einem Tastendruck ist das richtig so: er hat keinen Zustand. Dann gehört hier gar keine Lesequelle hin.",
109
+ "detail.readIsButtonHint": "Rolle button mit Schreibziel: der Alias-Datenpunkt bekommt read: false, und vis, Alexa und matter zeichnen ihn als Knopf. Die Lesequelle stört dabei nicht — mit ihr bekommt der Alias jeden Druck mit, auch einen an der echten Taste. Nach dem Druck bleibt der letzte Wert stehen, wie an der Quelle auch.",
110
+ "detail.buttonNoRead": "Knopf ohne Lesequelle — liest nicht mit",
104
111
  "detail.targetNoWrite": "Der Zielpunkt trägt write: false — sein Adapter reicht nichts ans Gerät weiter. ioBroker legt den Wert trotzdem ab, passieren wird aber nichts.",
105
112
  "detail.targetTypeHint": "Der Typ des Zielpunktes ist {0}. Er entscheidet, was von der Schreibformel ankommt: mixed und string nehmen Text, boolean und number rechnen ihn zurück.",
106
113
  "detail.targetTypeVoid": "Das Ziel ist {0} und nimmt keinen Text. Die Schreibformel liefert aber Text — der js-controller rechnet ihn beim Schreiben wieder auf {0} zurück, die Formel bleibt also wirkungslos. Geschaltet wird trotzdem.",
@@ -108,7 +115,6 @@
108
115
  "detail.tplWantsEmpty": "Die Vorlage lässt dieses Feld leer.",
109
116
  "detail.type": "Typ",
110
117
  "detail.unit": "Einheit",
111
- "detail.willBecome": "entsteht als",
112
118
  "detail.writeFormula": "Schreibformel",
113
119
  "detail.writeFormulaHint": "rechnet in die andere Richtung: was das Gerät beim Schalten empfangen soll.",
114
120
  "detail.writeFormulaPlaceholder": "z. B. val ? \"ON\" : \"OFF\"",
@@ -150,6 +156,7 @@
150
156
  "enums.room": "Raum",
151
157
  "enums.roomHint": "Ordnet den Alias einem Raum zu. Das steht nicht am Objekt, sondern in der Aufzählung „enum.rooms\" — Alexa, Google und die Oberflächen gruppieren danach.",
152
158
  "enums.suggested": "vorgeschlagen",
159
+ "enums.notLoadedField": "Räume und Funktionen konnten nicht geladen werden — Raum und Funktion werden deshalb nicht geschrieben; der Alias selbst entsteht trotzdem. Die Werkbank versucht es von selbst noch dreimal, sonst den Reiter neu laden.",
153
160
  "enums.unknown": "Aufzählungen nicht geladen — Raum und Funktion bleiben unangetastet.",
154
161
  "enums.unused": "leer",
155
162
  "enums.willCreate": "neu anlegen",
@@ -181,7 +188,8 @@
181
188
  "guess.mark": "vermutet",
182
189
  "guess.noneFit": "Frei bleibt {0}. Kein übriger Datenpunkt passt dazu — vermutet wird nach Namen, Werteliste, Einheit und Typ, und nichts kam über die Schwelle.",
183
190
  "guess.noneFull": "Alle Plätze des Musters sind besetzt — es bleibt nichts vorzuschlagen.",
184
- "guess.noneHand": "{0} Zeilen sind von Hand entschieden und bleiben unangetastet.",
191
+ "guess.noneHand": "{0} Zeilen sind von Hand entschieden und bleiben unangetastet: {1}.",
192
+ "guess.noneHandOne": "{0} ist von Hand entschieden und bleibt unangetastet.",
185
193
  "guess.noneUsed": "Frei bleibt {0}. Jeder übrige Datenpunkt steckt schon als Lese- oder Schreibquelle in einer belegten Zeile.",
186
194
  "guess.slotsMore": "{0} und {1} weitere",
187
195
  "guess.tplAsk": "Dieses Gerät ist als „{0}“ identifiziert; die Zuordnung steht damit schon. Der Vorschlag belegt zusätzliche Plätze aus den übrigen Datenpunkten — ein Versuch, kein Wissen, jede Zuordnung wäre einzeln zu prüfen. Gedacht für Sonderfälle, etwa wenn das Gerät mehr kann, als die Vorlage abdeckt.",
@@ -302,6 +310,8 @@
302
310
  "mq.stFine": "angelegt, sendet",
303
311
  "mq.stMissing": "Punkt fehlt",
304
312
  "mq.stMute": "kann nicht senden",
313
+ "mq.stOff": "angelegt, aber abgeschaltet",
314
+ "mq.stUnknown": "angelegt, Senden unbekannt",
305
315
  "mq.title": "MQTT-Gerät",
306
316
  "mq.unknownCommand": "unbekannter Befehl",
307
317
  "mq.write": "Anlegen ({0})",
@@ -315,13 +325,22 @@
315
325
  "pattern.manualInstead": "von Hand statt {0}",
316
326
  "pattern.missing": "{0} fehlt",
317
327
  "pattern.noMatch": "kein Muster passt zu „{0}“",
328
+ "pattern.offGuess": "Mit „{0}“ würde es greifen.",
329
+ "pattern.offMissing": "Das {0}-Muster greift gerade nicht: {1} fehlt. Deshalb hat keine Zeile einen Platz.",
330
+ "pattern.offCandidate": "{0} passt auf {1}, ist aber nicht angehakt.",
331
+ "pattern.slotHeldBy": "{0} hat schon {1} — diese Zeile bekommt so keinen Platz. Oben unter „Platz im Muster“ einen freien Platz wählen.",
332
+ "pattern.tickIt": "anhaken",
333
+ "pattern.offCandidateType": "{0} passt mit seiner Rolle auf {1}, ist aber nicht angehakt und hat den Typ {2} statt {3}.",
334
+ "pattern.tickItType": "anhaken, {0}",
318
335
  "pattern.noPlaceBecauseType": "Das {0}-Muster greift gerade nicht: {1} verlangt {2}, eingestellt ist {3}. Deshalb hat keine Zeile einen Platz.",
319
336
  "pattern.noPlaceLegend": "So hinterlegte Zeilen haben keinen Platz im {0}-Muster",
320
337
  "pattern.noPlaceLong": "kein Platz im {0}-Muster — der Punkt wird trotzdem angelegt und ist voll benutzbar, nur Alexa & Co. zählen ihn separat",
321
338
  "pattern.notDetected": "nicht erkannt",
339
+ "pattern.orRole": "oder {0}",
322
340
  "pattern.required": "PFLICHT — fehlt",
323
341
  "pattern.search": "tippen zum Suchen …",
324
342
  "pattern.slotName": "Der Punkt sitzt auf dem Platz „{0}\" des {1}-Musters. Diese Platznamen kommen aus dem type-detector und sind überall gleich — sie werden nicht übersetzt, weil der Datenpunkt im Alias genau so heißt.",
343
+ "pattern.pointsTaken": "✓ {0} Punkte",
325
344
  "pattern.slotsTaken": "✓ {0}/{1} belegt",
326
345
  "pattern.toSlot": " → Platz {0} im {1}-Muster",
327
346
  "pattern.typeMismatch": "Der Platz {0} verlangt {1}, eingestellt ist {2} — so fällt der Punkt aus dem Muster, auch wenn die Rolle stimmt.",
@@ -379,6 +398,7 @@
379
398
  "swap.now": "Jetzt tauschen",
380
399
  "swap.removeIt": "diesen Punkt beim Tauschen entfernen",
381
400
  "swap.same": "Das ist dieselbe Quelle wie bisher.",
401
+ "swap.pickFirst": "Erst eine neue Quelle wählen.",
382
402
  "swap.search": "Suchen (Kennung oder Name) …",
383
403
  "swap.title": "Quelle tauschen",
384
404
  "swap.to": "Neue Quelle",
@@ -424,15 +444,19 @@
424
444
  "tpl.presentIn": "{0} vorhanden",
425
445
  "tpl.remembered": "gemerkt",
426
446
  "tpl.rememberedHint": "Dieser Alias ist aus dieser Vorlage entstanden — sie bleibt, bis du sie ausdrücklich umstellst.",
427
- "tpl.showAllPoints": "Alle Datenpunkte des Geräts",
447
+ "tpl.showAllPoints": "Alle Datenpunkte des Geräts laden",
428
448
  "tpl.showAllPointsHelp": "Verlässt den Vorschlag der Vorlage und zeigt jeden Datenpunkt, den das Gerät hat — auch die, die keine Vorlage aufführt. Von hier lässt sich ein einzelner davon in den Alias holen.",
429
- "tpl.showProposal": "Vorschlag zeigen",
449
+ "tpl.showProposal": "Vorschlag der Vorlage laden",
430
450
  "tpl.showProposalHelp": "Zurück zu dem, was die Vorlage für dieses Gerät vorschlägt.",
431
451
  "tpl.skippedEmpty": "„{0}“ hätte die höhere Punktzahl, baut an diesem Gerät aber keine einzige Zeile — übergangen",
432
452
  "tpl.skippedHead": "{0} weggelassen, weil die Quelle fehlt",
433
453
  "tpl.skippedChip": "{0} ohne Quelle",
434
454
  "tpl.skippedNowhere": "{0}: weder in {1}",
435
455
  "tpl.skippedSource": "{0}: {1} fehlt",
456
+ "tpl.skippedWhy": "{0}: {1}",
457
+ "tpl.whyNoPoint": "{0} gibt es nicht",
458
+ "tpl.whyNoJson": "{0} liefert kein JSON",
459
+ "tpl.whyNoField": "{0} hat kein Feld {1}",
436
460
  "tpl.whyNone": "Warum keine Vorlage greift",
437
461
  "tpl.whyThis": "Warum diese Vorlage",
438
462
  "tpl.withoutField": "{0} ohne {1}",
@@ -528,7 +552,7 @@
528
552
  "tree.filterAria": "Objekte filtern",
529
553
  "tree.loading": "lade Objekte …",
530
554
  "tree.looksLikeDevice": "sieht wie ein einzelnes Gerät aus",
531
- "tree.noObjectHint": "kein Objekt — nur aus der ID abgeleitet",
555
+ "tree.noObjectHint": "kein echter Ordner",
532
556
  "tree.nothingFound": "nichts gefunden",
533
557
  "tree.sourceGone": "{0} Punkt(e) dieses Alias zeigen auf eine Quelle, die es nicht gibt. Der js-controller merkt sich das dauerhaft — hilft nur löschen und neu anlegen.",
534
558
  "tree.sources": "Alias anlegen",
@@ -543,7 +567,7 @@
543
567
  "tv.badNoStates": "Es fehlen die Datenpunkte (zustaende).",
544
568
  "tv.badNotObject": "Erwartet wird eine einzelne Vorlage als JSON-Objekt.",
545
569
  "tv.badRecog": "„erkennung“ muss ein Objekt sein.",
546
- "tv.badState": "{0} Datenpunkte ohne Namen oder ohne Lesepfad.",
570
+ "tv.badState": "{0} Datenpunkte ohne Namen oder ohne Lese- und Schreibpfad.",
547
571
  "tv.badType": "Den Gerätetyp „{0}“ kennt dieser Detector nicht.",
548
572
  "tv.clickRow": "Zeile anklicken, um den Punkt zu bearbeiten",
549
573
  "tv.delete": "Löschen",
@@ -39,7 +39,6 @@
39
39
  "check.sourceHasValue": "source has a value",
40
40
  "check.targetCan": "target can do what is asked",
41
41
  "check.targetCanFine": "all fine",
42
- "check.targetNoRead": "{0}: the read source never changes",
43
42
  "check.targetNoWrite": "{0}: the target is not writable",
44
43
  "check.targetVoidFormula": "{0}: write formula runs into nothing, the target is {1}",
45
44
  "check.title": "Checks — always running, never touching anything",
@@ -70,7 +69,7 @@
70
69
  "detail.display": "Display",
71
70
  "detail.elsewhere": "(elsewhere)",
72
71
  "detail.expects": "expects {0}",
73
- "detail.formula": "Formula",
72
+ "detail.formula": "Read formula",
74
73
  "detail.formulaBroken": "formula is faulty",
75
74
  "detail.formulaHint": "\"ON\"/\"OFF\" → boolean is done by the alias itself, no formula needed.",
76
75
  "detail.formulaPlaceholder": "empty = take the value unchanged",
@@ -78,6 +77,7 @@
78
77
  "detail.formulaTooSlow": "This formula took {0} ms and is no longer executed. Reloading the tab gives it one more try.",
79
78
  "detail.jsonField": "Field in the JSON",
80
79
  "detail.matchesState": "✓ matches the state",
80
+ "detail.nameTaken": "Another row already has the name {0}.",
81
81
  "detail.name": "Name",
82
82
  "detail.namePlaceholder": "e.g. UNREACH",
83
83
  "detail.noSource": "— no source known —",
@@ -90,17 +90,24 @@
90
90
  "detail.pickOne": "— please choose —",
91
91
  "detail.rawNothing": "— the source delivers nothing —",
92
92
  "detail.rawValue": "Raw value",
93
+ "detail.readOnlySlotWrites": "{0} is read-only in the pattern — “writes to” should be “— do not write —”, otherwise a written value goes to the device.",
93
94
  "detail.readsFrom": "reads from",
94
95
  "detail.removePoint": "Remove this point",
95
- "detail.reportsNothing": "never changes",
96
+ "detail.readIsButton": "button",
97
+ "detail.slot": "Place in pattern",
98
+ "detail.slotNone": "— no place —",
99
+ "detail.slotNotGiven": "{0} is chosen but not assigned — see “Role” for what does not fit (usually the type).",
100
+ "detail.slotRequired": "required",
101
+ "detail.slotHint": "Sets role, type, unit and value list as the pattern prescribes for this place. Datapoint and name stay.",
96
102
  "detail.role": "Role",
97
103
  "detail.sameAsRead": "— same as read —",
98
- "detail.separateHint": "separate — the switch shows the state, not the command",
104
+ "detail.separateHint": "Reads from somewhere else than it writes to: what is shown is what the device reports back — not the command sent last.",
99
105
  "detail.sourceGone": "source does not exist",
100
106
  "detail.stateSays": "state says {0}",
101
107
  "detail.takeFromAlias": "Take this one value back from the stored alias",
102
108
  "detail.takeOne": "Take this one value from the template",
103
- "detail.targetNoRead": "The read source carries read: false — no update ever arrives there. A value may still sit in it: the one somebody wrote last, often the adapter's initial value. The alias will then show that one value forever. For a button press that is correct: it has no state. Then no read source belongs here at all.",
109
+ "detail.readIsButtonHint": "Role button with a write target: the alias datapoint gets read: false, and vis, Alexa and matter draw it as a button. The read source does no harm — with it the alias sees every press, including one on the physical button. After a press the last value stays, as it does on the source.",
110
+ "detail.buttonNoRead": "Button without read source — does not read along",
104
111
  "detail.targetNoWrite": "The target point carries write: false — its adapter passes nothing on to the device. ioBroker stores the value anyway, but nothing will happen.",
105
112
  "detail.targetTypeHint": "The target point is of type {0}. It decides what survives of the write formula: mixed and string take text, boolean and number convert it back.",
106
113
  "detail.targetTypeVoid": "The target is {0} and takes no text. The write formula does produce text — the js-controller converts it back to {0} on write, so the formula has no effect. Switching still works.",
@@ -108,7 +115,6 @@
108
115
  "detail.tplWantsEmpty": "The template leaves this field empty.",
109
116
  "detail.type": "Type",
110
117
  "detail.unit": "Unit",
111
- "detail.willBecome": "will become",
112
118
  "detail.writeFormula": "Write formula",
113
119
  "detail.writeFormulaHint": "converts the other way: what the device should receive when switched.",
114
120
  "detail.writeFormulaPlaceholder": "e.g. val ? \"ON\" : \"OFF\"",
@@ -150,6 +156,7 @@
150
156
  "enums.room": "Room",
151
157
  "enums.roomHint": "Assigns the alias to a room. This is not stored on the object but in the \"enum.rooms\" enumeration — Alexa, Google and the dashboards group by it.",
152
158
  "enums.suggested": "suggested",
159
+ "enums.notLoadedField": "Rooms and functions could not be loaded — so room and function will not be written; the alias itself is still created. The workbench retries three times by itself, otherwise reload the tab.",
153
160
  "enums.unknown": "Enumerations not loaded — room and function stay untouched.",
154
161
  "enums.unused": "empty",
155
162
  "enums.willCreate": "create new",
@@ -181,7 +188,8 @@
181
188
  "guess.mark": "guessed",
182
189
  "guess.noneFit": "{0} stays free. No remaining datapoint fits — guessing goes by name, value list, unit and type, and nothing cleared the threshold.",
183
190
  "guess.noneFull": "Every slot of the pattern is taken — there is nothing left to suggest.",
184
- "guess.noneHand": "{0} rows were decided by hand and are left untouched.",
191
+ "guess.noneHand": "{0} rows were decided by hand and are left untouched: {1}.",
192
+ "guess.noneHandOne": "{0} was decided by hand and is left untouched.",
185
193
  "guess.noneUsed": "{0} stays free. Every remaining datapoint already serves as a read or write source in a filled row.",
186
194
  "guess.slotsMore": "{0} and {1} more",
187
195
  "guess.tplAsk": "This device is identified as “{0}”, so the assignment is already settled. The suggestion fills additional slots from the remaining data points — an attempt, not knowledge, and every match would need checking. Meant for special cases, e.g. when the device can do more than the template covers.",
@@ -302,6 +310,8 @@
302
310
  "mq.stFine": "present, sends",
303
311
  "mq.stMissing": "point missing",
304
312
  "mq.stMute": "cannot send",
313
+ "mq.stOff": "present, but switched off",
314
+ "mq.stUnknown": "present, sending unknown",
305
315
  "mq.title": "MQTT device",
306
316
  "mq.unknownCommand": "unknown command",
307
317
  "mq.write": "Create ({0})",
@@ -315,13 +325,22 @@
315
325
  "pattern.manualInstead": "by hand instead of {0}",
316
326
  "pattern.missing": "{0} missing",
317
327
  "pattern.noMatch": "no pattern matches \"{0}\"",
328
+ "pattern.offGuess": "With “{0}” it would apply.",
329
+ "pattern.offMissing": "The {0} pattern does not apply right now: {1} is missing. That is why no row has a slot.",
330
+ "pattern.offCandidate": "{0} fits {1} but is not ticked.",
331
+ "pattern.slotHeldBy": "{1} already holds {0} — this row gets no place that way. Pick a free place under “Place in pattern” above.",
332
+ "pattern.tickIt": "tick it",
333
+ "pattern.offCandidateType": "{0} fits {1} by its role, but is not ticked and has type {2} instead of {3}.",
334
+ "pattern.tickItType": "tick it, {0}",
318
335
  "pattern.noPlaceBecauseType": "The {0} pattern does not apply right now: {1} requires {2}, but {3} is set. That is why no row has a slot.",
319
336
  "pattern.noPlaceLegend": "Rows shaded like this have no slot in the {0} pattern",
320
337
  "pattern.noPlaceLong": "no slot in the {0} pattern — the point is still created and fully usable, only Alexa and friends count it separately",
321
338
  "pattern.notDetected": "not detected",
339
+ "pattern.orRole": "or {0}",
322
340
  "pattern.required": "REQUIRED — missing",
323
341
  "pattern.search": "type to search …",
324
342
  "pattern.slotName": "This point sits in slot \"{0}\" of the {1} pattern. Slot names come from the type-detector and are the same everywhere — they are not translated, because the alias datapoint is named exactly that.",
343
+ "pattern.pointsTaken": "✓ {0} points",
325
344
  "pattern.slotsTaken": "✓ {0}/{1} filled",
326
345
  "pattern.toSlot": " → slot {0} in the {1} pattern",
327
346
  "pattern.typeMismatch": "Slot {0} requires {1}, but {2} is set — the state drops out of the pattern even though the role is right.",
@@ -379,6 +398,7 @@
379
398
  "swap.now": "Swap now",
380
399
  "swap.removeIt": "remove this point while swapping",
381
400
  "swap.same": "That is the same source as before.",
401
+ "swap.pickFirst": "Pick a new source first.",
382
402
  "swap.search": "Search (id or name) …",
383
403
  "swap.title": "Swap source",
384
404
  "swap.to": "New source",
@@ -424,15 +444,19 @@
424
444
  "tpl.presentIn": "{0} present",
425
445
  "tpl.remembered": "remembered",
426
446
  "tpl.rememberedHint": "This alias was built from this template — it stays until you change it deliberately.",
427
- "tpl.showAllPoints": "All datapoints of the device",
447
+ "tpl.showAllPoints": "Load all datapoints of the device",
428
448
  "tpl.showAllPointsHelp": "Leaves the template proposal and lists every datapoint the device has, including those no template mentions. From here you can pull a single one into the alias.",
429
- "tpl.showProposal": "show proposal",
449
+ "tpl.showProposal": "Load the template proposal",
430
450
  "tpl.showProposalHelp": "Back to what the template proposes for this device.",
431
451
  "tpl.skippedEmpty": "\"{0}\" would score higher but builds not a single row on this device — skipped",
432
452
  "tpl.skippedHead": "{0} left out because the source is missing",
433
453
  "tpl.skippedChip": "{0} without source",
434
454
  "tpl.skippedNowhere": "{0}: neither in {1}",
435
455
  "tpl.skippedSource": "{0}: {1} missing",
456
+ "tpl.skippedWhy": "{0}: {1}",
457
+ "tpl.whyNoPoint": "{0} does not exist",
458
+ "tpl.whyNoJson": "{0} delivers no JSON",
459
+ "tpl.whyNoField": "{0} has no field {1}",
436
460
  "tpl.whyNone": "Why no template applies",
437
461
  "tpl.whyThis": "Why this template",
438
462
  "tpl.withoutField": "{0} without {1}",
@@ -528,7 +552,7 @@
528
552
  "tree.filterAria": "Filter objects",
529
553
  "tree.loading": "loading objects …",
530
554
  "tree.looksLikeDevice": "looks like a single device",
531
- "tree.noObjectHint": "no object — derived from the ID only",
555
+ "tree.noObjectHint": "not a real folder",
532
556
  "tree.nothingFound": "nothing found",
533
557
  "tree.sourceGone": "{0} point(s) of this alias refer to a source that does not exist. The js-controller remembers this permanently — only deleting and recreating helps.",
534
558
  "tree.sources": "Create alias",
@@ -543,7 +567,7 @@
543
567
  "tv.badNoStates": "The datapoints (zustaende) are missing.",
544
568
  "tv.badNotObject": "A single template as a JSON object is expected.",
545
569
  "tv.badRecog": "“erkennung” must be an object.",
546
- "tv.badState": "{0} datapoints without a name or without a read path.",
570
+ "tv.badState": "{0} datapoints without a name or without any read or write path.",
547
571
  "tv.badType": "This detector does not know the device type “{0}”.",
548
572
  "tv.clickRow": "Click a row to edit the datapoint",
549
573
  "tv.delete": "Delete",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "id": "hm-rollladen",
3
- "version": 3,
3
+ "version": 4,
4
4
  "rang": 40,
5
5
  "name": {
6
6
  "de": "Homematic Rollladenaktor",
@@ -42,9 +42,10 @@
42
42
  "name": "STOP",
43
43
  "rolle": "button.stop",
44
44
  "typ": "boolean",
45
+ "lesen": "1.STOP",
45
46
  "schreiben": "1.STOP",
46
47
  "optional": true,
47
- "hinweis": "Nur schreiben - ein Tastendruck hat keinen Zustand zum Lesen."
48
+ "hinweis": "Liest und schreibt 1.STOP. Der Alias-Punkt bekommt trotzdem read: false - das kommt aus der Rolle button.stop -, und vis, Alexa und matter zeichnen einen Knopf. Mit der Lesequelle sieht der Alias auch einen Druck von aussen."
48
49
  },
49
50
  {
50
51
  "name": "DIRECTION",