iobroker.eos-admin 7.9.48 → 7.9.49

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.
@@ -1,17 +1,15 @@
1
1
  (() => {
2
2
  'use strict';
3
3
 
4
- window.NEXOWATT_EOS_OBJECTS_STATE_TOOLS_VERSION = 'v45-delete-services-hardfix';
4
+ window.NEXOWATT_EOS_OBJECTS_STATE_TOOLS_VERSION = 'v49-dp-write-performance-fix';
5
5
 
6
6
  const ACTIVE_CLASS = 'eos-objects-surface';
7
- const safe = fn => { try { return fn(); } catch (e) { return undefined; } };
7
+ const safe = fn => { try { return fn(); } catch (_) { return undefined; } };
8
8
 
9
9
  const isObjectsRoute = () => {
10
10
  const hash = String(window.location.hash || '');
11
11
  if (/#tab-objects/.test(hash)) return true;
12
- const url = String(window.location.href || '');
13
- if (/[?&]tab=objects/.test(url)) return true;
14
- return false;
12
+ return /[?&]tab=objects/.test(String(window.location.href || ''));
15
13
  };
16
14
 
17
15
  const setSurfaceState = () => {
@@ -52,119 +50,127 @@
52
50
  '[role="tooltip"]'
53
51
  ].join(',');
54
52
 
53
+ const valueCellSelector = [
54
+ '.eos-object-value-cell',
55
+ '[data-eos-object-value-cell]',
56
+ '[title*="Wert schreiben"]',
57
+ '[title*="Button sofort testen"]'
58
+ ].join(',');
59
+
60
+ const collect = (base, selector) => {
61
+ const out = [];
62
+ if (!base) return out;
63
+ if (base.nodeType === Node.ELEMENT_NODE && base.matches?.(selector)) out.push(base);
64
+ if (base.querySelectorAll) out.push(...Array.from(base.querySelectorAll(selector)));
65
+ return out;
66
+ };
67
+
55
68
  const releaseNativeControls = root => safe(() => {
56
69
  if (!setSurfaceState()) return;
57
70
  const base = root && root.nodeType ? root : document;
58
- const nodes = [];
59
- if (base.nodeType === Node.ELEMENT_NODE && base.matches?.(interactiveSelector)) nodes.push(base);
60
- nodes.push(...Array.from(base.querySelectorAll?.(interactiveSelector) || []));
61
- nodes.forEach(el => {
71
+ collect(base, `${interactiveSelector},${nativeLayerSelector}`).forEach(el => {
62
72
  if (!el || el.closest?.('#eos-assist-root,.eos-assist-root')) return;
63
73
  el.classList?.remove('eos-security-hidden-delete', 'eos-protected-delete-control', 'eos-disabled-by-security');
64
74
  el.removeAttribute?.('data-eos-security-blocked');
65
75
  el.removeAttribute?.('aria-disabled');
66
- el.style.pointerEvents = 'auto';
67
- if (el.style.visibility === 'hidden') el.style.visibility = 'visible';
68
- if (el.style.display === 'none' && !el.classList?.contains('eos-native-logout-hidden')) el.style.display = '';
69
- });
70
- const layers = [];
71
- if (base.nodeType === Node.ELEMENT_NODE && base.matches?.(nativeLayerSelector)) layers.push(base);
72
- layers.push(...Array.from(base.querySelectorAll?.(nativeLayerSelector) || []));
73
- layers.forEach(el => {
74
- if (!el) return;
75
- el.style.pointerEvents = 'auto';
76
+ if (el.style?.pointerEvents === 'none') el.style.pointerEvents = 'auto';
77
+ if (el.style?.visibility === 'hidden') el.style.visibility = 'visible';
78
+ if (el.style?.display === 'none' && !el.classList?.contains('eos-native-logout-hidden')) el.style.display = '';
76
79
  });
77
80
  });
78
81
 
79
- const formatAge = ts => {
80
- const n = Number(ts);
81
- if (!Number.isFinite(n) || n <= 0) return 'unbekannt';
82
- try { return new Date(n).toLocaleString(); } catch { return String(n); }
83
- };
84
-
85
82
  const annotateValueCells = root => safe(() => {
86
83
  if (!setSurfaceState()) return;
87
84
  const base = root && root.nodeType ? root : document;
88
- const cells = [];
89
- const valueCellSelector = '.eos-object-value-cell,[data-eos-object-value-cell],[title*="Wert schreiben"],[title*="Button sofort testen"]';
90
- if (base.nodeType === Node.ELEMENT_NODE && base.matches?.(valueCellSelector)) cells.push(base);
91
- cells.push(...Array.from(base.querySelectorAll?.(valueCellSelector) || []));
92
- cells.forEach(cell => {
85
+ collect(base, valueCellSelector).forEach(cell => {
93
86
  const id = cell.getAttribute('data-eos-object-value-cell') || cell.closest?.('[id]')?.getAttribute('id') || '';
94
- const title = cell.getAttribute('title') || '';
95
- const writable = cell.getAttribute('data-eos-object-writable') === '1' || /Wert schreiben|Button sofort testen/i.test(title);
87
+ const nativeTitle = cell.getAttribute('title') || '';
88
+ const writable = cell.getAttribute('data-eos-object-writable') === '1' || /Wert schreiben|Button sofort testen/i.test(nativeTitle);
96
89
  const visibleValue = (cell.textContent || '').trim().replace(/\s+/g, ' ') || '(leer)';
90
+ const cache = `${id}|${writable ? 1 : 0}|${visibleValue}`;
91
+
97
92
  cell.style.pointerEvents = 'auto';
98
93
  cell.style.cursor = writable ? 'pointer' : 'default';
94
+ cell.classList.add('eos-object-value-cell');
99
95
  cell.classList.toggle('eos-object-value-writable', writable);
100
96
  cell.classList.toggle('eos-object-value-readonly', !writable);
101
- cell.classList.add('eos-object-value-cell');
102
- cell.setAttribute('data-eos-current-visible-value', visibleValue);
103
- const hint = writable
104
- ? 'Klicken: Wert schreiben / Button sofort testen'
105
- : 'Nur lesbarer Wert';
106
- cell.setAttribute('title', [
107
- id ? `ID: ${id}` : null,
108
- `Aktueller Wert: ${visibleValue}`,
109
- `Status: ${writable ? 'beschreibbar' : 'nur lesbar'}`,
110
- `Letzte Anzeige-Aktualisierung: ${formatAge(Date.now())}`,
111
- hint,
112
- 'Zusätzliche Statusdetails zeigt der native EOS/ioBroker-Hover an.'
113
- ].filter(Boolean).join('\n'));
97
+
98
+ if (cell.getAttribute('data-eos-value-cache') !== cache) {
99
+ cell.setAttribute('data-eos-value-cache', cache);
100
+ cell.setAttribute('data-eos-current-visible-value', visibleValue);
101
+ const nextTitle = [
102
+ id ? `ID: ${id}` : null,
103
+ `Aktueller Wert: ${visibleValue}`,
104
+ `Status: ${writable ? 'beschreibbar' : 'nur lesbar'}`,
105
+ writable ? 'Klicken: Wert schreiben / Button sofort testen' : 'Nur lesbarer Wert'
106
+ ].filter(Boolean).join('\n');
107
+ if (cell.getAttribute('title') !== nextTitle) cell.setAttribute('title', nextTitle);
108
+ }
114
109
  });
115
110
  });
116
111
 
117
- const handlePointerAssist = event => {
112
+ const run = root => {
118
113
  if (!setSurfaceState()) return;
119
- const cell = event.target?.closest?.('.eos-object-value-cell');
120
- if (!cell) return;
121
- if (cell.getAttribute('data-eos-object-writable') === '1') {
122
- cell.classList.add('eos-object-value-hover');
123
- }
114
+ releaseNativeControls(root);
115
+ annotateValueCells(root);
124
116
  };
125
- const handlePointerLeave = event => {
126
- const cell = event.target?.closest?.('.eos-object-value-cell');
127
- cell?.classList.remove('eos-object-value-hover');
117
+
118
+ let scheduled = false;
119
+ const pendingRoots = new Set();
120
+ const schedule = root => {
121
+ if (!setSurfaceState()) return;
122
+ if (root) pendingRoots.add(root);
123
+ if (scheduled) return;
124
+ scheduled = true;
125
+ const execute = () => {
126
+ scheduled = false;
127
+ const roots = pendingRoots.size ? Array.from(pendingRoots) : [document];
128
+ pendingRoots.clear();
129
+ roots.slice(0, 60).forEach(run);
130
+ };
131
+ if ('requestIdleCallback' in window) window.requestIdleCallback(execute, { timeout: 500 });
132
+ else window.requestAnimationFrame(execute);
128
133
  };
129
134
 
130
- const run = root => {
135
+ const handlePointerAssist = event => {
131
136
  if (!setSurfaceState()) return;
132
- releaseNativeControls(root);
133
- annotateValueCells(root);
137
+ 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');
139
+ };
140
+
141
+ const handlePointerLeave = event => {
142
+ event.target?.closest?.('.eos-object-value-cell')?.classList.remove('eos-object-value-hover');
134
143
  };
135
144
 
136
- window.addEventListener('hashchange', () => setTimeout(() => run(document), 30));
137
- window.addEventListener('popstate', () => setTimeout(() => run(document), 30));
145
+ window.addEventListener('hashchange', () => window.setTimeout(() => schedule(document), 30));
146
+ window.addEventListener('popstate', () => window.setTimeout(() => schedule(document), 30));
138
147
  document.addEventListener('mouseover', handlePointerAssist, true);
139
148
  document.addEventListener('mouseout', handlePointerLeave, true);
140
149
  document.addEventListener('click', event => {
141
150
  if (!setSurfaceState()) return;
142
- const nativeInteractive = event.target?.closest?.(interactiveSelector + ',' + nativeLayerSelector + ',.eos-object-value-cell');
151
+ const nativeInteractive = event.target?.closest?.(`${interactiveSelector},${nativeLayerSelector},.eos-object-value-cell`);
143
152
  if (nativeInteractive) {
144
- // Intentionally do not stop propagation. This keeps the native ObjectBrowser write dialog and button states working.
153
+ // Keep native React/ObjectBrowser handlers untouched; only repair stale CSS/attributes after the click.
145
154
  releaseNativeControls(nativeInteractive);
146
- window.setTimeout(() => releaseNativeControls(document), 50);
147
- window.setTimeout(() => annotateValueCells(document), 120);
155
+ window.setTimeout(() => schedule(nativeInteractive), 80);
148
156
  }
149
157
  }, false);
150
158
 
151
159
  const observer = new MutationObserver(records => {
152
160
  if (!setSurfaceState()) return;
153
- let roots = [];
154
161
  for (const rec of records) {
155
162
  rec.addedNodes?.forEach(node => {
156
- if (node.nodeType === Node.ELEMENT_NODE) roots.push(node);
163
+ if (node.nodeType === Node.ELEMENT_NODE) schedule(node);
157
164
  });
158
165
  }
159
- if (!roots.length) roots = [document];
160
- window.requestAnimationFrame?.(() => roots.forEach(run)) || roots.forEach(run);
161
166
  });
162
167
 
163
168
  const start = () => {
164
169
  setSurfaceState();
165
- run(document);
170
+ schedule(document);
166
171
  observer.observe(document.documentElement, { childList: true, subtree: true });
167
- window.setInterval(() => run(document), 2500);
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);
168
174
  };
169
175
 
170
176
  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 = 'v48-role-guard-fix';
4
+ window.NEXOWATT_EOS_ROLE_UI_VERSION = 'v49-dp-write-performance-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;
@@ -280,7 +280,7 @@
280
280
  const startObserver = () => safe(() => {
281
281
  if (state.observer || !document.documentElement) return;
282
282
  state.observer = new MutationObserver(scheduleApply);
283
- state.observer.observe(document.documentElement, { childList: true, subtree: true, attributes: true, attributeFilter: ['href', 'class', 'aria-label', 'title'] });
283
+ state.observer.observe(document.documentElement, { childList: true, subtree: true, attributes: true, attributeFilter: ['href', 'class', 'aria-label'] });
284
284
  });
285
285
 
286
286
  const start = () => {
@@ -1,7 +1,7 @@
1
1
  (() => {
2
2
  'use strict';
3
3
 
4
- const VERSION = 'v48-role-guard-fix';
4
+ const VERSION = 'v49-dp-write-performance-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'];
package/io-package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "eos-admin",
4
- "version": "7.9.48",
4
+ "version": "7.9.49",
5
5
  "titleLang": {
6
6
  "en": "NexoWatt EOS Admin",
7
7
  "de": "NexoWatt EOS Admin",
@@ -18,6 +18,10 @@
18
18
  "connectionType": "local",
19
19
  "dataSource": "push",
20
20
  "news": {
21
+ "7.9.49": {
22
+ "en": "Reliable manual writes for writable states, corrected boolean switch handling and faster objects page updates.",
23
+ "de": "Zuverlässiges manuelles Beschreiben beschreibbarer Datenpunkte, korrigierte Boolean-/Switch-Behandlung und schnellere Datenpunkte-Oberfläche."
24
+ },
21
25
  "7.9.48": {
22
26
  "en": "Role guard fix: formal admin/installer/end-user roles, end-user auto-routing away from the blank intro page, and role-filtered EOS navigation.",
23
27
  "de": "Rollen-Fix: klare Admin-/Installateur-/Endkundenrollen, automatische Endkunden-Weiterleitung weg von der leeren Intro-Seite und rollenbasierte EOS-Navigation."
@@ -251,7 +255,7 @@
251
255
  "icon": "admin.svg",
252
256
  "messagebox": true,
253
257
  "enabled": true,
254
- "extIcon": "https://unpkg.com/iobroker.eos-admin@7.9.48/admin/admin.svg",
258
+ "extIcon": "https://unpkg.com/iobroker.eos-admin@7.9.49/admin/admin.svg",
255
259
  "keywords": [
256
260
  "NexoWatt",
257
261
  "EOS",
@@ -262,7 +266,7 @@
262
266
  "licensed"
263
267
  ],
264
268
  "compact": true,
265
- "readme": "https://unpkg.com/iobroker.eos-admin@7.9.48/README.md",
269
+ "readme": "https://unpkg.com/iobroker.eos-admin@7.9.49/README.md",
266
270
  "authors": [
267
271
  "bluefox <bluefox@ccu.io>",
268
272
  "hobbyquaker <hq@ccu.io>"
@@ -331,7 +335,7 @@
331
335
  "nondeletable": false,
332
336
  "allowAdapterUpdate": true,
333
337
  "allowAdapterDelete": false,
334
- "meta": "https://unpkg.com/iobroker.eos-admin@7.9.48/io-package.json",
338
+ "meta": "https://unpkg.com/iobroker.eos-admin@7.9.49/io-package.json",
335
339
  "npmPackage": "iobroker.eos-admin",
336
340
  "packetName": "iobroker.eos-admin"
337
341
  },
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "iobroker.eos-admin",
3
3
  "description": "NexoWatt EOS Admin interface with reliable repository update mapping and technical log preservation.",
4
- "version": "7.9.48",
4
+ "version": "7.9.49",
5
5
  "contributors": [
6
6
  "bluefox <dogafox@gmail.com>",
7
7
  "apollon77",