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.
- package/README.md +54 -0
- package/admin/css/werkbank.css +8 -0
- package/admin/js/aufzaehlungen.js +24 -8
- package/admin/js/baum.js +13 -2
- package/admin/js/detail.js +302 -87
- package/admin/js/entwurf.js +44 -14
- package/admin/js/enums.js +7 -0
- package/admin/js/ergebnis.js +26 -6
- package/admin/js/erkennung.js +45 -0
- package/admin/js/mqtt.js +128 -67
- package/admin/js/musterwahl.js +103 -26
- package/admin/js/objekte.js +3 -0
- package/admin/js/pruefungen.js +1 -2
- package/admin/js/quellentausch.js +59 -12
- package/admin/js/schreiben.js +22 -18
- package/admin/js/vorlagen.js +28 -5
- package/admin/js/vorlagenblatt.js +49 -7
- package/admin/js/vorschlagen.js +60 -7
- package/admin/js/werte.js +98 -20
- package/admin/js/zuordnung.js +39 -16
- package/admin/js/zustand.js +5 -1
- package/admin/js/zustandsliste.js +185 -25
- package/admin/sprachen/de.json +35 -11
- package/admin/sprachen/en.json +35 -11
- package/admin/vorlagen/hm-rollladen.json +3 -2
- package/admin/vorlagen/messpunkt.json +6 -2
- package/admin/vorlagen/tasmota-lampe.json +6 -2
- package/admin/vorlagen/tasmota-mehrfach.json +6 -2
- package/admin/vorlagen/tasmota-rgb.json +6 -2
- package/admin/vorlagen/tasmota-steckdose.json +6 -2
- package/io-package.json +14 -14
- package/package.json +1 -1
package/admin/js/musterwahl.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Muster (passt / n von m belegt) und der Knopf, der freie Plaetze
|
|
3
3
|
vorschlaegt. Gebaut wird beides hier, eingehaengt woanders: der Block
|
|
4
4
|
je nach Ansicht im Vorlagenkasten oder in der Leiste ueber der Liste,
|
|
5
|
-
der Knopf immer in der Leiste.
|
|
5
|
+
der Knopf mit ihm (seit 19.09.2026; vorher immer in der Leiste).
|
|
6
6
|
|
|
7
7
|
Herausgeloest aus zeichneErgebnis - der Abschnitt hing an `e`, dem
|
|
8
8
|
Erkennungsergebnis und sonst an nichts. */
|
|
@@ -10,11 +10,84 @@
|
|
|
10
10
|
import { S } from './zustand.js';
|
|
11
11
|
import { D, el, $ } from './basis.js';
|
|
12
12
|
import { tr, sprachtext } from './sprache.js';
|
|
13
|
-
import { VORSCHAU, musterVon, erkenneEntwurf, platzFuerRolle, typenFuerRolle, typKonflikte } from './erkennung.js';
|
|
13
|
+
import { VORSCHAU, musterVon, erkenneEntwurf, platzFuerRolle, typenFuerRolle, typKonflikte, fehlendePflicht } from './erkennung.js';
|
|
14
14
|
import { anMusterAnpassen } from './vorlagen.js';
|
|
15
15
|
import { ratePlaetze, rateAnzahl } from './vorschlagen.js';
|
|
16
16
|
import { zeichneErgebnis, entwurfAngefasst, knopfFrisch } from './ergebnis.js';
|
|
17
17
|
import { musterName, musterZeile } from './musternamen.js';
|
|
18
|
+
import { nurWasInsMusterPasst } from './zustandsliste.js';
|
|
19
|
+
import { kindZustaende } from './werte.js';
|
|
20
|
+
|
|
21
|
+
/* Musterwechsel ohne Vorlage: die Haken folgen dem neuen Muster (C54).
|
|
22
|
+
|
|
23
|
+
Vorher lief die Vorbelegung nur einmal, beim Aufbau des Entwurfs; wer
|
|
24
|
+
danach das Muster wechselte, behielt die Haken des alten. Jetzt gilt
|
|
25
|
+
dieselbe Regel wie beim Aufbau — angehakt ist, was im gewaehlten
|
|
26
|
+
Muster einen Platz hat —, dazu jede Zeile, an der schon etwas von Hand
|
|
27
|
+
eingestellt wurde (Ricardo, 19.09.2026).
|
|
28
|
+
|
|
29
|
+
Nicht mit Vorlage: die bestimmt selbst, was an ist. Nicht an einem
|
|
30
|
+
Alias, den es schon gibt: dort ist jeder Haken eine getroffene
|
|
31
|
+
Entscheidung, abwaehlen hiesse loeschen — dieselben Ausnahmen wie bei
|
|
32
|
+
der Vorbelegung in `berechnePlaetze`. */
|
|
33
|
+
function hakenNachMuster(e) {
|
|
34
|
+
if (e.vorlage) { return; }
|
|
35
|
+
if (S.current && S.current.indexOf('alias.') === 0) { return; }
|
|
36
|
+
if (e.ziel && kindZustaende(e.ziel).length > 0) { return; }
|
|
37
|
+
/* Greift das neue Muster gar nicht — auch nicht mit allen Zeilen an —,
|
|
38
|
+
gibt es nichts, dem die Haken folgen koennten. Vorher fielen dann
|
|
39
|
+
alle bis auf die geaenderten: an der Klimaanlage verschwanden mit
|
|
40
|
+
dem Wechsel auf airCondition (MODE noch nicht da) ausgerechnet
|
|
41
|
+
Sollwert und Schalter, und nach dem Raten fehlte der Sollwert, den
|
|
42
|
+
das Muster verlangt (U13, 19.09.2026). Die Haken bleiben also, wie
|
|
43
|
+
sie sind; das Raten kann darauf aufbauen. */
|
|
44
|
+
var probe = JSON.parse(JSON.stringify({ kanal: e.kanal, states: e.states }));
|
|
45
|
+
probe.states.forEach(function (s) { s.on = true; });
|
|
46
|
+
if (!erkenneEntwurf(probe, e.want).length) { return; }
|
|
47
|
+
nurWasInsMusterPasst(e, true);
|
|
48
|
+
/* Wie beim Aufbau: der Schalter jedes weiteren Kanals kommt mit, damit
|
|
49
|
+
„als ein Geraet" keinen Ausgang heimlich verliert. */
|
|
50
|
+
(e.kanalGeraete || []).forEach(function (k) {
|
|
51
|
+
if (k.typ === 'info') { return; } /* Wartungskanal: kein Ausgang */
|
|
52
|
+
(k.pflicht || []).forEach(function (pid) {
|
|
53
|
+
var kurz = pid.slice(S.current.length + 1).replace(/\./g, '_');
|
|
54
|
+
e.states.forEach(function (st) { if (st.n === kurz) { st.on = true; } });
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/* Die Meldung, wenn der Vorschlag nichts belegt hat - Begruendung dazu
|
|
60
|
+
in `baueMusterwahl`. Auf Modulebene, seit auch die Legende vorschlagen
|
|
61
|
+
kann (19.09.2026). */
|
|
62
|
+
function plaetzeText(frei) {
|
|
63
|
+
var kurz = frei.slice(0, 6).join(', ');
|
|
64
|
+
return frei.length > 6 ? tr('guess.slotsMore', kurz, frei.length - 6) : kurz;
|
|
65
|
+
}
|
|
66
|
+
function meldungZu(p) {
|
|
67
|
+
if (p.anzahl > 0) { return null; }
|
|
68
|
+
var satz = p.grund === 'voll' ? tr('guess.noneFull')
|
|
69
|
+
: p.grund === 'vergeben' ? tr('guess.noneUsed', plaetzeText(p.frei))
|
|
70
|
+
: p.grund === 'keinTreffer' ? tr('guess.noneFit', plaetzeText(p.frei))
|
|
71
|
+
: null;
|
|
72
|
+
/* Der Zusatz zur Handentscheidung (U15): Steht ein Platz nur deshalb
|
|
73
|
+
leer, weil die Zeile dazu von Hand entschieden ist, muss genau das
|
|
74
|
+
dastehen - sonst sieht es aus wie ein Fehler des Ratens. */
|
|
75
|
+
/* Mit Namen und in der richtigen Zahl — „1 Zeilen sind …" sagte weder
|
|
76
|
+
richtig Deutsch noch welche (Ricardo, 19.09.2026). */
|
|
77
|
+
if (satz && p.handWeg) {
|
|
78
|
+
var hn = (p.handNamen || []).slice(0, 4).join(', ') +
|
|
79
|
+
((p.handNamen || []).length > 4 ? ' …' : '');
|
|
80
|
+
satz += ' ' + (p.handWeg === 1 ? tr('guess.noneHandOne', hn) : tr('guess.noneHand', p.handWeg, hn));
|
|
81
|
+
}
|
|
82
|
+
return satz;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/* Vorschlagen und die Meldung dazu in einem Schritt. Gebraucht vom
|
|
86
|
+
Knopf, vom Pfeil hinter „… wuerde evtl. besser passen" und von der
|
|
87
|
+
orangen Legende, wenn das Muster erst nach dem Vorschlag greift. */
|
|
88
|
+
export function rateUndMelde(e) {
|
|
89
|
+
e.rateMeldung = meldungZu(ratePlaetze(e));
|
|
90
|
+
}
|
|
18
91
|
|
|
19
92
|
export function baueMusterwahl(e, haupt, pflichtFehlt) {
|
|
20
93
|
var vw = el('div', 'verdictwrap' + (haupt && !pflichtFehlt.length ? '' : ' bad'));
|
|
@@ -49,9 +122,13 @@ export function baueMusterwahl(e, haupt, pflichtFehlt) {
|
|
|
49
122
|
als `thermostat` erkannt, man wechselt also gar nichts, und ohne
|
|
50
123
|
Knopf bliebe es dabei.
|
|
51
124
|
|
|
52
|
-
Er wird hier gebaut, weil er zum Muster gehoert
|
|
53
|
-
|
|
54
|
-
|
|
125
|
+
Er wird hier gebaut, weil er zum Muster gehoert. Seit 19.09.2026
|
|
126
|
+
haengt er im blauen Vorlagenkasten, gleich hinter dem Musterfeld -
|
|
127
|
+
dort, wo die Erkennung steht, zu der er gehoert. Vorher stand er in
|
|
128
|
+
der Leiste ueber der Liste, und den Knopf, der den ganzen Bestand
|
|
129
|
+
des Geraets laedt, fand man im Kasten; beide standen am jeweils
|
|
130
|
+
falschen Ort (Ricardo). Nur im Aliasmodus, wo es keinen Kasten gibt,
|
|
131
|
+
bleibt er in der Leiste. */
|
|
55
132
|
/* Der Trockenlauf, der beides beantwortet: ob der Knopf etwas
|
|
56
133
|
anzubieten haette, und - wenn nicht - warum nicht. Frueher lief er
|
|
57
134
|
zweimal (einmal fuer `rateMoeglich`, einmal fuer den Klick); einmal
|
|
@@ -66,22 +143,6 @@ export function baueMusterwahl(e, haupt, pflichtFehlt) {
|
|
|
66
143
|
Gezeigt wird sie, solange keine Vermutung offen ist: dann fuehrt die
|
|
67
144
|
blaue Leiste das Wort, und zwei Meldungen uebereinander widersprechen
|
|
68
145
|
sich nur. */
|
|
69
|
-
function plaetzeText(frei) {
|
|
70
|
-
var kurz = frei.slice(0, 6).join(', ');
|
|
71
|
-
return frei.length > 6 ? tr('guess.slotsMore', kurz, frei.length - 6) : kurz;
|
|
72
|
-
}
|
|
73
|
-
function meldungZu(p) {
|
|
74
|
-
if (p.anzahl > 0) { return null; }
|
|
75
|
-
var satz = p.grund === 'voll' ? tr('guess.noneFull')
|
|
76
|
-
: p.grund === 'vergeben' ? tr('guess.noneUsed', plaetzeText(p.frei))
|
|
77
|
-
: p.grund === 'keinTreffer' ? tr('guess.noneFit', plaetzeText(p.frei))
|
|
78
|
-
: null;
|
|
79
|
-
/* Der Zusatz zur Handentscheidung (U15): Steht ein Platz nur deshalb
|
|
80
|
-
leer, weil die Zeile dazu von Hand entschieden ist, muss genau das
|
|
81
|
-
dastehen - sonst sieht es aus wie ein Fehler des Ratens. */
|
|
82
|
-
if (satz && p.handWeg) { satz += ' ' + tr('guess.noneHand', p.handWeg); }
|
|
83
|
-
return satz;
|
|
84
|
-
}
|
|
85
146
|
/* Ungefragt gezeigt wird sie nur, wo sie etwas erklaert: dass kein
|
|
86
147
|
Knopf dasteht, obwohl Plaetze frei sind. Der Fall „alle Plaetze
|
|
87
148
|
besetzt" bleibt stumm - er sagt „alles gut, nichts zu tun", und das
|
|
@@ -131,7 +192,7 @@ export function baueMusterwahl(e, haupt, pflichtFehlt) {
|
|
|
131
192
|
tr('guess.button'));
|
|
132
193
|
rateKnopf.title = beilaeufig ? tr('guess.tplHint') : tr('guess.hint');
|
|
133
194
|
var rateLos = function () {
|
|
134
|
-
|
|
195
|
+
rateUndMelde(e);
|
|
135
196
|
entwurfAngefasst();
|
|
136
197
|
S.openRow = null;
|
|
137
198
|
zeichneErgebnis();
|
|
@@ -178,8 +239,15 @@ export function baueMusterwahl(e, haupt, pflichtFehlt) {
|
|
|
178
239
|
}
|
|
179
240
|
var r = erkenneEntwurf(pruefE, m), mark;
|
|
180
241
|
if (!r.length) {
|
|
181
|
-
|
|
182
|
-
|
|
242
|
+
/* Genannt wird, was wirklich fehlt — nicht jeder Pflichtplatz. An
|
|
243
|
+
der Klimaanlage stand „✕ MODE fehlt" neben einem bestaetigten
|
|
244
|
+
MODE; gefehlt hat der Sollwert (U13, 19.09.2026). Findet die
|
|
245
|
+
Grobpruefung nichts, bleibt die alte Auskunft. */
|
|
246
|
+
var fehlend = fehlendePflicht(pruefE.states, m);
|
|
247
|
+
if (!fehlend.length) {
|
|
248
|
+
fehlend = musterVon(m).states.filter(function (x) { return x.required; })
|
|
249
|
+
.map(function (x) { return x.name; });
|
|
250
|
+
}
|
|
183
251
|
/* Ein Pflichtplatz kann auch deshalb leer sein, weil die Zeile
|
|
184
252
|
dafuer im falschen Datentyp steht - die Rolle stimmt, der Typ
|
|
185
253
|
nicht, und der Punkt faellt aus dem Muster. „SET fehlt" ist dann
|
|
@@ -211,7 +279,14 @@ export function baueMusterwahl(e, haupt, pflichtFehlt) {
|
|
|
211
279
|
}
|
|
212
280
|
} else {
|
|
213
281
|
var g = r[0].states.filter(function (x) { return x.id; }).length;
|
|
214
|
-
|
|
282
|
+
/* Ein Platz, der mehrfach vergeben wird (`multiple`, bei `info` das
|
|
283
|
+
ACTUAL), bekommt je Punkt einen Eintrag — die Gesamtzahl wuchs mit
|
|
284
|
+
jedem Haken, und „24/31“ klang, als fehlten sieben. Wo es keine
|
|
285
|
+
Hoechstzahl gibt, steht auch keine: nur die Zahl der Punkte
|
|
286
|
+
(Ricardo, 19.09.2026). Betrifft info, mediaPlayer, weatherForecast. */
|
|
287
|
+
var mehrfach = r[0].states.some(function (x) { return x.multiple; });
|
|
288
|
+
mark = mehrfach ? tr('pattern.pointsTaken', g)
|
|
289
|
+
: tr('pattern.slotsTaken', g, r[0].states.length);
|
|
215
290
|
/* Hier stand frueher „(Rollen werden angepasst)" hinter der
|
|
216
291
|
Belegung. Der Vermerk erschien an jeder Zeile ausser der gerade
|
|
217
292
|
gewaehlten — an einem Fensterkontakt also an sieben von acht.
|
|
@@ -316,6 +391,7 @@ export function baueMusterwahl(e, haupt, pflichtFehlt) {
|
|
|
316
391
|
} else {
|
|
317
392
|
e.want = neuTyp;
|
|
318
393
|
}
|
|
394
|
+
hakenNachMuster(e);
|
|
319
395
|
S.openRow = null;
|
|
320
396
|
/* Kein Fokus zurueck ins Feld: der Fokus oeffnete die Liste sofort
|
|
321
397
|
wieder, und nach der Wahl stand sie erneut offen — man musste
|
|
@@ -475,7 +551,8 @@ export function baueMusterwahl(e, haupt, pflichtFehlt) {
|
|
|
475
551
|
/* Beim Vorschlag entscheidet nicht das Muster, sondern die
|
|
476
552
|
Rollen, die das Auswaehlen setzt - genau wie in `mWaehlen`. */
|
|
477
553
|
if (e.vorschlag && !e.roh) { anMusterAnpassen(e, bestes); } else { e.want = bestes; }
|
|
478
|
-
|
|
554
|
+
hakenNachMuster(e);
|
|
555
|
+
rateUndMelde(e);
|
|
479
556
|
entwurfAngefasst();
|
|
480
557
|
S.openRow = null;
|
|
481
558
|
zeichneErgebnis();
|
package/admin/js/objekte.js
CHANGED
|
@@ -170,6 +170,7 @@ export function horcheAufObjekte() {
|
|
|
170
170
|
|
|
171
171
|
var gabEs = S.objects[id] !== undefined;
|
|
172
172
|
if (obj) { S.objects[id] = obj; } else { delete S.objects[id]; }
|
|
173
|
+
S.objektStand++;
|
|
173
174
|
if (gabEs !== (S.objects[id] !== undefined)) { nachziehBaum = true; }
|
|
174
175
|
|
|
175
176
|
var betrifft = S.current && (id === S.current || id.indexOf(S.current + '.') === 0);
|
|
@@ -214,6 +215,7 @@ export function horcheAufObjekte() {
|
|
|
214
215
|
export function uebernimmObjekt(id, o) {
|
|
215
216
|
var gabEs = S.objects[id] !== undefined;
|
|
216
217
|
if (o) { S.objects[id] = o; } else { delete S.objects[id]; }
|
|
218
|
+
S.objektStand++;
|
|
217
219
|
return gabEs !== (S.objects[id] !== undefined);
|
|
218
220
|
}
|
|
219
221
|
|
|
@@ -250,6 +252,7 @@ export function kaputteAliaseNeu() {
|
|
|
250
252
|
}
|
|
251
253
|
|
|
252
254
|
export function indexNeu() {
|
|
255
|
+
S.objektStand++;
|
|
253
256
|
S.keysSorted = Object.keys(S.objects).sort();
|
|
254
257
|
S.kleinIndex = {};
|
|
255
258
|
kaputteAliaseNeu();
|
package/admin/js/pruefungen.js
CHANGED
|
@@ -116,8 +116,7 @@ export function pruefungZielKann(e) {
|
|
|
116
116
|
if (!treffer.length) { return { s: 'ok', t: tr('check.targetCan'), d: tr('check.targetCanFine') }; }
|
|
117
117
|
var text = treffer.map(function (x) {
|
|
118
118
|
if (x.art === 'formelBlind') { return tr('check.targetVoidFormula', x.n, x.typ); }
|
|
119
|
-
|
|
120
|
-
return tr('check.targetNoRead', x.n);
|
|
119
|
+
return tr('check.targetNoWrite', x.n);
|
|
121
120
|
}).join(', ');
|
|
122
121
|
return { s: 'warn', t: tr('check.targetCan'), d: text };
|
|
123
122
|
}
|
|
@@ -478,10 +478,18 @@ function zeichneTausch() {
|
|
|
478
478
|
});
|
|
479
479
|
|
|
480
480
|
var go = $('#btn-swap-go');
|
|
481
|
-
|
|
481
|
+
/* Der Knopf bleibt ueber das Neuzeichnen hinweg dasselbe Element.
|
|
482
|
+
Wer ihn sperrt, setzt deshalb auch den Grund — sonst traegt er den
|
|
483
|
+
des vorher gewaehlten Geraets weiter („Erst entscheiden — LOWBAT,
|
|
484
|
+
UNREACH, WORKING …" an der bisherigen Quelle; Lauf vom 19.09.2026,
|
|
485
|
+
W10). */
|
|
486
|
+
if (!S.tauschNeu) {
|
|
487
|
+
if (go) { go.disabled = true; go.title = tr('swap.pickFirst'); }
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
482
490
|
if (S.tauschNeu === alt) {
|
|
483
491
|
body.appendChild(el('div', 'aside w', tr('swap.same')));
|
|
484
|
-
if (go) { go.disabled = true; }
|
|
492
|
+
if (go) { go.disabled = true; go.title = tr('swap.same'); }
|
|
485
493
|
return;
|
|
486
494
|
}
|
|
487
495
|
|
|
@@ -505,7 +513,28 @@ function zeichneTausch() {
|
|
|
505
513
|
zum Entfernen an und opfert seinen Schalter, statt den Knoten
|
|
506
514
|
darueber zu nehmen (Ricardos Frage, 12.09.2026). */
|
|
507
515
|
var eltern = S.tauschNeu.split('.').slice(0, -1).join('.');
|
|
508
|
-
|
|
516
|
+
/* Nur eine Hilfestellung - lieber zu selten als zu oft (Ricardo,
|
|
517
|
+
19.09.2026). Deshalb zwei Bedingungen, die beide halten muessen:
|
|
518
|
+
|
|
519
|
+
1. Die Wahl ist ein Blatt: alle Datenpunkte darunter haengen direkt
|
|
520
|
+
an ihr, es gibt keine Unterordner. Das trifft `…Licht.tele`,
|
|
521
|
+
`…stat`, `…cmnd` und den Homematic-Kanal `1` - genau die Faelle,
|
|
522
|
+
in denen die Ebene darueber das Geraet ist. Ein Geraet mit
|
|
523
|
+
Unterordnern ist selbst schon die richtige Ebene; darueber liegt
|
|
524
|
+
ein Ordner voller anderer Geraete, und der findet fast immer
|
|
525
|
+
irgendwo passende Namen. Gemessen 19.09.2026: ueber `PC_Jonas`
|
|
526
|
+
fand `mqtt-client.0.SmartHome` 7 von 7 - beim 3D-Drucker, am
|
|
527
|
+
Bastelzimmer_Licht und am RGB-Deckenlicht (W24).
|
|
528
|
+
2. Die bisherige Quelle liegt nicht darunter. Sonst zaehlt die Ebene
|
|
529
|
+
darueber deren eigene Punkte mit: `mqtt.Steckdose` gegen das
|
|
530
|
+
Nachbargeraet `mqtt.Stumm` ergab „7 von 7, hier nur 2", und die 7
|
|
531
|
+
waren die der alten Steckdose (Lauf vom 19.09.2026). */
|
|
532
|
+
var pre = S.tauschNeu + '.';
|
|
533
|
+
var istBlatt = kindZustaende(S.tauschNeu).every(function (id) {
|
|
534
|
+
return id.indexOf(pre) === 0 && id.slice(pre.length).indexOf('.') === -1;
|
|
535
|
+
});
|
|
536
|
+
var altDarunter = !!alt && (alt === eltern || alt.indexOf(eltern + '.') === 0);
|
|
537
|
+
if (eltern.split('.').length > 2 && S.tauschNeu !== alt && istBlatt && !altDarunter) {
|
|
509
538
|
var trefferHier = plan.filter(function (z) { return z.neuR; }).length;
|
|
510
539
|
var planOben = tauschPlan(kanal, eltern);
|
|
511
540
|
var trefferOben = planOben.filter(function (z) { return z.neuR; }).length;
|
|
@@ -733,6 +762,31 @@ export function tauscheAus() {
|
|
|
733
762
|
holeZweig(neu, function () { waehle(kanal); zeichneErgebnis(); });
|
|
734
763
|
};
|
|
735
764
|
|
|
765
|
+
/* Der Kanal kommt zuletzt, und nur, wenn alles davor durchging.
|
|
766
|
+
|
|
767
|
+
Er traegt `native.quelle`, und daraus liest der Dialog beim naechsten
|
|
768
|
+
Oeffnen, was „heute" die Quelle ist (`alteQuelle`). Bis 19.09.2026
|
|
769
|
+
ging er zusammen mit den Punkten hinaus, jeder fuer sich. Scheiterte
|
|
770
|
+
ein Punkt, stand der Kanal trotzdem auf dem neuen Geraet: nach
|
|
771
|
+
„Schliessen" nannte der Dialog ABC2 als heutige Quelle, obwohl SET
|
|
772
|
+
noch aus ABC1 las, und ABC2 galt als „dieselbe Quelle wie bisher" —
|
|
773
|
+
der Tausch liess sich nicht mehr zu Ende bringen (Lauf vom
|
|
774
|
+
19.09.2026, W21). Jetzt bleibt der Kanal auf der alten Quelle, bis
|
|
775
|
+
alle Punkte umgeschrieben und alle angehakten geloescht sind; ein
|
|
776
|
+
neuer Tausch auf dasselbe Geraet holt dann nach, was fehlte. */
|
|
777
|
+
var kanalArbeit = arbeit.filter(function (a) { return a.id === kanal; });
|
|
778
|
+
arbeit = arbeit.filter(function (a) { return a.id !== kanal; });
|
|
779
|
+
|
|
780
|
+
var kanalZuletzt = function () {
|
|
781
|
+
if (fehler.length || !kanalArbeit.length) { return fertig(); }
|
|
782
|
+
var a = kanalArbeit[0];
|
|
783
|
+
socket.emit('setObject', a.id, a.obj, function (err) {
|
|
784
|
+
if (err) { fehler.push(a.id + ': ' + err); }
|
|
785
|
+
else { S.objects[a.id] = a.obj; }
|
|
786
|
+
fertig();
|
|
787
|
+
});
|
|
788
|
+
};
|
|
789
|
+
|
|
736
790
|
var weiter = function () {
|
|
737
791
|
/* Schlug beim Umschreiben etwas fehl, wird nichts geloescht.
|
|
738
792
|
|
|
@@ -745,13 +799,13 @@ export function tauscheAus() {
|
|
|
745
799
|
umgeschriebenen Punkte stoeren dabei nicht - sie stehen ja bereits
|
|
746
800
|
richtig (12.09.2026). */
|
|
747
801
|
if (fehler.length) { return fertig(); }
|
|
748
|
-
if (!loeschen.length) { return
|
|
802
|
+
if (!loeschen.length) { return kanalZuletzt(); }
|
|
749
803
|
var offen2 = loeschen.length;
|
|
750
804
|
loeschen.forEach(function (id) {
|
|
751
805
|
socket.emit('delObject', id, function (err) {
|
|
752
806
|
if (err) { fehler.push(id + ': ' + err); }
|
|
753
807
|
else { delete S.objects[id]; }
|
|
754
|
-
if (--offen2 === 0) { indexNeu();
|
|
808
|
+
if (--offen2 === 0) { indexNeu(); kanalZuletzt(); }
|
|
755
809
|
});
|
|
756
810
|
});
|
|
757
811
|
};
|
|
@@ -766,10 +820,3 @@ export function tauscheAus() {
|
|
|
766
820
|
});
|
|
767
821
|
});
|
|
768
822
|
}
|
|
769
|
-
|
|
770
|
-
/* Der Knopf gilt fuer dasselbe wie Verlegen und Entfernen: einen Alias,
|
|
771
|
-
den es gibt. */
|
|
772
|
-
export function tauschKnopf(zeigen) {
|
|
773
|
-
var b = $('#btn-swap');
|
|
774
|
-
if (b) { b.hidden = !zeigen; }
|
|
775
|
-
}
|
package/admin/js/schreiben.js
CHANGED
|
@@ -11,7 +11,7 @@ import { enumsVon } from './aufzaehlungen.js';
|
|
|
11
11
|
import './katalog.js';
|
|
12
12
|
import { holeZweig, indexNeu , nachziehenErledigt } from './objekte.js';
|
|
13
13
|
import { zeichneBaum } from './baum.js';
|
|
14
|
-
import './werte.js';
|
|
14
|
+
import { knopfZeile } from './werte.js';
|
|
15
15
|
import { ausgangName, vorschlag } from './vorlagen.js';
|
|
16
16
|
import {
|
|
17
17
|
waehle,
|
|
@@ -290,15 +290,14 @@ export function baueObjekte(e) {
|
|
|
290
290
|
if (s.f) { alias.read = s.f; }
|
|
291
291
|
if (s.fw && s.srcW) { alias.write = s.fw; }
|
|
292
292
|
|
|
293
|
-
/* Ein
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
var istTaster = taster(s);
|
|
293
|
+
/* Ein Knopf traegt `read: false` - sonst malen vis, Alexa und matter
|
|
294
|
+
einen Kippschalter statt eines Knopfes. Entschieden wird an der
|
|
295
|
+
Rolle, nicht an der Lesequelle (`knopfZeile`, 19.09.2026). */
|
|
297
296
|
var common = {
|
|
298
297
|
name: s.caption || s.n,
|
|
299
298
|
role: s.role,
|
|
300
299
|
type: s.typ || 'mixed',
|
|
301
|
-
read: !
|
|
300
|
+
read: !knopfZeile(s),
|
|
302
301
|
write: !!s.srcW,
|
|
303
302
|
alias: alias
|
|
304
303
|
};
|
|
@@ -364,17 +363,17 @@ export function verwaiste(e) {
|
|
|
364
363
|
}
|
|
365
364
|
|
|
366
365
|
/* Was dem Schreiben im Weg steht. */
|
|
367
|
-
/* Ein Taster: keine Lesequelle, ein Schreibziel,
|
|
368
|
-
selbst, dass es nichts zu melden hat (`common.read === false`).
|
|
366
|
+
/* Ein Taster: keine Lesequelle, ein Schreibziel, Rolle `button…`.
|
|
369
367
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
368
|
+
Bis 19.09.2026 entschied hier `common.read === false` am Ziel. Das
|
|
369
|
+
Feld setzt aber jeder Adapter anders (hm-rpc an `BOOST_MODE`, einem
|
|
370
|
+
Schalter); die Rollenliste von ioBroker legt den Knopf ueber Rolle und
|
|
371
|
+
Schreiben fest, und so jetzt auch die Werkbank (`knopfZeile`). */
|
|
374
372
|
export function taster(s) {
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
373
|
+
/* Seit 19.09.2026 an der Rolle wie `read: false` selbst. Die Zeile ohne
|
|
374
|
+
Lesequelle ist beim Knopf erlaubt, aber nicht mehr die Vorgabe - neu
|
|
375
|
+
angelegte Knoepfe lesen aus ihrem Schreibziel mit. */
|
|
376
|
+
return !!(s && !s.srcR && knopfZeile(s));
|
|
378
377
|
}
|
|
379
378
|
|
|
380
379
|
export function pruefeSchreiben(e) {
|
|
@@ -1062,8 +1061,15 @@ export function zeichneLoeschen() {
|
|
|
1062
1061
|
sie zwar mit Typ, aber der Hinweistext sprach nur von „Datenpunkten
|
|
1063
1062
|
darunter", und die Zahl oben sagt nur, wie viele Zeilen es sind.
|
|
1064
1063
|
Wer sie zaehlt, weiss nicht, dass er drei Geraete mitnimmt. */
|
|
1064
|
+
/* Nur was wirklich UNTER dem Ziel liegt. Mit dem Haken „Alle N
|
|
1065
|
+
Ausgaenge" stehen auch die Geschwister in der Loeschliste; die hat
|
|
1066
|
+
der Nutzer ausdruecklich gewaehlt, und der Haken nennt sie schon.
|
|
1067
|
+
Bis 20.09.2026 zaehlten sie hier mit — die Warnung sprach von
|
|
1068
|
+
„darunter", und weil der Name um die Laenge des Ziels gekuerzt
|
|
1069
|
+
wurde, blieb von `…ZWEIKANAL.Licht_Esstisch` nur „isch" (Lauf vom
|
|
1070
|
+
19.09.2026, G37). */
|
|
1065
1071
|
var kanaeleDrunter = alle.filter(function (id) {
|
|
1066
|
-
if (id
|
|
1072
|
+
if (id.indexOf(ziel + '.') !== 0) { return false; }
|
|
1067
1073
|
var o = S.objects[id];
|
|
1068
1074
|
return o && (o.type === 'channel' || o.type === 'device');
|
|
1069
1075
|
});
|
|
@@ -1337,7 +1343,6 @@ export function zeigeVerlegen(altVorgabe, zielVorgabe) {
|
|
|
1337
1343
|
(T25, 18.09.2026). Verlegt wird ohnehin alles unter dem Pfad; ein
|
|
1338
1344
|
fehlender Kanal fehlt danach am neuen Ort genauso. */
|
|
1339
1345
|
if (!alt || alt.indexOf('alias.') !== 0 || !knotenDa(alt)) { return; }
|
|
1340
|
-
S.verlegeZiel = alt;
|
|
1341
1346
|
|
|
1342
1347
|
var vorbelegt = (typeof zielVorgabe === 'string' && zielVorgabe &&
|
|
1343
1348
|
zielVorgabe.indexOf('alias.') === 0) ? zielVorgabe : alt;
|
|
@@ -1725,7 +1730,6 @@ export function verlegeAlias(alt, neu) {
|
|
|
1725
1730
|
leereOrdnerRaeumen(alt, function () {
|
|
1726
1731
|
S.current = neu;
|
|
1727
1732
|
S.entwurf = null;
|
|
1728
|
-
S.verlegeZiel = null;
|
|
1729
1733
|
melden(fehler, neu);
|
|
1730
1734
|
});
|
|
1731
1735
|
}
|
package/admin/js/vorlagen.js
CHANGED
|
@@ -515,16 +515,35 @@ export function wendeAn(v, kanal, gruende, instanz, auchOhneTreffer) {
|
|
|
515
515
|
});
|
|
516
516
|
}
|
|
517
517
|
|
|
518
|
-
|
|
518
|
+
/* `gruende` sagt, WARUM ein Weg nicht ging — es gibt drei Faelle, und
|
|
519
|
+
sie verlangen dreierlei Handgriffe: der Punkt fehlt ganz, der Punkt
|
|
520
|
+
liefert kein JSON, oder das Feld darin fehlt. Bis 20.09.2026 stand
|
|
521
|
+
fuer alle drei derselbe Satz („tele_SENSOR: tele.SENSOR ·
|
|
522
|
+
SML.Total_Summe fehlt"), auch wenn es den Punkt gar nicht gab
|
|
523
|
+
(Ricardo, 20.09.2026; Lauf vom 19.09.2026, C45). */
|
|
524
|
+
var quelle = null, feld = null, leseformel = null, gescheitert = [], gruende = [];
|
|
519
525
|
for (var wi = 0; wi < wege.length; wi++) {
|
|
520
526
|
var w = wege[wi];
|
|
521
527
|
var id0 = finde(w.punkt);
|
|
522
528
|
var wo = auf(w.punkt) + (w.feld ? ' · ' + auf(w.feld) : '');
|
|
523
|
-
if (!id0) {
|
|
529
|
+
if (!id0) {
|
|
530
|
+
gescheitert.push(wo);
|
|
531
|
+
gruende.push(tr('tpl.whyNoPoint', auf(w.punkt)));
|
|
532
|
+
continue;
|
|
533
|
+
}
|
|
524
534
|
if (w.feld) {
|
|
525
535
|
var f0 = auf(w.feld);
|
|
526
536
|
var j0 = jsonVon(id0);
|
|
527
|
-
if (!j0
|
|
537
|
+
if (!j0) {
|
|
538
|
+
gescheitert.push(wo);
|
|
539
|
+
gruende.push(tr('tpl.whyNoJson', auf(w.punkt)));
|
|
540
|
+
continue;
|
|
541
|
+
}
|
|
542
|
+
if (feldWert(j0, f0) === undefined) {
|
|
543
|
+
gescheitert.push(wo);
|
|
544
|
+
gruende.push(tr('tpl.whyNoField', auf(w.punkt), f0));
|
|
545
|
+
continue;
|
|
546
|
+
}
|
|
528
547
|
}
|
|
529
548
|
quelle = id0; feld = w.feld; leseformel = w.leseformel;
|
|
530
549
|
break;
|
|
@@ -549,7 +568,9 @@ export function wendeAn(v, kanal, gruende, instanz, auchOhneTreffer) {
|
|
|
549
568
|
e.states.push({
|
|
550
569
|
n: z.name, on: !z.vorgabeAus, role: z.rolle || '', typ: z.typ || '',
|
|
551
570
|
unit: z.einheit || '', wr: true,
|
|
552
|
-
|
|
571
|
+
/* Ein Knopf liest aus seinem Schreibziel mit (19.09.2026): der
|
|
572
|
+
Alias sieht jeden Druck, `read: false` kommt aus der Rolle. */
|
|
573
|
+
srcR: /^button/.test(String(z.rolle || '')) ? ziel0 : '', srcW: ziel0,
|
|
553
574
|
f: '', fw: z.schreibformel ? auf(z.schreibformel) : '',
|
|
554
575
|
caption: z.beschriftung || '',
|
|
555
576
|
dec: z.nachkommastellen === undefined ? undefined : String(z.nachkommastellen),
|
|
@@ -564,7 +585,9 @@ export function wendeAn(v, kanal, gruende, instanz, auchOhneTreffer) {
|
|
|
564
585
|
einen Stelle und die Vorlage hatte an zwei geschaut. */
|
|
565
586
|
e.uebersprungen.push(gescheitert.length > 1
|
|
566
587
|
? tr('tpl.skippedNowhere', z.name, gescheitert.join(tr('tpl.nor')))
|
|
567
|
-
:
|
|
588
|
+
: (gruende.length
|
|
589
|
+
? tr('tpl.skippedWhy', z.name, gruende[0])
|
|
590
|
+
: tr('tpl.skippedSource', z.name, gescheitert[0] || auf(z.lesen))));
|
|
568
591
|
/* Eine Pflichtzeile, die ihre Quelle nicht findet, heisst: die
|
|
569
592
|
Vorlage passt zu diesem Geraet nicht. Die Erkennung sieht das
|
|
570
593
|
nicht — sie prueft, ob die genannten *Punkte* da sind, nicht ob
|
|
@@ -12,7 +12,7 @@ import { funktionsAuswahl, kennungZuFunktion } from './aufzaehlungen.js';
|
|
|
12
12
|
import { musterVon, platzFuerRolle, typenFuerRolle } from './erkennung.js';
|
|
13
13
|
import { musterZeile, musterName } from './musternamen.js';
|
|
14
14
|
import { ladeVorlagen, aendereVorlagen, setzeMeta, pruefeVorlage, wendeAn, hinweisTaugt, INSTANZ_ID } from './vorlagen.js';
|
|
15
|
-
import { kindZustaende, hatPunkt, feldAusFormel } from './werte.js';
|
|
15
|
+
import { kindZustaende, hatPunkt, feldAusFormel, pfadTeile, pfadAus } from './werte.js';
|
|
16
16
|
import { opt } from './entwurf.js';
|
|
17
17
|
import { zeichneErgebnis } from './ergebnis.js';
|
|
18
18
|
import { delKnopf } from './schreiben.js';
|
|
@@ -120,6 +120,20 @@ function vArbeitsstand() {
|
|
|
120
120
|
return vorlageMitId(vGewaehlt);
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
+
/* Den Namen einer Vorlage in einer Sprache setzen.
|
|
124
|
+
|
|
125
|
+
Das Einlesen nimmt eine Datei auch ohne `name` an (die Liste zeigt dann
|
|
126
|
+
die Kennung). Die Namensfelder schrieben aber blind in `x.name.de` —
|
|
127
|
+
ohne `name` warf das, `vAendern` brach ab, und die Eingabe ging
|
|
128
|
+
verloren: den fehlenden Namen konnte man nie nachtragen (Ricardo,
|
|
129
|
+
19.09.2026). Ein Name als blosser Text wird wie bisher zu beiden
|
|
130
|
+
Sprachen. */
|
|
131
|
+
function setzeName(x, sprache, w) {
|
|
132
|
+
if (typeof x.name === 'string') { x.name = { de: x.name, en: x.name }; }
|
|
133
|
+
if (!x.name || typeof x.name !== 'object' || Array.isArray(x.name)) { x.name = {}; }
|
|
134
|
+
x.name[sprache] = w;
|
|
135
|
+
}
|
|
136
|
+
|
|
123
137
|
function vAendern(feld, wert) {
|
|
124
138
|
if (!vEinlesen && !vEntwurf) {
|
|
125
139
|
var o = vorlageMitId(vGewaehlt);
|
|
@@ -269,9 +283,9 @@ export function zeichneVorlagenBlatt(host) {
|
|
|
269
283
|
}
|
|
270
284
|
|
|
271
285
|
kb.appendChild(feld(tr('tpls.nameDe'), textIn(v.name, 'de'),
|
|
272
|
-
function (x, w) {
|
|
286
|
+
function (x, w) { setzeName(x, 'de', w); }, '140px'));
|
|
273
287
|
kb.appendChild(feld(tr('tpls.nameEn'), textIn(v.name, 'en'),
|
|
274
|
-
function (x, w) {
|
|
288
|
+
function (x, w) { setzeName(x, 'en', w); }, '140px'));
|
|
275
289
|
kb.appendChild(feld(tr('tpls.rank'), v.rang || 0,
|
|
276
290
|
function (x, w) { x.rang = Number(w) || 0; }, '60px'));
|
|
277
291
|
var rh = el('span', 'hint', eigen ? tr('tv.rankOwn') : tr('tv.rankPackage'));
|
|
@@ -996,7 +1010,12 @@ function vPruefeEingelesen(o) {
|
|
|
996
1010
|
if (!o || typeof o !== 'object' || Array.isArray(o)) { return tr('tv.badNotObject'); }
|
|
997
1011
|
if (!o.id || typeof o.id !== 'string') { return tr('tv.badNoId'); }
|
|
998
1012
|
if (!Array.isArray(o.zustaende) || !o.zustaende.length) { return tr('tv.badNoStates'); }
|
|
999
|
-
|
|
1013
|
+
/* Lese- ODER Schreibpfad, wie beim Speichern und Anwenden. Eine Zeile
|
|
1014
|
+
nur mit `schreiben` (ein Befehl ohne Rueckmeldung, frueher auch der
|
|
1015
|
+
STOP am Rollladen) ist gueltig; bis 19.09.2026 wies das Einlesen sie
|
|
1016
|
+
ab, und die Werkbank gab Dateien aus, die sie selbst nicht wieder
|
|
1017
|
+
annahm (I7). */
|
|
1018
|
+
var schlecht = o.zustaende.filter(function (z) { return !z || !z.name || !(z.lesen || z.schreiben); });
|
|
1000
1019
|
if (schlecht.length) { return tr('tv.badState', schlecht.length); }
|
|
1001
1020
|
|
|
1002
1021
|
if (o.name !== undefined &&
|
|
@@ -1304,6 +1323,16 @@ export function vorlagenVorbereiten(e) {
|
|
|
1304
1323
|
if (quelle && s.ausVorlage) {
|
|
1305
1324
|
(quelle.zustaende || []).forEach(function (z) { if (z.name === s.ausVorlage) { zv = z; } });
|
|
1306
1325
|
}
|
|
1326
|
+
/* Steht der Punkt in der Erkennung der Vorlage, muss er da sein -
|
|
1327
|
+
auch wenn seine Zeile optional ist. `hm-rollladen` fuehrt STOP als
|
|
1328
|
+
optionale Zeile, verlangt `1.STOP` aber in der Erkennung: nur er
|
|
1329
|
+
trennt den Rollladen vom Dimmer. Vorgehakt wurde bis 19.09.2026 nur
|
|
1330
|
+
nach `optional`, und wer die Vorlage aktualisierte, ohne selbst
|
|
1331
|
+
nachzuhaken, speicherte eine, die schon bei LEVEL allein greift -
|
|
1332
|
+
im Probelauf 23 Geraete statt der Rolllaeden (I20). */
|
|
1333
|
+
var erfQ = (quelle && quelle.erkennung && quelle.erkennung.erforderlich) || [];
|
|
1334
|
+
var inErkennung = !!(zv && ((zv.lesen && erfQ.indexOf(zv.lesen) > -1) ||
|
|
1335
|
+
(zv.schreiben && erfQ.indexOf(zv.schreiben) > -1)));
|
|
1307
1336
|
zeilen.push({
|
|
1308
1337
|
s: s,
|
|
1309
1338
|
name: s.n,
|
|
@@ -1320,7 +1349,11 @@ export function vorlagenVorbereiten(e) {
|
|
|
1320
1349
|
einer Vorlage kam, erbt deren Einstufung. Von Hand ergaenzte
|
|
1321
1350
|
Punkte bleiben freiwillig — jeder Pflichtpunkt macht die
|
|
1322
1351
|
Vorlage genauer und zugleich sproeder. */
|
|
1323
|
-
pflicht: r.abs ? false : (zv ? !zv.optional : !!w)
|
|
1352
|
+
pflicht: r.abs ? false : (zv ? (!zv.optional || inErkennung) : !!w),
|
|
1353
|
+
/* Die Zeile bleibt, was sie in der Vorlage war: optional fuer den
|
|
1354
|
+
Alias, Pflicht nur fuer die Erkennung. Der Haken „muss da sein"
|
|
1355
|
+
legt sonst beides zusammen fest. */
|
|
1356
|
+
optionalBleibt: !!(zv && zv.optional && inErkennung)
|
|
1324
1357
|
});
|
|
1325
1358
|
});
|
|
1326
1359
|
|
|
@@ -1328,7 +1361,7 @@ export function vorlagenVorbereiten(e) {
|
|
|
1328
1361
|
zeilen.forEach(function (z) {
|
|
1329
1362
|
var f = feldAusFormel(z.s.f);
|
|
1330
1363
|
if (!f || z.lesenAbs) { return; }
|
|
1331
|
-
var oben = f
|
|
1364
|
+
var oben = pfadAus([pfadTeile(f)[0]]);
|
|
1332
1365
|
var da = false;
|
|
1333
1366
|
inhalte.forEach(function (x) { if (x.punkt === z.lesenRoh && x.feld === oben) { da = true; } });
|
|
1334
1367
|
if (!da) { inhalte.push({ punkt: z.lesenRoh, feld: oben, an: false }); }
|
|
@@ -1410,6 +1443,15 @@ export function baueVorlage(z) {
|
|
|
1410
1443
|
z.inhalte.forEach(function (x) { if (x.an) { inh[x.punkt] = x.feld; } });
|
|
1411
1444
|
if (Object.keys(inh).length) { v.erkennung.inhalt = inh; }
|
|
1412
1445
|
if (k.namenshinweis) { v.erkennung.namenshinweis = k.namenshinweis; }
|
|
1446
|
+
/* Was die Erkennung der Ursprungsvorlage sonst traegt - der Hinweis,
|
|
1447
|
+
warum ein Punkt Pflicht ist, etwa - bleibt erhalten. Der Dialog
|
|
1448
|
+
zeigt es nicht, also darf er es auch nicht verlieren (I20). */
|
|
1449
|
+
if (k.modus === 'update' && k.quelle && k.quelle.erkennung) {
|
|
1450
|
+
Object.keys(k.quelle.erkennung).forEach(function (feld) {
|
|
1451
|
+
if (['erforderlich', 'verboten', 'inhalt', 'namenshinweis'].indexOf(feld) > -1) { return; }
|
|
1452
|
+
v.erkennung[feld] = k.quelle.erkennung[feld];
|
|
1453
|
+
});
|
|
1454
|
+
}
|
|
1413
1455
|
|
|
1414
1456
|
if (k.mehrfach && k.instanz) {
|
|
1415
1457
|
v.mehrfach = { platzhalter: '%N%' };
|
|
@@ -1432,7 +1474,7 @@ export function baueVorlage(z) {
|
|
|
1432
1474
|
if (r.schreibenRoh) { zu.schreiben = zPfad(r, k, true, z.zeilen); }
|
|
1433
1475
|
if (f) { zu.feld = f; } else if (s.f) { zu.leseformel = s.f; }
|
|
1434
1476
|
if (s.fw && r.schreibenRoh) { zu.schreibformel = s.fw; }
|
|
1435
|
-
if (!r.pflicht) { zu.optional = true; }
|
|
1477
|
+
if (!r.pflicht || r.optionalBleibt) { zu.optional = true; }
|
|
1436
1478
|
if (r.aus) { zu.vorgabeAus = true; }
|
|
1437
1479
|
if (s.caption) { zu.beschriftung = s.caption; }
|
|
1438
1480
|
if (s.dec !== undefined && s.dec !== '') { zu.nachkommastellen = Number(s.dec); }
|