iobroker.eos-admin 7.10.1 → 7.10.3

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 (37) hide show
  1. package/INSTALL_TEST_V7.10.3.md +7 -0
  2. package/MERGE_UPDATE_README_V7.10.3.md +3 -0
  3. package/NEXOWATT_EOS_BUILD_INFO.json +16 -16
  4. package/NEXOWATT_EOS_PREBUILT_MANIFEST.json +1412 -0
  5. package/PUBLISH_STABLE_V7.10.3.md +10 -0
  6. package/README.md +4 -4
  7. package/README_STABILITY_V7.10.3.md +5 -0
  8. package/RELEASE_ACCEPTANCE_V7.10.3.md +3 -0
  9. package/RELEASE_NOTES_V7.10.3.md +7 -0
  10. package/adminWww/css/eos-auto-update.css +4 -3
  11. package/adminWww/index.html +2 -2
  12. package/adminWww/js/eos-auto-update.js +127 -33
  13. package/build/lib/eosAutoUpdate.js +418 -20
  14. package/build/lib/eosAutoUpdate.js.map +1 -0
  15. package/build/lib/eosRequestSecurity.js +4 -1
  16. package/build/lib/eosRequestSecurity.js.map +1 -0
  17. package/build/lib/web.js +40 -9
  18. package/build/lib/web.js.map +1 -1
  19. package/io-package.json +65 -5
  20. package/package.json +24 -16
  21. package/tools/nexowatt-auto-update-placement-selftest.cjs +46 -0
  22. package/tools/nexowatt-auto-update-selftest.cjs +144 -32
  23. package/tools/nexowatt-backend-runtime-selftest.cjs +147 -0
  24. package/tools/nexowatt-merge-update-selftest.cjs +7 -1
  25. package/tools/nexowatt-prebuilt-release-selftest.cjs +292 -0
  26. package/tools/nexowatt-stable-v7103-selftest.cjs +61 -0
  27. package/tools/nexowatt-validate-package.cjs +45 -12
  28. package/INSTALL_TEST_V7.10.1.md +0 -10
  29. package/MERGE_UPDATE_README_V7.10.1.md +0 -10
  30. package/PUBLISH_STABLE_V7.10.1.md +0 -10
  31. package/README_STABILITY_V7.10.1.md +0 -10
  32. package/RELEASE_ACCEPTANCE_V7.10.1.md +0 -10
  33. package/RELEASE_NOTES_V7.10.1.md +0 -10
  34. package/adminWww/js/eos-release-watch.js +0 -77
  35. package/build/lib/eosPassword.js +0 -72
  36. package/build/lib/nexowattStableAuth.js +0 -74
  37. package/tools/nexowatt-stable-v7101-selftest.cjs +0 -15
@@ -0,0 +1,10 @@
1
+ # NPM-Veröffentlichung 7.10.3
2
+
3
+ Aus einer frisch entpackten Repository-ZIP ausführen:
4
+
5
+ ```powershell
6
+ npm publish --dry-run
7
+ npm publish
8
+ ```
9
+
10
+ Der Publish-Workflow ist dependency-frei und darf weder `tsc`, `tsx`, `npm install` noch einen verschachtelten `npm pack`-Prozess verlangen.
package/README.md CHANGED
@@ -13,9 +13,9 @@ Standard-Port: 8081
13
13
 
14
14
  Damit überschreibt der Adapter den offiziellen `admin`-Adapter nicht mehr. Die alte ioBroker-Admin-Oberfläche kann parallel vorhanden bleiben, während NexoWatt-Systeme über `eos-admin.0` die EOS-Oberfläche nutzen.
15
15
 
16
- ## Aktueller Stable-Verkaufskandidat 7.9.98
16
+ ## Aktueller Stable-Verkaufskandidat 7.10.3
17
17
 
