iobroker.eos-admin 7.9.49 → 7.9.51

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.
@@ -0,0 +1,2 @@
1
+ const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./bootstrap-DPWrite50.js","./DefaultPropsProvider-BiXBMQYQ.js","./_commonjsHelpers-CE1G-McA.js","./iobroker_admin__mf_v__runtimeInit__mf_v__-g2X2zhAf.js","./emotion-serialize.esm-Q4o_CgeF.js","./iobroker_admin__loadShare___mf_0_emotion_mf_1_react__loadShare__-BywLg1Xe.js","./iobroker_admin__loadShare__react__loadShare__-LM7uyL45.js","./ace-DEUjyTIn.js","./iobroker_admin__loadShare___mf_0_iobroker_mf_1_adapter_mf_2_react_mf_2_v5__loadShare__-LFZoiUTt.js","./iobroker_admin__loadShare___mf_0_iobroker_mf_1_json_mf_2_config__loadShare__-CpA7pnS5.js","./iobroker_admin__loadShare__leaflet__loadShare__-CNUAG3AH.js","./ext-language_tools-ylAoucDh.js","./iobroker_admin__loadShare__react_mf_2_dropzone__loadShare__-CwjnV48g.js","./iobroker_admin__loadShare__react_mf_2_dom__loadShare__-CVeAXnkk.js","./preload-helper-BDBacUwf.js","./iobroker_admin__loadShare___mf_0_mui_mf_1_icons_mf_2_material__loadShare__-CJO5yP3h.js","./sdk-C-e-e2gd.js","./extends-CF3RwP-h.js","./assertThisInitialized-B0aQ9eG_.js","./useTheme-Bl-M1DYp.js","./createTheme-DFX8CY6F.js","./blue-CagzI6CQ.js","./index-DcK0RDrH.js","./ThemeProvider-BOFFjWAF.js","./emotion-cache.browser.esm-BgJfNZJN.js","./setYear-3ufv5yRD.js","./en-US-DBFJNIH6.js","./bootstrap-D57bMhRi.css"])))=>i.map(i=>d[i]);
2
+ import{_ as c}from"./preload-helper-BDBacUwf.js";import"./iobroker_admin__mf_v__runtimeInit__mf_v__-g2X2zhAf.js";(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const e of document.querySelectorAll('link[rel="modulepreload"]'))s(e);new MutationObserver(e=>{for(const r of e)if(r.type==="childList")for(const o of r.addedNodes)o.tagName==="LINK"&&o.rel==="modulepreload"&&s(o)}).observe(document,{childList:!0,subtree:!0});function n(e){const r={};return e.integrity&&(r.integrity=e.integrity),e.referrerPolicy&&(r.referrerPolicy=e.referrerPolicy),e.crossOrigin==="use-credentials"?r.credentials="include":e.crossOrigin==="anonymous"?r.credentials="omit":r.credentials="same-origin",r}function s(e){if(e.ep)return;e.ep=!0;const r=n(e);fetch(e.href,r)}})();c(()=>import("./bootstrap-DPWrite50.js").then(i=>i.o),__vite__mapDeps([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27]),import.meta.url);
@@ -0,0 +1,2 @@
1
+ const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./bootstrap-DPWrite51.js","./DefaultPropsProvider-BiXBMQYQ.js","./_commonjsHelpers-CE1G-McA.js","./iobroker_admin__mf_v__runtimeInit__mf_v__-g2X2zhAf.js","./emotion-serialize.esm-Q4o_CgeF.js","./iobroker_admin__loadShare___mf_0_emotion_mf_1_react__loadShare__-BywLg1Xe.js","./iobroker_admin__loadShare__react__loadShare__-LM7uyL45.js","./ace-DEUjyTIn.js","./iobroker_admin__loadShare___mf_0_iobroker_mf_1_adapter_mf_2_react_mf_2_v5__loadShare__-LFZoiUTt.js","./iobroker_admin__loadShare___mf_0_iobroker_mf_1_json_mf_2_config__loadShare__-CpA7pnS5.js","./iobroker_admin__loadShare__leaflet__loadShare__-CNUAG3AH.js","./ext-language_tools-ylAoucDh.js","./iobroker_admin__loadShare__react_mf_2_dropzone__loadShare__-CwjnV48g.js","./iobroker_admin__loadShare__react_mf_2_dom__loadShare__-CVeAXnkk.js","./preload-helper-BDBacUwf.js","./iobroker_admin__loadShare___mf_0_mui_mf_1_icons_mf_2_material__loadShare__-CJO5yP3h.js","./sdk-C-e-e2gd.js","./extends-CF3RwP-h.js","./assertThisInitialized-B0aQ9eG_.js","./useTheme-Bl-M1DYp.js","./createTheme-DFX8CY6F.js","./blue-CagzI6CQ.js","./index-DcK0RDrH.js","./ThemeProvider-BOFFjWAF.js","./emotion-cache.browser.esm-BgJfNZJN.js","./setYear-3ufv5yRD.js","./en-US-DBFJNIH6.js","./bootstrap-D57bMhRi.css"])))=>i.map(i=>d[i]);
2
+ import{_ as c}from"./preload-helper-BDBacUwf.js";import"./iobroker_admin__mf_v__runtimeInit__mf_v__-g2X2zhAf.js";(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const e of document.querySelectorAll('link[rel="modulepreload"]'))s(e);new MutationObserver(e=>{for(const r of e)if(r.type==="childList")for(const o of r.addedNodes)o.tagName==="LINK"&&o.rel==="modulepreload"&&s(o)}).observe(document,{childList:!0,subtree:!0});function n(e){const r={};return e.integrity&&(r.integrity=e.integrity),e.referrerPolicy&&(r.referrerPolicy=e.referrerPolicy),e.crossOrigin==="use-credentials"?r.credentials="include":e.crossOrigin==="anonymous"?r.credentials="omit":r.credentials="same-origin",r}function s(e){if(e.ep)return;e.ep=!0;const r=n(e);fetch(e.href,r)}})();c(()=>import("./bootstrap-DPWrite51.js").then(i=>i.o),__vite__mapDeps([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27]),import.meta.url);
@@ -4,7 +4,7 @@
4
4
  translate="no"
