signalk-czone 0.3.0-beta.16 → 0.3.0-beta.18
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/CHANGELOG.md +10 -0
- package/package.json +2 -2
- package/public/index.html +93 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.0-beta.18
|
|
4
|
+
|
|
5
|
+
- Adds in-page Signal K sign-in for browser and installed home-screen apps, without leaving CZone (#35, thanks @superstar76).
|
|
6
|
+
- Remembers login across app restarts, preserves the form during refreshes, and shows clear login errors with the Admin UI login retained as a fallback.
|
|
7
|
+
|
|
8
|
+
## 0.3.0-beta.17
|
|
9
|
+
|
|
10
|
+
- Prompts for Signal K login when CZone circuit requests are unauthorized, returning to the webapp after sign-in (#34).
|
|
11
|
+
- Shows permission and circuit-loading errors with a retry option instead of waiting indefinitely for Signal K.
|
|
12
|
+
|
|
3
13
|
## 0.3.0-beta.16
|
|
4
14
|
|
|
5
15
|
- Adds an opt-in Victron GX switch pane for CZone circuits on the GX, Remote Console and VRM (#33).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "signalk-czone",
|
|
3
|
-
"version": "0.3.0-beta.
|
|
3
|
+
"version": "0.3.0-beta.18",
|
|
4
4
|
"description": "BETA: Signal K CZone monitoring and control using dynamically parsed ZCF configuration and NMEA 2000",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"keywords": [
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"icon.png"
|
|
43
43
|
],
|
|
44
44
|
"scripts": {
|
|
45
|
-
"test": "node test/zcf.test.js && node test/zcf-fixtures.test.js && node test/circuit-state.test.js && node test/czone.test.js && node test/nmea2000.test.js && node test/control-x-plus.test.js && node test/signalk.test.js && node test/write.test.js && node test/circuit-status.test.js && node test/status-mapping-regression.test.js && node test/state.test.js && node test/config-network.test.js && node test/config-namespace.test.js && node test/monitoring.test.js && node test/confirm-off.test.js && node test/webapp-current.test.js && node test/bridge.test.js && node test/victron-switches.test.js && node test/victron-label-steady.test.js && node test/switch-pane-plugin.test.js && node test/trends.test.js && node test/inputs.test.js && node --require ./test/helpers/gx-low-space.cjs test/trends.test.js"
|
|
45
|
+
"test": "node test/zcf.test.js && node test/zcf-fixtures.test.js && node test/circuit-state.test.js && node test/czone.test.js && node test/nmea2000.test.js && node test/control-x-plus.test.js && node test/signalk.test.js && node test/write.test.js && node test/circuit-status.test.js && node test/status-mapping-regression.test.js && node test/state.test.js && node test/config-network.test.js && node test/config-namespace.test.js && node test/monitoring.test.js && node test/confirm-off.test.js && node test/webapp-current.test.js && node test/webapp-auth.test.js && node test/bridge.test.js && node test/victron-switches.test.js && node test/victron-label-steady.test.js && node test/switch-pane-plugin.test.js && node test/trends.test.js && node test/inputs.test.js && node --require ./test/helpers/gx-low-space.cjs test/trends.test.js"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"signalk-czone-zcf": "0.1.0-beta.2"
|
package/public/index.html
CHANGED
|
@@ -75,6 +75,12 @@
|
|
|
75
75
|
.hero h1, .hero p { position:relative; z-index:1; }
|
|
76
76
|
.hero h1 { margin:0; font-size:30px; line-height:1.15; letter-spacing:-.7px; }
|
|
77
77
|
.hero p { margin:9px 0 0; color:#a9c2d9; font-size:14px; }
|
|
78
|
+
.login-form { display:grid; gap:8px; max-width:280px; margin:10px 0; }
|
|
79
|
+
.login-form input { padding:10px 12px; border-radius:10px; border:1px solid var(--line); background:rgba(255,255,255,.05); color:var(--text); font-size:16px; }
|
|
80
|
+
.login-form button { padding:10px 12px; border-radius:10px; border:0; background:linear-gradient(135deg,#168eea,#2164b8); color:#fff; font-weight:700; font-size:15px; cursor:pointer; }
|
|
81
|
+
.login-form button:disabled { opacity:.6; cursor:default; }
|
|
82
|
+
.login-form .login-error { color:#ff8a9a; font-size:13px; }
|
|
83
|
+
.login-form .login-error:empty { display:none; }
|
|
78
84
|
.search { position:relative; }
|
|
79
85
|
.search input { width:100%; border:1px solid var(--line); background:#0b1d30; color:white; border-radius:999px; padding:11px 15px 11px 38px; outline:none; }
|
|
80
86
|
.search input:focus { border-color:rgba(56,169,255,.65); box-shadow:0 0 0 3px rgba(56,169,255,.10); }
|
|
@@ -353,7 +359,7 @@
|
|
|
353
359
|
<div class="connection" id="connection"><i></i><span>Connecting to Signal K…</span></div>
|
|
354
360
|
<section class="hero">
|
|
355
361
|
<h1 id="heroTitle">Your Boat, Your Circuits</h1>
|
|
356
|
-
<p>Monitor and control your boat's electrical systems.</p><div id="writeGate" style="display:none;margin-top:12px;padding:10px 12px;border:1px solid rgba(255,195,77,.35);border-radius:10px;background:rgba(255,195,77,.08);color:#ffd98a;font-size:12px;line-height:1.5"></div>
|
|
362
|
+
<p>Monitor and control your boat's electrical systems.</p><div id="loadError" role="alert" hidden style="position:relative;z-index:1;margin-top:12px;padding:12px;border:1px solid var(--amber);border-radius:10px;background:var(--panel);line-height:1.5"></div><div id="writeGate" style="display:none;margin-top:12px;padding:10px 12px;border:1px solid rgba(255,195,77,.35);border-radius:10px;background:rgba(255,195,77,.08);color:#ffd98a;font-size:12px;line-height:1.5"></div>
|
|
357
363
|
</section>
|
|
358
364
|
<section class="section trend-panel" id="trendPanel">
|
|
359
365
|
<div class="section-head"><span>📈</span><div><h2 id="trendTitle"></h2><span class="subtle" id="trendSub"></span></div><button type="button" class="sort trend-close" onclick="closeTrend()" aria-label="Close trend">✕ Close</button></div>
|
|
@@ -700,10 +706,90 @@
|
|
|
700
706
|
if (Array.isArray(data.modes)) modes = data.modes;
|
|
701
707
|
if (data.activeMode) activeMode = data.activeMode;
|
|
702
708
|
}
|
|
709
|
+
function showLoadError(message, signIn) {
|
|
710
|
+
const el = document.querySelector('#loadError');
|
|
711
|
+
// Keep a sign-in form that is already showing: someone may be typing in it.
|
|
712
|
+
if (signIn && !el.hidden && el.loginForm) return;
|
|
713
|
+
el.loginForm = null;
|
|
714
|
+
el.textContent = message + ' ';
|
|
715
|
+
if (signIn) el.appendChild(loginForm(el));
|
|
716
|
+
const link = document.createElement('a');
|
|
717
|
+
link.style.color = 'var(--cyan)';
|
|
718
|
+
if (signIn) {
|
|
719
|
+
const here = window.location.pathname + window.location.search + window.location.hash;
|
|
720
|
+
link.href = '/admin/#/login?redirect=' + encodeURIComponent(here);
|
|
721
|
+
link.textContent = 'Or sign in on the Signal K login page';
|
|
722
|
+
} else {
|
|
723
|
+
link.href = window.location.href;
|
|
724
|
+
link.textContent = 'Retry';
|
|
725
|
+
link.onclick = event => { event.preventDefault(); window.location.reload(); };
|
|
726
|
+
}
|
|
727
|
+
el.appendChild(link);
|
|
728
|
+
el.hidden = false;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
// Sign in on the page itself. A home-screen app (iPad, Android) keeps its
|
|
732
|
+
// own login, separate from the browser's, and has no address bar; the link
|
|
733
|
+
// to the admin page stays as the alternative. "Remember me" keeps the login
|
|
734
|
+
// after the app is closed, for as long as Signal K's token lasts.
|
|
735
|
+
function loginForm(banner) {
|
|
736
|
+
const form = document.createElement('form');
|
|
737
|
+
form.className = 'login-form';
|
|
738
|
+
const field = (name, type, autocomplete, placeholder) => {
|
|
739
|
+
const input = document.createElement('input');
|
|
740
|
+
input.name = name; input.type = type; input.required = true;
|
|
741
|
+
input.autocomplete = autocomplete; input.placeholder = placeholder;
|
|
742
|
+
form.appendChild(input);
|
|
743
|
+
return input;
|
|
744
|
+
};
|
|
745
|
+
const user = field('username', 'text', 'username', 'Signal K user');
|
|
746
|
+
const pass = field('password', 'password', 'current-password', 'Password');
|
|
747
|
+
const button = document.createElement('button');
|
|
748
|
+
button.type = 'submit'; button.textContent = 'Log in';
|
|
749
|
+
form.appendChild(button);
|
|
750
|
+
const error = document.createElement('span');
|
|
751
|
+
error.className = 'login-error';
|
|
752
|
+
form.appendChild(error);
|
|
753
|
+
form.onsubmit = async event => {
|
|
754
|
+
event.preventDefault();
|
|
755
|
+
error.textContent = '';
|
|
756
|
+
button.disabled = true;
|
|
757
|
+
try {
|
|
758
|
+
const r = await fetch('/signalk/v1/auth/login', { method:'POST', credentials:'include', headers:{ 'Content-Type':'application/json' },
|
|
759
|
+
body: JSON.stringify({ username: user.value, password: pass.value, rememberMe: true }) });
|
|
760
|
+
if (r.ok) { window.location.reload(); return; }
|
|
761
|
+
error.textContent = r.status === 401 ? 'Wrong user name or password.' : 'Signal K refused the login (HTTP ' + r.status + ').';
|
|
762
|
+
} catch (_) {
|
|
763
|
+
error.textContent = 'Signal K could not be reached.';
|
|
764
|
+
}
|
|
765
|
+
button.disabled = false;
|
|
766
|
+
};
|
|
767
|
+
banner.loginForm = form;
|
|
768
|
+
return form;
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
async function showCircuitRequestError(response) {
|
|
772
|
+
let signIn = response.status === 401;
|
|
773
|
+
if (response.status === 403) {
|
|
774
|
+
try {
|
|
775
|
+
const r = await fetch('/skServer/loginStatus', { cache:'no-store', credentials:'include' });
|
|
776
|
+
if (r.ok) signIn = (await r.json()).status === 'notLoggedIn';
|
|
777
|
+
} catch (_) { /* Keep the permission error if login status cannot be checked. */ }
|
|
778
|
+
}
|
|
779
|
+
const message = signIn
|
|
780
|
+
? 'Sign in to Signal K to load CZone circuits.'
|
|
781
|
+
: response.status === 403
|
|
782
|
+
? 'Signal K denied access to CZone circuits. Your account may not have the required permissions.'
|
|
783
|
+
: 'Could not load CZone circuits (HTTP ' + response.status + '). Check the Signal K server and CZone plugin.';
|
|
784
|
+
showLoadError(message, signIn);
|
|
785
|
+
// The plotter hides the banner, and its login is the Navico embedder's token.
|
|
786
|
+
updateSocketIndicator(signIn ? (MFD ? 'Sign in required: set the token in the Navico embedder' : 'Sign in required') : 'Unable to load CZone circuits', false);
|
|
787
|
+
}
|
|
788
|
+
|
|
703
789
|
async function refreshOnce(reason = 'manual') {
|
|
704
790
|
try {
|
|
705
791
|
const r = await fetch(`${API}/circuits`, { cache:'no-store', credentials:'include' });
|
|
706
|
-
if (!r.ok)
|
|
792
|
+
if (!r.ok) { await showCircuitRequestError(r); return false; }
|
|
707
793
|
const data = await r.json();
|
|
708
794
|
const incoming = Array.isArray(data.circuits) ? data.circuits : [];
|
|
709
795
|
console.debug('[CZone UI] REST refresh', reason, { circuits: incoming.length, modes: Array.isArray(data.modes) ? data.modes.length : 0, sendingEnabled: data.sendingEnabled === true, nmeaReady: data.nmeaReady === true });
|
|
@@ -719,12 +805,14 @@
|
|
|
719
805
|
: socketEverOpen
|
|
720
806
|
? `<i></i><span>Live updates lost · reconnecting… (circuits as of ${new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' })})</span>`
|
|
721
807
|
: '<i></i><span>Circuits loaded · connecting live updates…</span>';
|
|
808
|
+
document.querySelector('#loadError').hidden = true;
|
|
722
809
|
render();
|
|
723
810
|
return true;
|
|
724
811
|
} catch (e) {
|
|
725
812
|
console.warn('[CZone UI] REST refresh failed', reason, e);
|
|
726
813
|
document.querySelector('#connection').classList.remove('ok');
|
|
727
|
-
|
|
814
|
+
showLoadError('Could not load CZone circuits. Check the Signal K connection and CZone plugin. ' + e.message, false);
|
|
815
|
+
updateSocketIndicator('Unable to load CZone circuits', false);
|
|
728
816
|
return false;
|
|
729
817
|
}
|
|
730
818
|
}
|
|
@@ -1334,7 +1422,8 @@
|
|
|
1334
1422
|
}
|
|
1335
1423
|
|
|
1336
1424
|
refreshVesselName();
|
|
1337
|
-
refreshOnce('initial-load').then(
|
|
1425
|
+
refreshOnce('initial-load').then(loaded => {
|
|
1426
|
+
if (!loaded) return;
|
|
1338
1427
|
if (circuits.length) {
|
|
1339
1428
|
connectSocket();
|
|
1340
1429
|
sendSubscriptions();
|