18
- Version 7.9.98 verwendet die native **Übersicht** als Startseite und ergänzt dort die ausschließlich lesende EMS-Live-Diagnose. Der Inhaltsbereich ist vertikal scrollbar, während Header und Hauptnavigation stehen bleiben.
18
+ Version 7.10.3 übernimmt den validierten und absturzfreien Stand 7.10.2. Die Einstellung **Automatische Adapter-Updates** liegt jetzt ausschließlich in den **Systemeinstellungen** hinter dem Schraubenschlüssel. Auf Übersicht, Module und allen anderen Hauptseiten wird die Karte nicht mehr angezeigt. Die restart-sichere Updateverwaltung, der verzögerte Repository-Abgleich und die vollständige Backend-Paketprüfung bleiben unverändert.
19
19
 
20
20
  Verwaltete Installateur- und Endkundenkonten verwenden das Startpasswort `nexowatt`. Nach der ersten Anmeldung beziehungsweise nach einem Reset muss unmittelbar ein persönliches Passwort vergeben werden. Der Reset und die persönliche Passwortvergabe schreiben das reale ioBroker-Benutzerkennwort; EOS-Metadaten werden davon getrennt aktualisiert.
21
21
 
@@ -33,7 +33,7 @@ Minimaler Repository-Eintrag:
33
33
  {
34
34
  "eos-admin": {
35
35
  "name": "eos-admin",
36
- "version": "7.9.98",
36
+ "version": "7.10.3",
37
37
  "title": "NexoWatt EOS Admin",
38
38
  "desc": {
39
39
  "de": "NexoWatt EOS Administrationsoberfläche als eigenständiger Adapter.",
@@ -141,7 +141,7 @@ npm publish --dry-run --tag latest
141
141
  npm publish --tag latest
142
142
  ```
143
143
 
144
- Die Version muss vor jedem erneuten Publish erhöht werden. Für 7.9.98 ist die Veröffentlichung des separat geprüften `iobroker.eos-admin-7.9.98.tgz` der sicherste Weg.
144
+ Die Version muss vor jedem erneuten Publish erhöht werden. Für 7.10.3 wird direkt aus der vollständig geprüften Repository-ZIP veröffentlicht; `npm publish` benötigt dabei weiterhin weder `tsc` noch `tsx`.
145
145
 
146
146
  ## Lizenz
147
147
 
@@ -0,0 +1,5 @@
1
+ # NexoWatt EOS Admin 7.10.3 – Stabilitätsstand
2
+
3
+ Version 7.10.3 übernimmt die validierten Backend-, Paket- und Restart-Schutzmaßnahmen aus 7.10.2 unverändert. Die sichtbare Karte **Automatische Adapter-Updates** wird ausschließlich im Dialog **Systemeinstellungen** hinter dem Schraubenschlüssel eingebunden. Sie darf weder in der Übersicht noch in Module, Dienste, Datenpunkte oder anderen Hauptseiten erscheinen.
4
+
5
+ Die Browserdateien `src-admin/public/js/eos-auto-update.js` und `adminWww/js/eos-auto-update.js` sowie die zugehörigen CSS-Dateien müssen identisch sein. Der Cache-Schlüssel `7103` erzwingt nach dem Update das Laden der neuen Platzierungslogik.
@@ -0,0 +1,3 @@
1
+ # Release Acceptance 7.10.3
2
+
3
+ Akzeptiert, wenn Paketvalidierung, Stabilitätstests, Prebuilt-Manifest, Backend-Runtime, Publish-Dry-Run und der Auto-Update-Placement-Test erfolgreich sind. Die Update-Karte ist ausschließlich im System-Settings-Dialog zulässig.
@@ -0,0 +1,7 @@
1
+ # Release Notes 7.10.3
2
+
3
+ - Automatische NexoWatt-Adapter-Updates aus dem normalen Seiteninhalt entfernt.
4
+ - Update-Karte ausschließlich in **Systemeinstellungen** hinter dem Schraubenschlüssel platziert.
5
+ - Keine Änderung an Stable-Repository, verwalteten Adaptern oder Update-Backendlogik.
6
+ - Eigener Browser-Cache-Schlüssel `7103` verhindert die Verwendung der alten Platzierung.
7
+ - Neuer Placement-Selbsttest verhindert eine spätere Rückkehr auf Übersicht oder Module.
@@ -1,4 +1,5 @@
1
- /* NexoWatt EOS native stable auto-update card */
2
- .eos-nexowatt-auto-update{box-sizing:border-box;display:flex;align-items:center;justify-content:space-between;gap:28px;width:calc(100% - 32px);margin:0 16px 14px;padding:22px 24px;border:1px solid rgba(0,229,138,.28);border-radius:20px;background:linear-gradient(120deg,rgba(0,71,66,.62),rgba(4,25,39,.96) 52%,rgba(4,21,34,.98));color:#effbff;box-shadow:0 16px 36px rgba(0,0,0,.18)}
1
+ /* NexoWatt EOS stable auto-update settings card: visible only inside the System Settings dialog. */
2
+ .eos-nexowatt-auto-update{box-sizing:border-box;display:flex;align-items:center;justify-content:space-between;gap:28px;flex:none;width:calc(100% - 32px);margin:16px;padding:20px 24px;border:1px solid rgba(0,229,138,.28);border-radius:20px;background:linear-gradient(120deg,rgba(0,71,66,.62),rgba(4,25,39,.96) 52%,rgba(4,21,34,.98));color:#effbff;box-shadow:0 16px 36px rgba(0,0,0,.18)}
3
+ .eos-nexowatt-auto-update[data-context="system-settings"]{position:relative;z-index:1}
3
4
  .eos-nexowatt-auto-update[data-enabled="false"]{border-color:rgba(117,145,160,.34);background:linear-gradient(120deg,rgba(31,54,65,.72),rgba(4,21,34,.98))}
4
- .eos-nau-copy{min-width:0;flex:1}.eos-nau-eyebrow{display:block;margin-bottom:5px;color:#00e58a;font-size:.72rem;font-weight:900;letter-spacing:.13em;text-transform:uppercase}.eos-nau-copy h2{margin:0 0 6px;font-size:1.35rem;line-height:1.2}.eos-nau-copy p{max-width:960px;margin:0;color:#a9c2d0;font-size:.82rem;line-height:1.55}.eos-nau-meta{display:flex;flex-wrap:wrap;gap:8px 18px;margin-top:13px;color:#84a8ba;font-size:.72rem}.eos-nau-meta strong{color:#d9f6ee}.eos-nau-adapters{display:flex;flex-wrap:wrap;gap:6px;margin-top:12px}.eos-nau-adapters span{padding:5px 9px;border:1px solid rgba(0,229,138,.24);border-radius:999px;background:rgba(0,229,138,.08);color:#b9f6db;font-size:.7rem;font-weight:800}.eos-nau-error{margin-top:10px;padding:8px 10px;border:1px solid rgba(255,183,77,.38);border-radius:9px;background:rgba(255,152,0,.09);color:#ffd18d;font-size:.72rem}.eos-nau-switch{display:grid;grid-template-columns:50px auto;grid-template-rows:auto auto;align-items:center;column-gap:10px;min-width:160px;cursor:pointer}.eos-nau-switch input{position:absolute;opacity:0;pointer-events:none}.eos-nau-track{grid-row:1/3;position:relative;width:50px;height:28px;border:1px solid rgba(130,161,176,.55);border-radius:999px;background:#273e4c;transition:.2s}.eos-nau-thumb{position:absolute;top:3px;left:3px;width:20px;height:20px;border-radius:50%;background:#b6c8d1;box-shadow:0 3px 8px rgba(0,0,0,.28);transition:.2s}.eos-nau-switch input:checked+.eos-nau-track{border-color:#00e58a;background:rgba(0,229,138,.26)}.eos-nau-switch input:checked+.eos-nau-track .eos-nau-thumb{left:25px;background:#00e58a}.eos-nau-switch strong{align-self:end;font-size:.82rem}.eos-nau-switch small{align-self:start;color:#83a2b2;font-size:.67rem}.eos-nau-switch input:focus-visible+.eos-nau-track{outline:2px solid #4bdcff;outline-offset:3px}@media(max-width:760px){.eos-nexowatt-auto-update{align-items:flex-start;flex-direction:column}.eos-nau-switch{align-self:flex-end}}
5
+ .eos-nau-copy{min-width:0;flex:1}.eos-nau-eyebrow{display:block;margin-bottom:5px;color:#00e58a;font-size:.72rem;font-weight:900;letter-spacing:.13em;text-transform:uppercase}.eos-nau-copy h2{margin:0 0 6px;font-size:1.25rem;line-height:1.2}.eos-nau-copy p{max-width:960px;margin:0;color:#a9c2d0;font-size:.82rem;line-height:1.55}.eos-nau-meta{display:flex;flex-wrap:wrap;gap:8px 18px;margin-top:13px;color:#84a8ba;font-size:.72rem}.eos-nau-meta strong{color:#d9f6ee}.eos-nau-adapters{display:flex;flex-wrap:wrap;gap:6px;margin-top:12px}.eos-nau-adapters span{padding:5px 9px;border:1px solid rgba(0,229,138,.24);border-radius:999px;background:rgba(0,229,138,.08);color:#b9f6db;font-size:.7rem;font-weight:800}.eos-nau-error{margin-top:10px;padding:8px 10px;border:1px solid rgba(255,183,77,.38);border-radius:9px;background:rgba(255,152,0,.09);color:#ffd18d;font-size:.72rem}.eos-nau-switch{display:grid;grid-template-columns:50px auto;grid-template-rows:auto auto;align-items:center;column-gap:10px;min-width:160px;cursor:pointer}.eos-nau-switch input{position:absolute;opacity:0;pointer-events:none}.eos-nau-track{grid-row:1/3;position:relative;width:50px;height:28px;border:1px solid rgba(130,161,176,.55);border-radius:999px;background:#273e4c;transition:.2s}.eos-nau-thumb{position:absolute;top:3px;left:3px;width:20px;height:20px;border-radius:50%;background:#b6c8d1;box-shadow:0 3px 8px rgba(0,0,0,.28);transition:.2s}.eos-nau-switch input:checked+.eos-nau-track{border-color:#00e58a;background:rgba(0,229,138,.26)}.eos-nau-switch input:checked+.eos-nau-track .eos-nau-thumb{left:25px;background:#00e58a}.eos-nau-switch strong{align-self:end;font-size:.82rem}.eos-nau-switch small{align-self:start;color:#83a2b2;font-size:.67rem}.eos-nau-switch input:focus-visible+.eos-nau-track{outline:2px solid #4bdcff;outline-offset:3px}@media(max-width:760px){.eos-nexowatt-auto-update{align-items:flex-start;flex-direction:column}.eos-nau-switch{align-self:flex-end}}
@@ -38,7 +38,7 @@
38
38
  />
39
39
  <link rel="stylesheet" href="./css/nexowatt-native-shell.css?v=7101" />
40
40
  <link rel="stylesheet" href="./css/eos-ems-overview.css?v=7101" />
41
- <link rel="stylesheet" href="./css/eos-auto-update.css?v=7101" />
41
+ <link rel="stylesheet" href="./css/eos-auto-update.css?v=7103" />
42
42
  <link
43
43
  rel="manifest"
44
44
  href="manifest.json"
@@ -169,7 +169,7 @@
169
169
  <script defer src="./js/eos-role-ui.js?v=7101"></script>
170
170
  <script defer src="./js/eos-ems-overview.js?v=7101"></script>
171
171
 
172
- <script defer src="./js/eos-auto-update.js?v=7101"></script>
172
+ <script defer src="./js/eos-auto-update.js?v=7103"></script>
173
173
  <script defer src="./js/eos-assistant.js?v=7101"></script>
174
174
  </head>
175
175
  <body>
@@ -1,78 +1,172 @@
1
1
  (() => {
2
2
  'use strict';
3
- const VERSION = 'v7100-nexowatt-stable-auto-update';
3
+
4
+ const VERSION = 'v7103-system-settings-only';
4
5
  const ROOT_ID = 'eos-nexowatt-auto-update';
6
+ const SYSTEM_DIALOG_SELECTOR = '[role="dialog"][aria-labelledby="system-settings-dialog-title"]';
5
7
  const script = document.currentScript;
6
8
  const API = new URL('../nexowatt/updates/', script?.src || document.baseURI).href;
9
+
7
10
  let busy = false;
8
11
  let lastLoad = 0;
12
+ let latestStatus = null;
9
13
  let pollTimer = 0;
10
14
 
11
15
  const role = () => String(window.NEXOWATT_EOS_ACCESS_ROLE || 'admin').toLowerCase();
12
- const introActive = () => String(location.hash || '').replace(/^#\/?/, '').split(/[/?&]/)[0] === 'tab-intro';
13
- const esc = value => String(value ?? '').replace(/[&<>'"]/g, char => ({'&':'&amp;','<':'&lt;','>':'&gt;',"'":'&#39;','"':'&quot;'}[char]));
14
- const formatTime = value => value ? new Intl.DateTimeFormat(undefined, { dateStyle: 'short', timeStyle: 'medium' }).format(new Date(value)) : '–';
16
+ const esc = value =>
17
+ String(value ?? '').replace(/[&<>'"]/g, char =>
18
+ ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', "'": '&#39;', '"': '&quot;' })[char],
19
+ );
20
+ const formatTime = value =>
21
+ value
22
+ ? new Intl.DateTimeFormat(undefined, { dateStyle: 'short', timeStyle: 'medium' }).format(new Date(value))
23
+ : '–';
24
+
25
+ const getSystemSettingsMount = () => {
26
+ const dialog = document.querySelector(SYSTEM_DIALOG_SELECTOR);
27
+ if (!dialog) return null;
28
+
29
+ const content = dialog.querySelector('.MuiDialogContent-root');
30
+ if (!content) return null;
31
+
32
+ const appBar = Array.from(content.children).find(element => element.classList?.contains('MuiAppBar-root')) ||
33
+ content.querySelector('.MuiAppBar-root');
34
+
35
+ return { dialog, content, appBar };
36
+ };
37
+
38
+ const systemSettingsActive = () => Boolean(getSystemSettingsMount());
15
39
 
16
40
  const ensureRoot = () => {
17
- if (role() !== 'admin' || !introActive()) { document.getElementById(ROOT_ID)?.remove(); return null; }
18
- let root = document.getElementById(ROOT_ID);
19
- if (root) return root;
20
- root = document.createElement('section');
41
+ const existing = document.getElementById(ROOT_ID);
42
+ if (role() !== 'admin') {
43
+ existing?.remove();
44
+ return null;
45
+ }
46
+
47
+ const mount = getSystemSettingsMount();
48
+ if (!mount) {
49
+ existing?.remove();
50
+ return null;
51
+ }
52
+
53
+ if (existing && mount.content.contains(existing)) return existing;
54
+ existing?.remove();
55
+
56
+ const root = document.createElement('section');
21
57
  root.id = ROOT_ID;
22
58
  root.className = 'eos-nexowatt-auto-update';
23
- const hero = document.getElementById('eos-native-overview-hero');
24
- const ems = document.getElementById('eos-ems-overview-runtime');
25
- const parent = ems?.parentElement || hero?.parentElement || document.getElementById('app-paper');
26
- if (!parent) return null;
27
- if (ems) parent.insertBefore(root, ems); else if (hero?.nextSibling) parent.insertBefore(root, hero.nextSibling); else parent.append(root);
59
+ root.dataset.context = 'system-settings';
60
+ root.setAttribute('data-eos-system-settings-only', 'true');
61
+ root.setAttribute('aria-label', 'NexoWatt Stable-Update-Einstellungen');
62
+
63
+ if (mount.appBar?.parentElement === mount.content) {
64
+ mount.content.insertBefore(root, mount.appBar.nextSibling);
65
+ } else {
66
+ mount.content.prepend(root);
67
+ }
28
68
  return root;
29
69
  };
30
70
 
31
71
  const render = status => {
32
- const root = ensureRoot(); if (!root) return;
33
- const enabled = status?.enabled !== false;
34
- const managed = Array.isArray(status?.managedAdapters) ? status.managedAdapters : [];
35
- const error = String(status?.error || '');
72
+ latestStatus = { ...(latestStatus || {}), ...(status || {}) };
73
+ const root = ensureRoot();
74
+ if (!root) return;
75
+
76
+ const enabled = latestStatus?.enabled !== false;
77
+ const managed = Array.isArray(latestStatus?.managedAdapters) ? latestStatus.managedAdapters : [];
78
+ const error = String(latestStatus?.error || '');
79
+
36
80
  root.dataset.enabled = String(enabled);
37
- root.innerHTML = `<div class="eos-nau-copy"><span class="eos-nau-eyebrow">NexoWatt Stable</span><h2>Automatische Adapter-Updates</h2><p>${enabled ? 'Installierte NexoWatt-Adapter werden automatisch aktualisiert, sobald im erkannten Stable-Repository eine neuere stabile Version verfügbar ist.' : 'Automatische NexoWatt-Updates sind deaktiviert. Vorherige ioBroker-Update-Richtlinien wurden wiederhergestellt.'}</p><div class="eos-nau-meta"><span>Repository: <strong>${esc(status?.repository || 'wird erkannt')}</strong></span><span>Letzter Abgleich: <strong>${esc(formatTime(status?.lastSync))}</strong></span><span>Richtlinie: <strong>alle neueren Stable-Versionen</strong></span></div>${managed.length ? `<div class="eos-nau-adapters">${managed.map(name => `<span>${esc(name)}</span>`).join('')}</div>` : ''}${error ? `<div class="eos-nau-error">${esc(error)}</div>` : ''}</div><label class="eos-nau-switch"><input type="checkbox" ${enabled ? 'checked' : ''} ${busy ? 'disabled' : ''}><span class="eos-nau-track"><span class="eos-nau-thumb"></span></span><strong>${enabled ? 'Aktiv' : 'Deaktiviert'}</strong><small>jederzeit änderbar</small></label>`;
81
+ root.innerHTML = `<div class="eos-nau-copy"><span class="eos-nau-eyebrow">NexoWatt Stable</span><h2>Automatische Adapter-Updates</h2><p>${enabled ? 'Installierte NexoWatt-Adapter werden automatisch aktualisiert, sobald im erkannten Stable-Repository eine neuere stabile Version verfügbar ist.' : 'Automatische NexoWatt-Updates sind deaktiviert. Vorherige ioBroker-Update-Richtlinien wurden wiederhergestellt.'}</p><div class="eos-nau-meta"><span>Repository: <strong>${esc(latestStatus?.repository || 'wird erkannt')}</strong></span><span>Letzter Abgleich: <strong>${esc(formatTime(latestStatus?.lastSync))}</strong></span><span>Richtlinie: <strong>alle neueren Stable-Versionen</strong></span></div>${managed.length ? `<div class="eos-nau-adapters">${managed.map(name => `<span>${esc(name)}</span>`).join('')}</div>` : ''}${error ? `<div class="eos-nau-error">${esc(error)}</div>` : ''}</div><label class="eos-nau-switch"><input type="checkbox" ${enabled ? 'checked' : ''} ${busy ? 'disabled' : ''}><span class="eos-nau-track"><span class="eos-nau-thumb"></span></span><strong>${enabled ? 'Aktiv' : 'Deaktiviert'}</strong><small>jederzeit änderbar</small></label>`;
38
82
  root.querySelector('input')?.addEventListener('change', event => void save(Boolean(event.currentTarget.checked)));
39
83
  };
40
84
 
41
85
  const request = async (path, options = {}) => {
42
- const response = await fetch(new URL(path, API), { cache: 'no-store', credentials: 'same-origin', ...options });
86
+ const response = await fetch(new URL(path, API), {
87
+ cache: 'no-store',
88
+ credentials: 'same-origin',
89
+ ...options,
90
+ });
43
91
  const data = await response.json().catch(() => ({}));
44
92
  if (!response.ok) throw new Error(data.error || `HTTP ${response.status}`);
45
93
  return data;
46
94
  };
95
+
47
96
  const load = async (force = false) => {
48
- if (role() !== 'admin' || !introActive()) return;
97
+ if (role() !== 'admin' || !systemSettingsActive()) {
98
+ ensureRoot();
99
+ return;
100
+ }
49
101
  if (!force && Date.now() - lastLoad < 5000) return;
102
+
50
103
  lastLoad = Date.now();
51
- try { render(await request('status')); }
52
- catch (error) { render({ enabled: true, error: error?.message || error }); }
104
+ try {
105
+ render(await request('status'));
106
+ } catch (error) {
107
+ render({ enabled: latestStatus?.enabled !== false, error: error?.message || error });
108
+ }
53
109
  };
110
+
54
111
  const save = async enabled => {
55
112
  if (busy) return;
56
113
  busy = true;
114
+ render({ enabled, error: '' });
115
+
57
116
  try {
58
- render({ enabled });
59
- const status = await request('settings', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-NexoWatt-EOS-Auto-Update': '1' }, body: JSON.stringify({ enabled }) });
60
- render(status);
117
+ latestStatus = await request('settings', {
118
+ method: 'POST',
119
+ headers: {
120
+ 'Content-Type': 'application/json',
121
+ 'X-NexoWatt-EOS-Auto-Update': '1',
122
+ },
123
+ body: JSON.stringify({ enabled }),
124
+ });
61
125
  } catch (error) {
62
- render({ enabled: !enabled, error: error?.message || error });
63
- } finally { busy = false; }
126
+ latestStatus = { ...(latestStatus || {}), enabled: !enabled, error: error?.message || error };
127
+ } finally {
128
+ busy = false;
129
+ render(latestStatus || { enabled });
130
+ }
64
131
  };
65
132
 
66
133
  let scheduled = false;
67
- const schedule = () => { if (scheduled) return; scheduled = true; requestAnimationFrame(() => { scheduled = false; void load(); }); };
134
+ const schedule = () => {
135
+ if (scheduled) return;
136
+ scheduled = true;
137
+ requestAnimationFrame(() => {
138
+ scheduled = false;
139
+ void load();
140
+ });
141
+ };
142
+
68
143
  const start = () => {
69
144
  void load();
70
- window.addEventListener('hashchange', () => { lastLoad = 0; schedule(); });
145
+ window.addEventListener('hashchange', () => {
146
+ lastLoad = 0;
147
+ schedule();
148
+ });
71
149
  window.addEventListener('nexowatt-eos-admin-socket-ready', schedule);
72
- document.addEventListener('visibilitychange', () => { if (document.visibilityState === 'visible') schedule(); });
73
- new MutationObserver(() => { if (!document.getElementById(ROOT_ID) && introActive()) schedule(); }).observe(document.documentElement, { childList: true, subtree: true });
150
+ document.addEventListener('visibilitychange', () => {
151
+ if (document.visibilityState === 'visible') schedule();
152
+ });
153
+
154
+ new MutationObserver(() => {
155
+ const root = document.getElementById(ROOT_ID);
156
+ const settingsOpen = systemSettingsActive();
157
+ if ((settingsOpen && !root) || (!settingsOpen && root)) schedule();
158
+ }).observe(document.documentElement, { childList: true, subtree: true });
159
+
74
160
  pollTimer = window.setInterval(() => void load(true), 30000);
75
161
  };
76
- document.readyState === 'loading' ? document.addEventListener('DOMContentLoaded', start, { once: true }) : start();
77
- window.NEXOWATT_EOS_AUTO_UPDATE_UI = Object.freeze({ version: VERSION, refresh: load });
162
+
163
+ document.readyState === 'loading'
164
+ ? document.addEventListener('DOMContentLoaded', start, { once: true })
165
+ : start();
166
+
167
+ window.NEXOWATT_EOS_AUTO_UPDATE_UI = Object.freeze({
168
+ version: VERSION,
169
+ refresh: load,
170
+ location: 'system-settings',
171
+ });
78
172
  })();