5
5
  >
6
6
  <head>
7
- <script type="module" src="./assets/hostInit-OBG53iVO.js"></script>
7
+ <script type="module" src="./assets/hostInit-DPWrite51.js?v=51"></script>
8
8
 
9
9
  <meta charset="utf-8" />
10
10
  <link
@@ -31,7 +31,7 @@
31
31
  rel="stylesheet"
32
32
  href="css/leaflet.css"
33
33
  />
34
- <link rel="stylesheet" href="./css/eos-branding.css?v=49" />
34
+ <link rel="stylesheet" href="./css/eos-branding.css?v=51" />
35
35
  <link
36
36
  rel="manifest"
37
37
  href="manifest.json"
@@ -151,14 +151,14 @@
151
151
  false,
152
152
  );
153
153
  </script>
154
- <script type="module" crossorigin src="./assets/index-CQZugZ1z.js"></script>
154
+ <script type="module" crossorigin src="./assets/index-DPWrite51.js?v=51"></script>
155
155
  <link rel="modulepreload" crossorigin href="./assets/preload-helper-BDBacUwf.js">
156
156
  <link rel="modulepreload" crossorigin href="./assets/iobroker_admin__mf_v__runtimeInit__mf_v__-g2X2zhAf.js">
157
- <script defer src="./js/eos-branding.js?v=49"></script>
158
- <script defer src="./js/eos-security-ui.js?v=49"></script>
159
- <script defer src="./js/eos-role-ui.js?v=49"></script>
160
- <script defer src="./js/eos-assistant.js?v=49"></script>
161
- <script defer src="./js/eos-objects-state-tools.js?v=49"></script>
157
+ <script defer src="./js/eos-branding.js?v=51"></script>
158
+ <script defer src="./js/eos-security-ui.js?v=51"></script>
159
+ <script defer src="./js/eos-role-ui.js?v=51"></script>
160
+ <script defer src="./js/eos-assistant.js?v=51"></script>
161
+ <script defer src="./js/eos-objects-state-tools.js?v=51"></script>
162
162
  </head>
163
163
  <body>
164
164
  <noscript>You need to enable JavaScript to run this app.</noscript>
@@ -1,7 +1,7 @@
1
1
  (() => {
2
2
  'use strict';
3
3
 
4
- window.NEXOWATT_EOS_UI_VERSION = 'v49-dp-write-performance-fix';
4
+ window.NEXOWATT_EOS_UI_VERSION = 'v51-unrestricted-dp-write-fix';
5
5
 
6
6
  const BRAND = 'NexoWatt EOS';
7
7
  const EOS_MEANING = 'Energy Operation System';
@@ -1463,6 +1463,8 @@
1463
1463
  run();
1464
1464
  };
1465
1465
 
1466
+ const isObjectsRoute = () => /#\/?tab-objects\b|#tab-objects\b/.test(String(window.location.hash || '')) || /[?&]tab=objects/.test(String(window.location.href || ''));
1467
+
1466
1468
  const installObserver = () => safe(() => {
1467
1469
  const observer = new MutationObserver(mutations => {
1468
1470
  for (const mutation of mutations) {
@@ -1477,7 +1479,10 @@
1477
1479
  if (node.nodeType === Node.TEXT_NODE) {
1478
1480
  if (isAdapterConfigSurface() && node.parentElement?.closest?.('#app-paper')) patchMojibakeTextNode(node);
1479
1481
  else patchTextNode(node);
1480
- } else if (node.nodeType === Node.ELEMENT_NODE) state.pendingScopes.add(node);
1482
+ } else if (node.nodeType === Node.ELEMENT_NODE) {
1483
+ if (isObjectsRoute() && node.closest?.('.eos-object-value-cell,[data-eos-object-value-cell]')) return;
1484
+ state.pendingScopes.add(node);
1485
+ }
1481
1486
  });
1482
1487
  }
1483
1488
  if (state.pendingScopes.size) scheduleScopePatch();
@@ -1485,7 +1490,9 @@
1485
1490
  observer.observe(document.documentElement, {
1486
1491
  subtree: true,
1487
1492
  childList: true,
1488
- characterData: true,
1493
+ // v50: React changes thousands of text nodes on the data-point page.
1494
+ // Added nodes are still patched; existing text-node mutation tracking is too expensive.
1495
+ characterData: false,
1489
1496
  });
1490
1497
  });
1491
1498
 
