signalk-czone 0.3.0-beta.11 → 0.3.0-beta.13

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 CHANGED
@@ -1,7 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.0-beta.13
4
+
5
+ - Adds a fullscreen icon beside the CZone / Boat Systems branding, with expand/collapse states and accessible labels.
6
+ - Adds standalone tablet home-screen launch assets and safe-area spacing.
7
+ - Documents signalk-ssl setup, trusted CA certificate installation and device reboot steps for HTTPS app launch.
8
+
9
+ ## 0.3.0-beta.12
10
+
11
+ - Keeps the connection indicator disconnected when circuit-list REST refreshes succeed but the live WebSocket is down; shows connecting/reconnecting status and the snapshot time.
12
+
3
13
  ## 0.3.0-beta.11
4
14
 
15
+ - Uses the npm `latest` tag for default installation and App Store updates while retaining the BETA version and listing labels.
16
+
5
17
  - Routes circuit current to known physical output owners instead of groups referencing those outputs, restoring Starlink current and correcting Port Fresh Water Pump and Meitaki Nav. AIS attribution.
6
18
  - Preserves existing current mappings when output ownership is ambiguous and adds regression coverage across all six ZCF fixtures.
7
19
 
package/README.md CHANGED
@@ -208,15 +208,15 @@ Once this beta is published and available in your App Store:
208
208
  3. Open **Server → Plugin Config → CZone** and use **Upload and install ZCF** to upload your vessel's `.zcf` configuration.
209
209
  4. Enable the plugin after configuration, then check the **Diagnostics** tab and published currents before testing controls.
210
210
 
211
- The plugin is disabled by default until explicitly enabled. Availability of beta versions in the App Store depends on the server version and publication channel.
211
+ The plugin is disabled by default until explicitly enabled. Tested releases use the npm `latest` tag for default installation and Signal K App Store discovery; the version and listing retain their BETA labels.
212
212
 
213
- ### Explicit npm beta installation
213
+ ### Explicit npm installation
214
214
 
215
215
  After npm publication, run this as the Signal K service user from the server's configuration directory (normally `~/.signalk`, or the directory configured by `SIGNALK_NODE_CONFIG_DIR`):
216
216
 
217
217
  ```bash
218
218
  cd ~/.signalk
219
- npm install signalk-czone@beta --omit=dev --ignore-scripts
219
+ npm install signalk-czone@latest --omit=dev --ignore-scripts
220
220
  ```
221
221
 
222
222
  Restart Signal K and follow the configuration steps above. For Docker or appliance installations, use the corresponding container/configuration directory and restart method.
