iobroker.alias-workbench 0.8.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.
Files changed (58) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +516 -0
  3. package/admin/alias-workbench.png +0 -0
  4. package/admin/css/werkbank.css +916 -0
  5. package/admin/detector.js +1 -0
  6. package/admin/geraetewissen/tasmota-befehle.json +116 -0
  7. package/admin/i18n/de.json +552 -0
  8. package/admin/i18n/en.json +552 -0
  9. package/admin/js/aufzaehlungen.js +332 -0
  10. package/admin/js/basis.js +36 -0
  11. package/admin/js/baum.js +453 -0
  12. package/admin/js/detail.js +335 -0
  13. package/admin/js/einstellungen.js +40 -0
  14. package/admin/js/entwurf.js +908 -0
  15. package/admin/js/enums.js +23 -0
  16. package/admin/js/ergebnis.js +934 -0
  17. package/admin/js/erkennung.js +126 -0
  18. package/admin/js/katalog.js +313 -0
  19. package/admin/js/mqtt.js +885 -0
  20. package/admin/js/musternamen.js +94 -0
  21. package/admin/js/musterwahl.js +334 -0
  22. package/admin/js/objekte.js +243 -0
  23. package/admin/js/pruefungen.js +130 -0
  24. package/admin/js/quellentausch.js +570 -0
  25. package/admin/js/rollenwahl.js +199 -0
  26. package/admin/js/schreiben.js +1537 -0
  27. package/admin/js/sprache.js +137 -0
  28. package/admin/js/start.js +39 -0
  29. package/admin/js/verbindung.js +10 -0
  30. package/admin/js/vorlagen.js +572 -0
  31. package/admin/js/vorlagenblatt.js +1907 -0
  32. package/admin/js/vorschlagen.js +391 -0
  33. package/admin/js/werte.js +193 -0
  34. package/admin/js/zielleiste.js +339 -0
  35. package/admin/js/zuordnung.js +695 -0
  36. package/admin/js/zustand.js +47 -0
  37. package/admin/js/zustandsliste.js +515 -0
  38. package/admin/jsonConfig.json +249 -0
  39. package/admin/tab.html +148 -0
  40. package/admin/vorlagen/hm-dimmer.json +204 -0
  41. package/admin/vorlagen/hm-fensterkontakt-hm.json +176 -0
  42. package/admin/vorlagen/hm-fensterkontakt-hmip.json +191 -0
  43. package/admin/vorlagen/hm-heizgruppe.json +214 -0
  44. package/admin/vorlagen/hm-heizkoerper.json +236 -0
  45. package/admin/vorlagen/hm-rollladen.json +190 -0
  46. package/admin/vorlagen/hm-schaltaktor-lampe.json +197 -0
  47. package/admin/vorlagen/hm-schaltaktor-mehrfach.json +203 -0
  48. package/admin/vorlagen/hm-schaltaktor-steckdose.json +197 -0
  49. package/admin/vorlagen/hm-temperatur-feuchte.json +166 -0
  50. package/admin/vorlagen/hm-wandthermostat.json +228 -0
  51. package/admin/vorlagen/index.json +21 -0
  52. package/admin/vorlagen/messpunkt.json +225 -0
  53. package/admin/vorlagen/tasmota-lampe.json +263 -0
  54. package/admin/vorlagen/tasmota-mehrfach.json +203 -0
  55. package/admin/vorlagen/tasmota-rgb.json +239 -0
  56. package/admin/vorlagen/tasmota-steckdose.json +265 -0
  57. package/io-package.json +122 -0
  58. package/package.json +48 -0