@@ -1,14 +1,15 @@
1
1
  (() => {
2
2
  'use strict';
3
3
 
4
- window.NEXOWATT_EOS_OBJECTS_STATE_TOOLS_VERSION = 'v49-dp-write-performance-fix';
4
+ window.NEXOWATT_EOS_OBJECTS_STATE_TOOLS_VERSION = 'v51-unrestricted-dp-write-fix';
5
5
 
6
6
  const ACTIVE_CLASS = 'eos-objects-surface';
7
7
  const safe = fn => { try { return fn(); } catch (_) { return undefined; } };
8
+ const busyIds = new Set();
8
9
 
9
10
  const isObjectsRoute = () => {
10
11
  const hash = String(window.location.hash || '');
11
- if (/#tab-objects/.test(hash)) return true;
12
+ if (/#\/?tab-objects\b|#tab-objects\b/.test(hash)) return true;
12
13
  return /[?&]tab=objects/.test(String(window.location.href || ''));
13
14
  };
14
15
 
@@ -54,6 +55,7 @@
54
55
  '.eos-object-value-cell',
55
56
  '[data-eos-object-value-cell]',
56
57
  '[title*="Wert schreiben"]',
58
+ '[title*="Wert klicken"]',
57
59
  '[title*="Button sofort testen"]'
58
60
  ].join(',');
59
61
 
@@ -65,6 +67,27 @@
65
67
  return out;
66
68
  };
67
69
 
70
+ const getSocket = () => window.NEXOWATT_EOS_OBJECTS_SOCKET || window.NEXOWATT_EOS_SOCKET || window.socket || null;
71
+
72
+ const normalizeText = value => String(value ?? '').replace(/\s+/g, ' ').trim();
73
+
74
+ const isWritableCell = cell => {
75
+ if (!cell) return false;
76
+ // v51: the React ObjectBrowser now marks every state value cell as writable.
77
+ // Do not treat common.write=false as an EOS-side lock; the socket/ACL layer is
78
+ // the only authority that may reject a write.
79
+ if (cell.getAttribute('data-eos-object-writable') === '1') return true;
80
+ if (cell.hasAttribute('data-eos-object-value-cell') && cell.getAttribute('data-eos-object-writable') !== '0') return true;
81
+ const title = `${cell.getAttribute('title') || ''} ${cell.textContent || ''}`;
82
+ return /wert\s*(klicken|schreiben)|write\s*value|sofort\s*testen/i.test(title);
83
+ };
84
+
85
+ const isStateValueCandidate = cell => !!cell && (
86
+ cell.hasAttribute?.('data-eos-object-value-cell') ||
87
+ cell.classList?.contains('eos-object-value-cell') ||
88
+ isWritableCell(cell)
89
+ );
90
+
68
91
  const releaseNativeControls = root => safe(() => {
69
92
  if (!setSurfaceState()) return;
70
93
  const base = root && root.nodeType ? root : document;
@@ -84,9 +107,8 @@
84
107
  const base = root && root.nodeType ? root : document;
85
108
  collect(base, valueCellSelector).forEach(cell => {
86
109
  const id = cell.getAttribute('data-eos-object-value-cell') || cell.closest?.('[id]')?.getAttribute('id') || '';
87
- const nativeTitle = cell.getAttribute('title') || '';
88
- const writable = cell.getAttribute('data-eos-object-writable') === '1' || /Wert schreiben|Button sofort testen/i.test(nativeTitle);
89
- const visibleValue = (cell.textContent || '').trim().replace(/\s+/g, ' ') || '(leer)';
110
+ const writable = isWritableCell(cell);
111
+ const visibleValue = normalizeText(cell.textContent || '') || '(leer)';
90
112
  const cache = `${id}|${writable ? 1 : 0}|${visibleValue}`;
91
113
 
92
114
  cell.style.pointerEvents = 'auto';
@@ -94,6 +116,7 @@
94
116
  cell.classList.add('eos-object-value-cell');
95
117
  cell.classList.toggle('eos-object-value-writable', writable);
96
118
  cell.classList.toggle('eos-object-value-readonly', !writable);
119
+ if (writable) cell.setAttribute('tabindex', '0');
97
120
 
98
121
  if (cell.getAttribute('data-eos-value-cache') !== cache) {
99
122
  cell.setAttribute('data-eos-value-cache', cache);
@@ -102,13 +125,304 @@
102
125
  id ? `ID: ${id}` : null,
103
126
  `Aktueller Wert: ${visibleValue}`,
104
127
  `Status: ${writable ? 'beschreibbar' : 'nur lesbar'}`,
105
- writable ? 'Klicken: Wert schreiben / Button sofort testen' : 'Nur lesbarer Wert'
128
+ writable ? 'Klicken: Wert schreiben' : 'Nur lesbarer Wert'
106
129
  ].filter(Boolean).join('\n');
107
130
  if (cell.getAttribute('title') !== nextTitle) cell.setAttribute('title', nextTitle);
108
131
  }
109
132
  });
110
133
  });
111
134
 
135
+ const parseBoolean = value => {
136
+ if (value === true || value === 1) return true;
137
+ if (value === false || value === 0 || value == null) return false;
138
+ const normalized = String(value).trim().toLowerCase();
139
+ return normalized === 'true' || normalized === '1' || normalized === 'on' || normalized === 'yes' || normalized === 'ja';
140
+ };
141
+
142
+ const isNullToken = value => /^\s*(\(null\)|null|undefined)?\s*$/i.test(String(value ?? ''));
143
+
144
+ const coerceValue = (rawValue, type) => {
145
+ const raw = String(rawValue ?? '');
146
+ if (isNullToken(raw) && type !== 'string' && type !== 'json') return null;
147
+ if (type === 'boolean') return parseBoolean(raw);
148
+ if (type === 'number') {
149
+ const normalized = raw.replace(',', '.').trim();
150
+ const number = Number(normalized);
151
+ if (!Number.isFinite(number)) throw new Error(`Ungültige Zahl: ${raw}`);
152
+ return number;
153
+ }
154
+ if (type === 'json' || type === 'object') {
155
+ if (isNullToken(raw)) return null;
156
+ return JSON.parse(raw);
157
+ }
158
+ return raw;
159
+ };
160
+
161
+ const stateToString = value => {
162
+ if (value === null || value === undefined) return 'null';
163
+ if (typeof value === 'object') {
164
+ try { return JSON.stringify(value, null, 2); } catch (_) { return String(value); }
165
+ }
166
+ return String(value);
167
+ };
168
+
169
+ const hasNativeWriteDialog = () => {
170
+ const dialogs = Array.from(document.querySelectorAll('.MuiDialog-root,[role="dialog"]'));
171
+ return dialogs.some(dialog => /write value|wert schreiben|set value|wert setzen/i.test(dialog.textContent || ''));
172
+ };
173
+
174
+
175
+ let cachedEosAdminInstance = null;
176
+
177
+ const formatWriteError = error => {
178
+ if (!error) return 'unbekannter Fehler';
179
+ if (typeof error === 'string') return error;
180
+ if (error.message) return error.message;
181
+ try { return JSON.stringify(error); } catch (_) { return String(error); }
182
+ };
183
+
184
+ const normalizeInstanceId = value => {
185
+ if (!value) return null;
186
+ if (typeof value === 'string') {
187
+ const id = value.replace(/^system\.adapter\./, '').trim();
188
+ return /^eos-admin\.\d+$/.test(id) ? id : null;
189
+ }
190
+ if (typeof value === 'object') {
191
+ return normalizeInstanceId(value._id || value.id || value.instance || value.namespace);
192
+ }
193
+ return null;
194
+ };
195
+
196
+ const resolveEosAdminInstance = async socket => {
197
+ if (cachedEosAdminInstance) return cachedEosAdminInstance;
198
+ const explicit = normalizeInstanceId(window.NEXOWATT_EOS_ADMIN_INSTANCE || window.adminInstance || window.adapterInstance);
199
+ if (explicit) return (cachedEosAdminInstance = explicit);
200
+
201
+ if (socket && typeof socket.getCurrentInstance === 'function') {
202
+ try {
203
+ const current = normalizeInstanceId(await socket.getCurrentInstance());
204
+ if (current) return (cachedEosAdminInstance = current);
205
+ } catch (_) {
206
+ // ignore and try instance list/fallback
207
+ }
208
+ }
209
+
210
+ if (socket && typeof socket.getAdapterInstances === 'function') {
211
+ try {
212
+ const instances = await socket.getAdapterInstances('eos-admin');
213
+ const list = Array.isArray(instances) ? instances : [];
214
+ const alive = list.find(instance => normalizeInstanceId(instance) && instance?.common?.enabled !== false);
215
+ const id = normalizeInstanceId(alive || list[0]);
216
+ if (id) return (cachedEosAdminInstance = id);
217
+ } catch (_) {
218
+ // ignore and use default
219
+ }
220
+ }
221
+
222
+ return (cachedEosAdminInstance = 'eos-admin.0');
223
+ };
224
+
225
+ const callEosAdmin = async (socket, command, message) => {
226
+ const instance = await resolveEosAdminInstance(socket);
227
+ if (!instance) throw new Error('EOS Admin Instanz konnte nicht ermittelt werden');
228
+
229
+ if (socket && typeof socket.sendTo === 'function') {
230
+ const result = socket.sendTo(instance, command, message);
231
+ if (result && typeof result.then === 'function') return result;
232
+ if (result !== undefined) return result;
233
+ }
234
+
235
+ const rawSocket = socket?._socket || socket?.socket || window.socket;
236
+ if (rawSocket && typeof rawSocket.emit === 'function') {
237
+ return await new Promise((resolve, reject) => {
238
+ try {
239
+ rawSocket.emit('sendTo', instance, command, message, response => resolve(response));
240
+ } catch (error) {
241
+ reject(error);
242
+ }
243
+ });
244
+ }
245
+
246
+ throw new Error('socket.sendTo ist nicht verfügbar');
247
+ };
248
+
249
+ const writeStateUnrestricted = async (socket, id, state) => {
250
+ let directError = null;
251
+ if (!socket) throw new Error('Socket ist nicht bereit');
252
+
253
+ if (typeof socket.setState === 'function') {
254
+ try {
255
+ await socket.setState(id, state);
256
+ return { ok: true, via: 'socket' };
257
+ } catch (error) {
258
+ directError = error;
259
+ }
260
+ } else {
261
+ directError = new Error('socket.setState ist nicht verfügbar');
262
+ }
263
+
264
+ try {
265
+ const response = await callEosAdmin(socket, 'eos:writeState', { id, state });
266
+ if (response === 'permissionError') throw new Error('sendTo permissionError');
267
+ if (response && response.ok === false) throw new Error(response.error || 'EOS Backend hat das Schreiben abgelehnt');
268
+ if (response && response.error) throw new Error(response.error);
269
+ return { ok: true, via: 'eos-admin' };
270
+ } catch (backendError) {
271
+ throw new Error(`Direkt: ${formatWriteError(directError)}\nEOS-Backend: ${formatWriteError(backendError)}`);
272
+ }
273
+ };
274
+
275
+ window.NEXOWATT_EOS_WRITE_STATE_UNRESTRICTED = writeStateUnrestricted;
276
+
277
+ const injectDialogStyle = () => {
278
+ if (document.getElementById('eos-dp-write-style')) return;
279
+ const style = document.createElement('style');
280
+ style.id = 'eos-dp-write-style';
281
+ style.textContent = `
282
+ .eos-dp-write-backdrop{position:fixed;inset:0;z-index:9000;background:rgba(0,0,0,.55);display:flex;align-items:center;justify-content:center;padding:18px;font-family:Roboto,Arial,sans-serif}.eos-dp-write-dialog{width:min(620px,calc(100vw - 36px));max-height:calc(100vh - 36px);overflow:auto;background:#061827;color:#fff;border:1px solid rgba(0,255,180,.45);border-radius:16px;box-shadow:0 18px 60px rgba(0,0,0,.65),0 0 34px rgba(0,255,180,.22);padding:18px}.eos-dp-write-dialog h3{margin:0 0 8px;font-size:19px;color:#18f4c0}.eos-dp-write-id{font-family:monospace;font-size:12px;opacity:.82;word-break:break-all;margin-bottom:12px}.eos-dp-write-meta{font-size:12px;opacity:.76;margin-bottom:12px}.eos-dp-write-dialog label{display:block;font-size:13px;margin:12px 0 6px}.eos-dp-write-dialog input[type=text],.eos-dp-write-dialog textarea,.eos-dp-write-dialog select{box-sizing:border-box;width:100%;background:#07111d;color:#fff;border:1px solid rgba(255,255,255,.28);border-radius:8px;padding:10px;font-size:15px;outline:none}.eos-dp-write-dialog textarea{min-height:130px;font-family:monospace}.eos-dp-write-actions{display:flex;gap:10px;justify-content:flex-end;margin-top:18px}.eos-dp-write-actions button{border:0;border-radius:10px;padding:9px 16px;font-weight:700;cursor:pointer}.eos-dp-write-primary{background:#00d6a1;color:#03130f}.eos-dp-write-secondary{background:#273746;color:#fff}.eos-dp-write-actions button:disabled{opacity:.55;cursor:wait}.eos-dp-write-error{color:#ff7676;margin-top:10px;font-size:13px;white-space:pre-wrap}.eos-object-value-cell.eos-dp-write-pulse{outline:2px solid #00d6a1;outline-offset:-2px}`;
283
+ document.head.appendChild(style);
284
+ };
285
+
286
+ const closeExistingDialog = () => document.querySelector('.eos-dp-write-backdrop')?.remove();
287
+
288
+ const openWriteDialog = async (cell, event) => {
289
+ const id = cell?.getAttribute('data-eos-object-value-cell') || cell?.closest?.('[id]')?.id || '';
290
+ if (!id || busyIds.has(id)) return;
291
+ const socket = getSocket();
292
+ if (!socket || typeof socket.setState !== 'function') {
293
+ window.alert('EOS: Socket für Datenpunkt-Schreiben ist noch nicht bereit. Bitte Seite einmal hart neu laden (Strg+F5).');
294
+ return;
295
+ }
296
+
297
+ busyIds.add(id);
298
+ cell?.classList.add('eos-dp-write-pulse');
299
+ window.setTimeout(() => cell?.classList.remove('eos-dp-write-pulse'), 900);
300
+
301
+ try {
302
+ const [objResult, stateResult] = await Promise.allSettled([
303
+ typeof socket.getObject === 'function' ? socket.getObject(id) : Promise.resolve(null),
304
+ typeof socket.getState === 'function' ? socket.getState(id) : Promise.resolve(null),
305
+ ]);
306
+ const obj = objResult.status === 'fulfilled' ? objResult.value : null;
307
+ const state = stateResult.status === 'fulfilled' ? stateResult.value : null;
308
+ if (!obj || obj.type !== 'state') {
309
+ busyIds.delete(id);
310
+ return;
311
+ }
312
+ const common = obj?.common || {};
313
+ if (common.type === 'file') {
314
+ busyIds.delete(id);
315
+ window.alert('EOS: Datei-Datenpunkte werden über die Datei-/Objektansicht geöffnet und nicht als State-Wert geschrieben.');
316
+ return;
317
+ }
318
+ const role = String(common.role || '');
319
+ let type = common.type || typeof state?.val;
320
+ if (!type || type === 'undefined') type = 'string';
321
+ if (type === 'object') type = 'json';
322
+ const current = state?.val;
323
+ const states = common.states && typeof common.states === 'object' ? common.states : null;
324
+
325
+ if (/^button/.test(role)) {
326
+ await writeStateUnrestricted(socket, id, { val: true, ack: false, q: 0 });
327
+ busyIds.delete(id);
328
+ return;
329
+ }
330
+
331
+ injectDialogStyle();
332
+ closeExistingDialog();
333
+
334
+ const backdrop = document.createElement('div');
335
+ backdrop.className = 'eos-dp-write-backdrop';
336
+ const dialog = document.createElement('div');
337
+ dialog.className = 'eos-dp-write-dialog';
338
+ dialog.setAttribute('role', 'dialog');
339
+ dialog.setAttribute('aria-modal', 'true');
340
+ dialog.innerHTML = `
341
+ <h3>Datenpunkt schreiben</h3>
342
+ <div class="eos-dp-write-id"></div>
343
+ <div class="eos-dp-write-meta"></div>
344
+ <label class="eos-dp-write-label">Neuer Wert</label>
345
+ <div class="eos-dp-write-input-wrap"></div>
346
+ <label style="display:flex;align-items:center;gap:8px;margin-top:12px"><input class="eos-dp-write-ack" type="checkbox"> ack=true schreiben</label>
347
+ <div class="eos-dp-write-error" hidden></div>
348
+ <div class="eos-dp-write-actions"><button class="eos-dp-write-secondary" type="button">Abbrechen</button><button class="eos-dp-write-primary" type="button">Schreiben</button></div>`;
349
+ backdrop.appendChild(dialog);
350
+ document.body.appendChild(backdrop);
351
+
352
+ dialog.querySelector('.eos-dp-write-id').textContent = id;
353
+ dialog.querySelector('.eos-dp-write-meta').textContent = `Typ: ${type}${role ? ` · Rolle: ${role}` : ''}${common.unit ? ` · Einheit: ${common.unit}` : ''}`;
354
+
355
+ const inputWrap = dialog.querySelector('.eos-dp-write-input-wrap');
356
+ let input;
357
+ if (states) {
358
+ input = document.createElement('select');
359
+ Object.keys(states).forEach(key => {
360
+ const option = document.createElement('option');
361
+ option.value = key;
362
+ option.textContent = `${states[key]} (${key})`;
363
+ input.appendChild(option);
364
+ });
365
+ input.value = current == null ? Object.keys(states)[0] || '' : String(current);
366
+ } else if (type === 'boolean') {
367
+ input = document.createElement('select');
368
+ input.innerHTML = '<option value="true">true</option><option value="false">false</option>';
369
+ input.value = parseBoolean(current) ? 'true' : 'false';
370
+ } else if (type === 'json') {
371
+ input = document.createElement('textarea');
372
+ input.value = stateToString(current);
373
+ } else {
374
+ input = document.createElement('input');
375
+ input.type = 'text';
376
+ input.value = stateToString(current);
377
+ }
378
+ input.className = 'eos-dp-write-input';
379
+ inputWrap.appendChild(input);
380
+
381
+ const errorEl = dialog.querySelector('.eos-dp-write-error');
382
+ const cancelBtn = dialog.querySelector('.eos-dp-write-secondary');
383
+ const writeBtn = dialog.querySelector('.eos-dp-write-primary');
384
+ const ackInput = dialog.querySelector('.eos-dp-write-ack');
385
+
386
+ const close = () => {
387
+ backdrop.remove();
388
+ busyIds.delete(id);
389
+ };
390
+
391
+ const write = async () => {
392
+ errorEl.hidden = true;
393
+ writeBtn.disabled = true;
394
+ cancelBtn.disabled = true;
395
+ try {
396
+ let value = input.value;
397
+ const effectiveType = states ? (common.type || type) : type;
398
+ value = coerceValue(value, effectiveType);
399
+ await writeStateUnrestricted(socket, id, { val: value, ack: !!ackInput.checked, q: 0 });
400
+ close();
401
+ } catch (error) {
402
+ errorEl.textContent = `Schreiben fehlgeschlagen: ${error?.message || error}`;
403
+ errorEl.hidden = false;
404
+ writeBtn.disabled = false;
405
+ cancelBtn.disabled = false;
406
+ }
407
+ };
408
+
409
+ cancelBtn.addEventListener('click', close);
410
+ writeBtn.addEventListener('click', write);
411
+ backdrop.addEventListener('click', e => { if (e.target === backdrop) close(); });
412
+ input.addEventListener('keydown', e => {
413
+ if ((e.key === 'Enter' && type !== 'json') || (e.key === 'Enter' && e.ctrlKey)) {
414
+ e.preventDefault();
415
+ write();
416
+ }
417
+ if (e.key === 'Escape') close();
418
+ });
419
+ window.setTimeout(() => input.focus(), 30);
420
+ } catch (error) {
421
+ busyIds.delete(id);
422
+ window.alert(`EOS: Datenpunkt konnte nicht geschrieben werden: ${error?.message || error}`);
423
+ }
424
+ };
425
+
112
426
  const run = root => {
113
427
  if (!setSurfaceState()) return;
114
428
  releaseNativeControls(root);
@@ -126,7 +440,7 @@
126
440
  scheduled = false;
127
441
  const roots = pendingRoots.size ? Array.from(pendingRoots) : [document];
128
442
  pendingRoots.clear();
129
- roots.slice(0, 60).forEach(run);
443
+ roots.slice(0, 80).forEach(run);
130
444
  };
131
445
  if ('requestIdleCallback' in window) window.requestIdleCallback(execute, { timeout: 500 });
132
446
  else window.requestAnimationFrame(execute);
@@ -135,26 +449,57 @@
135
449
  const handlePointerAssist = event => {
136
450
  if (!setSurfaceState()) return;
137
451
  const cell = event.target?.closest?.('.eos-object-value-cell');
138
- if (cell?.getAttribute('data-eos-object-writable') === '1') cell.classList.add('eos-object-value-hover');
452
+ if (isWritableCell(cell)) cell.classList.add('eos-object-value-hover');
139
453
  };
140
454
 
141
455
  const handlePointerLeave = event => {
142
456
  event.target?.closest?.('.eos-object-value-cell')?.classList.remove('eos-object-value-hover');
143
457
  };
144
458
 
459
+ const shouldSkipFallback = cell => !!cell?.querySelector?.('.MuiSwitch-root,.admin-button,button,.copyButton');
460
+
461
+ const scheduleFallbackWriter = event => {
462
+ if (!setSurfaceState()) return;
463
+ const cell = event.target?.closest?.('.eos-object-value-cell,[data-eos-object-value-cell]');
464
+ if (!cell || !isStateValueCandidate(cell) || shouldSkipFallback(cell)) return;
465
+ const id = cell.getAttribute('data-eos-object-value-cell') || '';
466
+ if (!id) return;
467
+ window.setTimeout(() => {
468
+ if (!setSurfaceState()) return;
469
+ if (!cell.isConnected || hasNativeWriteDialog()) return;
470
+ openWriteDialog(cell, event);
471
+ }, 180);
472
+ };
473
+
145
474
  window.addEventListener('hashchange', () => window.setTimeout(() => schedule(document), 30));
146
475
  window.addEventListener('popstate', () => window.setTimeout(() => schedule(document), 30));
147
476
  document.addEventListener('mouseover', handlePointerAssist, true);
148
477
  document.addEventListener('mouseout', handlePointerLeave, true);
149
478
  document.addEventListener('click', event => {
150
479
  if (!setSurfaceState()) return;
151
- const nativeInteractive = event.target?.closest?.(`${interactiveSelector},${nativeLayerSelector},.eos-object-value-cell`);
480
+ const cell = event.target?.closest?.('.eos-object-value-cell,[data-eos-object-value-cell]');
481
+ if (cell) {
482
+ releaseNativeControls(cell);
483
+ scheduleFallbackWriter(event);
484
+ }
485
+ }, true);
486
+ document.addEventListener('click', event => {
487
+ if (!setSurfaceState()) return;
488
+ const nativeInteractive = event.target?.closest?.(`${interactiveSelector},${nativeLayerSelector},.eos-object-value-cell,[data-eos-object-value-cell]`);
152
489
  if (nativeInteractive) {
153
- // Keep native React/ObjectBrowser handlers untouched; only repair stale CSS/attributes after the click.
154
490
  releaseNativeControls(nativeInteractive);
491
+ scheduleFallbackWriter(event);
155
492
  window.setTimeout(() => schedule(nativeInteractive), 80);
156
493
  }
157
494
  }, false);
495
+ document.addEventListener('keydown', event => {
496
+ if (!setSurfaceState()) return;
497
+ if (event.key !== 'Enter') return;
498
+ const cell = event.target?.closest?.('.eos-object-value-cell,[data-eos-object-value-cell]');
499
+ if (!cell || !isStateValueCandidate(cell) || shouldSkipFallback(cell)) return;
500
+ event.preventDefault();
501
+ openWriteDialog(cell, event);
502
+ }, true);
158
503
 
159
504
  const observer = new MutationObserver(records => {
160
505
  if (!setSurfaceState()) return;
@@ -169,8 +514,7 @@
169
514
  setSurfaceState();
170
515
  schedule(document);
171
516
  observer.observe(document.documentElement, { childList: true, subtree: true });
172
- // Low-frequency safety pass only; v45 scanned the complete page every 2.5s and hurt large object lists.
173
- window.setInterval(() => schedule(document), 15000);
517
+ window.setInterval(() => schedule(document), 60000);
174
518
  };
175
519
 
176
520
  if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', start, { once: true });
@@ -1,7 +1,7 @@
1
1
  (() => {
2
2
  'use strict';
3
3
 
4
- window.NEXOWATT_EOS_ROLE_UI_VERSION = 'v49-dp-write-performance-fix';
4
+ window.NEXOWATT_EOS_ROLE_UI_VERSION = 'v51-unrestricted-dp-write-fix';
5
5
 
6
6
  const ASSET_BASE = (() => {
7
7
  const script = document.currentScript?.src || document.querySelector('script[src*="eos-role-ui.js"]')?.src || window.location.href;
@@ -38,6 +38,8 @@
38
38
 
39
39
  const stripTab = value => String(value || '').replace(/^#?\/?/, '').replace(/^tab-/, '');
40
40
 
41
+ const isObjectsRoute = () => /#\/?tab-objects\b|#tab-objects\b/.test(String(window.location.hash || '')) || /[?&]tab=objects/.test(String(window.location.href || ''));
42
+
41
43
  const hashTab = () => {
42
44
  const hash = decodeURIComponent(window.location.hash || '');
43
45
  const match = hash.match(/#\/?(tab-[a-z0-9_-]+(?:-\d+)?)\b/i);
@@ -279,7 +281,12 @@
279
281
 
280
282
  const startObserver = () => safe(() => {
281
283
  if (state.observer || !document.documentElement) return;
282
- state.observer = new MutationObserver(scheduleApply);
284
+ state.observer = new MutationObserver(records => {
285
+ // Large object lists change row classes constantly. Role filtering only needs
286
+ // to react to structural/nav changes there, not every selection highlight.
287
+ if (isObjectsRoute() && records.every(record => record.type === 'attributes' && record.attributeName === 'class')) return;
288
+ scheduleApply();
289
+ });
283
290
  state.observer.observe(document.documentElement, { childList: true, subtree: true, attributes: true, attributeFilter: ['href', 'class', 'aria-label'] });
284
291
  });
285
292
 
@@ -1,7 +1,7 @@
1
1
  (() => {
2
2
  'use strict';
3
3
 
4
- const VERSION = 'v49-dp-write-performance-fix';
4
+ const VERSION = 'v51-unrestricted-dp-write-fix';
5
5
  const LEGACY_ADMIN = 'admin';
6
6
  const LEGACY_ADMIN_INSTANCE = 'admin.0';
7
7
  const CORE_PROTECTED_ADAPTERS = ['admin', 'eos-admin', 'backitup', 'nexowatt-devices', 'nexowatt-device', 'nexowatt-dev', 'nexowatt-ui'];
@@ -1 +1 @@
1
- {"id":"iobroker_admin","name":"iobroker_admin","metaData":{"name":"iobroker_admin","type":"app","buildInfo":{"buildVersion":"1.0.0","buildName":"iobroker_admin"},"remoteEntry":{"name":"remoteEntry.js","path":"","type":"module"},"ssrRemoteEntry":{"name":"remoteEntry.js","path":"","type":"module"},"types":{"path":"","name":""},"globalName":"iobroker_admin","pluginVersion":"0.2.5","publicPath":"./"},"shared":[{"id":"iobroker_admin:@emotion/react","name":"@emotion/react","version":"11.14.0","requiredVersion":"*","assets":{"js":{"async":[],"sync":["assets/emotion-react.browser.esm-DrKG52pD.js"]},"css":{"async":[],"sync":[]}}},{"id":"iobroker_admin:@emotion/styled","name":"@emotion/styled","version":"11.14.1","requiredVersion":"*","assets":{"js":{"async":[],"sync":["assets/emotion-styled.browser.esm-DOdpWCne.js"]},"css":{"async":[],"sync":[]}}},{"id":"iobroker_admin:@iobroker/adapter-react-v5","name":"@iobroker/adapter-react-v5","version":"8.2.9","requiredVersion":"*","assets":{"js":{"async":[],"sync":["assets/index-D2ymscJA.js"]},"css":{"async":[],"sync":[]}}},{"id":"iobroker_admin:@iobroker/json-config","name":"@iobroker/json-config","version":"8.4.7","requiredVersion":"*","assets":{"js":{"async":["assets/index-D9Ej0pc9.js"],"sync":["assets/index-mSK_krgD.js"]},"css":{"async":[],"sync":[]}}},{"id":"iobroker_admin:@iobroker/dm-gui-components","name":"@iobroker/dm-gui-components","version":"9.1.0","requiredVersion":"*","assets":{"js":{"async":[],"sync":["assets/index-bCuipom1.js"]},"css":{"async":[],"sync":[]}}},{"id":"iobroker_admin:date-fns","name":"date-fns","version":"4.1.0","requiredVersion":"*","assets":{"js":{"async":[],"sync":["assets/index-Cqq5gIUF.js"]},"css":{"async":[],"sync":[]}}},{"id":"iobroker_admin:date-fns/locale","name":"date-fns/locale","version":"4.1.0","requiredVersion":"*","assets":{"js":{"async":[],"sync":["assets/locale-CLe3aX4T.js"]},"css":{"async":[],"sync":[]}}},{"id":"iobroker_admin:leaflet","name":"leaflet","version":"1.9.4","requiredVersion":"*","assets":{"js":{"async":[],"sync":["assets/leaflet-src-B651YTWC.js"]},"css":{"async":[],"sync":[]}}},{"id":"iobroker_admin:leaflet-geosearch","name":"leaflet-geosearch","version":"4.2.2","requiredVersion":"*","assets":{"js":{"async":[],"sync":["assets/geosearch.module-BA4ww39P.js"]},"css":{"async":[],"sync":[]}}},{"id":"iobroker_admin:react-ace","name":"react-ace","version":"13.0.0","requiredVersion":"*","assets":{"js":{"async":[],"sync":["assets/index-C_anklzA.js"]},"css":{"async":[],"sync":[]}}},{"id":"iobroker_admin:react-dropzone","name":"react-dropzone","version":"15.0.0","requiredVersion":"*","assets":{"js":{"async":[],"sync":["assets/index-CXZCHHB6.js"]},"css":{"async":[],"sync":[]}}},{"id":"iobroker_admin:semver","name":"semver","version":"7.7.4","requiredVersion":"*","assets":{"js":{"async":[],"sync":["assets/index-CkI9667U.js"]},"css":{"async":[],"sync":[]}}},{"id":"iobroker_admin:@mui/x-date-pickers","name":"@mui/x-date-pickers","version":"7.29.4","requiredVersion":"*","assets":{"js":{"async":[],"sync":["assets/index-uASdmaE6.js"]},"css":{"async":[],"sync":[]}}},{"id":"iobroker_admin:react","name":"react","version":"18.3.1","requiredVersion":"*","assets":{"js":{"async":[],"sync":["assets/index-4wMOc304.js"]},"css":{"async":[],"sync":[]}}},{"id":"iobroker_admin:react-dom","name":"react-dom","version":"18.3.1","requiredVersion":"*","assets":{"js":{"async":[],"sync":["assets/index-D2Em8S_O.js"]},"css":{"async":[],"sync":[]}}},{"id":"iobroker_admin:@mui/material","name":"@mui/material","version":"6.5.0","requiredVersion":"*","assets":{"js":{"async":[],"sync":["assets/index-8JYjTPhv.js"]},"css":{"async":[],"sync":[]}}},{"id":"iobroker_admin:@mui/icons-material","name":"@mui/icons-material","version":"6.5.0","requiredVersion":"*","assets":{"js":{"async":[],"sync":["assets/index-jy_3EbfW.js"]},"css":{"async":[],"sync":[]}}}],"remotes":[],"exposes":[]}
1
+ {"id":"iobroker_admin","name":"iobroker_admin","metaData":{"name":"iobroker_admin","type":"app","buildInfo":{"buildVersion":"1.0.0","buildName":"iobroker_admin"},"remoteEntry":{"name":"remoteEntry-DPWrite51.js","path":"","type":"module"},"ssrRemoteEntry":{"name":"remoteEntry-DPWrite51.js","path":"","type":"module"},"types":{"path":"","name":""},"globalName":"iobroker_admin","pluginVersion":"0.2.5","publicPath":"./"},"shared":[{"id":"iobroker_admin:@emotion/react","name":"@emotion/react","version":"11.14.0","requiredVersion":"*","assets":{"js":{"async":[],"sync":["assets/emotion-react.browser.esm-DrKG52pD.js"]},"css":{"async":[],"sync":[]}}},{"id":"iobroker_admin:@emotion/styled","name":"@emotion/styled","version":"11.14.1","requiredVersion":"*","assets":{"js":{"async":[],"sync":["assets/emotion-styled.browser.esm-DOdpWCne.js"]},"css":{"async":[],"sync":[]}}},{"id":"iobroker_admin:@iobroker/adapter-react-v5","name":"@iobroker/adapter-react-v5","version":"8.2.9","requiredVersion":"*","assets":{"js":{"async":[],"sync":["assets/index-DPAdapter51.js"]},"css":{"async":[],"sync":[]}}},{"id":"iobroker_admin:@iobroker/json-config","name":"@iobroker/json-config","version":"8.4.7","requiredVersion":"*","assets":{"js":{"async":["assets/index-D9Ej0pc9.js"],"sync":["assets/index-mSK_krgD.js"]},"css":{"async":[],"sync":[]}}},{"id":"iobroker_admin:@iobroker/dm-gui-components","name":"@iobroker/dm-gui-components","version":"9.1.0","requiredVersion":"*","assets":{"js":{"async":[],"sync":["assets/index-bCuipom1.js"]},"css":{"async":[],"sync":[]}}},{"id":"iobroker_admin:date-fns","name":"date-fns","version":"4.1.0","requiredVersion":"*","assets":{"js":{"async":[],"sync":["assets/index-Cqq5gIUF.js"]},"css":{"async":[],"sync":[]}}},{"id":"iobroker_admin:date-fns/locale","name":"date-fns/locale","version":"4.1.0","requiredVersion":"*","assets":{"js":{"async":[],"sync":["assets/locale-CLe3aX4T.js"]},"css":{"async":[],"sync":[]}}},{"id":"iobroker_admin:leaflet","name":"leaflet","version":"1.9.4","requiredVersion":"*","assets":{"js":{"async":[],"sync":["assets/leaflet-src-B651YTWC.js"]},"css":{"async":[],"sync":[]}}},{"id":"iobroker_admin:leaflet-geosearch","name":"leaflet-geosearch","version":"4.2.2","requiredVersion":"*","assets":{"js":{"async":[],"sync":["assets/geosearch.module-BA4ww39P.js"]},"css":{"async":[],"sync":[]}}},{"id":"iobroker_admin:react-ace","name":"react-ace","version":"13.0.0","requiredVersion":"*","assets":{"js":{"async":[],"sync":["assets/index-C_anklzA.js"]},"css":{"async":[],"sync":[]}}},{"id":"iobroker_admin:react-dropzone","name":"react-dropzone","version":"15.0.0","requiredVersion":"*","assets":{"js":{"async":[],"sync":["assets/index-CXZCHHB6.js"]},"css":{"async":[],"sync":[]}}},{"id":"iobroker_admin:semver","name":"semver","version":"7.7.4","requiredVersion":"*","assets":{"js":{"async":[],"sync":["assets/index-CkI9667U.js"]},"css":{"async":[],"sync":[]}}},{"id":"iobroker_admin:@mui/x-date-pickers","name":"@mui/x-date-pickers","version":"7.29.4","requiredVersion":"*","assets":{"js":{"async":[],"sync":["assets/index-uASdmaE6.js"]},"css":{"async":[],"sync":[]}}},{"id":"iobroker_admin:react","name":"react","version":"18.3.1","requiredVersion":"*","assets":{"js":{"async":[],"sync":["assets/index-4wMOc304.js"]},"css":{"async":[],"sync":[]}}},{"id":"iobroker_admin:react-dom","name":"react-dom","version":"18.3.1","requiredVersion":"*","assets":{"js":{"async":[],"sync":["assets/index-D2Em8S_O.js"]},"css":{"async":[],"sync":[]}}},{"id":"iobroker_admin:@mui/material","name":"@mui/material","version":"6.5.0","requiredVersion":"*","assets":{"js":{"async":[],"sync":["assets/index-8JYjTPhv.js"]},"css":{"async":[],"sync":[]}}},{"id":"iobroker_admin:@mui/icons-material","name":"@mui/icons-material","version":"6.5.0","requiredVersion":"*","assets":{"js":{"async":[],"sync":["assets/index-jy_3EbfW.js"]},"css":{"async":[],"sync":[]}}}],"remotes":[],"exposes":[]}