iobroker.eos-admin 7.9.93 → 7.9.96
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.96.md +11 -0
- package/MERGE_UPDATE_README_V7.9.96.md +5 -0
- package/NEXOWATT_EOS_BUILD_INFO.json +19 -19
- package/NexoWatt_EOS_Admin_EMS_OVERVIEW_MERGE_VALIDATION.md +44 -0
- package/PUBLISH_STABLE_V7.9.96.md +10 -0
- package/RC73_EOS_ADMIN_MERGE_VALIDATION.md +78 -0
- package/README_EMS_OVERVIEW_MERGE.md +45 -0
- package/README_STABILITY_V7.9.96.md +11 -0
- package/RELEASE_ACCEPTANCE_V7.9.96.md +10 -0
- package/RELEASE_NOTES_V7.9.96.md +12 -0
- package/VALIDATION_EMS_OVERVIEW_MERGE.md +35 -0
- package/adminWww/assets/Adapters-B5_jQ7DE-v84.js +1 -1
- package/adminWww/assets/Intro-DkwRiz1n-v84.js +2 -2
- package/adminWww/assets/index-D2ymscJA-v84.js +1 -1
- package/adminWww/css/eos-ems-overview.css +62 -0
- package/adminWww/css/nexowatt-native-shell.css +104 -158
- package/adminWww/index.html +15 -11
- package/adminWww/js/eos-account-management.js +1 -1
- package/adminWww/js/eos-assistant.js +5 -556
- package/adminWww/js/eos-basic-settings.js +1 -1
- package/adminWww/js/eos-branding-sanitizer.js +1 -3
- package/adminWww/js/eos-ems-overview.js +140 -0
- package/adminWww/js/eos-native-security.js +1 -1
- package/adminWww/js/eos-policy-client.js +1 -1
- package/adminWww/js/eos-role-bootstrap.js +4 -4
- package/adminWww/js/eos-role-ui.js +34 -38
- package/adminWww/js/nexowatt-native-shell.js +11 -6
- package/adminWww/static/js/nexowatt-stable-login.js +79 -0
- package/adminWww/static/js/nexowatt-stable-v96.js +82 -0
- package/build/lib/chat/assistantKnowledge.js +6 -21
- package/build/lib/chat/chatOrchestrator.js +38 -33
- package/build/lib/chat/mcpClientManager.js +1 -1
- package/build/lib/nexowattStableAuth.js +74 -0
- package/build/lib/web.js +24 -7
- package/build/main.js +123 -308
- package/io-package.json +25 -16
- package/package.json +32 -15
- package/tools/nexowatt-account-management-selftest.cjs +2 -3
- package/tools/nexowatt-apply-ems-overview.cjs +115 -0
- package/tools/nexowatt-clean-legacy-runtime.cjs +4 -0
- package/tools/nexowatt-ems-overview-selftest.cjs +35 -0
- package/tools/nexowatt-internal-reserve-selftest.cjs +1 -3
- package/tools/nexowatt-login-layout-selftest.cjs +43 -22
- package/tools/nexowatt-merge-update-selftest.cjs +69 -0
- package/tools/nexowatt-modern-ui-selftest.cjs +4 -5
- package/tools/nexowatt-stable-v96-selftest.cjs +13 -0
- package/tools/nexowatt-sync-release-version.cjs +181 -0
- package/tools/nexowatt-ui-overview-runtime-selftest.cjs +16 -0
- package/tools/nexowatt-validate-package.cjs +41 -10
- package/tools/nexowatt-version-sync-selftest.cjs +86 -0
- package/INSTALL_TEST_V7.9.93.md +0 -12
- package/PUBLISH_STABLE_V7.9.93.md +0 -19
- package/README_STABILITY_V7.9.93.md +0 -15
- package/RELEASE_ACCEPTANCE_V7.9.93.md +0 -15
- package/RELEASE_NOTES_V7.9.93.md +0 -10
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
(() => {
|
|
2
|
+
'use strict';
|
|
3
|
+
const VERSION = 'v96-nexowatt-ems-overview-runtime';
|
|
4
|
+
const CONTRACT = 'nexowatt-ems-overview-v1';
|
|
5
|
+
const ROOT_ID = 'eos-ems-overview-runtime';
|
|
6
|
+
const POLL_MS = 5000;
|
|
7
|
+
const STALE_MS = 20000;
|
|
8
|
+
const COLORS = { ok: '#01bc69', info: '#48b9ff', warning: '#ffbd59', error: '#ff5f72' };
|
|
9
|
+
let timer = 0;
|
|
10
|
+
let inFlight = false;
|
|
11
|
+
let destroyed = false;
|
|
12
|
+
|
|
13
|
+
const esc = value => String(value ?? '').replace(/[&<>"']/g, character => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[character]));
|
|
14
|
+
const text = (value, fallback = '') => String(value ?? fallback).replace(/\s+/g, ' ').trim() || fallback;
|
|
15
|
+
const num = (value, fallback = 0) => Number.isFinite(Number(value)) ? Number(value) : fallback;
|
|
16
|
+
const parse = (value, fallback) => {
|
|
17
|
+
if (value && typeof value === 'object') return value;
|
|
18
|
+
try { return JSON.parse(String(value ?? '')); } catch (_) { return fallback; }
|
|
19
|
+
};
|
|
20
|
+
const severity = value => {
|
|
21
|
+
const normalized = String(value || '').toLowerCase();
|
|
22
|
+
if (normalized === 'error') return 'error';
|
|
23
|
+
if (normalized === 'warning' || normalized === 'warn') return 'warning';
|
|
24
|
+
if (normalized === 'ok') return 'ok';
|
|
25
|
+
return 'info';
|
|
26
|
+
};
|
|
27
|
+
const currentTab = () => String(location.hash || '').match(/tab-[a-z0-9_-]+/i)?.[0]?.toLowerCase() || 'tab-intro';
|
|
28
|
+
const currentRole = () => String(window.NEXOWATT_EOS_ACCESS_ROLE || 'admin').toLowerCase();
|
|
29
|
+
const technical = () => ['admin', 'service', 'installer', 'installateur'].includes(currentRole());
|
|
30
|
+
const formatPower = value => {
|
|
31
|
+
const watts = num(value); const absolute = Math.abs(watts); const sign = watts < 0 ? '−' : '';
|
|
32
|
+
return absolute >= 1000 ? `${sign}${(absolute / 1000).toLocaleString('de-DE', { minimumFractionDigits: 1, maximumFractionDigits: 2 })} kW` : `${sign}${Math.round(absolute).toLocaleString('de-DE')} W`;
|
|
33
|
+
};
|
|
34
|
+
const formatAge = timestamp => {
|
|
35
|
+
const value = num(timestamp); if (!value) return 'noch kein Regeltick';
|
|
36
|
+
const seconds = Math.max(0, Math.round((Date.now() - value) / 1000));
|
|
37
|
+
if (seconds < 2) return 'gerade eben';
|
|
38
|
+
if (seconds < 60) return `vor ${seconds} s`;
|
|
39
|
+
return `vor ${Math.round(seconds / 60)} min`;
|
|
40
|
+
};
|
|
41
|
+
const formatClock = timestamp => {
|
|
42
|
+
const value = num(timestamp); if (!value) return '—';
|
|
43
|
+
try { return new Date(value).toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit', second: '2-digit' }); } catch (_) { return '—'; }
|
|
44
|
+
};
|
|
45
|
+
const getSocket = () => window.NEXOWATT_EOS_ADMIN_SOCKET || window.NEXOWATT_EOS_SOCKET || null;
|
|
46
|
+
const stateVal = (states, id) => states?.[id]?.val;
|
|
47
|
+
|
|
48
|
+
const normalizeInstances = (states, aliveStates) => {
|
|
49
|
+
const prefixes = new Set();
|
|
50
|
+
Object.keys(states || {}).forEach(id => { const match = id.match(/^(nexowatt-ui\.\d+)\.info\.adminOverview\./); if (match) prefixes.add(match[1]); });
|
|
51
|
+
return Array.from(prefixes).map(instance => {
|
|
52
|
+
const summaryId = `${instance}.info.adminOverview.summaryJson`;
|
|
53
|
+
const summary = parse(stateVal(states, summaryId), {});
|
|
54
|
+
const events = parse(stateVal(states, `${instance}.info.adminOverview.eventsJson`), []);
|
|
55
|
+
const updatedAt = Math.max(num(summary.updatedAt), num(summary.generatedAt), num(stateVal(states, `${instance}.info.adminOverview.updatedAt`)), num(states?.[summaryId]?.ts));
|
|
56
|
+
const rawAlive = stateVal(aliveStates, `system.adapter.${instance}.alive`);
|
|
57
|
+
const alive = rawAlive == null ? null : (rawAlive === true || rawAlive === 1 || ['true', '1', 'online', 'active'].includes(String(rawAlive).toLowerCase()));
|
|
58
|
+
return { instance, alive, summary: { ...summary, status: severity(summary.status || stateVal(states, `${instance}.info.adminOverview.status`)), headline: text(summary.headline || stateVal(states, `${instance}.info.adminOverview.headline`), 'EMS-Diagnose wird aufgebaut'), reason: text(summary.reason || stateVal(states, `${instance}.info.adminOverview.reason`), ''), binding: text(summary.binding || stateVal(states, `${instance}.info.adminOverview.binding`), 'none') }, events: Array.isArray(events) ? events : [], updatedAt };
|
|
59
|
+
}).sort((a, b) => Number(b.alive === true) - Number(a.alive === true) || b.updatedAt - a.updatedAt || a.instance.localeCompare(b.instance));
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const ensureRoot = () => {
|
|
63
|
+
const reactCard = document.querySelector(`[data-nexowatt-contract="${CONTRACT}"]:not(#${ROOT_ID})`);
|
|
64
|
+
if (reactCard) { document.getElementById(ROOT_ID)?.remove(); return null; }
|
|
65
|
+
const paper = document.getElementById('app-paper');
|
|
66
|
+
if (!paper || currentTab() !== 'tab-intro') return null;
|
|
67
|
+
let root = document.getElementById(ROOT_ID);
|
|
68
|
+
if (!root) {
|
|
69
|
+
root = document.createElement('section');
|
|
70
|
+
root.id = ROOT_ID;
|
|
71
|
+
root.className = 'eos-ems-overview-card';
|
|
72
|
+
root.dataset.nexowattContract = CONTRACT;
|
|
73
|
+
}
|
|
74
|
+
const hero = document.getElementById('eos-native-overview-hero');
|
|
75
|
+
if (hero?.parentElement === paper) paper.insertBefore(root, hero.nextSibling); else paper.insertBefore(root, paper.firstChild || null);
|
|
76
|
+
return root;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const unavailable = message => {
|
|
80
|
+
const root = ensureRoot(); if (!root) return;
|
|
81
|
+
root.style.setProperty('--eos-ems-status-color', COLORS.info);
|
|
82
|
+
root.innerHTML = `<header class="eos-ems-overview-header"><div><span class="eos-ems-overview-eyebrow">NexoWatt EMS</span><h2>Live-Diagnose</h2><p>${esc(message)}</p></div><div class="eos-ems-overview-state"><span class="eos-ems-overview-state-dot"></span><strong>Nicht verfügbar</strong><small>read-only</small></div></header><footer class="eos-ems-overview-footer"><span>Die Kachel liest ausschließlich info.adminOverview.*.</span><span>Keine EMS-Regelung wurde verändert.</span></footer>`;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const fallbackDecisions = summary => {
|
|
86
|
+
const result = []; const charging = summary.charging || {}; const storage = summary.storage || {};
|
|
87
|
+
if (charging.available) result.push({ severity: num(charging.faultCount) > 0 ? 'error' : num(charging.waitingCount) > 0 ? 'info' : 'ok', title: `${num(charging.activeCount)} lädt · ${num(charging.waitingCount)} wartet · ${num(charging.faultCount)} gestört`, reason: text(charging.limiterText || charging.status, 'Lademanagement aktiv'), details: `Ist ${formatPower(charging.actualW)} · Soll ${formatPower(charging.targetW)}` });
|
|
88
|
+
if (storage.available) result.push({ severity: storage.writeOk === false ? 'error' : 'ok', title: `Speicher ${text(storage.topology, 'aktiv')}${storage.socPct != null ? ` · SoC ${Math.round(num(storage.socPct))} %` : ''}`, reason: text(storage.reason, 'Speicherregelung aktiv'), details: `Ist ${formatPower(storage.actualW)} · Soll ${formatPower(storage.targetW)}` });
|
|
89
|
+
return result;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const render = data => {
|
|
93
|
+
const root = ensureRoot(); if (!root) return;
|
|
94
|
+
const summary = data.summary || {}; const budget = summary.budget || {}; const charging = summary.charging || {}; const storage = summary.storage || {}; const ems = summary.ems || {}; const para14a = summary.para14a || {}; const tariff = summary.tariff || {}; const forecast = summary.forecast || {}; const peak = summary.peakShaving || {};
|
|
95
|
+
const stale = data.alive === false || !data.updatedAt || Date.now() - data.updatedAt > STALE_MS;
|
|
96
|
+
const status = stale ? 'error' : severity(summary.status); const statusColor = COLORS[status]; root.style.setProperty('--eos-ems-status-color', statusColor);
|
|
97
|
+
const totalBudget = Math.max(0, num(budget.totalW)); const remaining = Math.max(0, num(budget.remainingW)); const percent = totalBudget > 0 ? Math.max(0, Math.min(100, remaining / totalBudget * 100)) : 0;
|
|
98
|
+
const isTechnical = technical(); const decisions = (Array.isArray(summary.currentDecisions) ? summary.currentDecisions : fallbackDecisions(summary)).slice(0, isTechnical ? 5 : 2); const events = isTechnical ? data.events.slice(0, 6) : [];
|
|
99
|
+
const tags = [];
|
|
100
|
+
if (text(summary.binding, 'none') !== 'none') tags.push({ label: `Bindend: ${text(budget.bindingText || summary.binding)}`, warning: true });
|
|
101
|
+
if (para14a.communicationFallbackActive) tags.push({ label: `§14a-Fallback ${formatPower(para14a.fallbackCapW)}`, warning: true }); else if (para14a.binding) tags.push({ label: '§14a begrenzt', warning: true });
|
|
102
|
+
if (peak.active) tags.push({ label: `Peak-Shaving: ${text(peak.status, 'aktiv')}`, warning: true });
|
|
103
|
+
if (tariff.available) tags.push({ label: `Tarif: ${text(tariff.state, 'aktiv')}${tariff.priceEurPerKwh != null ? ` · ${num(tariff.priceEurPerKwh).toFixed(3)} €/kWh` : ''}` });
|
|
104
|
+
if (forecast.available) tags.push({ label: `Prognose: ${text(forecast.source, 'aktiv')}${forecast.fresh === false ? ' · veraltet' : ''}`, warning: forecast.fresh === false });
|
|
105
|
+
if (ems.safetyActive || ems.safetyEmergencyStop || ems.safetyValid === false) tags.push({ label: 'EOS Safety aktiv', warning: true });
|
|
106
|
+
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}`}`;
|
|
107
|
+
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>`;
|
|
108
|
+
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>';
|
|
109
|
+
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>`;
|
|
110
|
+
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><a href="${esc(detailsUrl)}" target="_blank" rel="noreferrer">Vollständige EMS-Diagnose öffnen ↗</a></footer>`;
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const load = async () => {
|
|
114
|
+
if (destroyed || inFlight || document.visibilityState === 'hidden' || currentTab() !== 'tab-intro') return;
|
|
115
|
+
const socket = getSocket();
|
|
116
|
+
if (!socket?.getForeignStates) { unavailable('Die Admin-Verbindung wird noch aufgebaut.'); return; }
|
|
117
|
+
inFlight = true;
|
|
118
|
+
try {
|
|
119
|
+
const [states, alive] = await Promise.all([socket.getForeignStates('nexowatt-ui.*.info.adminOverview.*'), socket.getForeignStates('system.adapter.nexowatt-ui.*.alive')]);
|
|
120
|
+
const instances = normalizeInstances(states || {}, alive || {});
|
|
121
|
+
if (instances.length) render(instances[0]); else unavailable('NexoWatt UI ist nicht installiert, offline oder der Diagnosevertrag info.adminOverview.* fehlt.');
|
|
122
|
+
} catch (error) { unavailable(`EMS-Diagnose konnte nicht gelesen werden: ${text(error?.message || error, 'unbekannter Fehler')}`); }
|
|
123
|
+
finally { inFlight = false; }
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const apply = () => {
|
|
127
|
+
document.getElementById('eos-role-safe-overview')?.remove();
|
|
128
|
+
document.documentElement.classList.remove('eos-safe-overview-active');
|
|
129
|
+
if (currentTab() !== 'tab-intro') { document.getElementById(ROOT_ID)?.remove(); return; }
|
|
130
|
+
ensureRoot(); void load();
|
|
131
|
+
};
|
|
132
|
+
const start = () => {
|
|
133
|
+
apply();
|
|
134
|
+
timer = window.setInterval(apply, POLL_MS);
|
|
135
|
+
window.addEventListener('hashchange', apply);
|
|
136
|
+
document.addEventListener('visibilitychange', apply);
|
|
137
|
+
};
|
|
138
|
+
document.readyState === 'loading' ? document.addEventListener('DOMContentLoaded', start, { once: true }) : start();
|
|
139
|
+
window.NEXOWATT_EOS_EMS_OVERVIEW = Object.freeze({ version: VERSION, contract: CONTRACT, refresh: apply, destroy() { destroyed = true; if (timer) clearInterval(timer); document.getElementById(ROOT_ID)?.remove(); } });
|
|
140
|
+
})();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
(() => {
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
const VERSION = '
|
|
4
|
+
const VERSION = 'v91-product-role-access';
|
|
5
5
|
const protectedAdapters = new Set([
|
|
6
6
|
'admin', 'eos-admin', 'backitup', 'nexowatt-devices', 'nexowatt-device', 'nexowatt-dev', 'nexowatt-ui',
|
|
7
7
|
]);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
(() => {
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
const VERSION = '
|
|
4
|
+
const VERSION = 'v96-first-login-password-native-overview';
|
|
5
5
|
const script = document.currentScript || document.querySelector('script[src*="eos-role-bootstrap.js"]');
|
|
6
6
|
const entry = script?.dataset?.eosEntry || '';
|
|
7
7
|
let launched = false;
|
|
@@ -172,7 +172,7 @@
|
|
|
172
172
|
overlay.querySelector('.eos-first-login-submit').textContent = t.submit;
|
|
173
173
|
overlay.querySelector('.eos-first-login-logout').textContent = t.logout;
|
|
174
174
|
const form = overlay.querySelector('form');
|
|
175
|
-
form.elements.username.value =
|
|
175
|
+
form.elements.username.value = userName;
|
|
176
176
|
const password = form.elements.password;
|
|
177
177
|
const repeat = form.elements.passwordRepeat;
|
|
178
178
|
const show = form.elements.show;
|
|
@@ -500,7 +500,7 @@
|
|
|
500
500
|
const { username, password, card, submit } = loginElements();
|
|
501
501
|
if (!username || !password || !card) return;
|
|
502
502
|
card.classList.add('eos-login-card-modern');
|
|
503
|
-
//
|
|
503
|
+
// v96 compact normal-login first activation: do not add role buttons or a second panel.
|
|
504
504
|
// The account is entered in the existing Loginname field; a blank password is accepted only
|
|
505
505
|
// after the backend confirms that this exact managed account is eligible.
|
|
506
506
|
card.querySelectorAll('.eos-login-role-selector').forEach(element => element.remove());
|
|
@@ -631,7 +631,7 @@
|
|
|
631
631
|
|
|
632
632
|
const contextUrl = new URL('nexowatt/security/context', base).href;
|
|
633
633
|
const controller = new AbortController();
|
|
634
|
-
const timeout = window.setTimeout(() => controller.abort(),
|
|
634
|
+
const timeout = window.setTimeout(() => controller.abort(), 8000);
|
|
635
635
|
|
|
636
636
|
window.NEXOWATT_EOS_BOOTSTRAP_READY = fetch(contextUrl, {
|
|
637
637
|
credentials: 'same-origin', cache: 'no-store', headers: { Accept: 'application/json' }, signal: controller.signal,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
(() => {
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
const VERSION = '
|
|
4
|
+
const VERSION = 'v96-native-overview-ems-live-reserve-filter';
|
|
5
5
|
window.NEXOWATT_EOS_ROLE_UI_VERSION = VERSION;
|
|
6
6
|
|
|
7
7
|
const state = {
|
|
@@ -251,47 +251,43 @@
|
|
|
251
251
|
if (/permissionerror|cannot get data/i.test(node.textContent || '')) node.classList.add('eos-role-hidden');
|
|
252
252
|
});
|
|
253
253
|
};
|
|
254
|
-
const
|
|
255
|
-
if (isLoginView() ||
|
|
256
|
-
|
|
257
|
-
|
|
254
|
+
const updateNativeOverviewRole = () => {
|
|
255
|
+
if (isLoginView() || currentRoute() !== 'tab-intro') return;
|
|
256
|
+
const config = state.role === 'admin'
|
|
257
|
+
? { label: 'Admin / Service', text: 'Systemstatus, Module, Dienste und Anlagenzugänge in einer modernen Serviceübersicht.' }
|
|
258
|
+
: state.role === 'installer'
|
|
259
|
+
? { label: 'Installateur', text: 'Inbetriebnahme, Fehlersuche, Geräte- und EMS-Diagnose – mit deinen freigegebenen Installateurrechten.' }
|
|
260
|
+
: { label: 'Endkunde', text: 'Energie, Laden, Gebäude und die aktuellen EMS-Entscheidungen auf einen Blick.' };
|
|
261
|
+
const hero = document.getElementById('eos-native-overview-hero');
|
|
262
|
+
const description = hero?.querySelector('p');
|
|
263
|
+
const role = hero?.querySelector('.eos-overview-role');
|
|
264
|
+
if (description) description.textContent = config.text;
|
|
265
|
+
if (role) {
|
|
266
|
+
role.innerHTML = '<span class="eos-overview-status-dot"></span>';
|
|
267
|
+
role.append(document.createTextNode(config.label));
|
|
268
|
+
role.setAttribute('data-nexowatt-overview-role', state.role);
|
|
258
269
|
}
|
|
270
|
+
};
|
|
271
|
+
const hideIntroEditControls = () => {
|
|
259
272
|
const paper = appPaper();
|
|
260
273
|
if (!paper) return;
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
overview = document.createElement('section');
|
|
266
|
-
overview.id = 'eos-role-safe-overview';
|
|
267
|
-
overview.className = 'eos-role-safe-overview';
|
|
268
|
-
paper.appendChild(overview);
|
|
269
|
-
}
|
|
270
|
-
const installer = state.role === 'installer';
|
|
271
|
-
const actions = actionDefinitions(state.role).filter(([, , tab]) => tab === 'basic-settings' || isRouteAllowed(state.role, tab));
|
|
272
|
-
overview.innerHTML = `
|
|
273
|
-
<header class="eos-overview-hero eos-role-overview-hero">
|
|
274
|
-
<div><span class="eos-overview-eyebrow">NexoWatt EOS</span><h1>Übersicht</h1><p>${installer ? 'Inbetriebnahme, Fehlersuche und Anlagenkonfiguration – passend zu deinen Installateurrechten.' : 'Deine freigegebenen Energie-, Smart-Home- und Sicherungsfunktionen.'}</p></div>
|
|
275
|
-
<div class="eos-overview-role"><span class="eos-overview-status-dot"></span>${installer ? 'Installateur' : 'Gast / Endkunde'}</div>
|
|
276
|
-
</header>
|
|
277
|
-
<div class="eos-overview-grid">${actions.map(([label, description, tab, icon]) => `
|
|
278
|
-
<a class="eos-overview-action" data-eos-role-tab="${tab}" href="${tab === 'basic-settings' ? '#tab-intro' : `#${tab}`}">
|
|
279
|
-
<span class="eos-overview-action-icon" aria-hidden="true">${iconFor(icon)}</span>
|
|
280
|
-
<span><strong>${label}</strong><small>${description}</small></span><i aria-hidden="true">›</i>
|
|
281
|
-
</a>`).join('')}</div>`;
|
|
282
|
-
overview.querySelectorAll('a[data-eos-role-tab]').forEach(link => {
|
|
283
|
-
link.addEventListener('click', event => {
|
|
284
|
-
event.preventDefault();
|
|
285
|
-
event.stopPropagation();
|
|
286
|
-
const targetTab = link.getAttribute('data-eos-role-tab') || '';
|
|
287
|
-
if (targetTab) navigateToTab(targetTab, link.textContent || '');
|
|
288
|
-
});
|
|
289
|
-
link.addEventListener('keydown', event => {
|
|
290
|
-
if (event.key !== 'Enter' && event.key !== ' ') return;
|
|
291
|
-
event.preventDefault();
|
|
292
|
-
navigateToTab(link.getAttribute('data-eos-role-tab') || '', link.textContent || '');
|
|
293
|
-
});
|
|
274
|
+
const admin = state.role === 'admin';
|
|
275
|
+
paper.querySelectorAll(':scope > .MuiFab-root,:scope > button.MuiFab-root').forEach(button => {
|
|
276
|
+
button.classList.toggle('eos-intro-admin-edit-hidden', !admin);
|
|
277
|
+
if (!admin) button.setAttribute('aria-hidden', 'true'); else button.removeAttribute('aria-hidden');
|
|
294
278
|
});
|
|
279
|
+
};
|
|
280
|
+
const ensureSafeOverview = () => safe(() => {
|
|
281
|
+
// 7.9.96: never cover the real Admin Intro with a second tile surface.
|
|
282
|
+
// Installer and end-user use the native card component; security remains
|
|
283
|
+
// enforced by route/instance filters and backend permissions.
|
|
284
|
+
removeSafeOverview();
|
|
285
|
+
if (isLoginView() || state.role === 'unknown' || currentRoute() !== 'tab-intro') return;
|
|
286
|
+
suppressPermissionErrors();
|
|
287
|
+
updateNativeOverviewRole();
|
|
288
|
+
hideIntroEditControls();
|
|
289
|
+
window.NEXOWATT_NATIVE_SHELL?.refresh?.();
|
|
290
|
+
window.NEXOWATT_EOS_EMS_OVERVIEW?.refresh?.();
|
|
295
291
|
});
|
|
296
292
|
|
|
297
293
|
const hideOfficialReserveSurfaces = () => safe(() => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
(() => {
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
const VERSION = '
|
|
4
|
+
const VERSION = 'v96-nexowatt-native-shell-native-overview';
|
|
5
5
|
const previous = window.NEXOWATT_NATIVE_SHELL;
|
|
6
6
|
if (previous?.version === VERSION) return;
|
|
7
7
|
previous?.destroy?.();
|
|
@@ -148,21 +148,26 @@
|
|
|
148
148
|
|
|
149
149
|
const ensureModernOverview = () => {
|
|
150
150
|
const paper = document.getElementById('app-paper');
|
|
151
|
-
const role = window.NEXOWATT_EOS_ACCESS_ROLE || 'unknown';
|
|
152
|
-
if (!paper || isLogin() || currentTab() !== 'tab-intro' || role
|
|
151
|
+
const role = String(window.NEXOWATT_EOS_ACCESS_ROLE || 'unknown').toLowerCase();
|
|
152
|
+
if (!paper || isLogin() || currentTab() !== 'tab-intro' || role === 'unknown') {
|
|
153
153
|
document.getElementById('eos-native-overview-hero')?.remove();
|
|
154
154
|
return;
|
|
155
155
|
}
|
|
156
|
+
const config = role === 'admin'
|
|
157
|
+
? { label: 'Admin / Service', text: 'Systemstatus, Module, Dienste und Anlagenzugänge in einer modernen Serviceübersicht.' }
|
|
158
|
+
: role === 'installer' || role === 'installateur'
|
|
159
|
+
? { label: 'Installateur', text: 'Inbetriebnahme, Fehlersuche, Geräte- und EMS-Diagnose – mit deinen freigegebenen Installateurrechten.' }
|
|
160
|
+
: { label: 'Endkunde', text: 'Energie, Laden, Gebäude und die aktuellen EMS-Entscheidungen auf einen Blick.' };
|
|
156
161
|
let hero = document.getElementById('eos-native-overview-hero');
|
|
157
162
|
if (!hero) {
|
|
158
163
|
hero = document.createElement('section');
|
|
159
164
|
hero.id = 'eos-native-overview-hero';
|
|
160
165
|
hero.className = 'eos-overview-hero eos-native-overview-hero';
|
|
161
|
-
hero.innerHTML = `
|
|
162
|
-
<div><span class="eos-overview-eyebrow">NexoWatt EOS</span><h1>Übersicht</h1><p>Systemstatus, Module, Dienste und Anlagenzugänge in einer modernen Serviceübersicht.</p></div>
|
|
163
|
-
<div class="eos-overview-role"><span class="eos-overview-status-dot"></span>Admin / Service</div>`;
|
|
164
166
|
paper.insertBefore(hero, paper.firstChild || null);
|
|
165
167
|
}
|
|
168
|
+
hero.innerHTML = `
|
|
169
|
+
<div><span class="eos-overview-eyebrow">NexoWatt EOS</span><h1>Übersicht</h1><p>${config.text}</p></div>
|
|
170
|
+
<div class="eos-overview-role" data-nexowatt-overview-role="${role}"><span class="eos-overview-status-dot"></span>${config.label}</div>`;
|
|
166
171
|
};
|
|
167
172
|
|
|
168
173
|
const apply = () => {
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/* NexoWatt EOS stable runtime v96 */
|
|
2
|
+
(() => {
|
|
3
|
+
'use strict';
|
|
4
|
+
const MANAGED_USERS = new Set(['installer', 'guest', 'user']);
|
|
5
|
+
const ASSIST_TEXT = /EOS\s*Assist/i;
|
|
6
|
+
const PASSWORDLESS_TEXT = /Erstanmeldung\s+ohne\s+Passwort|first\s+sign-?in\s+without\s+password/i;
|
|
7
|
+
const isLogin = () => /(?:\?|&)login(?:[=&]|$)/i.test(location.search) || /\/login(?:[/?#]|$)/i.test(location.pathname + location.hash);
|
|
8
|
+
const visible = el => !!el && el.getClientRects().length > 0;
|
|
9
|
+
const findInput = kind => {
|
|
10
|
+
const inputs = [...document.querySelectorAll('input')].filter(visible);
|
|
11
|
+
if (kind === 'password') return inputs.find(i => i.type === 'password');
|
|
12
|
+
return inputs.find(i => i.type !== 'password' && /user|login|name/i.test(`${i.name} ${i.id} ${i.autocomplete} ${i.getAttribute('aria-label')||''}`)) || inputs.find(i => i.type !== 'password');
|
|
13
|
+
};
|
|
14
|
+
const findSubmit = () => [...document.querySelectorAll('button,input[type="submit"]')].find(el => visible(el) && (/anmelden|login|sign in/i.test((el.textContent||el.value||'').trim()) || el.type === 'submit'));
|
|
15
|
+
const commonAncestor = (a,b) => {
|
|
16
|
+
if (!a || !b) return null;
|
|
17
|
+
const parents = new Set(); let n=a;
|
|
18
|
+
while(n){parents.add(n);n=n.parentElement;}
|
|
19
|
+
n=b; while(n&&!parents.has(n))n=n.parentElement;
|
|
20
|
+
return n;
|
|
21
|
+
};
|
|
22
|
+
const markLogin = () => {
|
|
23
|
+
if (!isLogin()) return;
|
|
24
|
+
document.documentElement.classList.add('nw-eos-login-v96');
|
|
25
|
+
document.body?.classList.add('nw-eos-login-v96');
|
|
26
|
+
const u=findInput('user'), p=findInput('password'), submit=findSubmit();
|
|
27
|
+
if (u && p) {
|
|
28
|
+
let card=commonAncestor(u,p);
|
|
29
|
+
while(card && card.parentElement && card.querySelectorAll('input').length < 2) card=card.parentElement;
|
|
30
|
+
if (card) {
|
|
31
|
+
card.setAttribute('data-nw-login-card','true');
|
|
32
|
+
let outer=card.parentElement;
|
|
33
|
+
for(let i=0;i<3 && outer?.parentElement;i++){
|
|
34
|
+
if(outer.getBoundingClientRect().width > card.getBoundingClientRect().width+20) break;
|
|
35
|
+
outer=outer.parentElement;
|
|
36
|
+
}
|
|
37
|
+
outer?.setAttribute('data-nw-login-shell','true');
|
|
38
|
+
}
|
|
39
|
+
const sync=()=>{
|
|
40
|
+
const name=(u.value||'').trim().toLowerCase();
|
|
41
|
+
const pass=p.value||'';
|
|
42
|
+
if(submit && MANAGED_USERS.has(name) && !pass){
|
|
43
|
+
submit.disabled=true;
|
|
44
|
+
submit.setAttribute('aria-disabled','true');
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
if(!u.dataset.nwV96){u.dataset.nwV96='1';u.addEventListener('input',sync);p.addEventListener('input',sync);sync();}
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
const hideLegacyUi = () => {
|
|
51
|
+
for (const el of document.querySelectorAll('button,a,[role="button"],div,span')) {
|
|
52
|
+
const text=(el.textContent||'').trim();
|
|
53
|
+
if(!text || text.length>100) continue;
|
|
54
|
+
if(ASSIST_TEXT.test(text) || PASSWORDLESS_TEXT.test(text)){
|
|
55
|
+
const target=el.closest('button,a,[role="button"],[data-eos-assist],.eos-assist') || el;
|
|
56
|
+
target.style.setProperty('display','none','important');
|
|
57
|
+
target.setAttribute('aria-hidden','true');
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
const apply=()=>{markLogin();hideLegacyUi();};
|
|
62
|
+
const style=document.createElement('style');
|
|
63
|
+
style.id='nw-eos-stable-v96-css';
|
|
64
|
+
style.textContent=`
|
|
65
|
+
html.nw-eos-login-v96,body.nw-eos-login-v96{min-height:100%;margin:0!important;overflow-x:hidden!important;overflow-y:auto!important}
|
|
66
|
+
.nw-eos-login-v96 [data-nw-login-shell="true"]{width:min(520px,calc(100vw - 28px))!important;min-height:590px!important;height:auto!important;max-height:none!important;overflow:visible!important;box-sizing:border-box!important;padding:22px!important}
|
|
67
|
+
.nw-eos-login-v96 [data-nw-login-card="true"]{width:min(460px,calc(100vw - 56px))!important;min-height:540px!important;height:auto!important;max-height:none!important;overflow:visible!important;box-sizing:border-box!important;padding-bottom:26px!important}
|
|
68
|
+
.nw-eos-login-v96 [data-nw-login-card="true"] *{scrollbar-width:none}
|
|
69
|
+
.nw-eos-login-v96 [data-nw-login-card="true"] *::-webkit-scrollbar{display:none!important;width:0!important;height:0!important}
|
|
70
|
+
.nw-eos-login-v96 [data-nw-login-card="true"] [role="alert"]{position:static!important;max-height:none!important;overflow:visible!important;white-space:normal!important}
|
|
71
|
+
[data-eos-assist],.eos-assist,.nexowatt-eos-assist{display:none!important}
|
|
72
|
+
@media(max-height:620px){.nw-eos-login-v96 [data-nw-login-shell="true"]{min-height:0!important;margin:16px auto!important}.nw-eos-login-v96 [data-nw-login-card="true"]{min-height:0!important}}
|
|
73
|
+
`;
|
|
74
|
+
(document.head||document.documentElement).appendChild(style);
|
|
75
|
+
const observer=new MutationObserver(()=>requestAnimationFrame(apply));
|
|
76
|
+
observer.observe(document.documentElement,{subtree:true,childList:true,attributes:true,attributeFilter:['class','style']});
|
|
77
|
+
if(document.readyState==='loading')document.addEventListener('DOMContentLoaded',apply,{once:true});else apply();
|
|
78
|
+
setInterval(apply,1500);
|
|
79
|
+
})();
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
(() => {
|
|
2
|
+
'use strict';
|
|
3
|
+
const VERSION = 'v96-spacious-login-first-password-fix';
|
|
4
|
+
const MANAGED_USERS = new Set(['installer', 'guest', 'user']);
|
|
5
|
+
const ASSIST_TEXT = /EOS\s*Assist/i;
|
|
6
|
+
const normalizeUser = value => String(value || '').trim().toLowerCase().replace(/^system\.user\./, '');
|
|
7
|
+
const styleId = 'nexowatt-stable-v96-style';
|
|
8
|
+
|
|
9
|
+
const ensureStyle = () => {
|
|
10
|
+
if (document.getElementById(styleId)) return;
|
|
11
|
+
const style = document.createElement('style');
|
|
12
|
+
style.id = styleId;
|
|
13
|
+
style.textContent = `
|
|
14
|
+
html.eos-login,html.eos-login body{margin:0!important;overflow-x:hidden!important;overflow-y:auto!important}
|
|
15
|
+
html.eos-login main.MuiPaper-root{box-sizing:border-box!important}
|
|
16
|
+
html.eos-login main.MuiPaper-root::before{width:min(520px,calc(100vw - 24px))!important;min-height:590px!important;height:min(640px,calc(100dvh - 24px))!important}
|
|
17
|
+
html.eos-login .eos-login-card-modern,
|
|
18
|
+
html.eos-login main.MuiPaper-root > .MuiPaper-root:has(#username),
|
|
19
|
+
html.eos-login main.MuiPaper-root > .MuiPaper-root:has(#password),
|
|
20
|
+
html.eos-login [data-nw-login-card="true"]{
|
|
21
|
+
width:min(460px,calc(100vw - 56px))!important;
|
|
22
|
+
min-height:540px!important;
|
|
23
|
+
height:auto!important;
|
|
24
|
+
max-width:460px!important;
|
|
25
|
+
max-height:none!important;
|
|
26
|
+
padding:34px 36px 32px!important;
|
|
27
|
+
overflow:visible!important;
|
|
28
|
+
box-sizing:border-box!important
|
|
29
|
+
}
|
|
30
|
+
html.eos-login .MuiAlert-root,html.eos-login [role="alert"]{position:static!important;width:100%!important;max-width:100%!important;max-height:none!important;overflow:visible!important;white-space:normal!important;overflow-wrap:anywhere!important;flex-shrink:0!important}
|
|
31
|
+
html.eos-login .eos-login-card-modern *,html.eos-login [data-nw-login-card="true"] *{box-sizing:border-box;max-width:100%}
|
|
32
|
+
html.eos-assist-disabled .eos-assist-root,html.eos-assist-disabled .eos-assist-header-root,[data-eos-assist-root]{display:none!important}
|
|
33
|
+
@media(max-width:560px){html.eos-login .eos-login-card-modern,html.eos-login main.MuiPaper-root > .MuiPaper-root:has(#username),html.eos-login main.MuiPaper-root > .MuiPaper-root:has(#password),html.eos-login [data-nw-login-card="true"]{width:calc(100vw - 38px)!important;max-width:none!important;min-height:0!important;padding:26px 22px!important}}
|
|
34
|
+
@media(max-height:690px){html.eos-login .eos-login-card-modern,html.eos-login main.MuiPaper-root > .MuiPaper-root:has(#username),html.eos-login main.MuiPaper-root > .MuiPaper-root:has(#password),html.eos-login [data-nw-login-card="true"]{min-height:0!important}}
|
|
35
|
+
`;
|
|
36
|
+
document.head.appendChild(style);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const findCard = (username, password) => username?.closest?.('.MuiPaper-root,form,main,section')
|
|
40
|
+
|| password?.closest?.('.MuiPaper-root,form,main,section');
|
|
41
|
+
const findSubmit = card => card?.querySelector?.('button[type="submit"]')
|
|
42
|
+
|| Array.from(card?.querySelectorAll?.('button') || []).find(button => /anmelden|login|sign in|aanmelden/i.test(button.textContent || ''));
|
|
43
|
+
|
|
44
|
+
const apply = () => {
|
|
45
|
+
ensureStyle();
|
|
46
|
+
document.documentElement.classList.add('eos-assist-disabled');
|
|
47
|
+
document.querySelectorAll('.eos-assist-root,.eos-assist-header-root,[data-eos-assist-root],.eos-passwordless-launcher')
|
|
48
|
+
.forEach(element => element.remove());
|
|
49
|
+
const username = document.querySelector('input#username,input[name="username"]');
|
|
50
|
+
const password = document.querySelector('input#password,input[name="password"]');
|
|
51
|
+
const name = normalizeUser(username?.value);
|
|
52
|
+
const pass = String(password?.value || '');
|
|
53
|
+
const card = findCard(username, password);
|
|
54
|
+
const submit = findSubmit(card);
|
|
55
|
+
if (submit && MANAGED_USERS.has(name) && !pass) {
|
|
56
|
+
submit.disabled = true;
|
|
57
|
+
submit.classList.add('Mui-disabled');
|
|
58
|
+
submit.setAttribute('aria-disabled', 'true');
|
|
59
|
+
}
|
|
60
|
+
document.querySelectorAll('body *').forEach(element => {
|
|
61
|
+
if (element.children.length) return;
|
|
62
|
+
const value = element.textContent || '';
|
|
63
|
+
if (/\[iobroker\.eos-admin@[^\]]+\]/i.test(value)) element.textContent = value.replace(/\[iobroker\.eos-admin@[^\]]+\]/ig, '[NexoWatt]');
|
|
64
|
+
if (ASSIST_TEXT.test(value) && element.closest?.('.eos-assist-root,.eos-assist-header-root')) element.closest('.eos-assist-root,.eos-assist-header-root')?.remove();
|
|
65
|
+
});
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
let scheduled = false;
|
|
69
|
+
const schedule = () => {
|
|
70
|
+
if (scheduled) return;
|
|
71
|
+
scheduled = true;
|
|
72
|
+
queueMicrotask(() => { scheduled = false; apply(); });
|
|
73
|
+
};
|
|
74
|
+
const start = () => {
|
|
75
|
+
apply();
|
|
76
|
+
new MutationObserver(schedule).observe(document.documentElement, { subtree: true, childList: true, characterData: true });
|
|
77
|
+
document.addEventListener('input', schedule, true);
|
|
78
|
+
document.addEventListener('change', schedule, true);
|
|
79
|
+
};
|
|
80
|
+
document.readyState === 'loading' ? document.addEventListener('DOMContentLoaded', start, { once: true }) : start();
|
|
81
|
+
window.NEXOWATT_EOS_STABLE_V96 = { version: VERSION, refresh: apply };
|
|
82
|
+
})();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* Curated "wiki" of
|
|
3
|
+
* Curated "wiki" of ioBroker how-to knowledge that is injected into the assistant's system prompt.
|
|
4
4
|
*
|
|
5
5
|
* This is meant to grow over time: add a new {@link KnowledgeSection} to {@link KNOWLEDGE_SECTIONS}
|
|
6
6
|
* for each task the assistant should know how to guide the user through. Keep entries short and
|
|
@@ -10,21 +10,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
10
10
|
exports.KNOWLEDGE_SECTIONS = void 0;
|
|
11
11
|
exports.renderAssistantKnowledge = renderAssistantKnowledge;
|
|
12
12
|
exports.KNOWLEDGE_SECTIONS = [
|
|
13
|
-
{
|
|
14
|
-
title: 'NexoWatt EOS product areas and role-aware support',
|
|
15
|
-
body: [
|
|
16
|
-
'EOS Assist can answer questions about the live NexoWatt EOS system: Overview/Cockpit, Modules,',
|
|
17
|
-
'Services, Datapoints, Structure (rooms/functions), System logs, Access & Rights, NexoWatt EOS',
|
|
18
|
-
'applications, System hosts, Files and NexoWatt Backup. It can also inspect installed modules,',
|
|
19
|
-
'running services, object/state metadata, current values, device assignments, logs and history',
|
|
20
|
-
'through the available read tools. Always query real system data before making a system-specific claim.',
|
|
21
|
-
'Respect the authenticated role: Admin/Service may receive full system and service detail;',
|
|
22
|
-
'Installers may receive commissioning and troubleshooting detail available to their account;',
|
|
23
|
-
'End users receive only their permitted Smart Home, display and operating information.',
|
|
24
|
-
'Internal package names and compatibility identifiers are technical details; use NexoWatt EOS product',
|
|
25
|
-
'terminology in normal answers and show technical IDs only when they are required for a precise action.',
|
|
26
|
-
].join(' '),
|
|
27
|
-
},
|
|
28
13
|
{
|
|
29
14
|
title: 'Open an adapter instance configuration',
|
|
30
15
|
body: [
|
|
@@ -212,7 +197,7 @@ exports.KNOWLEDGE_SECTIONS = [
|
|
|
212
197
|
{
|
|
213
198
|
title: 'Run a script in the javascript adapter (run_javascript)',
|
|
214
199
|
body: [
|
|
215
|
-
'To run logic WITH the full
|
|
200
|
+
'To run logic WITH the full ioBroker scripting API, use run_javascript (act mode). It sends the',
|
|
216
201
|
'code to the `javascript` adapter and runs it ONCE (not saved), returning { ok, error, output,',
|
|
217
202
|
'logs }. Choose `language` "js" (default) or "ts". Inside the script the usual API is available:',
|
|
218
203
|
'`getState("id").val`, `setState("id", value)`, `on({id, change:"ne"}, …)`, `schedule(...)`,',
|
|
@@ -225,10 +210,10 @@ exports.KNOWLEDGE_SECTIONS = [
|
|
|
225
210
|
].join('\n'),
|
|
226
211
|
},
|
|
227
212
|
{
|
|
228
|
-
title: 'Automation options in
|
|
213
|
+
title: 'Automation options in ioBroker — pick the right tool',
|
|
229
214
|
body: [
|
|
230
|
-
'
|
|
231
|
-
'SEPARATE adapter that is NOT part of a default
|
|
215
|
+
'ioBroker can automate from simple to fully programmable. IMPORTANT: every option below needs a',
|
|
216
|
+
'SEPARATE adapter that is NOT part of a default ioBroker installation — `scenes`, `scheduler`,',
|
|
232
217
|
'`fullcalendar` and `javascript` (which provides Blockly, JavaScript and TypeScript). Before you',
|
|
233
218
|
'recommend or use one, CHECK whether it is installed (list_adapters, and list_instances for a',
|
|
234
219
|
'running instance). If it is missing, tell the user it must be installed first and offer',
|
|
@@ -286,7 +271,7 @@ exports.KNOWLEDGE_SECTIONS = [
|
|
|
286
271
|
].join('\n'),
|
|
287
272
|
},
|
|
288
273
|
{
|
|
289
|
-
title: '
|
|
274
|
+
title: 'ioBroker JavaScript adapter API (cheat-sheet for scripts and run_javascript)',
|
|
290
275
|
body: [
|
|
291
276
|
'When you WRITE a script (for the Scripts tab or for run_javascript), use the ioBroker javascript',
|
|
292
277
|
'API — it is NOT plain Node.js. Key functions:',
|