@@ -225,9 +225,9 @@ For installation from a maintainer-provided npm tarball, run `npm install /path/
225
225
 
226
226
  ### Publishing (maintainers)
227
227
 
228
- Publish `signalk-czone-zcf@0.1.0-beta.1` first. Then run `npm install`, `npm test`, and `npm pack --dry-run` in this repository, verify the runtime files and App Store assets, and run `npm publish --tag beta` after npm authentication.
228
+ Publish `signalk-czone-zcf@0.1.0-beta.1` first. Then run `npm install`, `npm test`, and `npm pack --dry-run` in this repository, verify the runtime files and App Store assets, and run `npm publish --tag latest` after npm authentication.
229
229
 
230
- The package defaults to the `beta` tag. Promoting a beta to `latest` for default installs is a separate release decision.
230
+ The package defaults to `latest`, including versions named `-beta.N`. The GitHub publishing workflow runs when `package.json` or the workflow changes on `main`: it tests and packs the release, publishes it if the version is new, and sets and verifies the `latest` tag. If the version already exists, the workflow promotes that existing version without republishing its contents.
231
231
 
232
232
  ### Beta feedback
233
233
 
@@ -423,3 +423,34 @@ Only the installed path is stored in plugin configuration; the binary ZCF is not
423
423
  - **0.3.0-beta.5** — added explicit AC/DC source classification for downstream telemetry storage while retaining stable circuit-name paths and the diagnostics/status tooling.
424
424
 
425
425
  The beta is intended for extended real-world testing before a stable `0.3.0` release.
426
+
427
+ ## Fullscreen and tablet home-screen launch
428
+
429
+ Open `/signalk-czone/` directly in your browser. The **fullscreen icon** beside
430
+ the CZone / Boat Systems branding enters fullscreen; tap the icon again or use the browser's exit gesture
431
+ (or Escape on desktop) to leave. It appears only when the browser exposes the
432
+ fullscreen API. Embedded views may need permission from their parent page.
433
+
434
+ For a home-screen launch, open the app over **trusted HTTPS**, then use your
435
+ browser's **Install app** / **Add to Home Screen** option (Safari: Share → Add to
436
+ Home Screen). Compatible browsers launch CZone in standalone mode without the
437
+ usual address bar. Installation and fullscreen support vary by device/browser.
438
+ The manifest uses relative URLs, so it follows your server's hostname and port;
439
+ no boat IP address is hard-coded. Plain HTTP on a LAN IP may only offer a shortcut.
440
+
441
+ ### Set up HTTPS for a full-screen app
442
+
443
+ To launch CZone from the tablet or device's home screen without the URL bar and
444
+ browser controls, first set up trusted HTTPS on your Signal K server:
445
+
446
+ 1. Install and configure the **signalk-ssl** plugin.
447
+ 2. In Signal K **Server Settings**, turn on the **Use SSL** toggle and apply the settings. Restart Signal K if prompted.
448
+ 3. On each tablet or device, download the server's CA certificate (`ca.crt`). For example: [https://einstein.local:3443/signalk-ssl/ca.crt](https://einstein.local:3443/signalk-ssl/ca.crt). Replace `einstein.local` and port `3443` with your server's hostname and HTTPS port.
449
+ 4. Import/install `ca.crt` as a trusted CA certificate in the device's certificate settings. Enable trust for the certificate if the device requires a separate step.
450
+ 5. After the certificate installs successfully, you will most likely need to **reboot the tablet or device** before the browser recognises it.
451
+ 6. Open CZone over HTTPS, for example [https://einstein.local:3443/signalk-czone/](https://einstein.local:3443/signalk-czone/), and check that the browser trusts the connection. Use **Install app** / **Add to Home Screen**, then launch CZone from its new home-screen icon.
452
+
453
+ Certificate installation and app installation menus vary by device and browser.
454
+
455
+ The app still needs a live Signal K connection. No offline caching or service
456
+ worker is installed, and installing it does not grant circuit-write permission.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "signalk-czone",
3
- "version": "0.3.0-beta.11",
3
+ "version": "0.3.0-beta.13",
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": [
@@ -57,6 +57,6 @@
57
57
  },
58
58
  "publishConfig": {
59
59
  "access": "public",
60
- "tag": "beta"
60
+ "tag": "latest"
61
61
  }
62
62
  }
@@ -0,0 +1,36 @@
1
+ /* Fullscreen is optional: the ordinary page and home-screen launch work without it. */
2
+ (() => {
3
+ const button = document.getElementById('fullscreenToggle');
4
+ const root = document.documentElement;
5
+ const request = root.requestFullscreen || root.webkitRequestFullscreen;
6
+ const exit = document.exitFullscreen || document.webkitExitFullscreen;
7
+ const enabled = document.fullscreenEnabled ?? document.webkitFullscreenEnabled;
8
+ if (!button || !request || !exit || enabled === false) return;
9
+
10
+ const current = () => document.fullscreenElement || document.webkitFullscreenElement;
11
+ const update = () => {
12
+ const active = !!current();
13
+ const label = active ? 'Exit fullscreen' : 'Show CZone fullscreen';
14
+ button.setAttribute('aria-label', label);
15
+ button.setAttribute('aria-pressed', String(active));
16
+ button.title = label;
17
+ };
18
+ button.hidden = false;
19
+ update();
20
+ document.addEventListener('fullscreenchange', update);
21
+ document.addEventListener('webkitfullscreenchange', update);
22
+ button.addEventListener('click', async () => {
23
+ button.disabled = true;
24
+ try {
25
+ // Invoke directly in the tap handler; browsers require user activation.
26
+ if (current()) await exit.call(document);
27
+ else await request.call(root);
28
+ } catch (error) {
29
+ console.warn('[CZone UI] Fullscreen unavailable', error);
30
+ alert('Fullscreen is unavailable in this browser or embedded view. Try opening CZone directly in a browser, or adding it to your home screen.');
31
+ } finally {
32
+ button.disabled = false;
33
+ update();
34
+ }
35
+ });
36
+ })();
Binary file
Binary file
Binary file
package/public/index.html CHANGED
@@ -5,7 +5,10 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
6
6
  <meta name="theme-color" content="#071321">
7
7
  <link rel="icon" type="image/png" href="icon.png">
8
- <link rel="apple-touch-icon" href="icon.png">
8
+ <link rel="apple-touch-icon" sizes="180x180" href="icon-180.png">
9
+ <link rel="manifest" href="manifest.webmanifest">
10
+ <meta name="apple-mobile-web-app-capable" content="yes">
11
+ <meta name="apple-mobile-web-app-title" content="CZone">
9
12
  <title>CZone Circuits</title>
10
13
  <style>
11
14
  :root {
@@ -39,7 +42,16 @@
39
42
  .sidebar, .rightbar { background: rgba(5, 16, 29, .78); backdrop-filter: blur(18px); border-color: var(--line); }
40
43
  .sidebar { border-right: 1px solid var(--line); padding: 22px 16px; position: sticky; top: 0; height: 100vh; overflow-y: auto; }
41
44
  .rightbar { border-left: 1px solid var(--line); padding: 22px 16px; position: sticky; top: 0; height: 100vh; overflow-y: auto; }
42
- .brand { display:flex; align-items:center; gap:11px; padding: 2px 10px 25px; }
45
+ .brand { display:flex; flex-wrap:nowrap; align-items:center; gap:11px; padding: 2px 10px 25px; }
46
+ .fullscreen-toggle { margin-left:auto; flex:0 0 44px; width:44px; height:44px; display:grid; place-items:center; padding:10px; border:1px solid rgba(56,169,255,.25); border-radius:12px; background:linear-gradient(145deg,rgba(24,213,232,.12),rgba(56,169,255,.05)); color:var(--cyan); cursor:pointer; transition:background .15s, border-color .15s; }
47
+ .fullscreen-toggle:hover { background:rgba(24,213,232,.18); border-color:var(--cyan); }
48
+ .fullscreen-toggle:disabled { opacity:.55; cursor:wait; }
49
+ .fullscreen-toggle svg { width:22px; height:22px; }
50
+ .fullscreen-toggle .fullscreen-collapse, .fullscreen-toggle[aria-pressed="true"] .fullscreen-expand { display:none; }
51
+ .fullscreen-toggle[aria-pressed="true"] .fullscreen-collapse { display:inline; }
52
+ .fullscreen-toggle[hidden] { display:none; }
53
+ .fullscreen-toggle:focus-visible { outline:2px solid var(--cyan); outline-offset:2px; }
54
+ body { padding:env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left); }
43
55
  .brand-mark { width:38px; height:38px; display:block; object-fit:contain; border-radius:10px; box-shadow: 0 8px 25px rgba(31,154,255,.24); }
44
56
  .brand strong { display:block; font-size:17px; letter-spacing:.2px; }
45
57
  .brand span { color:var(--muted); font-size:12px; }
@@ -225,7 +237,12 @@
225
237
  <div class="app">
226
238
  <div class="leftwell">
227
239
  <aside class="sidebar">
228
- <div class="brand"><img class="brand-mark" src="icon.png" alt="CZone" width="38" height="38"><div><strong>CZone</strong><span>Boat Systems</span></div></div>
240
+ <div class="brand"><img class="brand-mark" src="icon.png" alt="CZone" width="38" height="38"><div><strong>CZone</strong><span>Boat Systems</span></div><button id="fullscreenToggle" class="fullscreen-toggle" type="button" aria-label="Show CZone fullscreen" title="Show CZone fullscreen" aria-pressed="false" hidden>
241
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false">
242
+ <path class="fullscreen-expand" d="M8 3H3v5m13-5h5v5M3 16v5h5m13-5v5h-5"/>
243
+ <path class="fullscreen-collapse" d="M3 8h5V3m8 0v5h5M8 21v-5H3m18 0h-5v5"/>
244
+ </svg>
245
+ </button></div>
229
246
  <div class="nav" id="mainNav"></div>
230
247
  <div class="separator"></div>
231
248
  <div class="section-label">Categories</div>
@@ -260,6 +277,7 @@
260
277
  </main>
261
278
 
262
279
  </div>
280
+ <script src="fullscreen.js"></script>
263
281
  <script>
264
282
  (() => {
265
283
  const API = '/plugins/signalk-czone';
@@ -293,7 +311,7 @@
293
311
  ];
294
312
  const topLevelCategories = new Set(['AC', 'DC', 'Favorites']);
295
313
  let circuits = [], status = {}, modes = [], activeMode = null, vesselName = '', view = 'all', category = null, query = '', sort = 'name', socket = null;
296
- let socketReconnectTimer = null, watchdogTimer = null, lastSocketUpdateAt = 0, socketConnected = false;
314
+ let socketReconnectTimer = null, watchdogTimer = null, lastSocketUpdateAt = 0, socketConnected = false, socketEverOpen = false;
297
315
  const WS_WATCHDOG_MS = 120000;
298
316
  const COMMAND_CONFIRM_MS = 15000;
299
317
  const COMMAND_RECONCILE_DELAY_MS = 200;
@@ -565,8 +583,16 @@
565
583
  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 });
566
584
  mergeRest(data);
567
585
  if (incoming.length === 0) throw new Error('CZone plugin returned an empty circuit list');
568
- document.querySelector('#connection').classList.add('ok');
569
- document.querySelector('#connection').innerHTML = '<i></i><span>Connected · live circuit data</span>';
586
+ // "Connected" only while the live feed is up. This read is a one-off
587
+ // (first load, or the watchdog's reconciliation): with the feed down
588
+ // the page would otherwise say Connected while nothing on it moves.
589
+ const conn = document.querySelector('#connection');
590
+ conn.classList.toggle('ok', socketConnected);
591
+ conn.innerHTML = socketConnected
592
+ ? '<i></i><span>Connected · live circuit data</span>'
593
+ : socketEverOpen
594
+ ? `<i></i><span>Live updates lost · reconnecting… (circuits as of ${new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' })})</span>`
595
+ : '<i></i><span>Circuits loaded · connecting live updates…</span>';
570
596
  render();
571
597
  return true;
572
598
  } catch (e) {
@@ -1018,6 +1044,7 @@
1018
1044
  socket = new WebSocket(SK);
1019
1045
  socket.onopen = () => {
1020
1046
  socketConnected = true;
1047
+ socketEverOpen = true;
1021
1048
  lastSocketUpdateAt = Date.now();
1022
1049
  updateSocketIndicator('Connected · WebSocket live', true);
1023
1050
  sendSubscriptions();
@@ -1046,7 +1073,7 @@
1046
1073
  };
1047
1074
  socket.onclose = () => {
1048
1075
  socketConnected = false;
1049
- updateSocketIndicator('Reconnecting to Signal K…', false);
1076
+ updateSocketIndicator(socketEverOpen ? 'Live updates lost · reconnecting…' : 'Connecting live updates…', false);
1050
1077
  console.warn('[CZone WS] closed; reconnecting');
1051
1078
  clearTimeout(socketReconnectTimer);
1052
1079
  socketReconnectTimer = setTimeout(connectSocket, 3000);
@@ -0,0 +1,25 @@
1
+ {
2
+ "id": "./",
3
+ "name": "CZone Boat Systems",
4
+ "short_name": "CZone",
5
+ "description": "Monitor and control your boat\u2019s CZone electrical systems through Signal K.",
6
+ "start_url": "./",
7
+ "scope": "./",
8
+ "display": "standalone",
9
+ "background_color": "#06101d",
10
+ "theme_color": "#071321",
11
+ "icons": [
12
+ {
13
+ "src": "icon-192.png",
14
+ "sizes": "192x192",
15
+ "type": "image/png",
16
+ "purpose": "any"
17
+ },
18
+ {
19
+ "src": "icon-512.png",
20
+ "sizes": "512x512",
21
+ "type": "image/png",
22
+ "purpose": "any"
23
+ }
24
+ ]
25
+ }