iobroker.eos-admin 7.9.98 → 7.9.100
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/INSTALL_TEST_V7.9.100.md +11 -0
- package/MERGE_UPDATE_README_V7.9.100.md +5 -0
- package/NEXOWATT_EOS_BUILD_INFO.json +20 -20
- package/PUBLISH_STABLE_V7.9.100.md +15 -0
- package/README.md +6 -16
- package/README_STABILITY_V7.9.100.md +3 -0
- package/RELEASE_ACCEPTANCE_V7.9.100.md +11 -0
- package/RELEASE_NOTES_V7.9.100.md +11 -0
- package/adminWww/assets/Intro-DkwRiz1n-v84.js +2 -2
- package/adminWww/assets/bootstrap-COulQZax-v84.js +2 -2
- package/adminWww/css/eos-auto-update.css +4 -0
- package/adminWww/css/eos-ems-overview.css +3 -5
- package/adminWww/css/nexowatt-native-shell.css +26 -112
- package/adminWww/index.html +21 -18
- package/adminWww/js/eos-account-management.js +19 -28
- package/adminWww/js/eos-assistant.js +1 -1
- package/adminWww/js/eos-auto-update.js +78 -0
- package/adminWww/js/eos-ems-overview.js +2 -3
- package/adminWww/js/eos-release-watch.js +77 -0
- package/adminWww/js/eos-role-bootstrap.js +11 -8
- package/adminWww/js/eos-role-ui.js +2 -2
- package/adminWww/js/nexowatt-native-shell.js +1 -1
- package/adminWww/static/js/nexowatt-stable-login.js +3 -3
- package/adminWww/static/js/{nexowatt-stable-v98.js → nexowatt-stable-v100.js} +2 -2
- package/build/lib/eosAutoUpdate.js +43 -0
- package/build/lib/eosPassword.js +62 -0
- package/build/lib/eosRequestSecurity.js +31 -0
- package/build/lib/web.js +194 -151
- package/build/main.js +2 -1
- package/io-package.json +7 -11
- package/package.json +23 -13
- package/src/lib/eosAutoUpdate.ts +320 -0
- package/tools/nexowatt-account-management-selftest.cjs +19 -15
- package/tools/nexowatt-apply-ems-overview.cjs +2 -4
- package/tools/nexowatt-auto-update-selftest.cjs +34 -0
- package/tools/nexowatt-cache-update-selftest.cjs +12 -0
- package/tools/nexowatt-clean-legacy-runtime.cjs +1 -1
- package/tools/nexowatt-ems-overview-selftest.cjs +1 -1
- package/tools/nexowatt-login-layout-selftest.cjs +11 -11
- package/tools/nexowatt-merge-update-selftest.cjs +1 -1
- package/tools/nexowatt-modern-ui-selftest.cjs +2 -2
- package/tools/nexowatt-password-write-selftest.cjs +42 -44
- package/tools/nexowatt-request-security-selftest.cjs +11 -0
- package/tools/nexowatt-runtime-cleanup-selftest.cjs +6 -6
- package/tools/nexowatt-stable-v100-selftest.cjs +27 -0
- package/tools/nexowatt-sync-release-version.cjs +1 -42
- package/tools/nexowatt-ui-overview-runtime-selftest.cjs +3 -1
- package/tools/nexowatt-validate-package.cjs +23 -23
- package/tools/nexowatt-version-sync-selftest.cjs +1 -18
- package/INSTALL_TEST_V7.9.98.md +0 -39
- package/MERGE_UPDATE_README_V7.9.98.md +0 -29
- package/PUBLISH_STABLE_V7.9.98.md +0 -33
- package/README_STABILITY_V7.9.98.md +0 -22
- package/RELEASE_ACCEPTANCE_V7.9.98.md +0 -48
- package/RELEASE_NOTES_V7.9.98.md +0 -32
- package/tools/nexowatt-account-password-flow-selftest.cjs +0 -186
- package/tools/nexowatt-stable-v98-selftest.cjs +0 -14
|
@@ -0,0 +1,4 @@
|
|
|
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)}
|
|
3
|
+
.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}}
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
}
|
|
18
18
|
#eos-ems-overview-runtime { width: calc(100% - 32px); max-width: none; margin: 0 16px 20px; }
|
|
19
19
|
.eos-ems-overview-card * { box-sizing: border-box; }
|
|
20
|
-
.eos-ems-overview-header { display:flex; align-items:flex-start; justify-content:
|
|
20
|
+
.eos-ems-overview-header { display:flex; align-items:flex-start; justify-content:flex-start; gap:20px; }
|
|
21
21
|
.eos-ems-overview-header h2 { margin:5px 0 6px; font-size:clamp(1.2rem,2vw,1.65rem); color:#f5fbff; }
|
|
22
22
|
.eos-ems-overview-header p { margin:0; max-width:760px; color:#9eb2c2; line-height:1.45; }
|
|
23
23
|
.eos-ems-overview-eyebrow { color:#49e39e; font-size:.72rem; font-weight:900; letter-spacing:.13em; text-transform:uppercase; }
|
|
@@ -48,9 +48,7 @@
|
|
|
48
48
|
.eos-ems-overview-event time { color:#6f8799; font-size:.68rem; font-variant-numeric:tabular-nums; }
|
|
49
49
|
.eos-ems-overview-event div { display:grid; gap:2px; min-width:0; }
|
|
50
50
|
.eos-ems-overview-empty { color:#718797; font-size:.76rem; }
|
|
51
|
-
.eos-ems-overview-footer { display:flex; justify-content:
|
|
52
|
-
.eos-ems-overview-footer a { color:#5ee0c2; font-weight:800; text-decoration:none; }
|
|
53
|
-
.eos-ems-overview-footer a:hover,.eos-ems-overview-footer a:focus-visible { color:#9af2dd; text-decoration:underline; }
|
|
51
|
+
.eos-ems-overview-footer { display:flex; justify-content:flex-start; gap:12px; margin-top:12px; color:#60798a; font-size:.67rem; }
|
|
54
52
|
@media (max-width: 1100px) { .eos-ems-overview-card { flex-basis:100%; max-width:none; } }
|
|
55
53
|
@media (max-width: 760px) {
|
|
56
54
|
#eos-ems-overview-runtime,.eos-ems-overview-card { min-width:0; width:calc(100% - 16px); margin:8px; padding:16px; }
|
|
@@ -61,7 +59,7 @@
|
|
|
61
59
|
.eos-ems-overview-footer { display:grid; }
|
|
62
60
|
}
|
|
63
61
|
|
|
64
|
-
/*
|
|
62
|
+
/* v100: startup and compatibility messages stay compact. */
|
|
65
63
|
.eos-ems-overview-card.is-loading,
|
|
66
64
|
.eos-ems-overview-card.is-unavailable {
|
|
67
65
|
min-height: 0;
|
|
@@ -1292,7 +1292,7 @@ html.eos-route-intro #app-paper [class*="card"] {
|
|
|
1292
1292
|
.eos-assist-header-root .eos-assist-panel { right: 8px !important; width: calc(100vw - 16px); }
|
|
1293
1293
|
}
|
|
1294
1294
|
|
|
1295
|
-
/* NexoWatt EOS 7.9.
|
|
1295
|
+
/* NexoWatt EOS 7.9.100 – orderly login, moderately enlarged. */
|
|
1296
1296
|
html.eos-login,
|
|
1297
1297
|
html.eos-login body {
|
|
1298
1298
|
min-height: 100%;
|
|
@@ -1407,119 +1407,33 @@ html.eos-login form { max-width: 100% !important; }
|
|
|
1407
1407
|
html.eos-safe-overview-active #app-paper > :not(.MuiSnackbar-root):not([role="presentation"]) { pointer-events: auto !important; }
|
|
1408
1408
|
.eos-intro-admin-edit-hidden { display: none !important; }
|
|
1409
1409
|
|
|
1410
|
-
/* NexoWatt EOS 7.9.98 – stable scrolling, clean login fields and expandable errors. */
|
|
1411
|
-
html.eos-app,
|
|
1412
|
-
html.eos-app body,
|
|
1413
|
-
html.eos-app #root {
|
|
1414
|
-
height: 100% !important;
|
|
1415
|
-
max-height: 100% !important;
|
|
1416
|
-
overflow: hidden !important;
|
|
1417
|
-
}
|
|
1418
|
-
html.eos-app #app-paper {
|
|
1419
|
-
height: calc(100vh - var(--nx-content-top) - 12px) !important;
|
|
1420
|
-
height: calc(100dvh - var(--nx-content-top) - 12px) !important;
|
|
1421
|
-
min-height: 0 !important;
|
|
1422
|
-
max-height: calc(100vh - var(--nx-content-top) - 12px) !important;
|
|
1423
|
-
max-height: calc(100dvh - var(--nx-content-top) - 12px) !important;
|
|
1424
|
-
overflow-x: hidden !important;
|
|
1425
|
-
overflow-y: auto !important;
|
|
1426
|
-
overscroll-behavior: contain;
|
|
1427
|
-
scrollbar-gutter: stable;
|
|
1428
|
-
scrollbar-width: thin;
|
|
1429
|
-
scrollbar-color: rgba(0, 229, 138, .88) rgba(3, 18, 29, .72);
|
|
1430
|
-
}
|
|
1431
|
-
html.eos-app #app-paper::-webkit-scrollbar { width: 11px; }
|
|
1432
|
-
html.eos-app #app-paper::-webkit-scrollbar-track {
|
|
1433
|
-
border-radius: 12px;
|
|
1434
|
-
background: rgba(3, 18, 29, .72);
|
|
1435
|
-
}
|
|
1436
|
-
html.eos-app #app-paper::-webkit-scrollbar-thumb {
|
|
1437
|
-
min-height: 46px;
|
|
1438
|
-
border: 2px solid rgba(3, 18, 29, .72);
|
|
1439
|
-
border-radius: 12px;
|
|
1440
|
-
background: linear-gradient(180deg, #00e58a, #39c9ef);
|
|
1441
|
-
}
|
|
1442
|
-
html.eos-app #app-paper::-webkit-scrollbar-thumb:hover {
|
|
1443
|
-
background: linear-gradient(180deg, #21f2a1, #60dcff);
|
|
1444
|
-
}
|
|
1445
1410
|
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
border-radius: 24px !important;
|
|
1450
|
-
}
|
|
1451
|
-
html.eos-login main.MuiPaper-root > .eos-login-card-modern,
|
|
1452
|
-
html.eos-login main.MuiPaper-root > .MuiPaper-root:has(#username),
|
|
1453
|
-
html.eos-login main.MuiPaper-root > .MuiPaper-root:has(#password) {
|
|
1454
|
-
width: min(400px, calc(100vw - 48px)) !important;
|
|
1455
|
-
max-width: 400px !important;
|
|
1456
|
-
min-height: 0 !important;
|
|
1457
|
-
height: auto !important;
|
|
1458
|
-
padding: 30px 32px 28px !important;
|
|
1459
|
-
gap: 0 !important;
|
|
1460
|
-
overflow: visible !important;
|
|
1461
|
-
border-radius: 22px !important;
|
|
1462
|
-
}
|
|
1463
|
-
html.eos-login .eos-login-card-modern > .MuiGrid2-root,
|
|
1464
|
-
html.eos-login main.MuiPaper-root > .MuiPaper-root:has(#username) > .MuiGrid2-root,
|
|
1465
|
-
html.eos-login main.MuiPaper-root > .MuiPaper-root:has(#password) > .MuiGrid2-root {
|
|
1466
|
-
width: 100% !important;
|
|
1467
|
-
min-width: 0 !important;
|
|
1468
|
-
}
|
|
1469
|
-
html.eos-login .MuiFormControl-root {
|
|
1411
|
+
/* NexoWatt EOS 7.9.100 – edit controls belong to the lowest Intro card grid. */
|
|
1412
|
+
html.eos-route-intro .eos-native-intro-grid {
|
|
1413
|
+
position: relative !important;
|
|
1470
1414
|
width: 100% !important;
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
html.eos-login .MuiOutlinedInput-root {
|
|
1475
|
-
min-height: 46px !important;
|
|
1476
|
-
border-radius: 6px !important;
|
|
1477
|
-
background: rgba(4, 19, 31, .76) !important;
|
|
1478
|
-
}
|
|
1479
|
-
html.eos-login .MuiOutlinedInput-input {
|
|
1480
|
-
min-height: 0 !important;
|
|
1481
|
-
padding: 11px 14px !important;
|
|
1482
|
-
line-height: 22px !important;
|
|
1483
|
-
}
|
|
1484
|
-
html.eos-login .MuiOutlinedInput-input::placeholder {
|
|
1485
|
-
color: rgba(245, 251, 255, .92) !important;
|
|
1486
|
-
opacity: 1 !important;
|
|
1487
|
-
}
|
|
1488
|
-
html.eos-login .MuiOutlinedInput-notchedOutline legend {
|
|
1489
|
-
width: 0 !important;
|
|
1490
|
-
max-width: 0 !important;
|
|
1491
|
-
padding: 0 !important;
|
|
1492
|
-
}
|
|
1493
|
-
html.eos-login .MuiFormControlLabel-root {
|
|
1494
|
-
margin: 7px 0 5px !important;
|
|
1495
|
-
}
|
|
1496
|
-
html.eos-login .MuiButton-root[type="submit"] {
|
|
1497
|
-
min-height: 44px !important;
|
|
1498
|
-
margin: 8px 0 0 !important;
|
|
1499
|
-
}
|
|
1500
|
-
html.eos-login .eos-login-card-modern > .MuiBox-root:last-child,
|
|
1501
|
-
html.eos-login main.MuiPaper-root > .MuiPaper-root:has(#username) > .MuiBox-root:last-child,
|
|
1502
|
-
html.eos-login main.MuiPaper-root > .MuiPaper-root:has(#password) > .MuiBox-root:last-child {
|
|
1503
|
-
margin-top: 28px !important;
|
|
1415
|
+
min-height: 80px !important;
|
|
1416
|
+
padding-bottom: 80px !important;
|
|
1417
|
+
overflow: visible !important;
|
|
1504
1418
|
}
|
|
1505
|
-
html.eos-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1419
|
+
html.eos-route-intro .eos-overview-edit-controls {
|
|
1420
|
+
position: absolute !important;
|
|
1421
|
+
right: 16px !important;
|
|
1422
|
+
bottom: 16px !important;
|
|
1423
|
+
z-index: 3 !important;
|
|
1424
|
+
display: flex !important;
|
|
1425
|
+
align-items: center !important;
|
|
1426
|
+
gap: 8px !important;
|
|
1427
|
+
min-width: 52px !important;
|
|
1428
|
+
min-height: 52px !important;
|
|
1429
|
+
padding: 6px !important;
|
|
1430
|
+
border: 1px solid rgba(0, 229, 138, .24) !important;
|
|
1431
|
+
border-radius: 16px !important;
|
|
1432
|
+
background: rgba(3, 23, 34, .94) !important;
|
|
1433
|
+
box-shadow: 0 12px 30px rgba(0, 0, 0, .26) !important;
|
|
1510
1434
|
}
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
min-height: min(520px, calc(100dvh - 12px)) !important;
|
|
1516
|
-
}
|
|
1517
|
-
html.eos-login main.MuiPaper-root > .eos-login-card-modern,
|
|
1518
|
-
html.eos-login main.MuiPaper-root > .MuiPaper-root:has(#username),
|
|
1519
|
-
html.eos-login main.MuiPaper-root > .MuiPaper-root:has(#password) {
|
|
1520
|
-
width: calc(100vw - 38px) !important;
|
|
1521
|
-
max-width: none !important;
|
|
1522
|
-
padding: 25px 22px 23px !important;
|
|
1523
|
-
}
|
|
1435
|
+
html.eos-route-intro .eos-overview-edit-controls .MuiFab-root {
|
|
1436
|
+
position: static !important;
|
|
1437
|
+
inset: auto !important;
|
|
1438
|
+
margin: 0 !important;
|
|
1524
1439
|
}
|
|
1525
|
-
|
package/adminWww/index.html
CHANGED
|
@@ -8,8 +8,9 @@
|
|
|
8
8
|
<meta http-equiv="Cache-Control" content="no-store, no-cache, must-revalidate" />
|
|
9
9
|
<meta http-equiv="Pragma" content="no-cache" />
|
|
10
10
|
<meta http-equiv="Expires" content="0" />
|
|
11
|
-
<script src="./js/eos-
|
|
12
|
-
<script
|
|
11
|
+
<script src="./js/eos-release-watch.js?v=100"></script>
|
|
12
|
+
<script src="./js/eos-manual-write-policy.js?v=100"></script>
|
|
13
|
+
<script type="module" src="./assets/hostInit-v84.js?v=100"></script>
|
|
13
14
|
|
|
14
15
|
<meta charset="utf-8" />
|
|
15
16
|
<link
|
|
@@ -36,8 +37,9 @@
|
|
|
36
37
|
rel="stylesheet"
|
|
37
38
|
href="css/leaflet.css"
|
|
38
39
|
/>
|
|
39
|
-
<link rel="stylesheet" href="./css/nexowatt-native-shell.css?v=
|
|
40
|
-
<link rel="stylesheet" href="./css/eos-ems-overview.css?v=
|
|
40
|
+
<link rel="stylesheet" href="./css/nexowatt-native-shell.css?v=100" />
|
|
41
|
+
<link rel="stylesheet" href="./css/eos-ems-overview.css?v=100" />
|
|
42
|
+
<link rel="stylesheet" href="./css/eos-auto-update.css?v=100" />
|
|
41
43
|
<link
|
|
42
44
|
rel="manifest"
|
|
43
45
|
href="manifest.json"
|
|
@@ -157,23 +159,24 @@
|
|
|
157
159
|
false,
|
|
158
160
|
);
|
|
159
161
|
</script>
|
|
160
|
-
<link rel="modulepreload" crossorigin href="./assets/preload-helper-BDBacUwf.js">
|
|
161
|
-
<link rel="modulepreload" crossorigin href="./assets/iobroker_admin__mf_v__runtimeInit__mf_v__-g2X2zhAf.js">
|
|
162
|
-
<script defer src="./js/eos-role-bootstrap.js?v=
|
|
163
|
-
<script defer src="./js/eos-policy-client.js?v=
|
|
164
|
-
<script defer src="./js/eos-branding-sanitizer.js?v=
|
|
165
|
-
<script defer src="./js/nexowatt-native-shell.js?v=
|
|
166
|
-
<script defer src="./js/eos-native-security.js?v=
|
|
167
|
-
<script defer src="./js/eos-basic-settings.js?v=
|
|
168
|
-
<script defer src="./js/eos-role-ui.js?v=
|
|
169
|
-
<script defer src="./js/eos-ems-overview.js?v=
|
|
170
|
-
<script defer src="./js/eos-account-management.js?v=
|
|
171
|
-
<script defer src="./js/eos-
|
|
162
|
+
<link rel="modulepreload" crossorigin href="./assets/preload-helper-BDBacUwf.js?v=100">
|
|
163
|
+
<link rel="modulepreload" crossorigin href="./assets/iobroker_admin__mf_v__runtimeInit__mf_v__-g2X2zhAf.js?v=100">
|
|
164
|
+
<script defer src="./js/eos-role-bootstrap.js?v=100" data-eos-entry="./assets/index-CQZugZ1z-v84.js?v=100"></script>
|
|
165
|
+
<script defer src="./js/eos-policy-client.js?v=100"></script>
|
|
166
|
+
<script defer src="./js/eos-branding-sanitizer.js?v=100"></script>
|
|
167
|
+
<script defer src="./js/nexowatt-native-shell.js?v=100"></script>
|
|
168
|
+
<script defer src="./js/eos-native-security.js?v=100"></script>
|
|
169
|
+
<script defer src="./js/eos-basic-settings.js?v=100"></script>
|
|
170
|
+
<script defer src="./js/eos-role-ui.js?v=100"></script>
|
|
171
|
+
<script defer src="./js/eos-ems-overview.js?v=100"></script>
|
|
172
|
+
<script defer src="./js/eos-account-management.js?v=100"></script>
|
|
173
|
+
<script defer src="./js/eos-auto-update.js?v=100"></script>
|
|
174
|
+
<script defer src="./js/eos-assistant.js?v=100"></script>
|
|
172
175
|
</head>
|
|
173
176
|
<body>
|
|
174
177
|
<noscript>Für NexoWatt EOS muss JavaScript aktiviert sein.</noscript>
|
|
175
178
|
<div id="root"></div>
|
|
176
|
-
<script defer src="static/js/nexowatt-stable-login.js?v=
|
|
177
|
-
<script defer src="static/js/nexowatt-stable-
|
|
179
|
+
<script defer src="static/js/nexowatt-stable-login.js?v=100"></script>
|
|
180
|
+
<script defer src="static/js/nexowatt-stable-v100.js?v=100"></script>
|
|
178
181
|
</body>
|
|
179
182
|
</html>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
(() => {
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
const VERSION = '
|
|
4
|
+
const VERSION = 'v100-account-management-password-write';
|
|
5
5
|
const script = document.currentScript || document.querySelector('script[src*="eos-account-management.js"]');
|
|
6
6
|
const base = new URL('../', script?.src || window.location.href);
|
|
7
7
|
const abort = new AbortController();
|
|
@@ -19,10 +19,11 @@
|
|
|
19
19
|
introInstaller: 'Du kannst Endkundenzugänge zurücksetzen. Admin- und Installateurzugänge bleiben NexoWatt Service vorbehalten.',
|
|
20
20
|
loading: 'Zugänge werden geladen …', empty: 'Keine passenden Konten gefunden.', close: 'Schließen', refresh: 'Aktualisieren',
|
|
21
21
|
installer: 'Installateur', enduser: 'Endkunde / Gast', active: 'Passwort aktiv', first: 'Erstanmeldung offen', disabled: 'Deaktiviert',
|
|
22
|
-
reset: 'Passwort zurücksetzen', resetting: 'Wird zurückgesetzt …', resetConfirm: name => `Soll der Zugang „${name}“ wirklich zurückgesetzt werden?
|
|
23
|
-
resetDone: name => `Der Zugang „${name}“
|
|
24
|
-
resetFailed: 'Der Zugang konnte nicht zurückgesetzt werden.',
|
|
22
|
+
reset: 'Passwort zurücksetzen', resetting: 'Wird zurückgesetzt …', resetConfirm: name => `Soll der Zugang „${name}“ wirklich zurückgesetzt werden? Danach gilt einmalig das Startpasswort „nexowatt“ und anschließend muss ein persönliches Passwort vergeben werden.`,
|
|
23
|
+
resetDone: name => `Der Zugang „${name}“ wurde im EOS-Benutzerkonto auf „nexowatt“ zurückgesetzt. Nach der nächsten Anmeldung muss sofort ein persönliches Passwort vergeben werden.`,
|
|
24
|
+
resetFailed: 'Der Zugang konnte nicht zurückgesetzt werden.', lastSet: 'Letzte Passwortvergabe', lastReset: 'Letzter Reset', by: 'durch',
|
|
25
25
|
security: 'Admin/Service darf Installateur und Endkunde zurücksetzen. Installateure dürfen ausschließlich Endkunden zurücksetzen.',
|
|
26
|
+
errors: { permissionError: 'Die Admin-/Installateurberechtigung konnte nicht bestätigt werden. Bitte neu anmelden.', invalidRequest: 'Die Sicherheitsbestätigung fehlt. Bitte die Seite neu laden.', invalidRequestOrigin: 'Die Sitzung oder Adresse hat sich geändert. Bitte die Seite neu laden.', invalidTarget: 'Dieses Konto darf nicht über EOS zurückgesetzt werden.', accountDisabled: 'Das Konto ist deaktiviert.', accountNotFound: 'Das Benutzerkonto wurde nicht gefunden.', passwordApiUnavailable: 'Die EOS-Passwortverwaltung ist nicht verfügbar.', passwordNotPersisted: 'Das Startpasswort wurde nicht gespeichert.', passwordVerificationFailed: 'Das Startpasswort wurde geschrieben, aber nicht als gültige Anmeldung bestätigt.', passwordMetadataNotPersisted: 'Der Erstanmeldungsstatus wurde nicht gespeichert.', accountResetFailed: 'Das Konto konnte serverseitig nicht zurückgesetzt werden.' },
|
|
26
27
|
},
|
|
27
28
|
en: {
|
|
28
29
|
launcher: 'Accounts', titleAdmin: 'Accounts & roles', titleInstaller: 'End-user accounts',
|
|
@@ -30,10 +31,11 @@
|
|
|
30
31
|
introInstaller: 'You may reset end-user accounts. Admin and installer accounts remain reserved for NexoWatt Service.',
|
|
31
32
|
loading: 'Loading accounts …', empty: 'No matching accounts found.', close: 'Close', refresh: 'Refresh',
|
|
32
33
|
installer: 'Installer', enduser: 'End user / Guest', active: 'Password active', first: 'First activation open', disabled: 'Disabled',
|
|
33
|
-
reset: 'Reset password', resetting: 'Resetting …', resetConfirm: name => `Reset account “${name}”? The
|
|
34
|
-
resetDone: name => `Account “${name}” was reset
|
|
35
|
-
resetFailed: 'The account could not be reset.',
|
|
34
|
+
reset: 'Reset password', resetting: 'Resetting …', resetConfirm: name => `Reset account “${name}”? The one-time initial password will be “nexowatt”; a personal password must then be created immediately.`,
|
|
35
|
+
resetDone: name => `Account “${name}” was reset in the EOS user account to “nexowatt”. The next sign-in requires an immediate personal password change.`,
|
|
36
|
+
resetFailed: 'The account could not be reset.', lastSet: 'Last password setup', lastReset: 'Last reset', by: 'by',
|
|
36
37
|
security: 'Admin/Service may reset installer and end-user accounts. Installers may reset end-user accounts only.',
|
|
38
|
+
errors: { permissionError: 'Admin/Installer permission could not be confirmed. Sign in again.', invalidRequest: 'The security confirmation is missing. Reload the page.', invalidRequestOrigin: 'The session or address changed. Reload the page.', invalidTarget: 'This account cannot be reset through EOS.', accountDisabled: 'The account is disabled.', accountNotFound: 'The user account was not found.', passwordApiUnavailable: 'The EOS password service is unavailable.', passwordNotPersisted: 'The initial password was not saved.', passwordVerificationFailed: 'The initial password was written but could not be verified.', passwordMetadataNotPersisted: 'The first-login status was not saved.', accountResetFailed: 'The account could not be reset on the server.' },
|
|
37
39
|
},
|
|
38
40
|
nl: {
|
|
39
41
|
launcher: 'Toegangen', titleAdmin: 'Toegangen & rollen', titleInstaller: 'Eindgebruikerstoegangen',
|
|
@@ -41,26 +43,15 @@
|
|
|
41
43
|
introInstaller: 'Je kunt eindgebruikersaccounts resetten. Admin- en installateurstoegang blijft voor NexoWatt Service.',
|
|
42
44
|
loading: 'Toegangen worden geladen …', empty: 'Geen passende accounts gevonden.', close: 'Sluiten', refresh: 'Vernieuwen',
|
|
43
45
|
installer: 'Installateur', enduser: 'Eindgebruiker / Guest', active: 'Wachtwoord actief', first: 'Eerste activering open', disabled: 'Uitgeschakeld',
|
|
44
|
-
reset: 'Wachtwoord resetten', resetting: 'Wordt gereset …', resetConfirm: name => `Account „${name}“ resetten?
|
|
45
|
-
resetDone: name => `Account „${name}“ is
|
|
46
|
-
resetFailed: 'Het account kon niet worden gereset.',
|
|
46
|
+
reset: 'Wachtwoord resetten', resetting: 'Wordt gereset …', resetConfirm: name => `Account „${name}“ resetten? Eenmalig geldt het startwachtwoord „nexowatt“, waarna direct een persoonlijk wachtwoord moet worden ingesteld.`,
|
|
47
|
+
resetDone: name => `Account „${name}“ is in het EOS-gebruikersaccount teruggezet op „nexowatt“. Bij de volgende aanmelding moet direct een persoonlijk wachtwoord worden ingesteld.`,
|
|
48
|
+
resetFailed: 'Het account kon niet worden gereset.', lastSet: 'Laatste wachtwoordinstelling', lastReset: 'Laatste reset', by: 'door',
|
|
47
49
|
security: 'Admin/service mag installateur- en eindgebruikersaccounts resetten. Installateurs alleen eindgebruikers.',
|
|
50
|
+
errors: { permissionError: 'De Admin-/Installateurrechten konden niet worden bevestigd. Meld opnieuw aan.', invalidRequest: 'De beveiligingsbevestiging ontbreekt. Herlaad de pagina.', invalidRequestOrigin: 'De sessie of het adres is gewijzigd. Herlaad de pagina.', invalidTarget: 'Dit account kan niet via EOS worden gereset.', accountDisabled: 'Het account is uitgeschakeld.', accountNotFound: 'Het gebruikersaccount is niet gevonden.', passwordApiUnavailable: 'De EOS-wachtwoordservice is niet beschikbaar.', passwordNotPersisted: 'Het startwachtwoord is niet opgeslagen.', passwordVerificationFailed: 'Het startwachtwoord is geschreven maar kon niet worden geverifieerd.', passwordMetadataNotPersisted: 'De eerste-aanmeldstatus is niet opgeslagen.', accountResetFailed: 'Het account kon server-side niet worden gereset.' },
|
|
48
51
|
},
|
|
49
52
|
};
|
|
50
53
|
const t = () => i18n[language()];
|
|
51
|
-
const
|
|
52
|
-
const text = t();
|
|
53
|
-
return ({
|
|
54
|
-
permissionError: text.permissionError,
|
|
55
|
-
invalidRequestOrigin: text.originError,
|
|
56
|
-
accountDisabled: text.disabledError,
|
|
57
|
-
accountNotFound: text.notFoundError,
|
|
58
|
-
passwordApiUnavailable: text.writeError,
|
|
59
|
-
passwordWriteFailed: text.writeError,
|
|
60
|
-
passwordVerificationFailed: text.writeError,
|
|
61
|
-
userObjectUnavailableAfterPasswordChange: text.writeError,
|
|
62
|
-
})[String(code || '')] || text.resetFailed;
|
|
63
|
-
};
|
|
54
|
+
const errorText = code => t().errors?.[String(code || '')] || String(code || t().resetFailed);
|
|
64
55
|
const roleFromPolicy = policy => {
|
|
65
56
|
const raw = normalize(policy?.role || '');
|
|
66
57
|
if (policy?.isAdmin || policy?.isEosAdminGroup || /admin|service/.test(raw)) return 'admin';
|
|
@@ -157,17 +148,17 @@
|
|
|
157
148
|
reset.disabled = true; reset.textContent = t().resetting; setStatus('', '');
|
|
158
149
|
try {
|
|
159
150
|
const response = await fetch(new URL('nexowatt/account/reset', base).href, {
|
|
160
|
-
method: 'POST', credentials: '
|
|
151
|
+
method: 'POST', credentials: 'same-origin', cache: 'no-store',
|
|
161
152
|
headers: { 'Content-Type': 'application/json', Accept: 'application/json', 'X-NexoWatt-EOS-Account-Reset': '1' },
|
|
162
153
|
body: JSON.stringify({ user: account.id }),
|
|
163
154
|
});
|
|
164
155
|
const payload = await response.json().catch(() => ({}));
|
|
165
|
-
if (!response.ok || payload.error) throw new Error(
|
|
156
|
+
if (!response.ok || payload.error) throw new Error(payload.error || t().resetFailed);
|
|
166
157
|
setStatus(t().resetDone(label), 'success');
|
|
167
158
|
await loadAccounts();
|
|
168
159
|
} catch (error) {
|
|
169
160
|
reset.disabled = false; reset.textContent = t().reset;
|
|
170
|
-
setStatus(error?.message
|
|
161
|
+
setStatus(errorText(error?.message), 'error');
|
|
171
162
|
}
|
|
172
163
|
});
|
|
173
164
|
actions.appendChild(reset);
|
|
@@ -189,12 +180,12 @@
|
|
|
189
180
|
if (!state.overlay) return;
|
|
190
181
|
setStatus(t().loading, '');
|
|
191
182
|
try {
|
|
192
|
-
const response = await fetch(new URL('nexowatt/account/manage', base).href, { credentials: '
|
|
183
|
+
const response = await fetch(new URL('nexowatt/account/manage', base).href, { credentials: 'same-origin', cache: 'no-store', headers: { Accept: 'application/json' } });
|
|
193
184
|
const data = await response.json().catch(() => ({}));
|
|
194
185
|
if (!response.ok || data.error) throw new Error(data.error || t().resetFailed);
|
|
195
186
|
renderAccounts(data); setStatus('', '');
|
|
196
187
|
} catch (error) {
|
|
197
|
-
setStatus(error?.message
|
|
188
|
+
setStatus(errorText(error?.message), 'error');
|
|
198
189
|
}
|
|
199
190
|
};
|
|
200
191
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
(() => {
|
|
2
2
|
'use strict';
|
|
3
|
-
// NexoWatt EOS stable 7.9.
|
|
3
|
+
// NexoWatt EOS stable 7.9.100: EOS Assist remains disabled until the
|
|
4
4
|
// read-only system assistant is rebuilt and accepted separately.
|
|
5
5
|
window.NEXOWATT_EOS_ASSIST_DISABLED = true;
|
|
6
6
|
document.documentElement.classList.add('eos-assist-disabled');
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
(() => {
|
|
2
|
+
'use strict';
|
|
3
|
+
const VERSION = 'v100-nexowatt-stable-auto-update';
|
|
4
|
+
const ROOT_ID = 'eos-nexowatt-auto-update';
|
|
5
|
+
const script = document.currentScript;
|
|
6
|
+
const API = new URL('../nexowatt/updates/', script?.src || document.baseURI).href;
|
|
7
|
+
let busy = false;
|
|
8
|
+
let lastLoad = 0;
|
|
9
|
+
let pollTimer = 0;
|
|
10
|
+
|
|
11
|
+
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 => ({'&':'&','<':'<','>':'>',"'":''','"':'"'}[char]));
|
|
14
|
+
const formatTime = value => value ? new Intl.DateTimeFormat(undefined, { dateStyle: 'short', timeStyle: 'medium' }).format(new Date(value)) : '–';
|
|
15
|
+
|
|
16
|
+
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');
|
|
21
|
+
root.id = ROOT_ID;
|
|
22
|
+
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);
|
|
28
|
+
return root;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
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 || '');
|
|
36
|
+
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>`;
|
|
38
|
+
root.querySelector('input')?.addEventListener('change', event => void save(Boolean(event.currentTarget.checked)));
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const request = async (path, options = {}) => {
|
|
42
|
+
const response = await fetch(new URL(path, API), { cache: 'no-store', credentials: 'same-origin', ...options });
|
|
43
|
+
const data = await response.json().catch(() => ({}));
|
|
44
|
+
if (!response.ok) throw new Error(data.error || `HTTP ${response.status}`);
|
|
45
|
+
return data;
|
|
46
|
+
};
|
|
47
|
+
const load = async (force = false) => {
|
|
48
|
+
if (role() !== 'admin' || !introActive()) return;
|
|
49
|
+
if (!force && Date.now() - lastLoad < 5000) return;
|
|
50
|
+
lastLoad = Date.now();
|
|
51
|
+
try { render(await request('status')); }
|
|
52
|
+
catch (error) { render({ enabled: true, error: error?.message || error }); }
|
|
53
|
+
};
|
|
54
|
+
const save = async enabled => {
|
|
55
|
+
if (busy) return;
|
|
56
|
+
busy = true;
|
|
57
|
+
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);
|
|
61
|
+
} catch (error) {
|
|
62
|
+
render({ enabled: !enabled, error: error?.message || error });
|
|
63
|
+
} finally { busy = false; }
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
let scheduled = false;
|
|
67
|
+
const schedule = () => { if (scheduled) return; scheduled = true; requestAnimationFrame(() => { scheduled = false; void load(); }); };
|
|
68
|
+
const start = () => {
|
|
69
|
+
void load();
|
|
70
|
+
window.addEventListener('hashchange', () => { lastLoad = 0; schedule(); });
|
|
71
|
+
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 });
|
|
74
|
+
pollTimer = window.setInterval(() => void load(true), 30000);
|
|
75
|
+
};
|
|
76
|
+
document.readyState === 'loading' ? document.addEventListener('DOMContentLoaded', start, { once: true }) : start();
|
|
77
|
+
window.NEXOWATT_EOS_AUTO_UPDATE_UI = Object.freeze({ version: VERSION, refresh: load });
|
|
78
|
+
})();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
(() => {
|
|
2
2
|
'use strict';
|
|
3
|
-
const VERSION = '
|
|
3
|
+
const VERSION = 'v100-nexowatt-ems-overview-runtime';
|
|
4
4
|
const CONTRACT = 'nexowatt-ems-overview-v1';
|
|
5
5
|
const ROOT_ID = 'eos-ems-overview-runtime';
|
|
6
6
|
const POLL_MS = 5000;
|
|
@@ -161,11 +161,10 @@
|
|
|
161
161
|
if (tariff.available) tags.push({ label: `Tarif: ${text(tariff.state, 'aktiv')}${tariff.priceEurPerKwh != null ? ` · ${num(tariff.priceEurPerKwh).toFixed(3)} €/kWh` : ''}` });
|
|
162
162
|
if (forecast.available) tags.push({ label: `Prognose: ${text(forecast.source, 'aktiv')}${forecast.fresh === false ? ' · veraltet' : ''}`, warning: forecast.fresh === false });
|
|
163
163
|
if (ems.safetyActive || ems.safetyEmergencyStop || ems.safetyValid === false) tags.push({ label: 'EOS Safety aktiv', warning: true });
|
|
164
|
-
const detailsPort = Math.max(1, num(summary.details?.port, 8188)); const detailsPath = text(summary.details?.path, '/ems-apps.html?tab=status'); const detailsUrl = `${location.protocol}//${location.hostname}:${detailsPort}${detailsPath.startsWith('/') ? detailsPath : `/${detailsPath}`}`;
|
|
165
164
|
const metrics = `<div class="eos-ems-overview-metric"><span>EMS-Budget</span><strong>${totalBudget > 0 ? esc(formatPower(totalBudget)) : 'nicht begrenzt'}</strong><small>Rest ${esc(formatPower(remaining))}</small></div><div class="eos-ems-overview-metric"><span>Lademanagement</span><strong>${esc(formatPower(charging.actualW))} / ${esc(formatPower(charging.targetW))}</strong><small>${num(charging.activeCount)} lädt · ${num(charging.waitingCount)} wartet</small></div>${storage.available ? `<div class="eos-ems-overview-metric"><span>Speicher ${esc(text(storage.topology))}</span><strong>${esc(formatPower(storage.actualW))} / ${esc(formatPower(storage.targetW))}</strong><small>${storage.socPct != null ? `SoC ${Math.round(num(storage.socPct))} %` : esc(text(storage.reason, 'aktiv'))}</small></div>` : `<div class="eos-ems-overview-metric"><span>PV-Budget</span><strong>${esc(formatPower(budget.pvBudgetW))}</strong><small>PV-Rest ${esc(formatPower(budget.remainingPvW))}</small></div>`}<div class="eos-ems-overview-metric"><span>Letzter Regeltick</span><strong>${esc(formatAge(ems.lastTickTs || data.updatedAt))}</strong><small>${isTechnical ? `Zyklus ${num(ems.cycleMs)} ms` : esc(text(ems.decision, 'EMS aktiv'))}</small></div>`;
|
|
166
165
|
const decisionsHtml = decisions.length ? decisions.map(item => `<div class="eos-ems-overview-decision eos-ems-overview-decision--${severity(item.severity)}"><strong>${esc(text(item.title, 'EMS-Entscheidung'))}</strong><span>${esc(text(item.reason, 'Keine zusätzliche Begrenzung'))}</span>${isTechnical && item.details ? `<small>${esc(item.details)}</small>` : ''}</div>`).join('') : '<div class="eos-ems-overview-empty">Keine aktiven Regelentscheidungen. EOS arbeitet mit den aktuellen Messwerten.</div>';
|
|
167
166
|
const eventsHtml = isTechnical ? (events.length ? events.map(item => `<div class="eos-ems-overview-event eos-ems-overview-event--${severity(item.severity)}"><time>${esc(formatClock(item.ts))}</time><div><strong>${esc(text(item.title, 'EMS'))}</strong><span>${esc(text(item.message || item.reason, 'Zustand aktualisiert'))}</span></div></div>`).join('') : '<div class="eos-ems-overview-empty">Noch keine Zustandsänderung im begrenzten Ereignispuffer.</div>') : `<div class="eos-ems-overview-decision eos-ems-overview-decision--info"><strong>${esc(text(summary.headline, 'EMS aktiv'))}</strong><span>${esc(text(summary.reason, 'EOS optimiert die Anlage innerhalb aller Sicherheitsgrenzen.'))}</span></div>`;
|
|
168
|
-
root.innerHTML = `<header class="eos-ems-overview-header"><div><span class="eos-ems-overview-eyebrow">NexoWatt EMS · Live-Diagnose</span><h2>${esc(stale ? 'EMS-Diagnose ist nicht aktuell' : text(summary.headline, 'EMS arbeitet normal'))}</h2><p>${esc(stale ? 'Adapter oder Diagnosewerte sind älter als 20 Sekunden. Die Werte werden als veraltet markiert.' : text(summary.reason, 'EMS arbeitet innerhalb aller aktiven Grenzen.'))}</p></div><div class="eos-ems-overview-state"><span class="eos-ems-overview-state-dot"></span><strong>${stale ? 'Offline / veraltet' : ({ok:'Normal',info:'Information',warning:'Begrenzt',error:'Störung'}[status])}</strong><small>${esc(data.instance)} · ${esc(formatAge(data.updatedAt))}</small></div></header><div class="eos-ems-overview-metrics">${metrics}</div>${totalBudget > 0 ? `<div class="eos-ems-overview-budget" title="${percent.toFixed(1)} % Restbudget"><span style="width:${percent}%"></span></div>` : ''}${tags.length ? `<div class="eos-ems-overview-tags">${tags.map(tag => `<span class="${tag.warning ? 'is-warning' : ''}">${esc(tag.label)}</span>`).join('')}</div>` : ''}<div class="eos-ems-overview-columns"><div class="eos-ems-overview-panel"><h3>Aktuelle Regelentscheidungen</h3>${decisionsHtml}</div><div class="eos-ems-overview-panel"><h3>${isTechnical ? 'Letzte EMS-Ereignisse' : 'Systemerklärung'}</h3>${eventsHtml}</div></div><footer class="eos-ems-overview-footer"><span>Diagnose ist rein lesend. Keine Kachel besitzt Schreibhoheit.</span
|
|
167
|
+
root.innerHTML = `<header class="eos-ems-overview-header"><div><span class="eos-ems-overview-eyebrow">NexoWatt EMS · Live-Diagnose</span><h2>${esc(stale ? 'EMS-Diagnose ist nicht aktuell' : text(summary.headline, 'EMS arbeitet normal'))}</h2><p>${esc(stale ? 'Adapter oder Diagnosewerte sind älter als 20 Sekunden. Die Werte werden als veraltet markiert.' : text(summary.reason, 'EMS arbeitet innerhalb aller aktiven Grenzen.'))}</p></div><div class="eos-ems-overview-state"><span class="eos-ems-overview-state-dot"></span><strong>${stale ? 'Offline / veraltet' : ({ok:'Normal',info:'Information',warning:'Begrenzt',error:'Störung'}[status])}</strong><small>${esc(data.instance)} · ${esc(formatAge(data.updatedAt))}</small></div></header><div class="eos-ems-overview-metrics">${metrics}</div>${totalBudget > 0 ? `<div class="eos-ems-overview-budget" title="${percent.toFixed(1)} % Restbudget"><span style="width:${percent}%"></span></div>` : ''}${tags.length ? `<div class="eos-ems-overview-tags">${tags.map(tag => `<span class="${tag.warning ? 'is-warning' : ''}">${esc(tag.label)}</span>`).join('')}</div>` : ''}<div class="eos-ems-overview-columns"><div class="eos-ems-overview-panel"><h3>Aktuelle Regelentscheidungen</h3>${decisionsHtml}</div><div class="eos-ems-overview-panel"><h3>${isTechnical ? 'Letzte EMS-Ereignisse' : 'Systemerklärung'}</h3>${eventsHtml}</div></div><footer class="eos-ems-overview-footer"><span>Diagnose ist rein lesend. Keine Kachel besitzt Schreibhoheit.</span></footer>`;
|
|
169
168
|
};
|
|
170
169
|
|
|
171
170
|
const scheduleFastRetry = () => {
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
(() => {
|
|
2
|
+
'use strict';
|
|
3
|
+
const RELEASE = '7.9.100';
|
|
4
|
+
const RELEASE_KEY = 'nexowatt.eos.loadedRelease';
|
|
5
|
+
const RELOAD_KEY = 'nexowatt.eos.reloadTarget';
|
|
6
|
+
const CHECK_MS = 15000;
|
|
7
|
+
let refreshing = false;
|
|
8
|
+
|
|
9
|
+
const normalize = value => String(value || '').trim().replace(/^v/i, '');
|
|
10
|
+
const validRelease = value => /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(normalize(value));
|
|
11
|
+
const reloadUrl = version => {
|
|
12
|
+
const url = new URL(window.location.href);
|
|
13
|
+
url.searchParams.set('_eosRelease', normalize(version) || RELEASE);
|
|
14
|
+
url.searchParams.set('_eosReload', String(Date.now()));
|
|
15
|
+
return url.toString();
|
|
16
|
+
};
|
|
17
|
+
const purgeBrowserCaches = async () => {
|
|
18
|
+
const jobs = [];
|
|
19
|
+
try {
|
|
20
|
+
if ('caches' in window) {
|
|
21
|
+
jobs.push(caches.keys().then(keys => Promise.all(keys.map(key => caches.delete(key)))));
|
|
22
|
+
}
|
|
23
|
+
} catch (_) { /* Cache API is optional */ }
|
|
24
|
+
try {
|
|
25
|
+
if (navigator.serviceWorker?.getRegistrations) {
|
|
26
|
+
jobs.push(navigator.serviceWorker.getRegistrations().then(rows => Promise.all(rows.map(row => row.unregister()))));
|
|
27
|
+
}
|
|
28
|
+
} catch (_) { /* service workers may be unavailable */ }
|
|
29
|
+
await Promise.allSettled(jobs);
|
|
30
|
+
};
|
|
31
|
+
const reloadFor = async version => {
|
|
32
|
+
const target = normalize(version) || RELEASE;
|
|
33
|
+
if (refreshing || sessionStorage.getItem(RELOAD_KEY) === target) return;
|
|
34
|
+
refreshing = true;
|
|
35
|
+
sessionStorage.setItem(RELOAD_KEY, target);
|
|
36
|
+
document.documentElement.classList.add('eos-release-refreshing');
|
|
37
|
+
await purgeBrowserCaches();
|
|
38
|
+
window.location.replace(reloadUrl(target));
|
|
39
|
+
};
|
|
40
|
+
const checkServerVersion = async () => {
|
|
41
|
+
try {
|
|
42
|
+
const endpoint = new URL('./version', window.location.href);
|
|
43
|
+
endpoint.searchParams.set('_eosTs', String(Date.now()));
|
|
44
|
+
const response = await fetch(endpoint, {
|
|
45
|
+
cache: 'no-store',
|
|
46
|
+
credentials: 'same-origin',
|
|
47
|
+
headers: { 'Cache-Control': 'no-cache' },
|
|
48
|
+
});
|
|
49
|
+
if (!response.ok) return;
|
|
50
|
+
const serverVersion = normalize(await response.text());
|
|
51
|
+
if (validRelease(serverVersion) && serverVersion !== RELEASE) {
|
|
52
|
+
await reloadFor(serverVersion);
|
|
53
|
+
}
|
|
54
|
+
} catch (_) { /* adapter restart or temporary network interruption */ }
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
window.NEXOWATT_EOS_RELEASE = RELEASE;
|
|
58
|
+
const previous = normalize(localStorage.getItem(RELEASE_KEY));
|
|
59
|
+
localStorage.setItem(RELEASE_KEY, RELEASE);
|
|
60
|
+
const loadedFor = normalize(new URL(window.location.href).searchParams.get('_eosRelease'));
|
|
61
|
+
if (previous && previous !== RELEASE && loadedFor !== RELEASE) {
|
|
62
|
+
void reloadFor(RELEASE);
|
|
63
|
+
} else {
|
|
64
|
+
sessionStorage.removeItem(RELOAD_KEY);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
window.addEventListener('focus', () => void checkServerVersion());
|
|
68
|
+
document.addEventListener('visibilitychange', () => {
|
|
69
|
+
if (document.visibilityState === 'visible') void checkServerVersion();
|
|
70
|
+
});
|
|
71
|
+
window.setInterval(() => void checkServerVersion(), CHECK_MS);
|
|
72
|
+
window.NEXOWATT_EOS_RELEASE_WATCH = Object.freeze({
|
|
73
|
+
release: RELEASE,
|
|
74
|
+
check: checkServerVersion,
|
|
75
|
+
purge: purgeBrowserCaches,
|
|
76
|
+
});
|
|
77
|
+
})();
|