@@ -0,0 +1,335 @@
1
+ /* Die aufgeklappte Zeile: Quellen, Formeln, Rolle, Typ, Einheit. */
2
+
3
+ import { S } from './zustand.js';
4
+ import { socket } from './verbindung.js';
5
+ import { el } from './basis.js';
6
+ import { tr } from './sprache.js';
7
+ import { wertVon, jsonFelder, feldAusFormel, feldFormel, feldWert, jsonVon } from './werte.js';
8
+ import { musterVon, erkenneEntwurf } from './erkennung.js';
9
+ import { musterName } from './musternamen.js';
10
+ import { rollenFeld } from './rollenwahl.js';
11
+ import { opt , quellenAuswahl } from './entwurf.js';
12
+ import { zeichneErgebnis, entwurfAngefasst } from './ergebnis.js';
13
+
14
+ import { rateBehalten } from './vorschlagen.js';
15
+ import { dtZeile } from './vorlagenblatt.js';
16
+ import './mqtt.js';
17
+
18
+
19
+
20
+ /* ================== Aufgeklappte Zeile: hier wird bearbeitet ================== */
21
+ export function detailZeile(e, s, idx) {
22
+ var d = el('div', 'detail');
23
+ var quellen = quellenAuswahl(e);
24
+
25
+ function zeile(k, inhalt, cls) { return dtZeile(d, k, inhalt, cls); }
26
+
27
+ /* Wer die Zeile anfasst, hat entschieden - die Vermutung ist damit
28
+ keine mehr und faellt beim Zuruecknehmen nicht mehr weg. */
29
+ function neu() { s.geaendert = true; rateBehalten(s); entwurfAngefasst(); zeichneErgebnis(); }
30
+
31
+ /* --- Name, nur bei selbst angelegten --- */
32
+ if (s.manuell) {
33
+ var iN = el('input', 'tx w');
34
+ iN.type = 'text';
35
+ iN.value = s.n;
36
+ iN.placeholder = tr('detail.namePlaceholder');
37
+ if (!s.n) { iN.className += ' miss'; }
38
+ iN.addEventListener('click', function (ev) { ev.stopPropagation(); });
39
+ iN.addEventListener('change', function () {
40
+ s.n = iN.value.trim().toUpperCase().replace(/\s+/g, '_');
41
+ neu();
42
+ });
43
+ zeile(tr('detail.name'), iN);
44
+ } else if (s.urId) {
45
+ zeile(tr('detail.objectId'), s.urId);
46
+ } else {
47
+ zeile(tr('detail.willBecome'), (e.ziel || e.kanal) + '.' + s.n);
48
+ }
49
+
50
+ /* --- Quelle lesen + JSON-Feld --- */
51
+ var qf = el('div', 'fields');
52
+
53
+ var selR = el('select', 'tx');
54
+ selR.style.width = '260px';
55
+ selR.appendChild(opt('', quellen.length ? tr('detail.pickOne') : tr('detail.noSource')));
56
+ quellen.forEach(function (id) { selR.appendChild(opt(id, id.split('.').slice(2).join('.'))); });
57
+ if (s.srcR && quellen.indexOf(s.srcR) === -1) { selR.appendChild(opt(s.srcR, s.srcR + ' ' + tr('detail.elsewhere'))); }
58
+ selR.appendChild(opt('__frei__', tr('detail.otherObject')));
59
+ selR.value = s.srcR || '';
60
+ if (!s.srcR) { selR.className += ' miss'; }
61
+ selR.addEventListener('click', function (ev) { ev.stopPropagation(); });
62
+ selR.addEventListener('change', function () {
63
+ if (selR.value === '__frei__') { s.frei = true; s.srcR = ''; }
64
+ else { s.frei = false; s.srcR = selR.value; }
65
+ if (s.srcR && !S.werte[s.srcR]) {
66
+ socket.emit('getState', s.srcR, function (err, st) {
67
+ if (!err && st) { S.werte[s.srcR] = st; zeichneErgebnis(); }
68
+ });
69
+ }
70
+ neu();
71
+ });
72
+ var lR = el('label', 'fld');
73
+ lR.appendChild(el('span', null, tr('detail.datapoint')));
74
+ lR.appendChild(selR);
75
+ qf.appendChild(lR);
76
+
77
+ var felder = s.srcR ? jsonFelder(s.srcR) : null;
78
+ if (felder && felder.length) {
79
+ var selF = el('select', 'tx');
80
+ selF.style.width = '180px';
81
+ selF.appendChild(opt('', tr('detail.ownFormula')));
82
+ felder.forEach(function (f) { selF.appendChild(opt(f, f)); });
83
+ selF.value = feldAusFormel(s.f);
84
+ selF.addEventListener('click', function (ev) { ev.stopPropagation(); });
85
+ selF.addEventListener('change', function () {
86
+ s.f = feldFormel(selF.value);
87
+ /* Den Typ nur vorschlagen, wenn keiner feststeht. Vorher stand
88
+ hier hart `number` — nach der Wahl von POWER („ON") oder
89
+ Wifi.SSId stand der Punkt auf Zahl, und ein Platz, den das
90
+ Muster als boolean fuehrt, verlor seine Vorgabe: aus
91
+ switch.light + number wird kein Geraet mehr. */
92
+ if (selF.value && !s.typ) {
93
+ var j = jsonVon(s.srcR);
94
+ var w = j ? feldWert(j, selF.value) : undefined;
95
+ if (typeof w === 'boolean') { s.typ = 'boolean'; }
96
+ else if (typeof w === 'number') { s.typ = 'number'; }
97
+ else if (typeof w === 'string') { s.typ = 'string'; }
98
+ }
99
+ neu();
100
+ });
101
+ var lF = el('label', 'fld');
102
+ lF.appendChild(el('span', null, tr('detail.jsonField')));
103
+ lF.appendChild(selF);
104
+ qf.appendChild(lF);
105
+ }
106
+ zeile(tr('detail.readsFrom'), qf);
107
+
108
+ if (s.frei) {
109
+ var iFrei = el('input', 'tx w');
110
+ iFrei.type = 'text';
111
+ iFrei.value = s.srcR || '';
112
+ iFrei.placeholder = '0_userdata.0.…';
113
+ iFrei.addEventListener('click', function (ev) { ev.stopPropagation(); });
114
+ iFrei.addEventListener('change', function () { s.srcR = iFrei.value.trim(); neu(); });
115
+ zeile('Objekt-ID', iFrei);
116
+ }
117
+
118
+ /* --- Quelle schreiben --- */
119
+ var wf = el('div', 'fields');
120
+ var selW = el('select', 'tx');
121
+ selW.style.width = '260px';
122
+ /* Leer heisst „schreibt nicht" - und muss auch so heissen. Frueher
123
+ stand hier „dieselbe wie lesen", also das Gegenteil dessen, was
124
+ passierte (Ricardo, 25.08.2026). Wer wirklich auf die Lesequelle
125
+ schreiben will, nimmt den zweiten Eintrag; der traegt den Lesepunkt
126
+ ein, damit im Feld steht, worauf es hinausgeht. */
127
+ selW.appendChild(opt('', tr('detail.noWrite')));
128
+ if (s.srcR && s.srcW !== s.srcR) { selW.appendChild(opt('=lesen', tr('detail.sameAsRead'))); }
129
+ quellen.forEach(function (id) { selW.appendChild(opt(id, id.split('.').slice(2).join('.'))); });
130
+ if (s.srcW && quellen.indexOf(s.srcW) === -1) { selW.appendChild(opt(s.srcW, s.srcW)); }
131
+ selW.value = s.srcW || '';
132
+ selW.addEventListener('click', function (ev) { ev.stopPropagation(); });
133
+ selW.addEventListener('change', function () {
134
+ s.srcW = (selW.value === '=lesen') ? (s.srcR || '') : selW.value;
135
+ neu();
136
+ });
137
+ var lW = el('label', 'fld');
138
+ lW.appendChild(el('span', null, 'Datenpunkt'));
139
+ lW.appendChild(selW);
140
+ wf.appendChild(lW);
141
+ /* Der Hinweis gilt nur, wenn die beiden Quellen wirklich
142
+ auseinandergehen - bei gleicher Quelle ist nichts getrennt. */
143
+ if (s.srcW && s.srcW !== s.srcR) {
144
+ wf.appendChild(el('div', 'sugg', tr('detail.separateHint')));
145
+ }
146
+ zeile(tr('detail.writesTo'), wf);
147
+
148
+ /* --- Leseformel --- */
149
+ var fb = el('div');
150
+ var iF = el('input', 'tx w');
151
+ iF.type = 'text';
152
+ iF.value = s.f || '';
153
+ iF.placeholder = tr('detail.formulaPlaceholder');
154
+ iF.addEventListener('click', function (ev) { ev.stopPropagation(); });
155
+ iF.addEventListener('change', function () { s.f = iF.value.trim(); neu(); });
156
+ fb.appendChild(iF);
157
+ /* Der Hinweis gilt nur fuer Schaltpunkte. Bei einem Spannungswert
158
+ stand er auch — und ergab dort keinen Sinn. */
159
+ if ((s.typ || '') === 'boolean') {
160
+ fb.appendChild(el('div', 'sugg', tr('detail.formulaHint')));
161
+ }
162
+ zeile(tr('detail.formula'), fb);
163
+
164
+ if (s.srcW) {
165
+ var wb = el('div');
166
+ var iW = el('input', 'tx w');
167
+ iW.type = 'text';
168
+ iW.value = s.fw || '';
169
+ iW.placeholder = tr('detail.writeFormulaPlaceholder');
170
+ iW.addEventListener('click', function (ev) { ev.stopPropagation(); });
171
+ iW.addEventListener('change', function () { s.fw = iW.value.trim(); neu(); });
172
+ wb.appendChild(iW);
173
+ wb.appendChild(el('div', 'sugg', tr('detail.writeFormulaHint')));
174
+ zeile(tr('detail.writeFormula'), wb);
175
+ }
176
+
177
+ /* --- Rolle, mit Begründung --- */
178
+ var rb = el('div');
179
+ /* Welcher Platz ist schon vergeben, und von welcher Zeile? Das steht
180
+ als Vermerk am Gruppenkopf der Rollenliste. */
181
+ var belegtVon = {};
182
+ if (e.want) {
183
+ var fundR = erkenneEntwurf(e, e.want);
184
+ if (fundR.length) {
185
+ fundR[0].states.forEach(function (x) {
186
+ if (x.id) { belegtVon[x.name] = x.id.slice(e.kanal.length + 1); }
187
+ });
188
+ }
189
+ }
190
+ rb.appendChild(rollenFeld({
191
+ wert: s.role,
192
+ muster: e.want,
193
+ belegtVon: belegtVon,
194
+ breite: '300px',
195
+ beiWahl: function (r) { s.role = r; neu(); }
196
+ }));
197
+
198
+ var must = e.want && musterVon(e.want);
199
+ if (must) {
200
+ var treffer = null;
201
+ must.states.forEach(function (p) {
202
+ if (!treffer && p.role && new RegExp(p.role.source ? p.role.source : p.role).test(s.role)) { treffer = p; }
203
+ });
204
+ var hin = el('div', 'sugg');
205
+ if (treffer) {
206
+ hin.appendChild(document.createTextNode(tr('pattern.fitsOn')));
207
+ hin.appendChild(el('b', null, String(treffer.role)));
208
+ hin.appendChild(document.createTextNode(tr('pattern.toSlot', treffer.name, musterName(e.want) || e.want)));
209
+ } else {
210
+ hin.textContent = tr('pattern.noPlaceLong', musterName(e.want) || e.want);
211
+ }
212
+ rb.appendChild(hin);
213
+ }
214
+ zeile(tr('detail.role'), rb);
215
+
216
+ /* --- Typ und Anzeige --- */
217
+ var af = el('div', 'fields');
218
+
219
+ var selT = el('select', 'tx');
220
+ ['boolean', 'number', 'string', 'mixed'].forEach(function (t) { selT.appendChild(opt(t, t)); });
221
+ selT.value = s.typ || 'number';
222
+ selT.addEventListener('click', function (ev) { ev.stopPropagation(); });
223
+ selT.addEventListener('change', function () { s.typ = selT.value; neu(); });
224
+ var lT = el('label', 'fld');
225
+ lT.appendChild(el('span', null, tr('detail.type')));
226
+ lT.appendChild(selT);
227
+ af.appendChild(lT);
228
+
229
+ var iU = el('input', 'tx');
230
+ iU.type = 'text';
231
+ iU.style.width = '70px';
232
+ iU.value = s.unit || '';
233
+ iU.placeholder = '—';
234
+ iU.addEventListener('click', function (ev) { ev.stopPropagation(); });
235
+ iU.addEventListener('input', function () { s.unit = iU.value; s.geaendert = true; });
236
+ iU.addEventListener('change', function () { neu(); });
237
+ var lU = el('label', 'fld');
238
+ lU.appendChild(el('span', null, tr('detail.unit')));
239
+ lU.appendChild(iU);
240
+ af.appendChild(lU);
241
+
242
+ var selD = el('select', 'tx');
243
+ [['', tr('detail.asDelivered')], ['0', '0'], ['1', '1'], ['2', '2'], ['3', '3']].forEach(function (o) {
244
+ selD.appendChild(opt(o[0], o[1]));
245
+ });
246
+ selD.value = (s.dec === undefined ? '' : s.dec);
247
+ selD.addEventListener('click', function (ev) { ev.stopPropagation(); });
248
+ selD.addEventListener('change', function () { s.dec = selD.value; neu(); });
249
+ var lD = el('label', 'fld');
250
+ lD.appendChild(el('span', null, tr('detail.decimals')));
251
+ lD.appendChild(selD);
252
+ af.appendChild(lD);
253
+
254
+ var iC = el('input', 'tx');
255
+ iC.type = 'text';
256
+ iC.style.width = '150px';
257
+ iC.value = s.caption || '';
258
+ iC.placeholder = s.n;
259
+ iC.addEventListener('click', function (ev) { ev.stopPropagation(); });
260
+ iC.addEventListener('input', function () { s.caption = iC.value; s.geaendert = true; });
261
+ /* Was das Feld tut, stand nirgends - man tippte und sah keine
262
+ Wirkung. Der Wert wird der Anzeigename des Punkts (common.name)
263
+ und erscheint als Zusatz in der Zeile darueber. */
264
+ iC.title = tr('detail.captionHint');
265
+ var lC = el('label', 'fld');
266
+ lC.appendChild(el('span', null, tr('detail.caption')));
267
+ lC.appendChild(iC);
268
+ lC.appendChild(el('span', 'sugg', tr('detail.captionHint')));
269
+ af.appendChild(lC);
270
+
271
+ zeile(tr('detail.display'), af);
272
+
273
+ /* --- Rohwert und Ergebnis --- */
274
+ var a = wertVon(s);
275
+ zeile(tr('detail.rawValue'), a.roh === undefined ? tr('detail.rawNothing')
276
+ : (typeof a.roh === 'object' ? JSON.stringify(a.roh) : String(a.roh)), 'raw');
277
+
278
+ if (s.srcR && !S.objects[s.srcR]) {
279
+ var wbox = el('div');
280
+ wbox.appendChild(el('span', 'chip bad', tr('detail.sourceGone')));
281
+ zeile(tr('detail.attention'), wbox);
282
+ }
283
+
284
+ var box = el('div');
285
+ var rv = el('span', 'res', a.ok
286
+ ? (typeof a.val === 'object' ? JSON.stringify(a.val) : String(a.val)) + (s.unit ? ' ' + s.unit : '')
287
+ : a.txt);
288
+ if (!a.ok) { rv.style.color = 'var(--bad)'; }
289
+ box.appendChild(rv);
290
+ box.appendChild(document.createTextNode(' '));
291
+ if (a.ok) {
292
+ var t = typeof a.val;
293
+ var passt = !s.typ || s.typ === t || s.typ === 'mixed';
294
+ box.appendChild(el('span', 'chip ' + (passt ? 'ok' : 'warn'),
295
+ (passt ? '✓ ' : '≠ ') + (t === 'number' ? 'Zahl' : (t === 'boolean' ? 'boolean' : t))));
296
+ if (a.gewandelt) {
297
+ box.appendChild(document.createTextNode(' '));
298
+ box.appendChild(el('span', 'chip mut', tr('detail.converted')));
299
+ }
300
+ if (!passt) {
301
+ box.appendChild(document.createTextNode(' '));
302
+ box.appendChild(el('span', 'chip warn', tr('detail.expects', s.typ)));
303
+ }
304
+ var ist = s.urId ? S.werte[s.urId] : null;
305
+ if (ist && ist.val !== undefined && ist.val !== null) {
306
+ var gleich = String(ist.val) === String(a.val);
307
+ box.appendChild(document.createTextNode(' '));
308
+ box.appendChild(el('span', 'chip ' + (gleich ? 'ok' : 'warn'),
309
+ gleich ? tr('detail.matchesState') : tr('detail.stateSays', String(ist.val))));
310
+ }
311
+ } else if (a.fehler) {
312
+ box.appendChild(el('span', 'chip bad', tr('detail.formulaBroken')));
313
+ } else {
314
+ box.appendChild(el('span', 'chip bad', tr('detail.yieldsNothing')));
315
+ }
316
+ zeile(tr('detail.yields'), box);
317
+
318
+ if (s.manuell) {
319
+ var db = el('div');
320
+ var del = el('button', 'btn', tr('detail.removePoint'));
321
+ del.style.fontSize = '11.5px';
322
+ del.style.borderColor = 'var(--bad)';
323
+ del.style.color = 'var(--bad)';
324
+ del.addEventListener('click', function (ev) {
325
+ ev.stopPropagation();
326
+ e.states.splice(idx, 1);
327
+ S.openRow = null;
328
+ zeichneErgebnis();
329
+ });
330
+ db.appendChild(del);
331
+ zeile('', db);
332
+ }
333
+
334
+ return d;
335
+ }
@@ -0,0 +1,40 @@
1
+ import { socket } from './verbindung.js';
2
+
3
+ /* Die Einstellungen der Instanz.
4
+
5
+ Sie stehen in `system.adapter.alias-workbench.0.native` und damit im
6
+ Objektspeicher - nicht im Browser. Eine Einstellung, die bestimmt,
7
+ *wie* geschrieben wird, gehoert nicht in den localStorage eines
8
+ einzelnen Rechners; sie gilt fuer jeden, der die Werkbank oeffnet.
9
+ Der Klappzustand des Baums und der Expertenmodus liegen weiterhin
10
+ dort, wo sie hingehoeren: das sind Ansichtssachen.
11
+
12
+ Die Namen sind dieselben wie in `admin/jsonConfig.json`, damit die
13
+ Einstellungsseite sie ohne Zwischenschicht bedient. Fehlt ein Wert -
14
+ etwa weil die Instanz aelter ist als die Einstellung - gilt die
15
+ Vorgabe hier. */
16
+ var EINST_VORGABE = {
17
+ ikonRaeume: true,
18
+ ikonFunktionen: true,
19
+ ikonErsetzen: true,
20
+ vorlagenVomAdmin: true
21
+ };
22
+ var einstellungen = {};
23
+
24
+ export function einst(name) {
25
+ var v = einstellungen[name];
26
+ return v === undefined ? !!EINST_VORGABE[name] : !!v;
27
+ }
28
+
29
+ /* Die eigene Instanz - `%instance%` steht in der URL, sonst 0. */
30
+ export function instanzId() {
31
+ var m = String(location.search || '').match(/(?:^\?|&)(\d+)(?:&|$)/);
32
+ return 'system.adapter.alias-workbench.' + (m ? m[1] : '0');
33
+ }
34
+
35
+ export function ladeEinstellungen(danach) {
36
+ socket.emit('getObject', instanzId(), function (err, obj) {
37
+ if (!err && obj && obj.native) { einstellungen = obj.native; }
38
+ if (typeof danach === 'function') { danach(); }
39
+ });
40
+ }