iobroker.alias-workbench 0.11.0 → 0.12.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 +19 -7
- package/admin/css/werkbank.css +73 -0
- package/admin/i18n/de.json +65 -0
- package/admin/i18n/en.json +65 -0
- package/admin/i18n/es.json +65 -0
- package/admin/i18n/fr.json +65 -0
- package/admin/i18n/it.json +65 -0
- package/admin/i18n/nl.json +65 -0
- package/admin/i18n/pl.json +65 -0
- package/admin/i18n/pt.json +65 -0
- package/admin/i18n/ru.json +65 -0
- package/admin/i18n/uk.json +65 -0
- package/admin/i18n/zh-cn.json +65 -0
- package/admin/js/attrappe.js +749 -0
- package/admin/js/baum.js +52 -5
- package/admin/js/entwurf.js +133 -6
- package/admin/js/ergebnis.js +77 -2
- package/admin/js/quellentausch.js +823 -16
- package/admin/js/sprache.js +4 -0
- package/admin/js/zustand.js +28 -0
- package/admin/tab.html +24 -0
- package/io-package.json +14 -14
- package/package.json +2 -2
|
@@ -10,20 +10,30 @@
|
|
|
10
10
|
der ganze Unterschied zum Neu-Anlegen, das den Alias aus der Vorlage
|
|
11
11
|
heraus neu baut und dabei Namen, Rollen und Formeln mitbringt.
|
|
12
12
|
|
|
13
|
-
Zugeordnet wird in
|
|
13
|
+
Zugeordnet wird in vier Stufen, absteigend nach Verlaesslichkeit:
|
|
14
14
|
1. gleicher relativer Pfad (…ABC.1.STATE → …XYZ.1.STATE)
|
|
15
15
|
2. ueber die Vorlage (der Punkt weiss aus `native.ausZustand`,
|
|
16
16
|
aus welcher Zeile er stammt)
|
|
17
|
+
4. ueber den Alias-Punkt (sein eigener Name und seine eigene
|
|
18
|
+
Rolle - nicht die seiner alten Quelle)
|
|
17
19
|
3. ueber Rolle und Namen (nur als Vorschlag, sichtbar markiert)
|
|
18
|
-
Was nichts findet, bleibt leer - und laesst sich dann entfernen.
|
|
20
|
+
Was nichts findet, bleibt leer - und laesst sich dann entfernen.
|
|
21
|
+
|
|
22
|
+
Die Nummern sind die Reihenfolge ihrer Entstehung, nicht die der
|
|
23
|
+
Anwendung: Stufe 4 greift VOR dem Raten, weil sie belegter ist. Die
|
|
24
|
+
drei alten Nummern umzuhaengen haette jeden Pruefpunkt und jede
|
|
25
|
+
gespeicherte Messung ungueltig gemacht, ohne irgendetwas besser zu
|
|
26
|
+
machen. */
|
|
19
27
|
|
|
20
28
|
import { S } from './zustand.js';
|
|
21
29
|
import { $, el } from './basis.js';
|
|
22
|
-
import { tr, txt } from './sprache.js';
|
|
30
|
+
import { tr, txt, sprachtext } from './sprache.js';
|
|
23
31
|
import { socket } from './verbindung.js';
|
|
24
|
-
import {
|
|
32
|
+
import { pruefeVorlage, vorschlag } from './vorlagen.js';
|
|
33
|
+
import { erkenneEntwurf } from './erkennung.js';
|
|
34
|
+
import { aliasQuellen, kindZustaende, holeWerte, auswerten } from './werte.js';
|
|
25
35
|
import { holeZweig, indexNeu } from './objekte.js';
|
|
26
|
-
import { waehle, knotenDa } from './entwurf.js';
|
|
36
|
+
import { waehle, knotenDa, rohEntwurf } from './entwurf.js';
|
|
27
37
|
import { zeichneErgebnis } from './ergebnis.js';
|
|
28
38
|
|
|
29
39
|
/* Die Punkte des Alias, die ueberhaupt eine Quelle haben. */
|
|
@@ -120,10 +130,296 @@ function geraten(altQuelle, neuKanal) {
|
|
|
120
130
|
return treffer ? { id: treffer, ueber: 'rolle', rolle: rolle } : null;
|
|
121
131
|
}
|
|
122
132
|
|
|
133
|
+
/* Stufe 5: dieselbe Frage wie beim Anklicken im Baum.
|
|
134
|
+
|
|
135
|
+
Der Tausch hat lange eigene Wege gesucht - Pfad, gemerkte Vorlage,
|
|
136
|
+
Raten, Alias-Punkt. Dabei stellt er dieselbe Frage, die die Werkbank
|
|
137
|
+
beim Anklicken eines Geraets ohnehin beantwortet: Welcher Punkt DORT
|
|
138
|
+
ist das `ELECTRIC_POWER`? Beim Anlegen entsteht genau diese Zuordnung
|
|
139
|
+
- Musterplatz zu Quellpunkt, samt Formel - und beim Tauschen wurde sie
|
|
140
|
+
bisher nicht gelesen (Ricardo, 13.10.2026: „so wie wenn ich im Baum
|
|
141
|
+
klicke, da wird ja auch versucht zu erkennen oder Vorlage").
|
|
142
|
+
|
|
143
|
+
Das ist der Weg, der in BEIDE Richtungen traegt:
|
|
144
|
+
|
|
145
|
+
- Zu einem Geraet, das seine Punkte beschriftet (Matter: `role:
|
|
146
|
+
value.power`, `unit: W`), antwortet der Detektor.
|
|
147
|
+
- Zu einem Geraet, das gar nichts beschriftet, antwortet die Vorlage.
|
|
148
|
+
Gemessen am Testsystem an `mqtt-client.0.SmartHome.PC_Jonas`: alle
|
|
149
|
+
zehn Punkte `role: (keine)`, `type: mixed`, der type-detector macht
|
|
150
|
+
daraus `info` - aber `tasmota-steckdose` passt mit Rang 2000020 und
|
|
151
|
+
weiss, dass `stat.POWER` der Melder ist und die Leistung in
|
|
152
|
+
`tele.SENSOR` unter `ENERGY.Power` steht.
|
|
153
|
+
|
|
154
|
+
Und die FORMELN kommen mit, weil sie zur Quelle gehoeren und nicht
|
|
155
|
+
zum Alias. Das war der offene Rest vom Vormittag: Beim Tausch von
|
|
156
|
+
Tasmota auf Matter blieb `JSON.parse(val)?.ENERGY?.Power` stehen und
|
|
157
|
+
machte aus Messwerten `null`; auf dem Rueckweg fehlte sie. Jetzt
|
|
158
|
+
bringt jede Richtung ihre eigene mit.
|
|
159
|
+
|
|
160
|
+
Gefragt wird ueber den PLATZNAMEN. Was im Alias zu keinem Musterplatz
|
|
161
|
+
gehoert - Handgebautes -, findet auch dieser Weg nicht; solche Punkte
|
|
162
|
+
bleiben uebrig und muessen entschieden werden, wie bisher. */
|
|
163
|
+
export function zielKarte(neuKanal) {
|
|
164
|
+
var karte = {};
|
|
165
|
+
if (!neuKanal) { return karte; }
|
|
166
|
+
/* Erst die Vorlage: sie weiss mehr als der Detektor - Formeln, Felder
|
|
167
|
+
in einem JSON, getrennte Lese- und Schreibquellen. */
|
|
168
|
+
var v = null;
|
|
169
|
+
try { v = vorschlag(neuKanal); } catch { v = null; }
|
|
170
|
+
if (v && v.states && v.states.length) {
|
|
171
|
+
v.states.forEach(function (st) {
|
|
172
|
+
if (!st.n || !st.srcR) { return; }
|
|
173
|
+
karte[String(st.n).toLowerCase()] = {
|
|
174
|
+
read: st.srcR, write: st.srcW || '', f: st.f || '', fw: st.fw || '',
|
|
175
|
+
her: 'vorlage', was: v.tplName || (v.vorlage && v.vorlage.id) || ''
|
|
176
|
+
};
|
|
177
|
+
});
|
|
178
|
+
return karte;
|
|
179
|
+
}
|
|
180
|
+
/* Sonst der Detektor am Rohentwurf - er benennt die Plaetze genauso
|
|
181
|
+
(`SET`, `ELECTRIC_POWER`), nur ohne Formeln. */
|
|
182
|
+
var roh = null;
|
|
183
|
+
try { roh = rohEntwurf(neuKanal); } catch { roh = null; }
|
|
184
|
+
if (!roh) { return karte; }
|
|
185
|
+
var erk = [];
|
|
186
|
+
try { erk = erkenneEntwurf(roh, null) || []; } catch { erk = []; }
|
|
187
|
+
/* Die Kennung, die der Detektor nennt, ist KEIN Objekt.
|
|
188
|
+
|
|
189
|
+
`abbild` baut ihm ein eigenes Bild des Entwurfs: ein Kanal, alle
|
|
190
|
+
Punkte flach darunter, benannt nach der Alias-Zeile. Aus
|
|
191
|
+
`…Plug.ElectricalSensor-2.ELECTRIC_POWER` wird dort
|
|
192
|
+
`…Plug.ElectricalSensor-2_ELECTRIC_POWER` - mit Unterstrich, weil
|
|
193
|
+
ein Punkt im Namen am Alias eine Unterebene aufmachen wuerde. Wer
|
|
194
|
+
diese Kennung als Quelle einträgt, legt einen Alias an, der ins
|
|
195
|
+
Leere zeigt (gemessen in W30, 13.10.2026).
|
|
196
|
+
|
|
197
|
+
Die echte Quelle steht in der Zeile selbst: `srcR`/`srcW`. Also
|
|
198
|
+
ueber den Zeilennamen zurueckrechnen. */
|
|
199
|
+
var zeilen = {};
|
|
200
|
+
(roh.states || []).forEach(function (st) { if (st.n) { zeilen[st.n] = st; } });
|
|
201
|
+
var vor = neuKanal + '.';
|
|
202
|
+
((erk[0] && erk[0].states) || []).forEach(function (s) {
|
|
203
|
+
if (!s.id || !s.name) { return; }
|
|
204
|
+
var zeile = zeilen[String(s.id).indexOf(vor) === 0 ? String(s.id).slice(vor.length) : ''];
|
|
205
|
+
if (!zeile || !zeile.srcR) { return; }
|
|
206
|
+
karte[String(s.name).toLowerCase()] = {
|
|
207
|
+
read: zeile.srcR,
|
|
208
|
+
/* Schreiben nur, wo der Punkt es zulaesst - ein Melder bekommt
|
|
209
|
+
kein Schreibziel. */
|
|
210
|
+
write: zeile.srcW || '',
|
|
211
|
+
f: zeile.f || '', fw: zeile.fw || '', her: 'detektor', was: erk[0].type || ''
|
|
212
|
+
};
|
|
213
|
+
});
|
|
214
|
+
return karte;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/* Stufe 4: der Alias-Punkt selbst - sein Name und seine Rolle.
|
|
218
|
+
|
|
219
|
+
Die drei alten Stufen fragen alle, WOHER der Punkt bisher liest. Das
|
|
220
|
+
greift, solange beide Geraete aehnlich gebaut sind, und versagt
|
|
221
|
+
vollstaendig, sobald sie es nicht sind. Gemessen am 13.10.2026 am
|
|
222
|
+
Produktivsystem: eine Tasmota-Steckdose auf eine Matter-Steckdose
|
|
223
|
+
umhaengen ergab 0 von 8 Zuordnungen - bei zwei Geraeten, die dasselbe
|
|
224
|
+
tun.
|
|
225
|
+
|
|
226
|
+
Zweimal lag es an der Quelle, nie am Ziel:
|
|
227
|
+
- mqtt-client pflegt keine Rollen (`stat.POWER`, `cmnd.POWER` und
|
|
228
|
+
`tele.SENSOR` haben alle `role: undefined`), damit faellt der
|
|
229
|
+
Rollenvergleich in Stufe 3 ohne einen Treffer aus.
|
|
230
|
+
- Fuenf der acht Punkte lesen aus EINEM Punkt, dem Telemetrie-JSON
|
|
231
|
+
`tele.SENSOR`. Aus ihm laesst sich nichts ableiten; er heisst
|
|
232
|
+
`SENSOR` und traegt die Bedeutung erst in der Formel.
|
|
233
|
+
|
|
234
|
+
Dabei steht die Antwort fertig im Alias: `…PC.ELECTRIC_POWER` traegt
|
|
235
|
+
`role: value.power`, und am neuen Geraet liegt
|
|
236
|
+
`…ElectricalSensor-2.ELECTRIC_POWER` mit genau dieser Rolle. Die
|
|
237
|
+
Bedeutung wurde beim Anlegen in den Alias geschrieben - sie beim
|
|
238
|
+
Tauschen nicht wieder zu lesen, war die Luecke.
|
|
239
|
+
|
|
240
|
+
Zwei Guetegrade, und beide verlangen Eindeutigkeit:
|
|
241
|
+
- Name UND Rolle gleich, genau einmal -> starker Treffer, gilt wie
|
|
242
|
+
ein Pfadtreffer und bekommt eine Herkunftsmarke.
|
|
243
|
+
- nur die Rolle, und nur wenn sie unter dem neuen Geraet genau einmal
|
|
244
|
+
vorkommt -> Vorschlag wie Stufe 3, sichtbar als „vermutet".
|
|
245
|
+
Zwei Punkte mit `value.power` unter einem Knoten heben sich also auf,
|
|
246
|
+
statt dass einer von beiden gewaehlt wird. */
|
|
247
|
+
function ausAliasPunkt(punktId, name, neuKanal) {
|
|
248
|
+
var o = S.objects[punktId];
|
|
249
|
+
var rolle = ((o && o.common) || {}).role || '';
|
|
250
|
+
if (!rolle) { return null; }
|
|
251
|
+
/* Der blosse Punktname, auch wenn der Alias Unterebenen hat. */
|
|
252
|
+
var kurz = String(name).split('.').pop().toLowerCase();
|
|
253
|
+
var mitRolle = [], mitBeidem = [];
|
|
254
|
+
kindZustaende(neuKanal).forEach(function (id) {
|
|
255
|
+
var k = S.objects[id];
|
|
256
|
+
if (!k || !k.common || k.common.role !== rolle) { return; }
|
|
257
|
+
mitRolle.push(id);
|
|
258
|
+
if (id.split('.').pop().toLowerCase() === kurz) { mitBeidem.push(id); }
|
|
259
|
+
});
|
|
260
|
+
if (mitBeidem.length === 1) { return { id: mitBeidem[0], stark: true, rolle: rolle }; }
|
|
261
|
+
if (!mitBeidem.length && mitRolle.length === 1) {
|
|
262
|
+
return { id: mitRolle[0], stark: false, rolle: rolle };
|
|
263
|
+
}
|
|
264
|
+
return null;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/* Die gemerkte Vorlage gegen die NEUE Quelle halten.
|
|
268
|
+
|
|
269
|
+
Ein Alias merkt sich, aus welcher Vorlage er entstanden ist - am Kanal
|
|
270
|
+
`native.vorlage`, an jedem Punkt zusaetzlich `native.ausZustand`. Die
|
|
271
|
+
Merkung ist stark: „sie bleibt, bis du sie ausdruecklich umstellst",
|
|
272
|
+
und der Reiter wendet sie auch dann an, wenn die Erkennung sie nicht
|
|
273
|
+
mehr findet (dann steht „erzwungen" daneben).
|
|
274
|
+
|
|
275
|
+
Beim Tausch auf ein BAUGLEICHES Geraet ist das richtig und soll so
|
|
276
|
+
bleiben. Beim Tausch auf ein anders gebautes Geraet ist es falsch, und
|
|
277
|
+
zwar unsichtbar falsch. Gemessen am 13.10.2026 an Ricardos
|
|
278
|
+
`alias.0.Bastelzimmer.PC`, nach dem Tausch von einer Tasmota-Steckdose
|
|
279
|
+
auf eine Matter-Steckdose:
|
|
280
|
+
- Der Kanal trug weiter `vorlage: tasmota-steckdose`, der Reiter
|
|
281
|
+
meldete „GEMERKT / ERZWUNGEN ✕ cmnd.POWER" und „21 ohne Quelle".
|
|
282
|
+
- Schlimmer sind die FORMELN, die aus derselben Vorlage stammen und an
|
|
283
|
+
den Punkten stehen: `JSON.parse(val)?.ENERGY?.Power ?? null` passte
|
|
284
|
+
zum Telemetrie-JSON einer Tasmota. Matter liefert eine blanke Zahl,
|
|
285
|
+
`JSON.parse(37)` ergibt 37, `37?.ENERGY` ist undefined - und der
|
|
286
|
+
Punkt stand auf `null`. Vier Messwerte waren still leer, ohne
|
|
287
|
+
Fehlermeldung.
|
|
288
|
+
- Und an `SET` blieb die Schreibformel `val ? "ON" : "OFF"` stehen,
|
|
289
|
+
die einen String auf einen boolean-Punkt geschrieben haette.
|
|
290
|
+
|
|
291
|
+
Die Formel gehoert zur QUELLE, nicht zum Alias - sie beschreibt, wie
|
|
292
|
+
man aus dieser Bauart einen Wert holt. Wechselt die Bauart, ist sie in
|
|
293
|
+
beide Richtungen falsch (Ricardo: „wenn ich von matter auf mqtt
|
|
294
|
+
wechsle, muessten die ja wieder dazu"). Entschieden wird das nicht
|
|
295
|
+
automatisch: die Werkbank sagt es und bietet es an. */
|
|
296
|
+
export function merkungPruefen(kanal, neuKanal) {
|
|
297
|
+
var k = S.objects[kanal];
|
|
298
|
+
var id = k && k.native && k.native.vorlage;
|
|
299
|
+
if (!id || !neuKanal) { return null; }
|
|
300
|
+
var v = null;
|
|
301
|
+
(S.VORLAGEN || []).forEach(function (x) { if (x.id === id) { v = x; } });
|
|
302
|
+
/* Eine Merkung auf eine Vorlage, die es nicht mehr gibt, ist ebenso
|
|
303
|
+
ueberholt wie eine, die nicht mehr passt. */
|
|
304
|
+
if (!v) { return { id: id, name: id, passt: false, fehler: [tr('swap.tplGone')], ersatz: null }; }
|
|
305
|
+
var t = pruefeVorlage(v, neuKanal);
|
|
306
|
+
if (t.passt) { return { id: id, name: sprachtext(v.name), passt: true }; }
|
|
307
|
+
/* Passt eine ANDERE auf die neue Quelle, gehoert sie genannt - dann
|
|
308
|
+
weiss der Nutzer, dass es dort Formeln und Namen zu holen gibt. Die
|
|
309
|
+
Werkbank wendet sie hier nicht an: das ist „Alias aktualisieren",
|
|
310
|
+
ein eigener Weg mit eigener Vorschau. */
|
|
311
|
+
var ersatz = null;
|
|
312
|
+
(S.VORLAGEN || []).forEach(function (x) {
|
|
313
|
+
if (x.id === id) { return; }
|
|
314
|
+
var p = pruefeVorlage(x, neuKanal);
|
|
315
|
+
if (p.passt && (!ersatz || p.punkte > ersatz.punkte)) {
|
|
316
|
+
ersatz = { name: sprachtext(x.name), punkte: p.punkte };
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
return { id: id, name: sprachtext(v.name), passt: false,
|
|
320
|
+
fehler: (t.fehler || []).slice(0, 3), ersatz: ersatz };
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/* Welche Punkte des Alias tragen eine Merkung oder eine Formel? */
|
|
324
|
+
export function merkungStellen(kanal) {
|
|
325
|
+
var raus = { kanal: false, punkte: [], formeln: [] };
|
|
326
|
+
var k = S.objects[kanal];
|
|
327
|
+
if (k && k.native && (k.native.vorlage || k.native.vorlageVersion)) { raus.kanal = true; }
|
|
328
|
+
aliasPunkte(kanal).forEach(function (id) {
|
|
329
|
+
var o = S.objects[id];
|
|
330
|
+
var n = (o && o.native) || {};
|
|
331
|
+
var a = (o.common && o.common.alias) || {};
|
|
332
|
+
if (n.vorlage || n.ausZustand) { raus.punkte.push(id); }
|
|
333
|
+
if (a.read || a.write) { raus.formeln.push(id); }
|
|
334
|
+
});
|
|
335
|
+
return raus;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/* ================== Einheiten ==================
|
|
339
|
+
|
|
340
|
+
Wechselt beim Tausch die Einheit, stimmt danach jeder Wert nicht mehr
|
|
341
|
+
- und zwar unsichtbar. Tasmota liefert `ENERGY.Total` in kWh, Matter
|
|
342
|
+
`CONSUMPTION` in Wh: derselbe Zaehler, Faktor 1000. Der Alias zeigt
|
|
343
|
+
danach 35,4 statt 35380 und niemand merkt es.
|
|
344
|
+
|
|
345
|
+
Automatisch umgerechnet wird trotzdem NICHTS. Am 13.10.2026 hat sich
|
|
346
|
+
gezeigt, warum: Matter schreibt an `CURRENT` die Einheit `mA`,
|
|
347
|
+
liefert aber Ampere (0,926 bei 245 W an 228 V). Eine Automatik, die
|
|
348
|
+
der Beschriftung glaubt, haette daraus 926 A gemacht - sie baut den
|
|
349
|
+
Fehler ein, statt ihn zu finden.
|
|
350
|
+
|
|
351
|
+
Stattdessen stehen BEIDE WERTE nebeneinander, der der neuen Quelle
|
|
352
|
+
und der des Alias. Daran sieht man in einer Sekunde, ob der Faktor
|
|
353
|
+
stimmt - und beim CURRENT-Fall sofort, dass die Beschriftung luegt,
|
|
354
|
+
weil 0,926 und 0,92 schon gleich sind. Das sagt keine Tabelle. */
|
|
355
|
+
|
|
356
|
+
/* Faktor zur Basiseinheit. Nur Paare, die eindeutig sind: Temperatur
|
|
357
|
+
fehlt mit Absicht, °C nach °F ist kein Faktor, sondern eine Gerade,
|
|
358
|
+
und eine halb richtige Umrechnung ist schlimmer als keine. */
|
|
359
|
+
var EINHEITEN = {
|
|
360
|
+
mA: { b: 'A', f: 0.001 }, A: { b: 'A', f: 1 },
|
|
361
|
+
mV: { b: 'V', f: 0.001 }, V: { b: 'V', f: 1 }, kV: { b: 'V', f: 1000 },
|
|
362
|
+
mW: { b: 'W', f: 0.001 }, W: { b: 'W', f: 1 }, kW: { b: 'W', f: 1000 },
|
|
363
|
+
Wh: { b: 'Wh', f: 1 }, kWh: { b: 'Wh', f: 1000 }, MWh: { b: 'Wh', f: 1000000 },
|
|
364
|
+
Pa: { b: 'Pa', f: 1 }, hPa: { b: 'Pa', f: 100 },
|
|
365
|
+
mbar: { b: 'Pa', f: 100 }, bar: { b: 'Pa', f: 100000 },
|
|
366
|
+
ms: { b: 's', f: 0.001 }, s: { b: 's', f: 1 },
|
|
367
|
+
min: { b: 's', f: 60 }, h: { b: 's', f: 3600 },
|
|
368
|
+
mm: { b: 'm', f: 0.001 }, cm: { b: 'm', f: 0.01 },
|
|
369
|
+
m: { b: 'm', f: 1 }, km: { b: 'm', f: 1000 },
|
|
370
|
+
g: { b: 'g', f: 1 }, kg: { b: 'g', f: 1000 },
|
|
371
|
+
l: { b: 'l', f: 1 }, ml: { b: 'l', f: 0.001 }
|
|
372
|
+
};
|
|
373
|
+
|
|
374
|
+
/* Womit die neue Quelle multipliziert werden muesste, damit der Alias in
|
|
375
|
+
SEINER Einheit bleibt. `null`, wenn die beiden nichts miteinander zu
|
|
376
|
+
tun haben oder eine davon unbekannt ist. */
|
|
377
|
+
export function umrechenFaktor(altEinheit, neuEinheit) {
|
|
378
|
+
var a = EINHEITEN[String(altEinheit || '').trim()];
|
|
379
|
+
var n = EINHEITEN[String(neuEinheit || '').trim()];
|
|
380
|
+
if (!a || !n || a.b !== n.b || a.f === n.f) { return null; }
|
|
381
|
+
return n.f / a.f;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/* Als Rechenschritt, wie ihn ein Mensch schreiben wuerde: lieber
|
|
385
|
+
`/ 1000` als `* 0.001`. */
|
|
386
|
+
export function faktorText(faktor) {
|
|
387
|
+
if (faktor >= 1) { return '* ' + (Math.round(faktor * 1e6) / 1e6); }
|
|
388
|
+
var kehr = 1 / faktor;
|
|
389
|
+
var rund = Math.round(kehr);
|
|
390
|
+
if (Math.abs(kehr - rund) < 1e-9) { return '/ ' + rund; }
|
|
391
|
+
return '* ' + (Math.round(faktor * 1e9) / 1e9);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/* Welche Leseformel am Ende dasteht - nach allem, was an dieser Zeile
|
|
395
|
+
entschieden ist.
|
|
396
|
+
|
|
397
|
+
EINE Stelle fuer die Anzeige und fuer das Schreiben. Vorher rechnete
|
|
398
|
+
die Zeile nur die Formel der Stufe 5 aus und zeigte „keine", waehrend
|
|
399
|
+
die Umrechnung daneben `(val) / 1000` geschrieben haette - Anzeige und
|
|
400
|
+
Ergebnis liefen auseinander (Ricardo, 13.10.2026). Was dasteht, muss
|
|
401
|
+
das sein, was passiert. */
|
|
402
|
+
export function endFormel(zeile) {
|
|
403
|
+
var o = S.objects[zeile.id] || {};
|
|
404
|
+
var alt = ((o.common || {}).alias || {}).read || '';
|
|
405
|
+
/* Stufe 5 bringt die Formel des neuen Geraets mit - auch „keine". */
|
|
406
|
+
var formel = zeile.formelNeu ? (zeile.neuF || '') : alt;
|
|
407
|
+
/* Der Faktor, den der Nutzer gesetzt hat. 1 und leer heissen beide
|
|
408
|
+
„nichts rechnen"; ein ungueltiger Wert ebenso - wer `abc` tippt,
|
|
409
|
+
soll keine kaputte Formel bekommen. */
|
|
410
|
+
var fk = Number(((S.tauschEinheit || {})[zeile.id] || {}).faktor);
|
|
411
|
+
if (zeile.neuR && fk && isFinite(fk) && fk !== 1) {
|
|
412
|
+
formel = '(' + (formel || 'val') + ') ' + faktorText(fk);
|
|
413
|
+
}
|
|
414
|
+
return formel;
|
|
415
|
+
}
|
|
416
|
+
|
|
123
417
|
/* Die Zuordnungstabelle: je Alias-Punkt, was heute gilt und was danach
|
|
124
418
|
gelten soll. `stufe` sagt, wie verlaesslich der Vorschlag ist. */
|
|
125
419
|
export function tauschPlan(kanal, neuKanal) {
|
|
126
420
|
var altK = alteQuelle(kanal);
|
|
421
|
+
/* Einmal je Plan, nicht je Zeile: `vorschlag` prueft alle Vorlagen. */
|
|
422
|
+
var karte = zielKarte(neuKanal);
|
|
127
423
|
return aliasPunkte(kanal).map(function (id) {
|
|
128
424
|
var q = aliasQuellen(S.objects[id]);
|
|
129
425
|
var zeile = { id: id, name: id.slice(kanal.length + 1),
|
|
@@ -159,11 +455,79 @@ export function tauschPlan(kanal, neuKanal) {
|
|
|
159
455
|
var trW = suchePfad(zeile.altW);
|
|
160
456
|
if (trW) { zeile.neuW = trW; }
|
|
161
457
|
}
|
|
458
|
+
/* 5. wie beim Anklicken im Baum - Vorlage oder Detektor am ZIEL.
|
|
459
|
+
|
|
460
|
+
Gleich nach dem Pfadtreffer, denn das ist die belegteste Auskunft,
|
|
461
|
+
die es ueber das neue Geraet gibt. Nur der gleiche relative Pfad
|
|
462
|
+
(Stufe 1) geht noch davor: beim baugleichen Ersatz ist er exakt
|
|
463
|
+
und braucht keine Erkennung. */
|
|
464
|
+
if (!zeile.neuR) {
|
|
465
|
+
var zk = karte[String(zeile.name).split('.').pop().toLowerCase()];
|
|
466
|
+
if (zk && zk.read) {
|
|
467
|
+
/* Der Detektor ist auch nur eine Heuristik: Liegen am Ziel zwei
|
|
468
|
+
Punkte mit derselben Rolle, waehlt er einen davon - gemessen
|
|
469
|
+
in W26, wo ein zweiter `value.power` daneben lag und
|
|
470
|
+
`ELECTRIC_POWER` auf `ELECTRIC_POWER_B` landete. Heisst ein
|
|
471
|
+
Punkt dort GENAU wie der Platz und traegt dessen Rolle, ist er
|
|
472
|
+
gemeint; das ist keine Vermutung mehr.
|
|
473
|
+
|
|
474
|
+
Nur gegen den Detektor, nicht gegen die Vorlage: die hat ein
|
|
475
|
+
Mensch geschrieben und weiss es besser als jede Namensregel
|
|
476
|
+
(bei Tasmota heisst kein Punkt `ACTUAL`). */
|
|
477
|
+
var genau = (zk.her === 'detektor') ? ausAliasPunkt(id, zeile.name, neuKanal) : null;
|
|
478
|
+
if (genau && genau.stark && genau.id !== zk.read) {
|
|
479
|
+
var gc = (S.objects[genau.id] || {}).common || {};
|
|
480
|
+
zk = { read: genau.id, write: (gc.write ? genau.id : ''),
|
|
481
|
+
f: '', fw: '', her: zk.her, was: zk.was };
|
|
482
|
+
}
|
|
483
|
+
zeile.neuR = zk.read;
|
|
484
|
+
/* Ein Schreibziel bekommt nur, wer schreiben soll. Massgeblich
|
|
485
|
+
ist der ALIAS-Punkt, nicht das Ziel: `LOWBAT` ist ein Melder,
|
|
486
|
+
auch wenn der zigbee-Punkt `battery_low` beschreibbar waere
|
|
487
|
+
(gemessen 13.10.2026). */
|
|
488
|
+
var ac5 = (S.objects[id] || {}).common || {};
|
|
489
|
+
zeile.neuW = ac5.write ? (zk.write || '') : '';
|
|
490
|
+
zeile.stufe = 5;
|
|
491
|
+
zeile.zielHer = zk.her;
|
|
492
|
+
zeile.zielWas = zk.was;
|
|
493
|
+
/* Die Formeln gehoeren zur Quelle: die alte beschreibt das alte
|
|
494
|
+
Geraet und ist nach dem Tausch falsch. Leer heisst „keine" -
|
|
495
|
+
auch das ist eine Auskunft (Matter liefert blanke Zahlen). */
|
|
496
|
+
zeile.neuF = zk.f || '';
|
|
497
|
+
zeile.neuFw = zk.fw || '';
|
|
498
|
+
zeile.formelNeu = true;
|
|
499
|
+
if (zk.write && zk.write === zk.read) { zeile.einPunkt = true; }
|
|
500
|
+
}
|
|
501
|
+
}
|
|
162
502
|
/* 2. ueber die Vorlage */
|
|
163
503
|
if (!zeile.neuR) {
|
|
164
504
|
var v = ausVorlage(id, neuKanal);
|
|
165
505
|
if (v && v.read) { zeile.neuR = v.read; zeile.stufe = 2; if (v.write) { zeile.neuW = v.write; } }
|
|
166
506
|
}
|
|
507
|
+
/* 4. ueber den Alias-Punkt - vor dem Raten, weil belegter.
|
|
508
|
+
|
|
509
|
+
Das Schreibziel darf hier auf der Lesequelle liegen: Wo ein
|
|
510
|
+
einziger Punkt liest und schreibt, ist das die richtige Form und
|
|
511
|
+
nicht die Falle aus der Regel weiter unten. Verlangt werden drei
|
|
512
|
+
Dinge - ein starker Treffer, ein Alias-Punkt, der ueberhaupt
|
|
513
|
+
schreibt, und ein Ziel, das sich beschreiben laesst. Ein Melder
|
|
514
|
+
wie `ACTUAL` bekommt so nie ein Schreibziel. */
|
|
515
|
+
if (!zeile.neuR) {
|
|
516
|
+
var ap = ausAliasPunkt(id, zeile.name, neuKanal);
|
|
517
|
+
if (ap && ap.stark) {
|
|
518
|
+
zeile.neuR = ap.id;
|
|
519
|
+
zeile.stufe = 4;
|
|
520
|
+
zeile.ausAlias = ap.rolle;
|
|
521
|
+
var ac = (S.objects[id] || {}).common || {};
|
|
522
|
+
var zc = (S.objects[ap.id] || {}).common || {};
|
|
523
|
+
if (ac.write && zc.write) { zeile.neuW = ap.id; zeile.einPunkt = true; }
|
|
524
|
+
} else if (ap) {
|
|
525
|
+
zeile.neuR = ap.id;
|
|
526
|
+
zeile.stufe = 3;
|
|
527
|
+
zeile.geratenUeber = 'aliasRolle';
|
|
528
|
+
zeile.geratenRolle = ap.rolle;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
167
531
|
/* 3. geraten */
|
|
168
532
|
if (!zeile.neuR && zeile.altR) {
|
|
169
533
|
var g = geraten(zeile.altR, neuKanal);
|
|
@@ -198,17 +562,66 @@ export function tauschPlan(kanal, neuKanal) {
|
|
|
198
562
|
der Fehler, den W20 abschaffen sollte (Ricardo, 12.09.2026).
|
|
199
563
|
|
|
200
564
|
Danach steht `neuW` leer, und die Sperre aus W20 greift: der
|
|
201
|
-
Nutzer entscheidet, ob der Punkt entfernt wird.
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
565
|
+
Nutzer entscheidet, ob der Punkt entfernt wird.
|
|
566
|
+
|
|
567
|
+
Ausgenommen ist nur Stufe 4 mit `einPunkt`: dort ist die Gleichheit
|
|
568
|
+
nicht geraten, sondern gepruefte Absicht - ein Punkt, der liest und
|
|
569
|
+
schreibt. Matter legt die Steckdose so an (`…OnOffPlugInUnit-1.SET`,
|
|
570
|
+
`role: switch`, lesbar und beschreibbar), hm-rpc mit `1.STATE`
|
|
571
|
+
ebenso. Ohne diese Ausnahme meldete die Zeile „kein Schreibziel
|
|
572
|
+
gefunden" an einem Punkt, der genau dafuer da ist. */
|
|
573
|
+
if (!zeile.einfach && !zeile.einPunkt &&
|
|
574
|
+
zeile.neuW && zeile.neuW === zeile.neuR) { zeile.neuW = ''; }
|
|
575
|
+
/* 6. Von Hand gewaehlt - das letzte Wort hat der Mensch.
|
|
576
|
+
|
|
577
|
+
Was keine Stufe findet, liess sich bisher nur entfernen oder
|
|
578
|
+
stehenlassen. Das ist zu wenig: Wer weiss, welcher Punkt am neuen
|
|
579
|
+
Geraet gemeint ist, soll ihn benennen koennen (Ricardo,
|
|
580
|
+
13.10.2026, an `info_connection`).
|
|
581
|
+
|
|
582
|
+
Die Formel wird dabei NICHT angefasst. Woher sollte die Werkbank
|
|
583
|
+
wissen, wie aus diesem Punkt der Wert wird? Das steht in keiner
|
|
584
|
+
Vorlage und in keiner Rolle - also bleibt stehen, was dasteht, und
|
|
585
|
+
die Zeile sagt, dass es zu pruefen ist. */
|
|
586
|
+
var hand = (S.tauschHand || {})[id];
|
|
587
|
+
if (hand && S.objects[hand]) {
|
|
588
|
+
zeile.neuR = hand;
|
|
589
|
+
zeile.stufe = 6;
|
|
590
|
+
zeile.vonHand = true;
|
|
591
|
+
zeile.formelNeu = false;
|
|
592
|
+
zeile.einPunkt = false;
|
|
593
|
+
var ah = (S.objects[id] || {}).common || {};
|
|
594
|
+
var zh = (S.objects[hand] || {}).common || {};
|
|
595
|
+
/* Schreibziel nur, wenn beide Seiten es hergeben - und dann auf
|
|
596
|
+
denselben Punkt, denn einen zweiten hat niemand benannt. */
|
|
597
|
+
zeile.neuW = (ah.write && zh.write) ? hand : '';
|
|
598
|
+
if (zeile.neuW) { zeile.einPunkt = true; }
|
|
599
|
+
return zeile;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
/* Bei einfacher Quelle gilt die Lesequelle fuer beides - ausser das
|
|
603
|
+
ZIEL verlangt es anders.
|
|
604
|
+
|
|
605
|
+
Ob ein Alias-Punkt eine oder zwei Quellen hat, ist keine
|
|
606
|
+
Eigenschaft des Alias, sondern des Geraets, an dem er haengt.
|
|
607
|
+
Matter hat einen Punkt, der liest und schreibt (`SET`); Tasmota
|
|
608
|
+
hat zwei, `stat.POWER` meldet und `cmnd.POWER` befiehlt. Beim
|
|
609
|
+
Tausch von dem einen auf das andere muss die Bauform mitwandern.
|
|
610
|
+
|
|
611
|
+
Ohne diese Ausnahme ueberschrieb die Regel genau das, was Stufe 5
|
|
612
|
+
aus der Vorlage geholt hatte: Der Befehl waere auf dem Melder
|
|
613
|
+
gelandet - der Fehler, den W20 und W22 abschaffen (gemessen in
|
|
614
|
+
W30, 13.10.2026). */
|
|
615
|
+
if (zeile.einfach && zeile.neuR && !(zeile.stufe === 5 && zeile.neuW)) {
|
|
616
|
+
zeile.neuW = zeile.neuR;
|
|
617
|
+
}
|
|
205
618
|
return zeile;
|
|
206
619
|
});
|
|
207
620
|
}
|
|
208
621
|
|
|
209
622
|
/* Was der Tausch am Ende schreibt - dieselbe Form wie `baueObjekte`,
|
|
210
623
|
damit der Trockenlauf-Dialog sie unveraendert anzeigen kann. */
|
|
211
|
-
export function tauschObjekte(kanal, neuKanal, plan, weg) {
|
|
624
|
+
export function tauschObjekte(kanal, neuKanal, plan, weg, merkungLoesen) {
|
|
212
625
|
var raus = [];
|
|
213
626
|
var k = S.objects[kanal];
|
|
214
627
|
if (k) {
|
|
@@ -216,6 +629,10 @@ export function tauschObjekte(kanal, neuKanal, plan, weg) {
|
|
|
216
629
|
delete kn.ts; delete kn.from; delete kn.user; delete kn.acl; delete kn._id;
|
|
217
630
|
kn.native = kn.native || {};
|
|
218
631
|
kn.native.quelle = neuKanal;
|
|
632
|
+
/* `geraetetyp` bleibt: der sagt, was das Geraet IST (socket), und das
|
|
633
|
+
aendert sich beim Tausch der Quelle nicht. Weg muss nur, was auf
|
|
634
|
+
die alte Bauart zeigt. */
|
|
635
|
+
if (merkungLoesen) { delete kn.native.vorlage; delete kn.native.vorlageVersion; }
|
|
219
636
|
raus.push({ id: kanal, neu: false, obj: kn });
|
|
220
637
|
}
|
|
221
638
|
plan.forEach(function (z) {
|
|
@@ -227,7 +644,11 @@ export function tauschObjekte(kanal, neuKanal, plan, weg) {
|
|
|
227
644
|
delete neu.ts; delete neu.from; delete neu.user; delete neu.acl; delete neu._id;
|
|
228
645
|
var a = neu.common.alias || {};
|
|
229
646
|
if (z.neuW && z.neuW !== z.neuR) { a.id = { read: z.neuR, write: z.neuW }; }
|
|
230
|
-
|
|
647
|
+
/* `einPunkt`: Lesen und Schreiben fallen am neuen Geraet auf einen
|
|
648
|
+
Punkt zusammen (Stufe 4). Dann ist die einfache Form richtig -
|
|
649
|
+
die Objektform unten wuerde `write` weglassen und aus einem
|
|
650
|
+
Schalter einen Melder machen. */
|
|
651
|
+
else if (z.einfach || z.einPunkt) { a.id = z.neuR; }
|
|
231
652
|
else {
|
|
232
653
|
/* Getrennte Quellen ohne Schreibtreffer: die Objektform bleibt.
|
|
233
654
|
|
|
@@ -240,6 +661,41 @@ export function tauschObjekte(kanal, neuKanal, plan, weg) {
|
|
|
240
661
|
nirgendwohin, und das ist die ehrliche Auskunft. */
|
|
241
662
|
a.id = { read: z.neuR };
|
|
242
663
|
}
|
|
664
|
+
/* Kam die Zuordnung vom Ziel (Stufe 5), kommt auch die Formel von
|
|
665
|
+
dort - das ist der Kern der Sache: die Formel beschreibt, wie man
|
|
666
|
+
aus DIESER Bauart einen Wert holt.
|
|
667
|
+
|
|
668
|
+
Leer setzen heisst loeschen, und das ist richtig: Matter liefert
|
|
669
|
+
eine blanke Zahl, auf die keine JSON-Formel passt. Umgekehrt
|
|
670
|
+
bringt der Weg zurueck zu Tasmota `JSON.parse(val)?.ENERGY?.Power`
|
|
671
|
+
wieder mit. */
|
|
672
|
+
if (z.formelNeu) {
|
|
673
|
+
if (z.neuFw) { a.write = z.neuFw; } else { delete a.write; }
|
|
674
|
+
}
|
|
675
|
+
/* Die Leseformel kommt aus `endFormel` - derselben Rechnung, die
|
|
676
|
+
die Zeile anzeigt. Zwei Rechenwege waeren zwei Wahrheiten. */
|
|
677
|
+
var endF = endFormel(z);
|
|
678
|
+
if (endF) { a.read = endF; } else { delete a.read; }
|
|
679
|
+
/* Mit der Merkung gehen die Formeln. Sie stammen aus derselben
|
|
680
|
+
Vorlage und beschreiben die alte Bauart; stehen sie weiter da,
|
|
681
|
+
steht der Punkt still auf `null` (siehe `merkungPruefen`). */
|
|
682
|
+
if (merkungLoesen) {
|
|
683
|
+
delete a.read;
|
|
684
|
+
delete a.write;
|
|
685
|
+
neu.native = neu.native || {};
|
|
686
|
+
delete neu.native.vorlage;
|
|
687
|
+
delete neu.native.vorlageVersion;
|
|
688
|
+
delete neu.native.ausZustand;
|
|
689
|
+
}
|
|
690
|
+
/* Die Einheit - was der Nutzer dazu entschieden hat.
|
|
691
|
+
|
|
692
|
+
Hier steht nur noch `einheit`: der Wert bleibt roh, die
|
|
693
|
+
Beschriftung zieht nach. Das Umrechnen steckt in `endFormel`
|
|
694
|
+
(oben), damit Anzeige und Ergebnis dieselbe Rechnung benutzen. */
|
|
695
|
+
var eW = (S.tauschEinheit || {})[z.id] || {};
|
|
696
|
+
if (eW.unit !== undefined) {
|
|
697
|
+
if (eW.unit) { neu.common.unit = eW.unit; } else { delete neu.common.unit; }
|
|
698
|
+
}
|
|
243
699
|
neu.common.alias = a;
|
|
244
700
|
raus.push({ id: z.id, neu: false, obj: neu });
|
|
245
701
|
});
|
|
@@ -397,6 +853,10 @@ export function zeigeTausch() {
|
|
|
397
853
|
S.tauschNeu = '';
|
|
398
854
|
S.tauschWeg = {};
|
|
399
855
|
S.tauschPlan = null;
|
|
856
|
+
S.tauschMerkung = null;
|
|
857
|
+
S.tauschWerte = null;
|
|
858
|
+
S.tauschHand = {};
|
|
859
|
+
S.tauschEinheit = {};
|
|
400
860
|
/* Auch die Suche faengt neu an. Sie blieb sonst stehen, und beim
|
|
401
861
|
naechsten Oeffnen zeigte der Baum „Kein Geraet gefunden" — im
|
|
402
862
|
schlimmsten Fall genau dann, wenn im Feld noch das eben getauschte,
|
|
@@ -465,6 +925,12 @@ function zeichneTausch() {
|
|
|
465
925
|
S.tauschNeu = id;
|
|
466
926
|
S.tauschPlan = null;
|
|
467
927
|
S.tauschWeg = {};
|
|
928
|
+
S.tauschWerte = null;
|
|
929
|
+
S.tauschHand = {};
|
|
930
|
+
S.tauschEinheit = {};
|
|
931
|
+
/* Andere Quelle, andere Frage: ob die gemerkte Vorlage passt, haengt
|
|
932
|
+
am Ziel. Die Vorgabe wird in `zeichneTausch` neu gesetzt. */
|
|
933
|
+
S.tauschMerkung = null;
|
|
468
934
|
zeichneTausch();
|
|
469
935
|
};
|
|
470
936
|
zeichneAuswahlbaum(baumHost, S.tauschFilter, gewaehlt);
|
|
@@ -493,6 +959,31 @@ function zeichneTausch() {
|
|
|
493
959
|
return;
|
|
494
960
|
}
|
|
495
961
|
|
|
962
|
+
/* Erst die Werte des neuen Geraets, dann die Zuordnung.
|
|
963
|
+
|
|
964
|
+
Stufe 5 fragt dieselbe Erkennung wie der Baum, und die schaut bei
|
|
965
|
+
einem Tasmota in den Rohwert von `tele.SENSOR` hinein: ohne Werte
|
|
966
|
+
faellt die Vorlage durch, und der Tausch zu einem MQTT-Geraet faende
|
|
967
|
+
wieder nichts. Einmal je Ziel - `S.tauschWerte` merkt, welches
|
|
968
|
+
schon geholt ist. */
|
|
969
|
+
if (S.tauschWerte !== S.tauschNeu) {
|
|
970
|
+
var zielPunkte = kindZustaende(S.tauschNeu);
|
|
971
|
+
var fehlt = zielPunkte.some(function (id) { return S.werte[id] === undefined; });
|
|
972
|
+
if (fehlt) {
|
|
973
|
+
S.tauschWerte = S.tauschNeu;
|
|
974
|
+
holeWerte(S.tauschNeu, zielPunkte, function () {
|
|
975
|
+
/* Noch im selben Dialog und am selben Ziel? Sonst ist das
|
|
976
|
+
Ergebnis von gestern. */
|
|
977
|
+
if (S.tauschNeu !== S.tauschWerte) { return; }
|
|
978
|
+
S.tauschPlan = null;
|
|
979
|
+
zeichneTausch();
|
|
980
|
+
});
|
|
981
|
+
body.appendChild(el('div', 'aside w', tr('swap.loadingValues')));
|
|
982
|
+
return;
|
|
983
|
+
}
|
|
984
|
+
S.tauschWerte = S.tauschNeu;
|
|
985
|
+
}
|
|
986
|
+
|
|
496
987
|
/* Die Zuordnung, Zeile fuer Zeile. */
|
|
497
988
|
if (!S.tauschPlan) { S.tauschPlan = tauschPlan(kanal, S.tauschNeu); }
|
|
498
989
|
var plan = S.tauschPlan;
|
|
@@ -575,17 +1066,48 @@ function zeichneTausch() {
|
|
|
575
1066
|
neue Quelle zeigte). */
|
|
576
1067
|
var gleich = z.neuR && z.neuR === z.altR
|
|
577
1068
|
&& (z.einfach || (z.neuW || '') === (z.altW || ''));
|
|
578
|
-
|
|
1069
|
+
/* Der Name und seine Marke untereinander, nicht nebeneinander: Die
|
|
1070
|
+
Namensspalte ist 120 Pixel breit, und „ERKANNT" hinter einem
|
|
1071
|
+
langen Namen wie `ELECTRIC_POWER` lief in die Pfade der Nachbarn
|
|
1072
|
+
hinein (Ricardo, 13.10.2026, am Bild zu sehen). Eine Zeile ist
|
|
1073
|
+
hier ohnehin immer mindestens zweizeilig. */
|
|
1074
|
+
var nm = el('span', 'sn zuname', z.name);
|
|
579
1075
|
if (gleich) { nm.appendChild(el('span', 'rat', tr('swap.unchanged'))); }
|
|
580
1076
|
else if (z.stufe === 3) {
|
|
581
1077
|
/* Die Marke sagt jetzt auch, WORUEBER geraten wurde - ueber den
|
|
582
1078
|
blossen Punktnamen oder ueber die Rolle. Das sind zwei sehr
|
|
583
1079
|
verschiedene Vermutungen, und sie pruefen sich verschieden. */
|
|
584
1080
|
var rm = el('span', 'rat', tr('guess.mark'));
|
|
585
|
-
rm.title = z.geratenUeber === '
|
|
586
|
-
? tr('swap.
|
|
587
|
-
:
|
|
1081
|
+
rm.title = z.geratenUeber === 'aliasRolle'
|
|
1082
|
+
? tr('swap.guessedByAliasRole', z.geratenRolle || '?')
|
|
1083
|
+
: z.geratenUeber === 'rolle'
|
|
1084
|
+
? tr('swap.guessedByRole', z.geratenRolle || '?')
|
|
1085
|
+
: tr('swap.guessedByName', (z.altR || '').split('.').pop());
|
|
588
1086
|
nm.appendChild(rm);
|
|
1087
|
+
} else if (z.stufe === 6) {
|
|
1088
|
+
var hm2 = el('span', 'rat', tr('swap.byHandMark'));
|
|
1089
|
+
hm2.title = tr('swap.byHandHint');
|
|
1090
|
+
nm.appendChild(hm2);
|
|
1091
|
+
} else if (z.stufe === 5) {
|
|
1092
|
+
/* Woher die Zuordnung kommt - aus der Vorlage des neuen Geraets
|
|
1093
|
+
oder aus seinen Rollen. Das ist dieselbe Auskunft, die der
|
|
1094
|
+
Reiter beim Anklicken gibt, und sie gehoert hierher: wer
|
|
1095
|
+
zurueck auf einen Tasmota tauscht, soll sehen, dass die
|
|
1096
|
+
Tasmota-Vorlage dahintersteht. */
|
|
1097
|
+
var zh = el('span', 'herk', z.zielHer === 'vorlage'
|
|
1098
|
+
? tr('swap.fromTplMark') : tr('swap.fromDetectMark'));
|
|
1099
|
+
zh.title = z.zielHer === 'vorlage'
|
|
1100
|
+
? tr('swap.fromTpl', z.zielWas || '?')
|
|
1101
|
+
: tr('swap.fromDetect', z.zielWas || '?');
|
|
1102
|
+
nm.appendChild(zh);
|
|
1103
|
+
} else if (z.stufe === 4) {
|
|
1104
|
+
/* Kein „vermutet", aber auch kein stiller Treffer: dass die
|
|
1105
|
+
Zuordnung vom Alias-Punkt kommt und nicht vom Pfad, erklaert,
|
|
1106
|
+
warum ein Tausch zwischen zwei ganz verschiedenen Adaptern
|
|
1107
|
+
ueberhaupt aufgeht - und woran er haengt, wenn er danebenliegt. */
|
|
1108
|
+
var hm = el('span', 'herk', tr('swap.fromAliasMark'));
|
|
1109
|
+
hm.title = tr('swap.fromAlias', z.name.split('.').pop(), z.ausAlias || '?');
|
|
1110
|
+
nm.appendChild(hm);
|
|
589
1111
|
}
|
|
590
1112
|
r.appendChild(nm);
|
|
591
1113
|
var rechts = el('span');
|
|
@@ -620,6 +1142,211 @@ function zeichneTausch() {
|
|
|
620
1142
|
lb.appendChild(el('span', 'hint', tr('swap.removeIt')));
|
|
621
1143
|
return lb;
|
|
622
1144
|
};
|
|
1145
|
+
/* Einen Punkt des neuen Geraets von Hand benennen.
|
|
1146
|
+
|
|
1147
|
+
Die Liste zeigt die relativen Namen - `1.STATE`, `tele.SENSOR` -,
|
|
1148
|
+
weil der volle Pfad an jeder Zeile gleich anfaengt und nur Platz
|
|
1149
|
+
kostet. Sortiert wie im Baum. */
|
|
1150
|
+
var handAuswahl = function (zeile) {
|
|
1151
|
+
var huelle = el('div');
|
|
1152
|
+
huelle.style.marginTop = '4px';
|
|
1153
|
+
var vor = S.tauschNeu + '.';
|
|
1154
|
+
var sel = document.createElement('select');
|
|
1155
|
+
sel.className = 'tx';
|
|
1156
|
+
sel.style.maxWidth = '100%';
|
|
1157
|
+
var leer = document.createElement('option');
|
|
1158
|
+
leer.value = '';
|
|
1159
|
+
leer.textContent = tr('swap.pickByHand');
|
|
1160
|
+
sel.appendChild(leer);
|
|
1161
|
+
kindZustaende(S.tauschNeu).forEach(function (pid) {
|
|
1162
|
+
var o = document.createElement('option');
|
|
1163
|
+
o.value = pid;
|
|
1164
|
+
var c = (S.objects[pid] || {}).common || {};
|
|
1165
|
+
o.textContent = pid.slice(vor.length) + (c.role ? ' · ' + c.role : '') +
|
|
1166
|
+
(c.unit ? ' · ' + c.unit : '');
|
|
1167
|
+
if ((S.tauschHand || {})[zeile.id] === pid) { o.selected = true; }
|
|
1168
|
+
sel.appendChild(o);
|
|
1169
|
+
});
|
|
1170
|
+
sel.addEventListener('change', function () {
|
|
1171
|
+
S.tauschHand = S.tauschHand || {};
|
|
1172
|
+
if (sel.value) { S.tauschHand[zeile.id] = sel.value; }
|
|
1173
|
+
else { delete S.tauschHand[zeile.id]; }
|
|
1174
|
+
S.tauschPlan = null;
|
|
1175
|
+
zeichneTausch();
|
|
1176
|
+
});
|
|
1177
|
+
huelle.appendChild(sel);
|
|
1178
|
+
return huelle;
|
|
1179
|
+
};
|
|
1180
|
+
|
|
1181
|
+
/* Wechselt die Einheit, stehen beide Werte nebeneinander und man
|
|
1182
|
+
waehlt, was damit geschehen soll. Vorbelegt ist „nichts aendern" -
|
|
1183
|
+
empfohlen wird, aber nicht entschieden. */
|
|
1184
|
+
/* Ein Kuerzel neben dem Feld: „so waere es, wenn du der Beschriftung
|
|
1185
|
+
glaubst". Ein Klick setzt es ein - entschieden ist damit nichts,
|
|
1186
|
+
und genau deshalb steht es neben dem Feld und nicht darin. */
|
|
1187
|
+
var vorschlagKnopf = function (feld, wert, danach, beschriftung) {
|
|
1188
|
+
var b = el('button', 'btn klein', beschriftung || wert);
|
|
1189
|
+
b.title = tr('swap.suggestionHint');
|
|
1190
|
+
b.addEventListener('click', function (ev) {
|
|
1191
|
+
ev.preventDefault();
|
|
1192
|
+
feld.value = wert;
|
|
1193
|
+
danach();
|
|
1194
|
+
});
|
|
1195
|
+
return b;
|
|
1196
|
+
};
|
|
1197
|
+
|
|
1198
|
+
/* Einheit und Faktor - beide frei, beide auf dem Ist-Zustand.
|
|
1199
|
+
|
|
1200
|
+
Die erste Fassung bot drei feste Knoepfe: nichts, Einheit des
|
|
1201
|
+
Ziels uebernehmen, mit dem bekannten Faktor rechnen. Das greift
|
|
1202
|
+
zu kurz, sobald das Ziel GAR KEINE Einheit traegt - und das ist
|
|
1203
|
+
kein Randfall, sondern der Normalfall bei einem JSON-Sammelpunkt.
|
|
1204
|
+
`tele.SENSOR` hat keine Einheit; die steckt im Feld, nicht im
|
|
1205
|
+
Objekt: `ENERGY.Total` IST kWh, `CONSUMPTION` bei Matter IST Wh.
|
|
1206
|
+
Dass ioBroker das nicht weiss, macht den Wechsel nicht kleiner -
|
|
1207
|
+
er wird dadurch unsichtbar (Ricardo, 13.10.2026).
|
|
1208
|
+
|
|
1209
|
+
Also zwei Felder statt Knoepfe. Wer nichts anfasst, aendert
|
|
1210
|
+
nichts; was die Werkbank weiss, steht als Vorschlag daneben. Das
|
|
1211
|
+
deckt auch den Fall ab, in dem die Beschriftung LUEGT: Matter
|
|
1212
|
+
schreibt an `CURRENT` die Einheit mA und liefert Ampere, der
|
|
1213
|
+
angebotene Faktor waere also falsch - und die beiden Werte
|
|
1214
|
+
darueber zeigen es. */
|
|
1215
|
+
var einheitenWahl = function (zeile) {
|
|
1216
|
+
if (!zeile.neuR) { return document.createDocumentFragment(); }
|
|
1217
|
+
var altO = S.objects[zeile.id] || {}, neuO = S.objects[zeile.neuR] || {};
|
|
1218
|
+
var altE = ((altO.common || {}).unit || '').trim();
|
|
1219
|
+
var neuE = ((neuO.common || {}).unit || '').trim();
|
|
1220
|
+
|
|
1221
|
+
/* Verglichen werden die beiden QUELLEN, nicht der Alias mit dem
|
|
1222
|
+
Ziel.
|
|
1223
|
+
|
|
1224
|
+
Vorher stand hier `altE === neuE` mit der Einheit des ALIAS auf
|
|
1225
|
+
der einen Seite - und damit erschien der Kasten auch beim Tausch
|
|
1226
|
+
zwischen zwei gleichartigen Geraeten: Ein Alias in kWh, der aus
|
|
1227
|
+
`…PC_Bastelzimmer.tele.SENSOR` liest, sollte auf
|
|
1228
|
+
`…PC_Jonas.tele.SENSOR` umziehen. Beide JSON-Punkte tragen keine
|
|
1229
|
+
Einheit, es aendert sich also nichts - trotzdem stand da
|
|
1230
|
+
„am Alias kWh · neue Quelle: keine" samt Feldern und dem Hinweis
|
|
1231
|
+
auf die Aufzeichnung (Ricardo, 14.10.2026).
|
|
1232
|
+
|
|
1233
|
+
Mit dem Vergleich der Quellen verschwindet das von selbst, ohne
|
|
1234
|
+
Sonderregel fuer „identisches Geraet": Liefert die neue Quelle
|
|
1235
|
+
dasselbe wie die alte, gibt es nichts zu entscheiden. */
|
|
1236
|
+
var quellE = '';
|
|
1237
|
+
var altQ = zeile.altR && S.objects[zeile.altR];
|
|
1238
|
+
if (altQ) { quellE = ((altQ.common || {}).unit || '').trim(); }
|
|
1239
|
+
else { quellE = altE; } /* Quelle weg (W13): dann der Alias */
|
|
1240
|
+
if (quellE === neuE) { return document.createDocumentFragment(); }
|
|
1241
|
+
|
|
1242
|
+
S.tauschEinheit = S.tauschEinheit || {};
|
|
1243
|
+
var stand = S.tauschEinheit[zeile.id] || {};
|
|
1244
|
+
|
|
1245
|
+
var huelle = el('div');
|
|
1246
|
+
huelle.style.marginTop = '5px';
|
|
1247
|
+
huelle.style.paddingLeft = '8px';
|
|
1248
|
+
huelle.style.borderLeft = '2px solid var(--warn)';
|
|
1249
|
+
huelle.appendChild(el('div', 'hint',
|
|
1250
|
+
tr('swap.unitChanged', altE || tr('swap.unitNone'), neuE || tr('swap.unitNone'))));
|
|
1251
|
+
|
|
1252
|
+
/* Die beiden Werte - der eigentliche Punkt. Was der Alias heute
|
|
1253
|
+
zeigt und was nach dem Tausch dastuende. Beim Rohwert eines
|
|
1254
|
+
JSON-Punktes waere die ganze Nachricht unlesbar, deshalb wird
|
|
1255
|
+
er durch die neue Formel gereicht. */
|
|
1256
|
+
var wertVon = function (id) {
|
|
1257
|
+
var w = S.werte[id];
|
|
1258
|
+
if (!w || w.val === undefined || w.val === null) { return null; }
|
|
1259
|
+
return w.val;
|
|
1260
|
+
};
|
|
1261
|
+
var wRoh = wertVon(zeile.neuR), wAlt = wertVon(zeile.id);
|
|
1262
|
+
if (wAlt !== null) {
|
|
1263
|
+
huelle.appendChild(el('div', 'hint',
|
|
1264
|
+
tr('swap.unitValOld', String(wAlt), altE || '')));
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
var ergebnisZeile = el('div', 'sn');
|
|
1268
|
+
ergebnisZeile.style.marginTop = '3px';
|
|
1269
|
+
|
|
1270
|
+
var feldZeile = function (beschriftung, feld, knopf) {
|
|
1271
|
+
var zz = el('div');
|
|
1272
|
+
zz.style.display = 'flex';
|
|
1273
|
+
zz.style.gap = '6px';
|
|
1274
|
+
zz.style.alignItems = 'center';
|
|
1275
|
+
zz.style.marginTop = '4px';
|
|
1276
|
+
zz.appendChild(el('span', 'rx', beschriftung));
|
|
1277
|
+
zz.appendChild(feld);
|
|
1278
|
+
if (knopf) { zz.appendChild(knopf); }
|
|
1279
|
+
return zz;
|
|
1280
|
+
};
|
|
1281
|
+
|
|
1282
|
+
var eFeld = document.createElement('input');
|
|
1283
|
+
eFeld.className = 'tx';
|
|
1284
|
+
eFeld.type = 'text';
|
|
1285
|
+
eFeld.style.width = '90px';
|
|
1286
|
+
eFeld.value = (stand.unit !== undefined) ? stand.unit : altE;
|
|
1287
|
+
var fFeld = document.createElement('input');
|
|
1288
|
+
fFeld.className = 'tx';
|
|
1289
|
+
fFeld.type = 'text';
|
|
1290
|
+
fFeld.style.width = '90px';
|
|
1291
|
+
fFeld.value = (stand.faktor !== undefined) ? String(stand.faktor) : '1';
|
|
1292
|
+
|
|
1293
|
+
/* Nur rechnen und die eine Zeile neu schreiben - NICHT den Dialog
|
|
1294
|
+
neu zeichnen. Sonst springt der Fokus bei jedem Anschlag aus dem
|
|
1295
|
+
Feld, und tippen wird unmoeglich. */
|
|
1296
|
+
var frisch = function () {
|
|
1297
|
+
var e = eFeld.value.trim();
|
|
1298
|
+
var f = fFeld.value.trim();
|
|
1299
|
+
S.tauschEinheit[zeile.id] = {
|
|
1300
|
+
unit: (e === altE) ? undefined : e,
|
|
1301
|
+
faktor: (f === '' || f === '1') ? undefined : f
|
|
1302
|
+
};
|
|
1303
|
+
S.tauschPlan = null;
|
|
1304
|
+
ergebnisZeile.textContent = '';
|
|
1305
|
+
var neuPlan = tauschPlan(kanal, S.tauschNeu);
|
|
1306
|
+
S.tauschPlan = neuPlan;
|
|
1307
|
+
var zz = neuPlan.find(function (x) { return x.id === zeile.id; }) || zeile;
|
|
1308
|
+
var fz = endFormel(zz);
|
|
1309
|
+
var txtE = (e !== '') ? e : '';
|
|
1310
|
+
if (wRoh === null) {
|
|
1311
|
+
ergebnisZeile.textContent = tr('swap.unitResult', '—', txtE);
|
|
1312
|
+
return;
|
|
1313
|
+
}
|
|
1314
|
+
var r = auswerten(fz, wRoh);
|
|
1315
|
+
ergebnisZeile.textContent = tr('swap.unitResult',
|
|
1316
|
+
r.ok ? String(r.val) : (r.txt || '—'), txtE);
|
|
1317
|
+
formelNach(fz);
|
|
1318
|
+
};
|
|
1319
|
+
/* Die Formelzeile derselben Reihe nachziehen. Sie steht ausserhalb
|
|
1320
|
+
dieses Kastens, gehoert aber zur selben Entscheidung - bliebe
|
|
1321
|
+
sie stehen, zeigte sie eine Formel, die so nicht geschrieben
|
|
1322
|
+
wird. Genau der Fehler, den Ricardo vorhin an der Pfeilzeile
|
|
1323
|
+
gefunden hat, nur eine Ebene weiter. */
|
|
1324
|
+
var formelNach = function (fz) {
|
|
1325
|
+
var reihe = huelle.closest ? huelle.closest('.slot') : null;
|
|
1326
|
+
var ziel = reihe && reihe.querySelector('.formel-neu span:last-child');
|
|
1327
|
+
if (ziel) { ziel.textContent = fz || tr('swap.formulaNone'); }
|
|
1328
|
+
};
|
|
1329
|
+
eFeld.addEventListener('input', frisch);
|
|
1330
|
+
fFeld.addEventListener('input', frisch);
|
|
1331
|
+
|
|
1332
|
+
huelle.appendChild(feldZeile(tr('swap.unitField'), eFeld,
|
|
1333
|
+
neuE ? vorschlagKnopf(eFeld, neuE, frisch) : null));
|
|
1334
|
+
var faktor = umrechenFaktor(altE, neuE);
|
|
1335
|
+
huelle.appendChild(feldZeile(tr('swap.factorField'), fFeld,
|
|
1336
|
+
faktor ? vorschlagKnopf(fFeld, String(faktor), frisch, faktorText(faktor)) : null));
|
|
1337
|
+
if (!faktor && altE && neuE) {
|
|
1338
|
+
huelle.appendChild(el('div', 'hint', tr('swap.unitNoFactor')));
|
|
1339
|
+
}
|
|
1340
|
+
huelle.appendChild(ergebnisZeile);
|
|
1341
|
+
/* Haengt eine Aufzeichnung dran, gehoert der Hinweis dazu - die
|
|
1342
|
+
Historie liegt in der alten Einheit. */
|
|
1343
|
+
if (altO.common && altO.common.custom && Object.keys(altO.common.custom).length) {
|
|
1344
|
+
huelle.appendChild(el('div', 'hint', tr('swap.unitHistWarn')));
|
|
1345
|
+
}
|
|
1346
|
+
frisch();
|
|
1347
|
+
return huelle;
|
|
1348
|
+
};
|
|
1349
|
+
|
|
623
1350
|
/* Beschriftet, sonst raet man, welche der beiden Zeilen die neue ist. */
|
|
624
1351
|
var paarZeile = function (beschriftung, text, klasse, farbe) {
|
|
625
1352
|
var zz = el('div');
|
|
@@ -638,9 +1365,45 @@ function zeichneTausch() {
|
|
|
638
1365
|
rechts.appendChild(paarZeile(tr('write.diffNew'),
|
|
639
1366
|
z.neuR + (z.neuW && z.neuW !== z.neuR ? ' → ' + z.neuW : ''),
|
|
640
1367
|
'sn', gleich ? 'var(--ink-3)' : 'var(--ok)'));
|
|
1368
|
+
/* Von Hand benannt heisst: die Werkbank weiss nichts ueber diesen
|
|
1369
|
+
Punkt. Formel, Einheit und Wertebereich bleiben, wie sie am
|
|
1370
|
+
Alias stehen - und passen vielleicht nicht mehr. */
|
|
1371
|
+
if (z.vonHand) {
|
|
1372
|
+
var hw = el('div', 'hint');
|
|
1373
|
+
hw.style.color = 'var(--warn)';
|
|
1374
|
+
hw.textContent = tr('swap.byHandCheck');
|
|
1375
|
+
rechts.appendChild(hw);
|
|
1376
|
+
rechts.appendChild(handAuswahl(z));
|
|
1377
|
+
}
|
|
641
1378
|
/* Lesequelle gefunden, Schreibquelle nicht — das gehoert gesagt.
|
|
642
1379
|
Sonst steht nur die neue Lesequelle in Gruen da, und dass der
|
|
643
1380
|
Punkt danach nirgendwohin schreibt, merkt man erst am Geraet. */
|
|
1381
|
+
rechts.appendChild(einheitenWahl(z));
|
|
1382
|
+
/* Wechselt die Formel mit, gehoert das in die Zeile - sie
|
|
1383
|
+
entscheidet mit darueber, was am Alias ankommt, und man sieht es
|
|
1384
|
+
dem Pfad nicht an. */
|
|
1385
|
+
/* Was am Ende dasteht - einschliesslich einer gewaehlten
|
|
1386
|
+
Umrechnung. `endFormel` rechnet dasselbe wie `tauschObjekte`. */
|
|
1387
|
+
var altF = (S.objects[z.id] && S.objects[z.id].common &&
|
|
1388
|
+
S.objects[z.id].common.alias || {}).read || '';
|
|
1389
|
+
var endF = endFormel(z);
|
|
1390
|
+
if (altF !== endF) {
|
|
1391
|
+
/* Beschriftet wie die Pfade darueber. Vorher stand dort nur ein
|
|
1392
|
+
Pfeil zwischen zwei Formeln, und man musste raten, welche die
|
|
1393
|
+
neue ist - Ricardo hat zweimal nachgefragt (13.10.2026). */
|
|
1394
|
+
var fb = el('div');
|
|
1395
|
+
fb.style.marginTop = '3px';
|
|
1396
|
+
fb.appendChild(el('div', 'rx', tr('swap.formula')));
|
|
1397
|
+
fb.appendChild(paarZeile(tr('write.diffOld'),
|
|
1398
|
+
altF || tr('swap.formulaNone'), 'zq'));
|
|
1399
|
+
var fneu = paarZeile(tr('write.diffNew'),
|
|
1400
|
+
endF || tr('swap.formulaNone'), 'sn');
|
|
1401
|
+
/* Markiert, damit die Einheitenfelder sie beim Tippen
|
|
1402
|
+
mitschreiben koennen - sie zeichnen den Dialog nicht neu. */
|
|
1403
|
+
fneu.classList.add('formel-neu');
|
|
1404
|
+
fb.appendChild(fneu);
|
|
1405
|
+
rechts.appendChild(fb);
|
|
1406
|
+
}
|
|
644
1407
|
if (z.altW && z.altW !== z.altR && !z.neuW) {
|
|
645
1408
|
var kw = el('div');
|
|
646
1409
|
kw.style.color = 'var(--bad)';
|
|
@@ -656,12 +1419,56 @@ function zeichneTausch() {
|
|
|
656
1419
|
/* Was nichts findet, laesst sich mit entfernen - sonst bliebe ein
|
|
657
1420
|
Punkt zurueck, der ins Leere zeigt (Ricardos Wahl, 25.08.2026). */
|
|
658
1421
|
rechts.appendChild(entfernenKaestchen());
|
|
1422
|
+
/* Oder von Hand benennen. Wer sein Geraet kennt, weiss oft genau,
|
|
1423
|
+
welcher Punkt gemeint ist - die Werkbank kann es nur nicht
|
|
1424
|
+
ableiten (Ricardo, 13.10.2026). */
|
|
1425
|
+
rechts.appendChild(handAuswahl(z));
|
|
659
1426
|
}
|
|
660
1427
|
r.appendChild(rechts);
|
|
661
1428
|
k.appendChild(r);
|
|
662
1429
|
});
|
|
663
1430
|
body.appendChild(k);
|
|
664
1431
|
|
|
1432
|
+
/* --- Die gemerkte Vorlage, gegen die neue Quelle gehalten ---
|
|
1433
|
+
|
|
1434
|
+
Nur wenn sie NICHT mehr passt. Beim baugleichen Ersatz steht hier
|
|
1435
|
+
nichts, und das ist der haeufigere Fall. */
|
|
1436
|
+
var merk = merkungPruefen(kanal, S.tauschNeu);
|
|
1437
|
+
if (merk && !merk.passt) {
|
|
1438
|
+
var stellen = merkungStellen(kanal);
|
|
1439
|
+
if (S.tauschMerkung === null || S.tauschMerkung === undefined) { S.tauschMerkung = true; }
|
|
1440
|
+
var mk = el('div', 'aside w');
|
|
1441
|
+
mk.style.borderLeftColor = 'var(--warn)';
|
|
1442
|
+
mk.appendChild(el('b', null, tr('swap.tplStale', merk.name)));
|
|
1443
|
+
(merk.fehler || []).forEach(function (f) { mk.appendChild(el('div', 'hint', f)); });
|
|
1444
|
+
/* Die Formeln sind der eigentliche Grund, warum das nicht still
|
|
1445
|
+
stehenbleiben darf - sie machen aus Messwerten ein `null`, ohne
|
|
1446
|
+
dass irgendwo ein Fehler steht. */
|
|
1447
|
+
if (stellen.formeln.length) {
|
|
1448
|
+
mk.appendChild(el('div', 'hint', tr('swap.tplFormulas', stellen.formeln.length)));
|
|
1449
|
+
}
|
|
1450
|
+
if (merk.ersatz) {
|
|
1451
|
+
mk.appendChild(el('div', 'hint', tr('swap.tplOther', merk.ersatz.name)));
|
|
1452
|
+
}
|
|
1453
|
+
var ml = document.createElement('label');
|
|
1454
|
+
ml.style.display = 'flex';
|
|
1455
|
+
ml.style.gap = '6px';
|
|
1456
|
+
ml.style.alignItems = 'center';
|
|
1457
|
+
ml.style.marginTop = '7px';
|
|
1458
|
+
ml.style.cursor = 'pointer';
|
|
1459
|
+
var mcb = document.createElement('input');
|
|
1460
|
+
mcb.type = 'checkbox';
|
|
1461
|
+
mcb.checked = !!S.tauschMerkung;
|
|
1462
|
+
mcb.addEventListener('change', function () { S.tauschMerkung = mcb.checked; zeichneTausch(); });
|
|
1463
|
+
ml.appendChild(mcb);
|
|
1464
|
+
ml.appendChild(el('span', null, tr('swap.tplDrop',
|
|
1465
|
+
stellen.punkte.length + (stellen.kanal ? 1 : 0))));
|
|
1466
|
+
mk.appendChild(ml);
|
|
1467
|
+
body.appendChild(mk);
|
|
1468
|
+
} else {
|
|
1469
|
+
S.tauschMerkung = false;
|
|
1470
|
+
}
|
|
1471
|
+
|
|
665
1472
|
/* Wo die neue Quelle anders tickt als die alte. Die Formel des Alias
|
|
666
1473
|
bleibt beim Tausch stehen - rechnet die alte Quelle 0..100 und die
|
|
667
1474
|
neue 0..255, stimmt danach jeder Wert nicht mehr. Das sieht man dem
|
|
@@ -744,7 +1551,7 @@ export function tauscheAus() {
|
|
|
744
1551
|
b.disabled = true;
|
|
745
1552
|
b.textContent = tr('write.writing');
|
|
746
1553
|
|
|
747
|
-
var arbeit = tauschObjekte(kanal, neu, S.tauschPlan, S.tauschWeg);
|
|
1554
|
+
var arbeit = tauschObjekte(kanal, neu, S.tauschPlan, S.tauschWeg, !!S.tauschMerkung);
|
|
748
1555
|
var loeschen = Object.keys(S.tauschWeg).filter(function (id) { return S.tauschWeg[id]; });
|
|
749
1556
|
var fehler = [];
|
|
750
1557
|
|