iobroker.eos-admin 7.9.30 → 7.9.32
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/adminWww/css/eos-branding.css +210 -0
- package/adminWww/index.html +4 -4
- package/adminWww/js/eos-assistant.js +2 -2
- package/adminWww/js/eos-branding.js +156 -26
- package/adminWww/js/eos-security-ui.js +18 -1
- package/docs/NEXOWATT_EOS_UI_V31_NAV_SECURITY_ASSIST_DE.md +12 -0
- package/io-package.json +898 -890
- package/package.json +143 -143
|
@@ -3022,3 +3022,213 @@ html:not(.eos-security-admin):not(.eos-security-admin-user) [data-eos-security-a
|
|
|
3022
3022
|
html.eos-app .eos-native-drawer-header { left: 4px !important; }
|
|
3023
3023
|
.eos-assist-root { right: 14px !important; bottom: 16px !important; }
|
|
3024
3024
|
}
|
|
3025
|
+
|
|
3026
|
+
|
|
3027
|
+
/* === NexoWatt EOS v31: clean arrow tile, security text repair, AI-ready Assist === */
|
|
3028
|
+
:root { --eos-nav-toggle-slot: 82px; }
|
|
3029
|
+
html.eos-app .MuiDrawer-paper,
|
|
3030
|
+
html.eos-app .eos-drawer {
|
|
3031
|
+
padding-left: var(--eos-nav-toggle-slot) !important;
|
|
3032
|
+
}
|
|
3033
|
+
html.eos-app.eos-nav-compact .MuiDrawer-paper,
|
|
3034
|
+
html.eos-app.eos-nav-compact .eos-drawer {
|
|
3035
|
+
padding-left: var(--eos-nav-toggle-slot) !important;
|
|
3036
|
+
transform: none !important;
|
|
3037
|
+
visibility: visible !important;
|
|
3038
|
+
opacity: 1 !important;
|
|
3039
|
+
}
|
|
3040
|
+
html.eos-app .eos-native-drawer-header,
|
|
3041
|
+
html.eos-app .eos-nav-toggle-shell {
|
|
3042
|
+
position: absolute !important;
|
|
3043
|
+
left: 14px !important;
|
|
3044
|
+
top: 50% !important;
|
|
3045
|
+
transform: translateY(-50%) !important;
|
|
3046
|
+
width: 54px !important;
|
|
3047
|
+
min-width: 54px !important;
|
|
3048
|
+
max-width: 54px !important;
|
|
3049
|
+
height: 54px !important;
|
|
3050
|
+
min-height: 54px !important;
|
|
3051
|
+
max-height: 54px !important;
|
|
3052
|
+
padding: 0 !important;
|
|
3053
|
+
margin: 0 !important;
|
|
3054
|
+
display: grid !important;
|
|
3055
|
+
place-items: center !important;
|
|
3056
|
+
border-radius: 18px !important;
|
|
3057
|
+
background: linear-gradient(135deg, rgba(0, 0, 0, 0.84), rgba(0, 255, 136, 0.13)) !important;
|
|
3058
|
+
border: 1px solid rgba(0, 255, 136, 0.50) !important;
|
|
3059
|
+
box-shadow: inset 0 1px 0 rgba(255,255,255,.06), 0 0 18px rgba(0,255,136,.20) !important;
|
|
3060
|
+
overflow: visible !important;
|
|
3061
|
+
}
|
|
3062
|
+
html.eos-app .eos-native-drawer-header > div:first-child,
|
|
3063
|
+
html.eos-app .eos-nav-toggle-shell > div:first-child {
|
|
3064
|
+
width: 54px !important;
|
|
3065
|
+
min-width: 54px !important;
|
|
3066
|
+
max-width: 54px !important;
|
|
3067
|
+
height: 54px !important;
|
|
3068
|
+
min-height: 54px !important;
|
|
3069
|
+
max-height: 54px !important;
|
|
3070
|
+
padding: 0 !important;
|
|
3071
|
+
margin: 0 !important;
|
|
3072
|
+
display: grid !important;
|
|
3073
|
+
place-items: center !important;
|
|
3074
|
+
grid-template-columns: 1fr !important;
|
|
3075
|
+
gap: 0 !important;
|
|
3076
|
+
}
|
|
3077
|
+
html.eos-app .eos-native-drawer-header a,
|
|
3078
|
+
html.eos-app .eos-native-drawer-header img,
|
|
3079
|
+
html.eos-app .eos-native-drawer-header .MuiAvatar-root,
|
|
3080
|
+
html.eos-app .eos-native-drawer-header .MuiAvatar-img,
|
|
3081
|
+
html.eos-app .eos-native-drawer-header .eos-native-title,
|
|
3082
|
+
html.eos-app .eos-nav-toggle-decor-hidden {
|
|
3083
|
+
display: none !important;
|
|
3084
|
+
visibility: hidden !important;
|
|
3085
|
+
opacity: 0 !important;
|
|
3086
|
+
pointer-events: none !important;
|
|
3087
|
+
}
|
|
3088
|
+
html.eos-app .eos-native-drawer-header button.eos-nav-compact-toggle,
|
|
3089
|
+
html.eos-app .eos-native-drawer-header .eos-nav-compact-toggle,
|
|
3090
|
+
html.eos-app .eos-native-drawer-header button,
|
|
3091
|
+
html.eos-app .eos-native-drawer-header .MuiIconButton-root {
|
|
3092
|
+
position: static !important;
|
|
3093
|
+
transform: none !important;
|
|
3094
|
+
margin: 0 !important;
|
|
3095
|
+
width: 42px !important;
|
|
3096
|
+
height: 42px !important;
|
|
3097
|
+
min-width: 42px !important;
|
|
3098
|
+
min-height: 42px !important;
|
|
3099
|
+
display: grid !important;
|
|
3100
|
+
place-items: center !important;
|
|
3101
|
+
border-radius: 15px !important;
|
|
3102
|
+
background: radial-gradient(circle at 50% 50%, rgba(0,255,136,.24), rgba(0,0,0,.88) 72%) !important;
|
|
3103
|
+
border: 1px solid rgba(110,255,211,.58) !important;
|
|
3104
|
+
color: #eefcff !important;
|
|
3105
|
+
box-shadow: 0 0 16px rgba(0,255,136,.28), inset 0 0 12px rgba(0,255,136,.12) !important;
|
|
3106
|
+
}
|
|
3107
|
+
html.eos-app .eos-native-drawer-header button.eos-nav-compact-toggle svg,
|
|
3108
|
+
html.eos-app .eos-native-drawer-header .eos-nav-compact-toggle svg,
|
|
3109
|
+
html.eos-app .eos-native-drawer-header .MuiSvgIcon-root {
|
|
3110
|
+
width: 22px !important;
|
|
3111
|
+
height: 22px !important;
|
|
3112
|
+
}
|
|
3113
|
+
html.eos-app .eos-hidden-logout,
|
|
3114
|
+
html.eos-app .eos-hidden-logout *,
|
|
3115
|
+
html.eos-app .eos-native-logout-hidden,
|
|
3116
|
+
html.eos-app .eos-native-logout-hidden * {
|
|
3117
|
+
display: none !important;
|
|
3118
|
+
visibility: hidden !important;
|
|
3119
|
+
opacity: 0 !important;
|
|
3120
|
+
pointer-events: none !important;
|
|
3121
|
+
width: 0 !important;
|
|
3122
|
+
min-width: 0 !important;
|
|
3123
|
+
max-width: 0 !important;
|
|
3124
|
+
padding: 0 !important;
|
|
3125
|
+
margin: 0 !important;
|
|
3126
|
+
border: 0 !important;
|
|
3127
|
+
}
|
|
3128
|
+
.eos-assist-panel { max-height: min(690px, calc(100vh - 150px)) !important; }
|
|
3129
|
+
.eos-assist-answer { white-space: pre-line !important; }
|
|
3130
|
+
.eos-assist-foot strong { color: #00ff88; }
|
|
3131
|
+
@media (max-width: 720px) {
|
|
3132
|
+
:root { --eos-nav-toggle-slot: 72px; }
|
|
3133
|
+
html.eos-app .eos-native-drawer-header,
|
|
3134
|
+
html.eos-app .eos-nav-toggle-shell { left: 10px !important; width: 48px !important; min-width: 48px !important; max-width: 48px !important; height: 48px !important; min-height: 48px !important; }
|
|
3135
|
+
html.eos-app .eos-native-drawer-header > div:first-child { width: 48px !important; min-width: 48px !important; max-width: 48px !important; height: 48px !important; min-height: 48px !important; }
|
|
3136
|
+
html.eos-app .eos-native-drawer-header button.eos-nav-compact-toggle,
|
|
3137
|
+
html.eos-app .eos-native-drawer-header .eos-nav-compact-toggle,
|
|
3138
|
+
html.eos-app .eos-native-drawer-header button,
|
|
3139
|
+
html.eos-app .eos-native-drawer-header .MuiIconButton-root { width: 38px !important; height: 38px !important; min-width: 38px !important; min-height: 38px !important; }
|
|
3140
|
+
}
|
|
3141
|
+
|
|
3142
|
+
|
|
3143
|
+
/* === NexoWatt EOS v32: keep arrow button, remove empty left tile ============
|
|
3144
|
+
The previous patch hid the native arrow together with the decorative tile on
|
|
3145
|
+
some render paths. This patch removes only the empty native tile and creates
|
|
3146
|
+
one independent compact-toggle button, positioned clearly left of the menu.
|
|
3147
|
+
The navigation still has two states only: normal and compact. It never fully
|
|
3148
|
+
disappears. */
|
|
3149
|
+
:root { --eos-nav-toggle-slot: 78px; }
|
|
3150
|
+
html.eos-app .MuiDrawer-paper,
|
|
3151
|
+
html.eos-app .eos-drawer,
|
|
3152
|
+
html.eos-app.eos-nav-compact .MuiDrawer-paper,
|
|
3153
|
+
html.eos-app.eos-nav-compact .eos-drawer {
|
|
3154
|
+
padding-left: var(--eos-nav-toggle-slot) !important;
|
|
3155
|
+
transform: none !important;
|
|
3156
|
+
visibility: visible !important;
|
|
3157
|
+
opacity: 1 !important;
|
|
3158
|
+
}
|
|
3159
|
+
|
|
3160
|
+
/* Hide the decorative native tile only. The real button is injected below. */
|
|
3161
|
+
html.eos-app .eos-native-drawer-header,
|
|
3162
|
+
html.eos-app .eos-nav-toggle-shell {
|
|
3163
|
+
display: none !important;
|
|
3164
|
+
visibility: hidden !important;
|
|
3165
|
+
opacity: 0 !important;
|
|
3166
|
+
pointer-events: none !important;
|
|
3167
|
+
width: 0 !important;
|
|
3168
|
+
min-width: 0 !important;
|
|
3169
|
+
max-width: 0 !important;
|
|
3170
|
+
height: 0 !important;
|
|
3171
|
+
min-height: 0 !important;
|
|
3172
|
+
max-height: 0 !important;
|
|
3173
|
+
padding: 0 !important;
|
|
3174
|
+
margin: 0 !important;
|
|
3175
|
+
border: 0 !important;
|
|
3176
|
+
box-shadow: none !important;
|
|
3177
|
+
background: transparent !important;
|
|
3178
|
+
overflow: hidden !important;
|
|
3179
|
+
}
|
|
3180
|
+
|
|
3181
|
+
html.eos-app .eos-standalone-nav-toggle {
|
|
3182
|
+
position: fixed !important;
|
|
3183
|
+
left: 24px !important;
|
|
3184
|
+
top: calc(var(--eos-shell-top) + var(--eos-header-height) + var(--eos-nav-gap) + 14px) !important;
|
|
3185
|
+
z-index: 2500 !important;
|
|
3186
|
+
width: 44px !important;
|
|
3187
|
+
height: 44px !important;
|
|
3188
|
+
min-width: 44px !important;
|
|
3189
|
+
min-height: 44px !important;
|
|
3190
|
+
display: grid !important;
|
|
3191
|
+
place-items: center !important;
|
|
3192
|
+
border-radius: 16px !important;
|
|
3193
|
+
border: 1px solid rgba(110, 255, 211, 0.65) !important;
|
|
3194
|
+
background: radial-gradient(circle at 50% 50%, rgba(0,255,136,.22), rgba(0,0,0,.92) 74%) !important;
|
|
3195
|
+
color: #f4fffb !important;
|
|
3196
|
+
box-shadow: 0 0 16px rgba(0,255,136,.28), inset 0 0 12px rgba(0,255,136,.12) !important;
|
|
3197
|
+
cursor: pointer !important;
|
|
3198
|
+
outline: none !important;
|
|
3199
|
+
padding: 0 !important;
|
|
3200
|
+
margin: 0 !important;
|
|
3201
|
+
appearance: none !important;
|
|
3202
|
+
-webkit-appearance: none !important;
|
|
3203
|
+
}
|
|
3204
|
+
html.eos-app .eos-standalone-nav-toggle:hover,
|
|
3205
|
+
html.eos-app .eos-standalone-nav-toggle:focus-visible {
|
|
3206
|
+
border-color: rgba(0, 255, 136, .95) !important;
|
|
3207
|
+
box-shadow: 0 0 22px rgba(0,255,136,.42), inset 0 0 16px rgba(0,255,136,.18) !important;
|
|
3208
|
+
transform: translateY(-1px) !important;
|
|
3209
|
+
}
|
|
3210
|
+
html.eos-app .eos-standalone-nav-toggle svg {
|
|
3211
|
+
width: 22px !important;
|
|
3212
|
+
height: 22px !important;
|
|
3213
|
+
display: block !important;
|
|
3214
|
+
transition: transform .16s ease !important;
|
|
3215
|
+
}
|
|
3216
|
+
html.eos-app.eos-nav-compact .eos-standalone-nav-toggle svg {
|
|
3217
|
+
transform: rotate(180deg) !important;
|
|
3218
|
+
}
|
|
3219
|
+
html.eos-login .eos-standalone-nav-toggle,
|
|
3220
|
+
html:not(.eos-app) .eos-standalone-nav-toggle {
|
|
3221
|
+
display: none !important;
|
|
3222
|
+
}
|
|
3223
|
+
@media (max-width: 720px) {
|
|
3224
|
+
:root { --eos-nav-toggle-slot: 66px; }
|
|
3225
|
+
html.eos-app .eos-standalone-nav-toggle {
|
|
3226
|
+
left: 14px !important;
|
|
3227
|
+
top: calc(var(--eos-shell-top) + var(--eos-header-height) + var(--eos-nav-gap) + 10px) !important;
|
|
3228
|
+
width: 40px !important;
|
|
3229
|
+
height: 40px !important;
|
|
3230
|
+
min-width: 40px !important;
|
|
3231
|
+
min-height: 40px !important;
|
|
3232
|
+
border-radius: 14px !important;
|
|
3233
|
+
}
|
|
3234
|
+
}
|
package/adminWww/index.html
CHANGED
|
@@ -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=
|
|
34
|
+
<link rel="stylesheet" href="./css/eos-branding.css?v=32" />
|
|
35
35
|
<link
|
|
36
36
|
rel="manifest"
|
|
37
37
|
href="manifest.json"
|
|
@@ -154,9 +154,9 @@
|
|
|
154
154
|
<script type="module" crossorigin src="./assets/index-CQZugZ1z.js"></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=
|
|
158
|
-
<script defer src="./js/eos-security-ui.js?v=
|
|
159
|
-
<script defer src="./js/eos-assistant.js?v=
|
|
157
|
+
<script defer src="./js/eos-branding.js?v=32"></script>
|
|
158
|
+
<script defer src="./js/eos-security-ui.js?v=32"></script>
|
|
159
|
+
<script defer src="./js/eos-assistant.js?v=32"></script>
|
|
160
160
|
</head>
|
|
161
161
|
<body>
|
|
162
162
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
(() => {
|
|
2
2
|
'use strict';
|
|
3
|
-
//
|
|
3
|
+
// v31: EOS Assist is rendered by eos-branding.js so it can share route/security state.
|
|
4
4
|
// This file stays as a lightweight compatibility hook for cache-safe deployments.
|
|
5
|
-
window.NEXOWATT_EOS_ASSIST_VERSION = '
|
|
5
|
+
window.NEXOWATT_EOS_ASSIST_VERSION = 'v31-integrated-ai-ready';
|
|
6
6
|
})();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
(() => {
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
window.NEXOWATT_EOS_UI_VERSION = '
|
|
4
|
+
window.NEXOWATT_EOS_UI_VERSION = 'v32-nav-toggle-tile-fix';
|
|
5
5
|
|
|
6
6
|
const BRAND = 'NexoWatt EOS';
|
|
7
7
|
const EOS_MEANING = 'Energy Operation System';
|
|
@@ -25,6 +25,29 @@
|
|
|
25
25
|
[/\bioBroker\b/gi, BRAND],
|
|
26
26
|
];
|
|
27
27
|
|
|
28
|
+
|
|
29
|
+
const MOJIBAKE_REPLACEMENTS = new Map(Object.entries({
|
|
30
|
+
'dürfen': 'dürfen', 'Dürfen': 'Dürfen',
|
|
31
|
+
'für': 'für', 'Für': 'Für',
|
|
32
|
+
'müssen': 'müssen', 'Müssen': 'Müssen',
|
|
33
|
+
'können': 'können', 'Können': 'Können',
|
|
34
|
+
'möglich': 'möglich', 'Möglich': 'Möglich',
|
|
35
|
+
'Löschen': 'Löschen', 'löschen': 'löschen',
|
|
36
|
+
'schützen': 'schützen', 'Schützen': 'Schützen',
|
|
37
|
+
'Schützt': 'Schützt', 'schützt': 'schützt',
|
|
38
|
+
'Geschützte': 'Geschützte', 'geschützte': 'geschützte',
|
|
39
|
+
'ausgewählte': 'ausgewählte', 'Ausgewählte': 'Ausgewählte',
|
|
40
|
+
'ändern': 'ändern', 'Ändern': 'Ändern',
|
|
41
|
+
'über': 'über', 'Über': 'Über',
|
|
42
|
+
'Wähle': 'Wähle', 'wähle': 'wähle',
|
|
43
|
+
'öffnen': 'öffnen', 'Öffnen': 'Öffnen',
|
|
44
|
+
'schließen': 'schließen', 'Schließen': 'Schließen',
|
|
45
|
+
'Gerät': 'Gerät', 'gerät': 'gerät',
|
|
46
|
+
'Geräte': 'Geräte', 'geräte': 'geräte',
|
|
47
|
+
'Zugänge': 'Zugänge', 'zugänge': 'zugänge',
|
|
48
|
+
'ß': 'ß', 'Ä': 'Ä', 'Ö': 'Ö', 'Ü': 'Ü', 'ä': 'ä', 'ö': 'ö', 'ü': 'ü'
|
|
49
|
+
}));
|
|
50
|
+
|
|
28
51
|
const EXACT_LABELS = new Map(Object.entries({
|
|
29
52
|
'Admin': BRAND,
|
|
30
53
|
'NEXOWATT': 'NEXOWATT EOS',
|
|
@@ -82,6 +105,9 @@
|
|
|
82
105
|
const replaceBrand = value => {
|
|
83
106
|
if (!value || typeof value !== 'string') return value;
|
|
84
107
|
let next = value;
|
|
108
|
+
for (const [from, to] of MOJIBAKE_REPLACEMENTS) {
|
|
109
|
+
if (next.includes(from)) next = next.split(from).join(to);
|
|
110
|
+
}
|
|
85
111
|
for (const [pattern, replacement] of TEXT_REPLACEMENTS) next = next.replace(pattern, replacement);
|
|
86
112
|
const compact = next.trim();
|
|
87
113
|
if (EXACT_LABELS.has(compact)) next = next.replace(compact, EXACT_LABELS.get(compact));
|
|
@@ -511,17 +537,30 @@
|
|
|
511
537
|
};
|
|
512
538
|
|
|
513
539
|
const hideNativeLogoutNav = () => safe(() => {
|
|
514
|
-
const candidates = Array.from(document.querySelectorAll('.MuiDrawer-paper a, .MuiDrawer-paper button, .MuiDrawer-paper .MuiListItem-root, .MuiDrawer-paper .MuiListItemButton-root, .MuiDrawer-paper [role="button"], nav a, nav button, nav [role="button"]'));
|
|
540
|
+
const candidates = Array.from(document.querySelectorAll('.MuiDrawer-paper a, .MuiDrawer-paper button, .MuiDrawer-paper li, .MuiDrawer-paper .MuiListItem-root, .MuiDrawer-paper .MuiListItemButton-root, .MuiDrawer-paper [role="button"], nav a, nav button, nav li, nav [role="button"]'));
|
|
515
541
|
candidates.forEach(el => {
|
|
516
542
|
const text = normalize(`${el.textContent || ''} ${el.getAttribute?.('aria-label') || ''} ${el.getAttribute?.('title') || ''}`);
|
|
517
543
|
const href = String(el.getAttribute?.('href') || '');
|
|
518
544
|
const isLogout = /(?:^|\b)(abmelden|logout|ra_logout)(?:\b|$)/.test(text) || /(?:^|[/?#])logout(?:[/?#]|$)/i.test(href);
|
|
519
545
|
if (!isLogout) return;
|
|
520
|
-
const
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
546
|
+
const targets = new Set([
|
|
547
|
+
el,
|
|
548
|
+
el.closest('.MuiListItem-root'),
|
|
549
|
+
el.closest('li'),
|
|
550
|
+
el.closest('.MuiListItemButton-root'),
|
|
551
|
+
el.closest('.MuiButtonBase-root'),
|
|
552
|
+
el.closest('a, button, [role="button"]'),
|
|
553
|
+
].filter(Boolean));
|
|
554
|
+
targets.forEach(item => {
|
|
555
|
+
item.classList.add('eos-hidden-logout', 'eos-native-logout-hidden');
|
|
556
|
+
item.setAttribute('aria-hidden', 'true');
|
|
557
|
+
item.setAttribute('tabindex', '-1');
|
|
558
|
+
if (item.style) {
|
|
559
|
+
item.style.display = 'none';
|
|
560
|
+
item.style.visibility = 'hidden';
|
|
561
|
+
item.style.pointerEvents = 'none';
|
|
562
|
+
}
|
|
563
|
+
});
|
|
525
564
|
});
|
|
526
565
|
});
|
|
527
566
|
|
|
@@ -538,7 +577,7 @@
|
|
|
538
577
|
|| directChildren.find(el => !isListLike(el) && el.querySelector && (el.querySelector('button') || el.querySelector('img') || el.querySelector('.MuiAvatar-root')));
|
|
539
578
|
}
|
|
540
579
|
if (header) {
|
|
541
|
-
header.classList.add('eos-native-drawer-header');
|
|
580
|
+
header.classList.add('eos-native-drawer-header', 'eos-nav-toggle-shell');
|
|
542
581
|
const toggleButton = header.querySelector('button, .MuiIconButton-root, [role="button"]');
|
|
543
582
|
if (toggleButton && !toggleButton.dataset.eosNavCompactToggle) {
|
|
544
583
|
toggleButton.dataset.eosNavCompactToggle = 'true';
|
|
@@ -559,6 +598,9 @@
|
|
|
559
598
|
if (event.key === 'Enter' || event.key === ' ') toggleCompact(event);
|
|
560
599
|
}, true);
|
|
561
600
|
}
|
|
601
|
+
header.querySelectorAll('a,img,.MuiAvatar-root,.MuiAvatar-img,.eos-native-title').forEach(el => {
|
|
602
|
+
if (!el.closest?.('button')) el.classList.add('eos-nav-toggle-decor-hidden');
|
|
603
|
+
});
|
|
562
604
|
const img = header.querySelector('img');
|
|
563
605
|
if (img) patchImage(img);
|
|
564
606
|
const avatarImg = header.querySelector('.MuiAvatar-img');
|
|
@@ -808,18 +850,65 @@
|
|
|
808
850
|
};
|
|
809
851
|
};
|
|
810
852
|
|
|
811
|
-
const
|
|
853
|
+
const configuredAssistEndpoint = () => safe(() => {
|
|
854
|
+
const globalEndpoint = typeof window.NEXOWATT_EOS_ASSIST_ENDPOINT === 'string' ? window.NEXOWATT_EOS_ASSIST_ENDPOINT.trim() : '';
|
|
855
|
+
const storedEndpoint = localStorage.getItem('nexowatt:eosAssistEndpoint') || '';
|
|
856
|
+
return globalEndpoint || storedEndpoint.trim();
|
|
857
|
+
}) || '';
|
|
858
|
+
|
|
859
|
+
const localAssistAnswer = query => {
|
|
812
860
|
const q = normalize(query);
|
|
813
|
-
if (!q) return 'Beschreibe kurz, was eingerichtet werden soll, zum Beispiel: PV, Speicher,
|
|
814
|
-
if (/wallbox|evcs|lade|auto|ocpp/.test(q)) return
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
861
|
+
if (!q) return 'Beschreibe kurz, was eingerichtet werden soll, zum Beispiel: Wallbox, PV, Speicher, Modbus, MQTT, Sicherung oder Benutzerrechte. EOS Assist zeigt mehrere Integrationswege und nicht nur einen einzelnen Adapter.';
|
|
862
|
+
if (/wallbox|evcs|lade|auto|ocpp|ladestation|charge/.test(q)) return [
|
|
863
|
+
'Wallbox / Ladepunkt: Es gibt mehrere mögliche Integrationswege. Empfehlung nach Priorität prüfen:',
|
|
864
|
+
'1. Herstelleradapter: nutzen, wenn ein stabiler Adapter für Hersteller/Modell vorhanden ist.',
|
|
865
|
+
'2. Modbus TCP/RTU: bevorzugt für lokale EMS-Werte wie Leistung, Strom, Freigabe, Zählerstand und Phasen.',
|
|
866
|
+
'3. OCPP: passend, wenn die Wallbox als Ladepunkt-Backend angebunden werden soll oder mehrere Ladepunkte zentral verwaltet werden.',
|
|
867
|
+
'4. HTTP/REST oder MQTT: sinnvoll bei offenen APIs, eigener Firmware oder Gateway-Lösungen.',
|
|
868
|
+
'5. Datenpunkt-Mapping: Fallback, wenn Werte bereits aus einem anderen System kommen.',
|
|
869
|
+
'Nächster Schritt: Hersteller, Modell, IP-Adresse und verfügbare Schnittstellen nennen. Dann kann EOS Assist den besten Weg vorschlagen.'
|
|
870
|
+
].join('\n');
|
|
871
|
+
if (/keba|kecontact|mennekes|abl|alfen|easee|heidelberg|go-?e|openwb|zaptec|wallbe|duosida/.test(q)) return [
|
|
872
|
+
'Wallbox-Hersteller erkannt. Bitte nicht automatisch nur OCPP verwenden.',
|
|
873
|
+
'Prüfe zuerst: gibt es einen nativen Adapter oder eine lokale Modbus-/HTTP-Schnittstelle?',
|
|
874
|
+
'OCPP ist gut für Backend-/Ladepunktverwaltung; Modbus/HTTP ist oft besser für lokale EMS-Regelung und schnelle Leistungswerte.',
|
|
875
|
+
'Für EOS empfehle ich: lokale Schnittstelle für Regelung + OCPP nur, wenn Backend-Funktionen gebraucht werden.'
|
|
876
|
+
].join('\n');
|
|
877
|
+
if (/pv|solar|wechselrichter|sun2000|fronius|kostal|sma|huawei|growatt|sungrow/.test(q)) return 'PV/Wechselrichter: Herstelleradapter oder Modbus TCP prüfen. Für EOS sind Erzeugung, Bezug, Einspeisung, Batterieladeleistung und Statusdaten wichtig. Erst Livewerte validieren, dann Optimierung aktivieren.';
|
|
878
|
+
if (/speicher|batterie|akku|soc/.test(q)) return 'Speicher: SoC, Lade-/Entladeleistung, Betriebsmodus und Grenzwerte prüfen. Schreibbefehle erst freigeben, wenn Lese-Datenpunkte stabil und plausibel sind.';
|
|
879
|
+
if (/modbus/.test(q)) return 'Modbus: IP/Port 502, Unit-ID, Registerliste, Datentyp und Byte-Reihenfolge prüfen. Erst nur lesen testen, danach Schreibrechte gezielt und mit Schutzliste freischalten.';
|
|
880
|
+
if (/mqtt/.test(q)) return 'MQTT: Broker, Authentifizierung, Topic-Struktur und TLS prüfen. Mit Test-Topic starten, dann produktive Topics in EOS-Datenpunkte mappen.';
|
|
881
|
+
if (/backup|sicherung|restore|backitup/.test(q)) return 'Sicherung: BackItUp aktiv halten, Zielpfad/Cloud-Ziel testen, Test-Backup erstellen und Restore-Ablauf dokumentieren. BackItUp bleibt Systemadapter und sollte geschützt sein.';
|
|
882
|
+
if (/rechte|benutzer|installateur|kunde|admin|sicherheit|security/.test(q)) return 'Rechte: Administratoren verwalten Systemschutz. Installateure dürfen konfigurieren, aber geschützte Systemadapter nicht löschen, stoppen oder kritisch ändern. Endkunden bekommen Bedien- und Leserechte.';
|
|
883
|
+
if (/fehler|log|offline|404|timeout|startet nicht|server/.test(q)) return 'Fehlersuche: Systemlogs filtern, betroffene Instanz prüfen, letzte Änderung identifizieren, Dienst neu starten und danach Port, WebSocket, Repository und Paketversion kontrollieren.';
|
|
884
|
+
return 'EOS Assist Empfehlung: Gerätetyp, Hersteller/Modell und verfügbare Schnittstellen nennen. Danach wird der beste Integrationsweg gewählt: nativer Adapter, Modbus, OCPP, HTTP/REST, MQTT oder Datenpunkt-Mapping.';
|
|
885
|
+
};
|
|
886
|
+
|
|
887
|
+
const requestRemoteAssist = async query => {
|
|
888
|
+
const endpoint = configuredAssistEndpoint();
|
|
889
|
+
if (!endpoint) return '';
|
|
890
|
+
try {
|
|
891
|
+
const response = await fetch(endpoint, {
|
|
892
|
+
method: 'POST',
|
|
893
|
+
credentials: 'same-origin',
|
|
894
|
+
headers: { 'content-type': 'application/json' },
|
|
895
|
+
body: JSON.stringify({ query, route: window.location.hash || '', ui: 'nexowatt-eos-admin' }),
|
|
896
|
+
});
|
|
897
|
+
if (!response.ok) return '';
|
|
898
|
+
const data = await response.json().catch(() => ({}));
|
|
899
|
+
return String(data.answer || data.text || data.message || '').trim();
|
|
900
|
+
} catch { return ''; }
|
|
901
|
+
};
|
|
902
|
+
|
|
903
|
+
const assistAnswer = query => localAssistAnswer(query);
|
|
904
|
+
|
|
905
|
+
const sendAssistQuestion = async (root, query) => {
|
|
906
|
+
const out = root?.querySelector?.('.eos-assist-answer');
|
|
907
|
+
if (!out) return;
|
|
908
|
+
const endpoint = configuredAssistEndpoint();
|
|
909
|
+
if (endpoint) out.textContent = 'EOS Assist fragt die KI an ...';
|
|
910
|
+
const remote = endpoint ? await requestRemoteAssist(query) : '';
|
|
911
|
+
out.textContent = remote || localAssistAnswer(query);
|
|
823
912
|
};
|
|
824
913
|
|
|
825
914
|
const ensureEosAssist = () => safe(() => {
|
|
@@ -837,7 +926,7 @@
|
|
|
837
926
|
root.className = 'eos-assist-root';
|
|
838
927
|
root.innerHTML = `
|
|
839
928
|
<button class="eos-assist-button" type="button" aria-expanded="false">
|
|
840
|
-
<span class="eos-assist-dot"></span><strong>EOS Assist</strong><small>
|
|
929
|
+
<span class="eos-assist-dot"></span><strong>EOS Assist</strong><small>KI-Hilfe</small>
|
|
841
930
|
</button>
|
|
842
931
|
<div class="eos-assist-panel" role="dialog" aria-label="EOS Assist Einrichtungshilfe">
|
|
843
932
|
<div class="eos-assist-head">
|
|
@@ -847,15 +936,16 @@
|
|
|
847
936
|
</div>
|
|
848
937
|
<div class="eos-assist-steps"></div>
|
|
849
938
|
<div class="eos-assist-actions">
|
|
939
|
+
<button type="button" data-question="Wallbox einrichten: Welche Wege gibt es?">Wallbox</button>
|
|
850
940
|
<button type="button" data-question="Wie richte ich dieses Modul ein?">Modul einrichten</button>
|
|
851
941
|
<button type="button" data-question="Wie prüfe ich Fehler in den Logs?">Fehler prüfen</button>
|
|
852
942
|
<button type="button" data-question="Welche Rechte braucht der Installateur?">Rechte erklären</button>
|
|
853
943
|
<button type="button" data-question="Was muss ich vor einem Update beachten?">Update-Check</button>
|
|
854
944
|
</div>
|
|
855
945
|
<label class="eos-assist-input-label">Was möchtest du einrichten?</label>
|
|
856
|
-
<div class="eos-assist-input-row"><input class="eos-assist-input" placeholder="z. B. Wallbox,
|
|
946
|
+
<div class="eos-assist-input-row"><input class="eos-assist-input" placeholder="z. B. Wallbox Keba Modbus, OCPP, PV, Rechte..." /><button type="button" class="eos-assist-send">Fragen</button></div>
|
|
857
947
|
<div class="eos-assist-answer"></div>
|
|
858
|
-
<div class="eos-assist-foot">
|
|
948
|
+
<div class="eos-assist-foot">EOS Assist nutzt eine lokale Entscheidungslogik und ist für eine echte NexoWatt-KI-Anbindung vorbereitet.</div>
|
|
859
949
|
</div>
|
|
860
950
|
`;
|
|
861
951
|
document.body.appendChild(root);
|
|
@@ -897,7 +987,7 @@
|
|
|
897
987
|
event.preventDefault();
|
|
898
988
|
const value = root.querySelector('.eos-assist-input')?.value || '';
|
|
899
989
|
const out = root.querySelector('.eos-assist-answer');
|
|
900
|
-
|
|
990
|
+
sendAssistQuestion(root, value);
|
|
901
991
|
return;
|
|
902
992
|
}
|
|
903
993
|
if (target.hasAttribute('data-question')) {
|
|
@@ -906,13 +996,13 @@
|
|
|
906
996
|
const field = root.querySelector('.eos-assist-input');
|
|
907
997
|
if (field) field.value = question;
|
|
908
998
|
const out = root.querySelector('.eos-assist-answer');
|
|
909
|
-
|
|
999
|
+
sendAssistQuestion(root, question);
|
|
910
1000
|
}
|
|
911
1001
|
}, true);
|
|
912
1002
|
root.addEventListener('keydown', event => {
|
|
913
1003
|
if (event.key !== 'Enter' || !event.target?.classList?.contains('eos-assist-input')) return;
|
|
914
1004
|
const out = root.querySelector('.eos-assist-answer');
|
|
915
|
-
|
|
1005
|
+
sendAssistQuestion(root, event.target.value || '');
|
|
916
1006
|
}, true);
|
|
917
1007
|
}
|
|
918
1008
|
});
|
|
@@ -948,13 +1038,51 @@
|
|
|
948
1038
|
event.preventDefault();
|
|
949
1039
|
event.stopPropagation();
|
|
950
1040
|
event.stopImmediatePropagation?.();
|
|
951
|
-
|
|
1041
|
+
sendAssistQuestion(root, input?.value || '');
|
|
952
1042
|
state.assistOpen = true;
|
|
953
1043
|
root.classList.add('eos-assist-open');
|
|
954
1044
|
}
|
|
955
1045
|
}, true);
|
|
956
1046
|
});
|
|
957
1047
|
|
|
1048
|
+
|
|
1049
|
+
const ensureStandaloneNavToggle = () => safe(() => {
|
|
1050
|
+
const html = document.documentElement;
|
|
1051
|
+
if (!html.classList.contains('eos-app') || html.classList.contains('eos-login')) {
|
|
1052
|
+
document.getElementById('eos-standalone-nav-toggle')?.remove();
|
|
1053
|
+
return;
|
|
1054
|
+
}
|
|
1055
|
+
let button = document.getElementById('eos-standalone-nav-toggle');
|
|
1056
|
+
if (!button) {
|
|
1057
|
+
button = document.createElement('button');
|
|
1058
|
+
button.id = 'eos-standalone-nav-toggle';
|
|
1059
|
+
button.type = 'button';
|
|
1060
|
+
button.className = 'eos-standalone-nav-toggle';
|
|
1061
|
+
button.innerHTML = '<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M14.8 5.4 8.2 12l6.6 6.6" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"/></svg>';
|
|
1062
|
+
const toggle = event => {
|
|
1063
|
+
event.preventDefault();
|
|
1064
|
+
event.stopPropagation();
|
|
1065
|
+
event.stopImmediatePropagation?.();
|
|
1066
|
+
const compact = !document.documentElement.classList.contains('eos-nav-compact');
|
|
1067
|
+
document.documentElement.classList.toggle('eos-nav-compact', compact);
|
|
1068
|
+
safe(() => localStorage.setItem('nexowatt:eosNavCompact', compact ? '1' : '0'));
|
|
1069
|
+
button.setAttribute('aria-pressed', compact ? 'true' : 'false');
|
|
1070
|
+
button.setAttribute('title', compact ? 'Navigation normal anzeigen' : 'Navigation kompakt anzeigen');
|
|
1071
|
+
button.setAttribute('aria-label', compact ? 'Navigation normal anzeigen' : 'Navigation kompakt anzeigen');
|
|
1072
|
+
};
|
|
1073
|
+
button.addEventListener('click', toggle, true);
|
|
1074
|
+
button.addEventListener('keydown', event => {
|
|
1075
|
+
if (event.key === 'Enter' || event.key === ' ') toggle(event);
|
|
1076
|
+
}, true);
|
|
1077
|
+
document.body.appendChild(button);
|
|
1078
|
+
}
|
|
1079
|
+
const compact = document.documentElement.classList.contains('eos-nav-compact') || safe(() => localStorage.getItem('nexowatt:eosNavCompact') === '1');
|
|
1080
|
+
document.documentElement.classList.toggle('eos-nav-compact', !!compact);
|
|
1081
|
+
button.setAttribute('aria-pressed', compact ? 'true' : 'false');
|
|
1082
|
+
button.setAttribute('title', compact ? 'Navigation normal anzeigen' : 'Navigation kompakt anzeigen');
|
|
1083
|
+
button.setAttribute('aria-label', compact ? 'Navigation normal anzeigen' : 'Navigation kompakt anzeigen');
|
|
1084
|
+
});
|
|
1085
|
+
|
|
958
1086
|
const patchDocumentMeta = () => safe(() => {
|
|
959
1087
|
document.title = BRAND_LONG;
|
|
960
1088
|
const theme = document.querySelector('meta[name="theme-color"]');
|
|
@@ -973,6 +1101,7 @@
|
|
|
973
1101
|
patchLogin();
|
|
974
1102
|
patchShell();
|
|
975
1103
|
applyNavCompactPreference();
|
|
1104
|
+
ensureStandaloneNavToggle();
|
|
976
1105
|
installAssistDelegatedClick();
|
|
977
1106
|
ensureEosAssist();
|
|
978
1107
|
ensureRightsHelper();
|
|
@@ -993,6 +1122,7 @@
|
|
|
993
1122
|
patchLogin();
|
|
994
1123
|
patchShell();
|
|
995
1124
|
applyNavCompactPreference();
|
|
1125
|
+
ensureStandaloneNavToggle();
|
|
996
1126
|
installAssistDelegatedClick();
|
|
997
1127
|
ensureEosAssist();
|
|
998
1128
|
ensureRightsHelper();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
(() => {
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
const VERSION = '
|
|
4
|
+
const VERSION = 'v31-security-text-polish';
|
|
5
5
|
const LEGACY_ADMIN = 'admin';
|
|
6
6
|
const LEGACY_ADMIN_INSTANCE = 'admin.0';
|
|
7
7
|
const ASSET_BASE = (() => {
|
|
@@ -29,6 +29,21 @@
|
|
|
29
29
|
};
|
|
30
30
|
|
|
31
31
|
const normalize = value => String(value || '').replace(/\s+/g, ' ').trim();
|
|
32
|
+
|
|
33
|
+
const fixMojibake = value => {
|
|
34
|
+
let text = String(value || '');
|
|
35
|
+
const map = new Map(Object.entries({
|
|
36
|
+
'dürfen': 'dürfen', 'Dürfen': 'Dürfen', 'für': 'für', 'Für': 'Für',
|
|
37
|
+
'können': 'können', 'Können': 'Können', 'möglich': 'möglich', 'Möglich': 'Möglich',
|
|
38
|
+
'Löschen': 'Löschen', 'löschen': 'löschen', 'schützen': 'schützen', 'Schützen': 'Schützen',
|
|
39
|
+
'Schützt': 'Schützt', 'schützt': 'schützt', 'Geschützte': 'Geschützte', 'geschützte': 'geschützte',
|
|
40
|
+
'ausgewählte': 'ausgewählte', 'Ausgewählte': 'Ausgewählte', 'ändern': 'ändern', 'Ändern': 'Ändern',
|
|
41
|
+
'über': 'über', 'Über': 'Über', 'Wähle': 'Wähle', 'wähle': 'wähle',
|
|
42
|
+
'ß': 'ß', 'Ä': 'Ä', 'Ö': 'Ö', 'Ü': 'Ü', 'ä': 'ä', 'ö': 'ö', 'ü': 'ü'
|
|
43
|
+
}));
|
|
44
|
+
for (const [from, to] of map) if (text.includes(from)) text = text.split(from).join(to);
|
|
45
|
+
return text;
|
|
46
|
+
};
|
|
32
47
|
const normalizeFlat = value => normalize(value).toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, '');
|
|
33
48
|
const normalizeAdapter = value => {
|
|
34
49
|
let adapter = String(value || '').trim().toLowerCase();
|
|
@@ -163,6 +178,8 @@
|
|
|
163
178
|
changed = true;
|
|
164
179
|
}
|
|
165
180
|
}
|
|
181
|
+
const fixed = fixMojibake(value);
|
|
182
|
+
if (fixed !== value) { value = fixed; changed = true; }
|
|
166
183
|
if (changed) node.nodeValue = value;
|
|
167
184
|
}
|
|
168
185
|
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# NexoWatt EOS Admin v31
|
|
2
|
+
|
|
3
|
+
## Inhalt
|
|
4
|
+
|
|
5
|
+
- Kompakt-Pfeil im Navigationsrail sitzt zentriert in einer einzelnen Kachel.
|
|
6
|
+
- Die Abmelden-Kachel neben dem Pfeil wird robuster ausgeblendet.
|
|
7
|
+
- EOS-Security-Texte werden UTF-8-sicher zur Laufzeit repariert.
|
|
8
|
+
- EOS Assist wurde als KI-vorbereiteter Einrichtungsassistent erweitert. Bei Wallboxen werden mehrere Wege vorgeschlagen: Herstelleradapter, Modbus TCP/RTU, OCPP, MQTT/HTTP/REST und Datenpunkt-Mapping.
|
|
9
|
+
|
|
10
|
+
## Version
|
|
11
|
+
|
|
12
|
+
`7.9.31`
|