signalk-history-widgets 0.1.0 → 0.3.0
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/README.md +56 -0
- package/docs/screenshots/levels.png +0 -0
- package/docs/screenshots/wind-steering.png +0 -0
- package/package.json +6 -4
- package/plugin/index.js +51 -10
- package/plugin/index.js.map +1 -1
- package/public/assets/battery-BhqU92AD.js +1 -0
- package/public/assets/battery-config-CUDy2wrq.js +1 -0
- package/public/assets/{units-D0PJ17NW.js → chunk-U7YORXZ7-BLTBSYrv.js} +1 -1
- package/public/assets/{config-CVQm5UWB.css → config-DBAHPNIf.css} +1 -1
- package/public/assets/config-model-DvTH1O2f.js +1 -0
- package/public/assets/{config-CzQzAyc1.js → config-n3H05jK1.js} +2 -2
- package/public/assets/level-CwEJ7gss.js +1 -0
- package/public/assets/level-config-B0mMmED6.js +21 -0
- package/public/assets/level-model-0XbpyS2Q.js +1 -0
- package/public/assets/steer-BhT70PR7.js +1 -0
- package/public/assets/steer-config-CxpZ3N7O.js +28 -0
- package/public/assets/steer-model-DA5-9SMr.js +1 -0
- package/public/assets/styles-CjtPzFRR.js +1 -0
- package/public/assets/tank-Gyfo7ABV.js +1 -0
- package/public/assets/tank-config-C6CTiHir.js +1 -0
- package/public/assets/units-Cn4uhL0-.js +1 -0
- package/public/assets/widget-CH38Wyvl.js +1 -0
- package/public/battery-config.html +17 -0
- package/public/battery.html +19 -0
- package/public/config.html +5 -3
- package/public/steer-config.html +15 -0
- package/public/steer.html +17 -0
- package/public/tank-config.html +17 -0
- package/public/tank.html +19 -0
- package/public/widget.html +6 -3
- package/public/assets/widget-BPqCcSdj.js +0 -1
- /package/public/assets/{widget-D6wkrAFq.css → styles-D6wkrAFq.css} +0 -0
package/README.md
CHANGED
|
@@ -10,6 +10,9 @@ moves.
|
|
|
10
10
|
|
|
11
11
|

|
|
12
12
|
|
|
13
|
+
The package also brings a **Wind steering** dial and **Tank level** and
|
|
14
|
+
**Battery charge** widgets, described [below](#wind-steering).
|
|
15
|
+
|
|
13
16
|
## Use
|
|
14
17
|
|
|
15
18
|
1. Install **History Widgets** from the App Store and restart the server. The
|
|
@@ -40,6 +43,59 @@ Unit Preferences, unless the strip sets its own.
|
|
|
40
43
|
|
|
41
44
|

|
|
42
45
|
|
|
46
|
+
## Wind steering
|
|
47
|
+
|
|
48
|
+
A square dial for steering by the wind, bow always up. In its default
|
|
49
|
+
compass mode the card turns with the heading; set to wind angles it shows 0
|
|
50
|
+
to 180 degrees port and starboard instead. On the dial:
|
|
51
|
+
|
|
52
|
+

|
|
53
|
+
|
|
54
|
+
- the apparent wind (blue pointer, **A**) and the true wind (orange marker,
|
|
55
|
+
**T**), with AWS, TWS, AWA and TWA in the corners;
|
|
56
|
+
- dashed close-hauled lines for both tacks: red is the heading on port tack,
|
|
57
|
+
green on starboard tack, at the close-hauled angle off the true wind
|
|
58
|
+
(45 degrees unless set otherwise);
|
|
59
|
+
- the wind shift fan: how far the true wind direction swung over the last
|
|
60
|
+
minute (or the window you choose), so a lift or a header shows at a glance;
|
|
61
|
+
- the next waypoint (purple dot) when a destination is active, course over
|
|
62
|
+
ground (pink diamond) when the boat is moving, the current as an arrow
|
|
63
|
+
with its drift, and the rudder angle as a bar under the dial.
|
|
64
|
+
|
|
65
|
+
Press and hold the dial to choose the dial type, true or magnetic north,
|
|
66
|
+
true wind through the water or over the ground, the close-hauled angle, the
|
|
67
|
+
shift window, and which marks to show. Values older than ten seconds are
|
|
68
|
+
hidden rather than shown stale. It reads standard Signal K paths:
|
|
69
|
+
`navigation.headingTrue`, `navigation.courseOverGroundTrue`,
|
|
70
|
+
`navigation.course.calcValues.bearingTrue` and `environment.current.setTrue`
|
|
71
|
+
(with magnetic north `navigation.headingMagnetic`,
|
|
72
|
+
`navigation.courseOverGroundMagnetic`,
|
|
73
|
+
`navigation.course.calcValues.bearingMagnetic` and
|
|
74
|
+
`environment.current.setMagnetic`), plus `environment.wind.*`,
|
|
75
|
+
`navigation.speedOverGround`, `environment.current.drift` and
|
|
76
|
+
`steering.rudderAngle`.
|
|
77
|
+
|
|
78
|
+
## Tank level and battery charge
|
|
79
|
+
|
|
80
|
+
A tank's level or a battery bank's state of charge as a glass cylinder or, if
|
|
81
|
+
you prefer, a flat bar, with the percentage underneath. A tank that publishes
|
|
82
|
+
its capacity also shows the volume left, and a battery bank its voltage, in
|
|
83
|
+
the units set in the server's Unit Preferences. The colour follows the zones
|
|
84
|
+
set for the level in the server's metadata (`meta.zones`): green when the
|
|
85
|
+
level is normal, yellow in an alert or warn zone, red in an alarm or
|
|
86
|
+
emergency zone. Marks beside the bar show where the zones begin, so you see
|
|
87
|
+
how far the level is from the next one. Without zones the bar stays green.
|
|
88
|
+
|
|
89
|
+

|
|
90
|
+
|
|
91
|
+
Press and hold the widget to pick another tank or bank. The panel lists every
|
|
92
|
+
`tanks.*.*.currentLevel` (or every
|
|
93
|
+
`electrical.batteries.*.capacity.stateOfCharge`) on the server with its
|
|
94
|
+
current level, plus any other 0 to 100 % value, and offers the look (3D or
|
|
95
|
+
flat) and whether to show the volume or voltage as buttons, so it is set up
|
|
96
|
+
with a few taps and no keyboard. A level older than two minutes shows as
|
|
97
|
+
`--`.
|
|
98
|
+
|
|
43
99
|
## Requirements
|
|
44
100
|
|
|
45
101
|
- Signal K server 2.22.1 or newer (History API v2) on Node 24 or newer
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "signalk-history-widgets",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "History strip widgets for Signal K chartplotters: any value over the last minutes or hours as a vertical strip with min, average and max,
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "History strip, wind steering and tank level widgets for Signal K chartplotters: any value over the last minutes or hours as a vertical strip with min, average and max, a wind steering dial with close-hauled lines and the recent wind shift, and tank levels and battery charge coloured by their zones",
|
|
5
5
|
"author": "Dirk Wahrheit",
|
|
6
6
|
"main": "plugin/index.js",
|
|
7
7
|
"type": "module",
|
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
"signalk-category-instruments",
|
|
23
23
|
"wind",
|
|
24
24
|
"history",
|
|
25
|
-
"trend"
|
|
25
|
+
"trend",
|
|
26
|
+
"steering"
|
|
26
27
|
],
|
|
27
28
|
"signalk": {
|
|
28
29
|
"displayName": "History Widgets",
|
|
@@ -33,7 +34,8 @@
|
|
|
33
34
|
"screenshots": [
|
|
34
35
|
"./docs/screenshots/wind-strips.png",
|
|
35
36
|
"./docs/screenshots/config.png",
|
|
36
|
-
"./docs/screenshots/awa-24h.png"
|
|
37
|
+
"./docs/screenshots/awa-24h.png",
|
|
38
|
+
"./docs/screenshots/wind-steering.png"
|
|
37
39
|
]
|
|
38
40
|
},
|
|
39
41
|
"files": [
|
package/plugin/index.js
CHANGED
|
@@ -48,11 +48,27 @@ var strip = (id, title, size) => ({
|
|
|
48
48
|
configPanel: "strip-config",
|
|
49
49
|
lifecycle: "whileEnabled"
|
|
50
50
|
});
|
|
51
|
+
var level = (id, title) => ({
|
|
52
|
+
id,
|
|
53
|
+
title,
|
|
54
|
+
type: "iframe",
|
|
55
|
+
url: `${ASSET_BASE}/${id}.html`,
|
|
56
|
+
size: "1x2",
|
|
57
|
+
configPanel: `${id}-config`,
|
|
58
|
+
lifecycle: "whileEnabled"
|
|
59
|
+
});
|
|
60
|
+
var levelPanel = (id, title) => ({
|
|
61
|
+
id: `${id}-config`,
|
|
62
|
+
title,
|
|
63
|
+
type: "iframe",
|
|
64
|
+
url: `${ASSET_BASE}/${id}-config.html`,
|
|
65
|
+
lifecycle: "onOpen"
|
|
66
|
+
});
|
|
51
67
|
/** The Plotter Extensions manifest (apiVersion 1). */
|
|
52
68
|
function buildManifest(version) {
|
|
53
69
|
return {
|
|
54
70
|
name: "History Widgets",
|
|
55
|
-
description: "Any value over the last minutes or hours as a vertical strip with its minimum, average and maximum.",
|
|
71
|
+
description: "Any value over the last minutes or hours as a vertical strip with its minimum, average and maximum, a wind steering dial with the wind shift of the last minutes, and tank levels and battery charge coloured by their zones.",
|
|
56
72
|
version,
|
|
57
73
|
apiVersion: "1",
|
|
58
74
|
requires: [
|
|
@@ -61,14 +77,39 @@ function buildManifest(version) {
|
|
|
61
77
|
"signalk.stream"
|
|
62
78
|
],
|
|
63
79
|
optional: ["nightMode"],
|
|
64
|
-
widgets: [
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
80
|
+
widgets: [
|
|
81
|
+
strip("strip", "History strip", "1x2"),
|
|
82
|
+
strip("strip-wide", "History strip (wide)", "2x2"),
|
|
83
|
+
{
|
|
84
|
+
id: "wind-steering",
|
|
85
|
+
title: "Wind steering",
|
|
86
|
+
type: "iframe",
|
|
87
|
+
url: `${ASSET_BASE}/steer.html`,
|
|
88
|
+
size: "2x2",
|
|
89
|
+
configPanel: "wind-steering-config",
|
|
90
|
+
lifecycle: "whileEnabled"
|
|
91
|
+
},
|
|
92
|
+
level("tank", "Tank level"),
|
|
93
|
+
level("battery", "Battery charge")
|
|
94
|
+
],
|
|
95
|
+
panels: [
|
|
96
|
+
{
|
|
97
|
+
id: "strip-config",
|
|
98
|
+
title: "History strip",
|
|
99
|
+
type: "iframe",
|
|
100
|
+
url: `${ASSET_BASE}/config.html`,
|
|
101
|
+
lifecycle: "onOpen"
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
id: "wind-steering-config",
|
|
105
|
+
title: "Wind steering",
|
|
106
|
+
type: "iframe",
|
|
107
|
+
url: `${ASSET_BASE}/steer-config.html`,
|
|
108
|
+
lifecycle: "onOpen"
|
|
109
|
+
},
|
|
110
|
+
levelPanel("tank", "Tank level"),
|
|
111
|
+
levelPanel("battery", "Battery charge")
|
|
112
|
+
]
|
|
72
113
|
};
|
|
73
114
|
}
|
|
74
115
|
// src/index.ts
|
|
@@ -112,7 +153,7 @@ function historyWidgets(app) {
|
|
|
112
153
|
return {
|
|
113
154
|
id: PLUGIN_ID,
|
|
114
155
|
name: "History Widgets",
|
|
115
|
-
description: "History strip widgets for Freeboard-SK and other Plotter Extensions hosts",
|
|
156
|
+
description: "History strip and wind steering widgets for Freeboard-SK and other Plotter Extensions hosts",
|
|
116
157
|
schema: () => ({
|
|
117
158
|
type: "object",
|
|
118
159
|
properties: {}
|
package/plugin/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/assets.ts","../src/manifest.ts","../src/index.ts"],"sourcesContent":["import { readdir, readFile } from 'node:fs/promises'\nimport { extname, join, relative, sep } from 'node:path'\n\nconst TYPES: Record<string, string> = {\n '.html': 'text/html; charset=utf-8',\n '.js': 'text/javascript; charset=utf-8',\n '.css': 'text/css; charset=utf-8',\n '.svg': 'image/svg+xml',\n '.map': 'application/json'\n}\n\nexport interface Asset {\n type: string\n body: Buffer\n}\n\n/**\n * Every file of the built web pages, keyed by its URL path. Serving from this\n * list rather than from the file system keeps requests out of everything\n * else on disk.\n */\nexport async function loadAssets(dir: string): Promise<Map<string, Asset>> {\n const assets = new Map<string, Asset>()\n const entries = await readdir(dir, { recursive: true, withFileTypes: true })\n for (const entry of entries) {\n if (!entry.isFile()) continue\n const file = join(entry.parentPath, entry.name)\n const url = '/' + relative(dir, file).split(sep).join('/')\n assets.set(url, {\n type: TYPES[extname(file)] ?? 'application/octet-stream',\n body: await readFile(file)\n })\n }\n return assets\n}\n","export const PLUGIN_ID = 'signalk-history-widgets'\n\n/**\n * Where the widget pages are served. Not under /plugins/<id>, which is for\n * admins only: every user of the chartplotter loads these.\n */\nexport const ASSET_BASE = `/plotterext/${PLUGIN_ID}`\n\nconst strip = (id: string, title: string, size: string) => ({\n id,\n title,\n type: 'iframe',\n url: `${ASSET_BASE}/widget.html`,\n size,\n configPanel: 'strip-config',\n lifecycle: 'whileEnabled'\n})\n\n/** The Plotter Extensions manifest (apiVersion 1). */\nexport function buildManifest(version: string) {\n return {\n name: 'History Widgets',\n description:\n 'Any value over the last minutes or hours as a vertical strip with its minimum, average and maximum.',\n version,\n apiVersion: '1',\n requires: ['widgets', 'panels.iframe', 'signalk.stream'],\n optional: ['nightMode'],\n widgets: [\n strip('strip', 'History strip', '1x2'),\n strip('strip-wide', 'History strip (wide)', '2x2')\n ],\n panels: [\n {\n id: 'strip-config',\n title: 'History strip',\n type: 'iframe',\n url: `${ASSET_BASE}/config.html`,\n lifecycle: 'onOpen'\n }\n ]\n }\n}\n","import { readFileSync } from 'node:fs'\nimport { fileURLToPath } from 'node:url'\nimport type { Request, Response, NextFunction } from 'express'\nimport type { Plugin, ServerAPI } from '@signalk/server-api'\nimport { loadAssets, type Asset } from './assets.js'\nimport { ASSET_BASE, PLUGIN_ID, buildManifest } from './manifest.js'\n\nconst PUBLIC_DIR = fileURLToPath(new URL('../public', import.meta.url))\nconst PACKAGE_JSON = fileURLToPath(new URL('../package.json', import.meta.url))\n\n/** The express app the server hands plugins; ServerAPI leaves `use` out. */\ntype App = ServerAPI & {\n use?: (path: string, handler: (req: Request, res: Response, next: NextFunction) => void) => void\n}\n\nfunction packageVersion(): string {\n const pkg: unknown = JSON.parse(readFileSync(PACKAGE_JSON, 'utf8'))\n const version = (pkg as { version?: unknown }).version\n return typeof version === 'string' ? version : '0.0.0'\n}\n\nexport default function historyWidgets(app: App): Plugin {\n let running = false\n let assets = new Map<string, Asset>()\n let mounted = false\n let registered = false\n const manifest = buildManifest(packageVersion())\n\n const mount = () => {\n if (mounted || !app.use) return\n mounted = true\n // express cannot unmount a route, so a stopped plugin answers 404.\n app.use(ASSET_BASE, (req, res, next) => {\n const asset = running ? assets.get(req.path) : undefined\n if (!asset || (req.method !== 'GET' && req.method !== 'HEAD')) {\n next()\n return\n }\n res.type(asset.type).send(asset.body)\n })\n }\n\n const register = () => {\n if (registered) return\n registered = true\n app.registerResourceProvider({\n type: 'plotterExtensions',\n methods: {\n listResources: () => Promise.resolve(running ? { [PLUGIN_ID]: manifest } : {}),\n getResource: (id: string) =>\n running && id === PLUGIN_ID\n ? Promise.resolve(manifest)\n : Promise.reject(new Error(`No such plotterExtensions resource: ${id}`)),\n setResource: () => Promise.reject(new Error(`${PLUGIN_ID} is read-only`)),\n deleteResource: () => Promise.reject(new Error(`${PLUGIN_ID} is read-only`))\n }\n })\n }\n\n return {\n id: PLUGIN_ID,\n name: 'History Widgets',\n description
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/assets.ts","../src/manifest.ts","../src/index.ts"],"sourcesContent":["import { readdir, readFile } from 'node:fs/promises'\nimport { extname, join, relative, sep } from 'node:path'\n\nconst TYPES: Record<string, string> = {\n '.html': 'text/html; charset=utf-8',\n '.js': 'text/javascript; charset=utf-8',\n '.css': 'text/css; charset=utf-8',\n '.svg': 'image/svg+xml',\n '.map': 'application/json'\n}\n\nexport interface Asset {\n type: string\n body: Buffer\n}\n\n/**\n * Every file of the built web pages, keyed by its URL path. Serving from this\n * list rather than from the file system keeps requests out of everything\n * else on disk.\n */\nexport async function loadAssets(dir: string): Promise<Map<string, Asset>> {\n const assets = new Map<string, Asset>()\n const entries = await readdir(dir, { recursive: true, withFileTypes: true })\n for (const entry of entries) {\n if (!entry.isFile()) continue\n const file = join(entry.parentPath, entry.name)\n const url = '/' + relative(dir, file).split(sep).join('/')\n assets.set(url, {\n type: TYPES[extname(file)] ?? 'application/octet-stream',\n body: await readFile(file)\n })\n }\n return assets\n}\n","export const PLUGIN_ID = 'signalk-history-widgets'\n\n/**\n * Where the widget pages are served. Not under /plugins/<id>, which is for\n * admins only: every user of the chartplotter loads these.\n */\nexport const ASSET_BASE = `/plotterext/${PLUGIN_ID}`\n\nconst strip = (id: string, title: string, size: string) => ({\n id,\n title,\n type: 'iframe',\n url: `${ASSET_BASE}/widget.html`,\n size,\n configPanel: 'strip-config',\n lifecycle: 'whileEnabled'\n})\n\nconst level = (id: string, title: string) => ({\n id,\n title,\n type: 'iframe',\n url: `${ASSET_BASE}/${id}.html`,\n size: '1x2',\n configPanel: `${id}-config`,\n lifecycle: 'whileEnabled'\n})\n\nconst levelPanel = (id: string, title: string) => ({\n id: `${id}-config`,\n title,\n type: 'iframe',\n url: `${ASSET_BASE}/${id}-config.html`,\n lifecycle: 'onOpen'\n})\n\n/** The Plotter Extensions manifest (apiVersion 1). */\nexport function buildManifest(version: string) {\n return {\n name: 'History Widgets',\n description:\n 'Any value over the last minutes or hours as a vertical strip with its minimum, average and maximum, a wind steering dial with the wind shift of the last minutes, and tank levels and battery charge coloured by their zones.',\n version,\n apiVersion: '1',\n requires: ['widgets', 'panels.iframe', 'signalk.stream'],\n optional: ['nightMode'],\n widgets: [\n strip('strip', 'History strip', '1x2'),\n strip('strip-wide', 'History strip (wide)', '2x2'),\n {\n id: 'wind-steering',\n title: 'Wind steering',\n type: 'iframe',\n url: `${ASSET_BASE}/steer.html`,\n size: '2x2',\n configPanel: 'wind-steering-config',\n lifecycle: 'whileEnabled'\n },\n level('tank', 'Tank level'),\n level('battery', 'Battery charge')\n ],\n panels: [\n {\n id: 'strip-config',\n title: 'History strip',\n type: 'iframe',\n url: `${ASSET_BASE}/config.html`,\n lifecycle: 'onOpen'\n },\n {\n id: 'wind-steering-config',\n title: 'Wind steering',\n type: 'iframe',\n url: `${ASSET_BASE}/steer-config.html`,\n lifecycle: 'onOpen'\n },\n levelPanel('tank', 'Tank level'),\n levelPanel('battery', 'Battery charge')\n ]\n }\n}\n","import { readFileSync } from 'node:fs'\nimport { fileURLToPath } from 'node:url'\nimport type { Request, Response, NextFunction } from 'express'\nimport type { Plugin, ServerAPI } from '@signalk/server-api'\nimport { loadAssets, type Asset } from './assets.js'\nimport { ASSET_BASE, PLUGIN_ID, buildManifest } from './manifest.js'\n\nconst PUBLIC_DIR = fileURLToPath(new URL('../public', import.meta.url))\nconst PACKAGE_JSON = fileURLToPath(new URL('../package.json', import.meta.url))\n\n/** The express app the server hands plugins; ServerAPI leaves `use` out. */\ntype App = ServerAPI & {\n use?: (path: string, handler: (req: Request, res: Response, next: NextFunction) => void) => void\n}\n\nfunction packageVersion(): string {\n const pkg: unknown = JSON.parse(readFileSync(PACKAGE_JSON, 'utf8'))\n const version = (pkg as { version?: unknown }).version\n return typeof version === 'string' ? version : '0.0.0'\n}\n\nexport default function historyWidgets(app: App): Plugin {\n let running = false\n let assets = new Map<string, Asset>()\n let mounted = false\n let registered = false\n const manifest = buildManifest(packageVersion())\n\n const mount = () => {\n if (mounted || !app.use) return\n mounted = true\n // express cannot unmount a route, so a stopped plugin answers 404.\n app.use(ASSET_BASE, (req, res, next) => {\n const asset = running ? assets.get(req.path) : undefined\n if (!asset || (req.method !== 'GET' && req.method !== 'HEAD')) {\n next()\n return\n }\n res.type(asset.type).send(asset.body)\n })\n }\n\n const register = () => {\n if (registered) return\n registered = true\n app.registerResourceProvider({\n type: 'plotterExtensions',\n methods: {\n listResources: () => Promise.resolve(running ? { [PLUGIN_ID]: manifest } : {}),\n getResource: (id: string) =>\n running && id === PLUGIN_ID\n ? Promise.resolve(manifest)\n : Promise.reject(new Error(`No such plotterExtensions resource: ${id}`)),\n setResource: () => Promise.reject(new Error(`${PLUGIN_ID} is read-only`)),\n deleteResource: () => Promise.reject(new Error(`${PLUGIN_ID} is read-only`))\n }\n })\n }\n\n return {\n id: PLUGIN_ID,\n name: 'History Widgets',\n description:\n 'History strip and wind steering widgets for Freeboard-SK and other Plotter Extensions hosts',\n schema: () => ({ type: 'object', properties: {} }),\n\n start() {\n running = true\n mount()\n register()\n loadAssets(PUBLIC_DIR)\n .then((loaded) => {\n assets = loaded\n app.setPluginStatus(`Serving ${manifest.widgets.length} widgets`)\n })\n .catch((err: unknown) => {\n app.setPluginError(\n `Web pages missing: ${err instanceof Error ? err.message : String(err)}`\n )\n })\n },\n\n stop() {\n running = false\n }\n }\n}\n"],"mappings":";;;;;AAGA,IAAM,QAAgC;CACpC,SAAS;CACT,OAAO;CACP,QAAQ;CACR,QAAQ;CACR,QAAQ;AACV;;;;;;AAYA,eAAsB,WAAW,KAA0C;CACzE,MAAM,yBAAS,IAAI,IAAmB;CACtC,MAAM,UAAU,MAAM,QAAQ,KAAK;EAAE,WAAW;EAAM,eAAe;CAAK,CAAC;CAC3E,KAAK,MAAM,SAAS,SAAS;EAC3B,IAAI,CAAC,MAAM,OAAO,GAAG;EACrB,MAAM,OAAO,KAAK,MAAM,YAAY,MAAM,IAAI;EAC9C,MAAM,MAAM,MAAM,SAAS,KAAK,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,GAAG;EACzD,OAAO,IAAI,KAAK;GACd,MAAM,MAAM,QAAQ,IAAI,MAAM;GAC9B,MAAM,MAAM,SAAS,IAAI;EAC3B,CAAC;CACH;CACA,OAAO;AACT;;AClCA,IAAa,YAAY;;;;;AAMzB,IAAa,aAAa,eAAe;AAEzC,IAAM,SAAS,IAAY,OAAe,UAAkB;CAC1D;CACA;CACA,MAAM;CACN,KAAK,GAAG,WAAW;CACnB;CACA,aAAa;CACb,WAAW;AACb;AAEA,IAAM,SAAS,IAAY,WAAmB;CAC5C;CACA;CACA,MAAM;CACN,KAAK,GAAG,WAAW,GAAG,GAAG;CACzB,MAAM;CACN,aAAa,GAAG,GAAG;CACnB,WAAW;AACb;AAEA,IAAM,cAAc,IAAY,WAAmB;CACjD,IAAI,GAAG,GAAG;CACV;CACA,MAAM;CACN,KAAK,GAAG,WAAW,GAAG,GAAG;CACzB,WAAW;AACb;;AAGA,SAAgB,cAAc,SAAiB;CAC7C,OAAO;EACL,MAAM;EACN,aACE;EACF;EACA,YAAY;EACZ,UAAU;GAAC;GAAW;GAAiB;EAAgB;EACvD,UAAU,CAAC,WAAW;EACtB,SAAS;GACP,MAAM,SAAS,iBAAiB,KAAK;GACrC,MAAM,cAAc,wBAAwB,KAAK;GACjD;IACE,IAAI;IACJ,OAAO;IACP,MAAM;IACN,KAAK,GAAG,WAAW;IACnB,MAAM;IACN,aAAa;IACb,WAAW;GACb;GACA,MAAM,QAAQ,YAAY;GAC1B,MAAM,WAAW,gBAAgB;EACnC;EACA,QAAQ;GACN;IACE,IAAI;IACJ,OAAO;IACP,MAAM;IACN,KAAK,GAAG,WAAW;IACnB,WAAW;GACb;GACA;IACE,IAAI;IACJ,OAAO;IACP,MAAM;IACN,KAAK,GAAG,WAAW;IACnB,WAAW;GACb;GACA,WAAW,QAAQ,YAAY;GAC/B,WAAW,WAAW,gBAAgB;EACxC;CACF;AACF;;ACzEA,IAAM,aAAa,cAAc,IAAI,IAAI,aAAa,YAAY,GAAG,CAAC;AACtE,IAAM,eAAe,cAAc,IAAI,IAAI,mBAAmB,YAAY,GAAG,CAAC;AAO9E,SAAS,iBAAyB;CAEhC,MAAM,UADe,KAAK,MAAM,aAAa,cAAc,MAAM,CAChD,CAAA,CAA8B;CAC/C,OAAO,OAAO,YAAY,WAAW,UAAU;AACjD;AAEA,SAAwB,eAAe,KAAkB;CACvD,IAAI,UAAU;CACd,IAAI,yBAAS,IAAI,IAAmB;CACpC,IAAI,UAAU;CACd,IAAI,aAAa;CACjB,MAAM,WAAW,cAAc,eAAe,CAAC;CAE/C,MAAM,cAAc;EAClB,IAAI,WAAW,CAAC,IAAI,KAAK;EACzB,UAAU;EAEV,IAAI,IAAI,aAAa,KAAK,KAAK,SAAS;GACtC,MAAM,QAAQ,UAAU,OAAO,IAAI,IAAI,IAAI,IAAI,KAAA;GAC/C,IAAI,CAAC,SAAU,IAAI,WAAW,SAAS,IAAI,WAAW,QAAS;IAC7D,KAAK;IACL;GACF;GACA,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,KAAK,MAAM,IAAI;EACtC,CAAC;CACH;CAEA,MAAM,iBAAiB;EACrB,IAAI,YAAY;EAChB,aAAa;EACb,IAAI,yBAAyB;GAC3B,MAAM;GACN,SAAS;IACP,qBAAqB,QAAQ,QAAQ,UAAU,GAAG,YAAY,SAAS,IAAI,CAAC,CAAC;IAC7E,cAAc,OACZ,WAAW,OAAA,4BACP,QAAQ,QAAQ,QAAQ,IACxB,QAAQ,uBAAO,IAAI,MAAM,uCAAuC,IAAI,CAAC;IAC3E,mBAAmB,QAAQ,uBAAO,IAAI,MAAM,GAAG,UAAU,cAAc,CAAC;IACxE,sBAAsB,QAAQ,uBAAO,IAAI,MAAM,GAAG,UAAU,cAAc,CAAC;GAC7E;EACF,CAAC;CACH;CAEA,OAAO;EACL,IAAI;EACJ,MAAM;EACN,aACE;EACF,eAAe;GAAE,MAAM;GAAU,YAAY,CAAC;EAAE;EAEhD,QAAQ;GACN,UAAU;GACV,MAAM;GACN,SAAS;GACT,WAAW,UAAU,CAAC,CACnB,MAAM,WAAW;IAChB,SAAS;IACT,IAAI,gBAAgB,WAAW,SAAS,QAAQ,OAAO,SAAS;GAClE,CAAC,CAAC,CACD,OAAO,QAAiB;IACvB,IAAI,eACF,sBAAsB,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,GACvE;GACF,CAAC;EACL;EAEA,OAAO;GACL,UAAU;EACZ;CACF;AACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./chunk-U7YORXZ7-BLTBSYrv.js";import{t as e}from"./level-CwEJ7gss.js";e(`battery`);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./chunk-U7YORXZ7-BLTBSYrv.js";import{t as e}from"./level-config-B0mMmED6.js";e(`battery`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),t.credentials=e.crossOrigin===`use-credentials`?`include`:e.crossOrigin===`anonymous`?`omit`:`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var e=`plotterExt/1`,t={PARSE_ERROR:-32700,INVALID_REQUEST:-32600,METHOD_NOT_FOUND:-32601,INVALID_PARAMS:-32602,INTERNAL_ERROR:-32603,HOST_ERROR:-32e3,TIMEOUT:-32001,CONNECTION_CLOSED:-32002},n=class e extends Error{code;data;constructor(e,n={}){super(e),this.name=`RpcError`,this.code=n.code??t.HOST_ERROR;let r={...n.data??{}};n.reason!==void 0&&(r.reason=n.reason),this.data=Object.keys(r).length>0?r:void 0}get reason(){return typeof this.data?.reason==`string`?this.data.reason:void 0}toErrorObject(){return{code:this.code,message:this.message,...this.data?{data:this.data}:{}}}static fromErrorObject(t){return new e(t.message,{code:t.code,data:t.data})}static from(n){return n instanceof e?n:n instanceof Error?new e(n.message,{code:t.INTERNAL_ERROR}):new e(String(n),{code:t.INTERNAL_ERROR})}},r=`bus.ready`,i=`bus.handshake`;function a(e,t){return e===t||o(e.split(`.`),0,t.split(`.`),0)}function o(e,t,n,r){for(;t<e.length;){let i=e[t];if(i===`**`){if(t===e.length-1)return!0;for(let i=r;i<=n.length;i++)if(o(e,t+1,n,i))return!0;return!1}if(r>=n.length||i!==`*`&&i!==n[r])return!1;t++,r++}return r===n.length}function s(e,t){for(let n of e)if(a(n,t))return!0;return!1}function c(t){return{bus:e,msg:t}}function l(e){if(typeof e!=`object`||!e)return null;let t=e;return t.bus===`plotterExt/1`&&u(t.msg)?t.msg:null}function u(e){if(typeof e!=`object`||!e)return!1;let t=e;if(t.jsonrpc!==`2.0`)return!1;if(typeof t.method==`string`)return t.id===void 0||typeof t.id==`string`||typeof t.id==`number`;if(typeof t.id!=`string`&&typeof t.id!=`number`&&t.id!==null)return!1;let n=`result`in t,r=t.error,i=typeof r==`object`&&!!r&&typeof r.code==`number`&&typeof r.message==`string`;return n?!(`error`in t):i}function d(e){return`method`in e&&`id`in e&&e.id!==void 0}function f(e){return`method`in e&&(!(`id`in e)||e.id===void 0)}function p(e){return!(`method`in e)}function m(e,t={}){let n=t.listenWindow??globalThis,r=t.origin??n.location?.origin??`*`;return{post(t,n){n?.length?e.postMessage(t,{targetOrigin:r,transfer:n}):e.postMessage(t,r)},listen(t){let i=n=>{n.source===e&&(r===`*`||n.origin===r)&&t(n.data)};return n.addEventListener(`message`,i),()=>n.removeEventListener(`message`,i)}}}var h=1e4,g=class{callTimeoutMs;port;unlisten;onError;pending=new Map;methods=new Map;eventHandlers=new Set;idPrefix=Math.random().toString(36).slice(2,8);seq=0;closed=!1;constructor(e){this.port=e.port,this.callTimeoutMs=e.callTimeoutMs??h,this.onError=e.onError??(e=>console.warn(`[plotterext-bus]`,e)),this.unlisten=this.port.listen(e=>this.onData(e))}registerMethod(e,t){this.methods.set(e,t)}unregisterMethod(e){this.methods.delete(e)}onEvent(e,t){let n={patterns:e,fn:t};return this.eventHandlers.add(n),()=>this.eventHandlers.delete(n)}notify(e,t,n={}){this.send({jsonrpc:`2.0`,method:e,...t===void 0?{}:{params:t}},n.transfer)}call(e,r,i={}){if(this.closed)return Promise.reject(new n(`Bus endpoint is closed`,{code:t.CONNECTION_CLOSED,reason:`CLOSED`}));let a=`${this.idPrefix}-${++this.seq}`,o=i.timeoutMs??this.callTimeoutMs;return new Promise((s,c)=>{let l=o>0?setTimeout(()=>{this.pending.delete(a),c(new n(`Call timed out after ${o}ms: ${e}`,{code:t.TIMEOUT,reason:`TIMEOUT`}))},o):null;this.pending.set(a,{resolve:s,reject:c,timer:l}),this.send({jsonrpc:`2.0`,id:a,method:e,...r===void 0?{}:{params:r}},i.transfer)})}close(){if(!this.closed){this.closed=!0,this.unlisten();for(let[,e]of this.pending)e.timer&&clearTimeout(e.timer),e.reject(new n(`Bus endpoint closed`,{code:t.CONNECTION_CLOSED,reason:`CLOSED`}));this.pending.clear(),this.eventHandlers.clear()}}send(e,t){this.closed||this.port.post(c(e),t)}onData(e){let t=l(e);t&&(p(t)?this.onResponse(t):d(t)?this.onRequest(t):f(t)&&this.onNotification(t.method,t.params))}onResponse(e){if(e.id===null)return;let t=this.pending.get(e.id);t&&(this.pending.delete(e.id),t.timer&&clearTimeout(t.timer),`error`in e?t.reject(n.fromErrorObject(e.error)):t.resolve(e.result))}async onRequest(e){let r=this.methods.get(e.method);if(!r)this.send({jsonrpc:`2.0`,id:e.id,error:{code:t.METHOD_NOT_FOUND,message:`Method not found: ${e.method}`}});else try{let t=await r(e.params,{endpoint:this});this.send({jsonrpc:`2.0`,id:e.id,result:t===void 0?null:t})}catch(t){this.send({jsonrpc:`2.0`,id:e.id,error:n.from(t).toErrorObject()})}}onNotification(e,t){for(let n of[...this.eventHandlers])if(s(n.patterns,e))try{n.fn(e,t)}catch(e){this.onError(e)}}},_=class{handshake;endpoint;constructor(e,t){this.endpoint=e,this.handshake=t}get context(){return this.handshake.context}get apiVersion(){return this.handshake.apiVersion}get capabilities(){return this.handshake.capabilities}hasCapability(e){return this.handshake.capabilities.includes(e)}call(e,t,n){return this.endpoint.call(e,t,n)}notify(e,t){this.endpoint.notify(e,t)}async subscribe(e,t){let n=this.endpoint.onEvent(e,t),r;try{r=(await this.call(`events.subscribe`,{patterns:e})).subscriptionId}catch(e){throw n(),e}return async()=>{n(),await this.call(`events.unsubscribe`,{subscriptionId:r}).catch(()=>{})}}async publish(e,t,n){await this.call(`events.publish`,{topic:e,...t===void 0?{}:{params:t},...n?{scope:n}:{}})}state={get:async(e,t)=>(await this.call(`state.get`,{...t?{scope:t}:{},...e?{keys:e}:{}})).values??{},set:async(e,t)=>{await this.call(`state.set`,{...t?{scope:t}:{},values:e})}};signalk={subscribe:async(e,t)=>{let n=e.map(e=>`sk.${e}`),r=await this.subscribe(n,(e,n)=>t(n)),i;try{i=(await this.call(`signalk.subscribe`,{paths:e})).subscriptionId}catch(e){throw await r(),e}return async()=>{await r(),await this.call(`signalk.unsubscribe`,{subscriptionId:i}).catch(()=>{})}},put:(e,t)=>this.call(`signalk.put`,{path:e,value:t})};route={list:async()=>(await this.call(`route.list`)).routes??[],create:async e=>await this.call(`route.create`,e),get:async e=>await this.call(`route.get`,{routeId:e}),replace:async(e,t)=>await this.call(`route.replace`,{routeId:e,points:t}),save:async(e,t)=>await this.call(`route.save`,{routeId:e,...t??{}}),show:async e=>await this.call(`route.show`,{ref:e}),hide:async e=>{await this.call(`route.hide`,{routeId:e})},delete:async e=>{await this.call(`route.delete`,{routeId:e})}};chart={list:async()=>(await this.call(`chart.list`)).charts??[],setVisibility:async(e,t)=>{await this.call(`chart.setVisibility`,{ids:e,visible:t})},setOpacity:async(e,t)=>{await this.call(`chart.setOpacity`,{ids:e,opacity:t})},setOrder:async e=>{await this.call(`chart.setOrder`,{order:e})},setTime:async(e,t)=>{await this.call(`chart.setTime`,{ids:e,time:t})}};nightMode={get:async()=>await this.call(`nightMode.get`),set:async e=>{await this.call(`nightMode.set`,e)}};panels={list:async()=>(await this.call(`ui.listPanels`)).panels};overlays={list:async()=>(await this.call(`overlay.list`)).overlays,setVisibility:async(e,t)=>{await this.call(`overlay.setVisibility`,{ids:e,visible:t})},setOpacity:async(e,t)=>{await this.call(`overlay.setOpacity`,{ids:e,opacity:t})},frame:async e=>await this.call(`overlay.frame`,e,{transfer:[e.image]}),refresh:async e=>{await this.call(`overlay.refresh`,e)},setData:async e=>{await this.call(`overlay.setData`,e)},setLayer:async e=>{await this.call(`overlay.setLayer`,e)},setState:async(e,t)=>{await this.call(`overlay.setState`,{overlayId:e,values:t})}};windows={open:async e=>await this.call(`ui.openWindow`,e),update:async e=>await this.call(`ui.updateWindow`,e),focus:async e=>{await this.call(`ui.focusWindow`,e===void 0?{}:{windowId:e})},close:async e=>{await this.call(`ui.closeWindow`,e===void 0?{}:{windowId:e})},list:async()=>(await this.call(`ui.listWindows`)).windows};resourceGroup={apply:async e=>await this.call(`resourceGroup.apply`,{id:e})};map={getView:async()=>await this.call(`map.getView`),center:async(e,t)=>{await this.call(`map.center`,{position:e,...typeof t==`number`?{zoom:t}:{}})},fitBounds:async e=>{await this.call(`map.fitBounds`,{bounds:e})},pick:async(e={})=>await this.call(`map.pick`,e),cancelPick:async e=>{await this.call(`map.cancelPick`,{pickId:e})}};actions={setVisibility:async(e,t)=>{await this.call(`actions.setVisibility`,{ids:e,visible:t})}};buttons={setVisibility:async(e,t)=>{await this.call(`buttons.setVisibility`,{ids:e,visible:t})},decorate:async(e,t)=>{await this.call(`buttons.decorate`,{...t,ids:e})}};close(){this.endpoint.close()}};function v(e={}){let a=new g({port:e.port??m(globalThis.parent,{origin:`*`}),callTimeoutMs:e.callTimeoutMs,onError:e.onError}),o=e.id?{id:e.id}:void 0;return new Promise((s,c)=>{let l=!1,u=a.onEvent([i],(e,t)=>{l||(l=!0,p(),s(new _(a,t)))}),d=setInterval(()=>a.notify(r,o),e.readyIntervalMs??250),f=setTimeout(()=>{l||(l=!0,p(),a.close(),c(new n(`Timed out waiting for host handshake`,{code:t.TIMEOUT,reason:`HANDSHAKE_TIMEOUT`})))},e.timeoutMs??1e4),p=()=>{u(),clearInterval(d),clearTimeout(f)};a.notify(r,o)})}var y={path:`environment.wind.directionTrue`,label:``,span:30,spanUnit:`min`,unit:``,decimals:-1},b=6e4,x=6048e5,S=[{path:`environment.wind.directionTrue`,label:`TWD`,title:`True wind direction`},{path:`environment.wind.speedTrue`,label:`TWS`,title:`True wind speed`},{path:`environment.wind.angleTrueWater`,label:`TWA`,title:`True wind angle`},{path:`environment.wind.angleApparent`,label:`AWA`,title:`Apparent wind angle`},{path:`environment.wind.speedApparent`,label:`AWS`,title:`Apparent wind speed`},{path:`navigation.speedOverGround`,label:`SOG`,title:`Speed over ground`},{path:`navigation.speedThroughWater`,label:`STW`,title:`Speed through water`},{path:`navigation.courseOverGroundTrue`,label:`COG`,title:`Course over ground`},{path:`navigation.headingTrue`,label:`HDG`,title:`Heading`},{path:`environment.depth.belowTransducer`,label:`Depth`,title:`Depth`},{path:`environment.outside.pressure`,label:`Baro`,title:`Air pressure`},{path:`environment.outside.temperature`,label:`Air`,title:`Air temperature`},{path:`environment.water.temperature`,label:`Water`,title:`Water temperature`}];function C(e){return e.label?e.label:S.find(t=>t.path===e.path)?.label??e.path.split(`.`).pop()??``}function w(e){let t=e.span*(e.spanUnit===`h`?36e5:6e4);return Math.min(x,Math.max(b,t))}function T(e){let t=(e,t)=>typeof e==`string`?e.trim():t,n=(e,t)=>typeof e==`number`&&Number.isFinite(e)?e:t,r=n(e.span,y.span);return{path:t(e.path,y.path),label:t(e.label,y.label),span:r>0?r:y.span,spanUnit:e.spanUnit===`h`?`h`:`min`,unit:t(e.unit,y.unit),decimals:Math.min(4,Math.round(n(e.decimals,y.decimals)))}}function E(e){let t=e.split(`.`).pop()??``;return!/direction|heading|course|bearing/i.test(t)}function D(e){return typeof e==`object`&&!!e}async function O(e){try{let t=await fetch(`/signalk/v1/api/vessels/self/${e.replace(/\./g,`/`)}/meta`,{credentials:`include`});if(!t.ok)return;let n=await t.json();if(!D(n))return;let r={};typeof n.units==`string`&&(r.units=n.units);let i=n.displayUnits;return D(i)&&(r.displayUnits={},typeof i.targetUnit==`string`&&(r.displayUnits.targetUnit=i.targetUnit),typeof i.displayFormat==`string`&&(r.displayUnits.displayFormat=i.displayFormat)),r}catch{return}}function k(e,t=``,n=[]){if(!D(e))return n;if(`value`in e){if(typeof e.value==`number`){let r=e.meta;n.push({path:t,units:D(r)&&typeof r.units==`string`?r.units:void 0})}return n}for(let[r,i]of Object.entries(e))r!==`meta`&&r!==`$source`&&r!==`timestamp`&&r!==`values`&&k(i,t?`${t}.${r}`:r,n);return n}async function A(){try{let e=await fetch(`/signalk/v1/api/vessels/self`,{credentials:`include`});return e.ok?k(await e.json()).sort((e,t)=>e.path.localeCompare(t.path)):[]}catch{return[]}}async function j(){try{let e=await fetch(`/signalk/v2/api/history/_providers`,{credentials:`include`});if(!e.ok)return[];let t=await e.json();return D(t)?Object.keys(t):[]}catch{return[]}}var M=(e,t,n,r=0,i=0)=>({id:e,symbol:t,factor:n,offset:r,add:i}),N={rad:[M(`degree`,`°`,180/Math.PI),M(`radian`,`rad`,1)],"m/s":[M(`kn`,`kn`,1.94384),M(`m/s`,`m/s`,1),M(`km/h`,`km/h`,3.6),M(`mph`,`mph`,2.2369362920544025)],K:[M(`C`,`°C`,1,-273.15),M(`F`,`°F`,9/5,-273.15,32),M(`K`,`K`,1)],Pa:[M(`hPa`,`hPa`,.01),M(`mbar`,`mbar`,.01),M(`bar`,`bar`,1e-5),M(`psi`,`psi`,.0001450376807894691),M(`inHg`,`inHg`,.00029529987601298443),M(`mmHg`,`mmHg`,.0075006168507298),M(`Pa`,`Pa`,1)],m:[M(`m`,`m`,1),M(`meter`,`m`,1),M(`foot`,`ft`,3.280839895013124),M(`fathom`,`fathom`,.5467468562055768),M(`naut-mile`,`nmi`,.0005399568034557236),M(`kilometer`,`km`,.001),M(`mile`,`mi`,.000621371192237334)],"rad/s":[M(`deg/s`,`°/s`,57.29577951308232),M(`rpm`,`rpm`,9.549296585513723),M(`rad/s`,`rad/s`,1)],ratio:[M(`percent`,`%`,100),M(`ratio`,``,1)],Hz:[M(`rpm`,`rpm`,60),M(`hertz`,`Hz`,1)],V:[M(`V`,`V`,1)],A:[M(`A`,`A`,1)],W:[M(`watt`,`W`,1),M(`kW`,`kW`,.001)],m3:[M(`liter`,`l`,1e3),M(`gallon`,`gal`,264.1720512415585),M(`m3`,`m³`,1)],"m3/s":[M(`L/h`,`l/h`,36e5),M(`L/min`,`l/min`,6e4),M(`gal/h`,`gal/h`,264.17205236)]};function P(e,t){return t?(e+(t.offset??0))*t.factor+(t.add??0):e}function F(e){if(e)return/\.(0+)/.exec(e)?.[1]?.length??0}function I(e,t,n){let r=e?.units,i=r?N[r]??[]:[],a=e=>i.find(t=>t.id===e),o=a(t)??a(e?.displayUnits?.targetUnit)??i[0],s=r===`rad`,c=o&&o.id===e?.displayUnits?.targetUnit?F(e.displayUnits.displayFormat):void 0,l=o?.id===`degree`?0:c??1;return{conversion:o,symbol:o?.symbol??r??``,decimals:n>=0?n:l,angular:s}}export{O as a,E as c,w as d,v as f,j as i,C as l,P as n,A as o,I as r,S as s,N as t,T as u};
|
|
1
|
+
(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),t.credentials=e.crossOrigin===`use-credentials`?`include`:e.crossOrigin===`anonymous`?`omit`:`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var e=`plotterExt/1`,t={PARSE_ERROR:-32700,INVALID_REQUEST:-32600,METHOD_NOT_FOUND:-32601,INVALID_PARAMS:-32602,INTERNAL_ERROR:-32603,HOST_ERROR:-32e3,TIMEOUT:-32001,CONNECTION_CLOSED:-32002},n=class e extends Error{code;data;constructor(e,n={}){super(e),this.name=`RpcError`,this.code=n.code??t.HOST_ERROR;let r={...n.data??{}};n.reason!==void 0&&(r.reason=n.reason),this.data=Object.keys(r).length>0?r:void 0}get reason(){return typeof this.data?.reason==`string`?this.data.reason:void 0}toErrorObject(){return{code:this.code,message:this.message,...this.data?{data:this.data}:{}}}static fromErrorObject(t){return new e(t.message,{code:t.code,data:t.data})}static from(n){return n instanceof e?n:n instanceof Error?new e(n.message,{code:t.INTERNAL_ERROR}):new e(String(n),{code:t.INTERNAL_ERROR})}},r=`bus.ready`,i=`bus.handshake`;function a(e,t){return e===t||o(e.split(`.`),0,t.split(`.`),0)}function o(e,t,n,r){for(;t<e.length;){let i=e[t];if(i===`**`){if(t===e.length-1)return!0;for(let i=r;i<=n.length;i++)if(o(e,t+1,n,i))return!0;return!1}if(r>=n.length||i!==`*`&&i!==n[r])return!1;t++,r++}return r===n.length}function s(e,t){for(let n of e)if(a(n,t))return!0;return!1}function c(t){return{bus:e,msg:t}}function l(e){if(typeof e!=`object`||!e)return null;let t=e;return t.bus===`plotterExt/1`&&u(t.msg)?t.msg:null}function u(e){if(typeof e!=`object`||!e)return!1;let t=e;if(t.jsonrpc!==`2.0`)return!1;if(typeof t.method==`string`)return t.id===void 0||typeof t.id==`string`||typeof t.id==`number`;if(typeof t.id!=`string`&&typeof t.id!=`number`&&t.id!==null)return!1;let n=`result`in t,r=t.error,i=typeof r==`object`&&!!r&&typeof r.code==`number`&&typeof r.message==`string`;return n?!(`error`in t):i}function d(e){return`method`in e&&`id`in e&&e.id!==void 0}function f(e){return`method`in e&&(!(`id`in e)||e.id===void 0)}function p(e){return!(`method`in e)}function m(e,t={}){let n=t.listenWindow??globalThis,r=t.origin??n.location?.origin??`*`;return{post(t,n){n?.length?e.postMessage(t,{targetOrigin:r,transfer:n}):e.postMessage(t,r)},listen(t){let i=n=>{n.source===e&&(r===`*`||n.origin===r)&&t(n.data)};return n.addEventListener(`message`,i),()=>n.removeEventListener(`message`,i)}}}var h=1e4,g=class{callTimeoutMs;port;unlisten;onError;pending=new Map;methods=new Map;eventHandlers=new Set;idPrefix=Math.random().toString(36).slice(2,8);seq=0;closed=!1;constructor(e){this.port=e.port,this.callTimeoutMs=e.callTimeoutMs??h,this.onError=e.onError??(e=>console.warn(`[plotterext-bus]`,e)),this.unlisten=this.port.listen(e=>this.onData(e))}registerMethod(e,t){this.methods.set(e,t)}unregisterMethod(e){this.methods.delete(e)}onEvent(e,t){let n={patterns:e,fn:t};return this.eventHandlers.add(n),()=>this.eventHandlers.delete(n)}notify(e,t,n={}){this.send({jsonrpc:`2.0`,method:e,...t===void 0?{}:{params:t}},n.transfer)}call(e,r,i={}){if(this.closed)return Promise.reject(new n(`Bus endpoint is closed`,{code:t.CONNECTION_CLOSED,reason:`CLOSED`}));let a=`${this.idPrefix}-${++this.seq}`,o=i.timeoutMs??this.callTimeoutMs;return new Promise((s,c)=>{let l=o>0?setTimeout(()=>{this.pending.delete(a),c(new n(`Call timed out after ${o}ms: ${e}`,{code:t.TIMEOUT,reason:`TIMEOUT`}))},o):null;this.pending.set(a,{resolve:s,reject:c,timer:l}),this.send({jsonrpc:`2.0`,id:a,method:e,...r===void 0?{}:{params:r}},i.transfer)})}close(){if(!this.closed){this.closed=!0,this.unlisten();for(let[,e]of this.pending)e.timer&&clearTimeout(e.timer),e.reject(new n(`Bus endpoint closed`,{code:t.CONNECTION_CLOSED,reason:`CLOSED`}));this.pending.clear(),this.eventHandlers.clear()}}send(e,t){this.closed||this.port.post(c(e),t)}onData(e){let t=l(e);t&&(p(t)?this.onResponse(t):d(t)?this.onRequest(t):f(t)&&this.onNotification(t.method,t.params))}onResponse(e){if(e.id===null)return;let t=this.pending.get(e.id);t&&(this.pending.delete(e.id),t.timer&&clearTimeout(t.timer),`error`in e?t.reject(n.fromErrorObject(e.error)):t.resolve(e.result))}async onRequest(e){let r=this.methods.get(e.method);if(!r)this.send({jsonrpc:`2.0`,id:e.id,error:{code:t.METHOD_NOT_FOUND,message:`Method not found: ${e.method}`}});else try{let t=await r(e.params,{endpoint:this});this.send({jsonrpc:`2.0`,id:e.id,result:t===void 0?null:t})}catch(t){this.send({jsonrpc:`2.0`,id:e.id,error:n.from(t).toErrorObject()})}}onNotification(e,t){for(let n of[...this.eventHandlers])if(s(n.patterns,e))try{n.fn(e,t)}catch(e){this.onError(e)}}},_=class{handshake;endpoint;constructor(e,t){this.endpoint=e,this.handshake=t}get context(){return this.handshake.context}get apiVersion(){return this.handshake.apiVersion}get capabilities(){return this.handshake.capabilities}hasCapability(e){return this.handshake.capabilities.includes(e)}call(e,t,n){return this.endpoint.call(e,t,n)}notify(e,t){this.endpoint.notify(e,t)}async subscribe(e,t){let n=this.endpoint.onEvent(e,t),r;try{r=(await this.call(`events.subscribe`,{patterns:e})).subscriptionId}catch(e){throw n(),e}return async()=>{n(),await this.call(`events.unsubscribe`,{subscriptionId:r}).catch(()=>{})}}async publish(e,t,n){await this.call(`events.publish`,{topic:e,...t===void 0?{}:{params:t},...n?{scope:n}:{}})}state={get:async(e,t)=>(await this.call(`state.get`,{...t?{scope:t}:{},...e?{keys:e}:{}})).values??{},set:async(e,t)=>{await this.call(`state.set`,{...t?{scope:t}:{},values:e})}};signalk={subscribe:async(e,t)=>{let n=e.map(e=>`sk.${e}`),r=await this.subscribe(n,(e,n)=>t(n)),i;try{i=(await this.call(`signalk.subscribe`,{paths:e})).subscriptionId}catch(e){throw await r(),e}return async()=>{await r(),await this.call(`signalk.unsubscribe`,{subscriptionId:i}).catch(()=>{})}},put:(e,t)=>this.call(`signalk.put`,{path:e,value:t})};route={list:async()=>(await this.call(`route.list`)).routes??[],create:async e=>await this.call(`route.create`,e),get:async e=>await this.call(`route.get`,{routeId:e}),replace:async(e,t)=>await this.call(`route.replace`,{routeId:e,points:t}),save:async(e,t)=>await this.call(`route.save`,{routeId:e,...t??{}}),show:async e=>await this.call(`route.show`,{ref:e}),hide:async e=>{await this.call(`route.hide`,{routeId:e})},delete:async e=>{await this.call(`route.delete`,{routeId:e})}};chart={list:async()=>(await this.call(`chart.list`)).charts??[],setVisibility:async(e,t)=>{await this.call(`chart.setVisibility`,{ids:e,visible:t})},setOpacity:async(e,t)=>{await this.call(`chart.setOpacity`,{ids:e,opacity:t})},setOrder:async e=>{await this.call(`chart.setOrder`,{order:e})},setTime:async(e,t)=>{await this.call(`chart.setTime`,{ids:e,time:t})}};nightMode={get:async()=>await this.call(`nightMode.get`),set:async e=>{await this.call(`nightMode.set`,e)}};panels={list:async()=>(await this.call(`ui.listPanels`)).panels};overlays={list:async()=>(await this.call(`overlay.list`)).overlays,setVisibility:async(e,t)=>{await this.call(`overlay.setVisibility`,{ids:e,visible:t})},setOpacity:async(e,t)=>{await this.call(`overlay.setOpacity`,{ids:e,opacity:t})},frame:async e=>await this.call(`overlay.frame`,e,{transfer:[e.image]}),refresh:async e=>{await this.call(`overlay.refresh`,e)},setData:async e=>{await this.call(`overlay.setData`,e)},setLayer:async e=>{await this.call(`overlay.setLayer`,e)},setState:async(e,t)=>{await this.call(`overlay.setState`,{overlayId:e,values:t})}};windows={open:async e=>await this.call(`ui.openWindow`,e),update:async e=>await this.call(`ui.updateWindow`,e),focus:async e=>{await this.call(`ui.focusWindow`,e===void 0?{}:{windowId:e})},close:async e=>{await this.call(`ui.closeWindow`,e===void 0?{}:{windowId:e})},list:async()=>(await this.call(`ui.listWindows`)).windows};resourceGroup={apply:async e=>await this.call(`resourceGroup.apply`,{id:e})};map={getView:async()=>await this.call(`map.getView`),center:async(e,t)=>{await this.call(`map.center`,{position:e,...typeof t==`number`?{zoom:t}:{}})},fitBounds:async e=>{await this.call(`map.fitBounds`,{bounds:e})},pick:async(e={})=>await this.call(`map.pick`,e),cancelPick:async e=>{await this.call(`map.cancelPick`,{pickId:e})}};actions={setVisibility:async(e,t)=>{await this.call(`actions.setVisibility`,{ids:e,visible:t})}};buttons={setVisibility:async(e,t)=>{await this.call(`buttons.setVisibility`,{ids:e,visible:t})},decorate:async(e,t)=>{await this.call(`buttons.decorate`,{...t,ids:e})}};close(){this.endpoint.close()}};function v(e={}){let a=new g({port:e.port??m(globalThis.parent,{origin:`*`}),callTimeoutMs:e.callTimeoutMs,onError:e.onError}),o=e.id?{id:e.id}:void 0;return new Promise((s,c)=>{let l=!1,u=a.onEvent([i],(e,t)=>{l||(l=!0,p(),s(new _(a,t)))}),d=setInterval(()=>a.notify(r,o),e.readyIntervalMs??250),f=setTimeout(()=>{l||(l=!0,p(),a.close(),c(new n(`Timed out waiting for host handshake`,{code:t.TIMEOUT,reason:`HANDSHAKE_TIMEOUT`})))},e.timeoutMs??1e4),p=()=>{u(),clearInterval(d),clearTimeout(f)};a.notify(r,o)})}export{v as t};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
:root{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light dark;--fg:#1d2329;--muted:#5b6b78;--bg:#fff;--line:#c9d3db;--accent:#1976d2}@media (prefers-color-scheme:dark){:root{--lightningcss-light: ;--lightningcss-dark:initial;--fg:#e8edf2;--muted:#9fb3c4;--bg:#1b2127;--line:#3a4652;--accent:#42a5f5}}body{color:var(--fg);background:var(--bg);margin:0;padding:12px 16px;font-family:Helvetica Neue,Arial,sans-serif;font-size:15px}.chips{flex-wrap:wrap;gap:6px;margin-bottom:12px;display:flex}.chip{border:1px solid var(--line);min-width:52px;min-height:36px;color:var(--fg);background:0 0;border-radius:18px;font-size:14px}.chip.on{background:var(--accent);border-color:var(--accent);color:#fff}.row{grid-template-columns:110px 1fr;align-items:center;gap:8px;margin-bottom:10px;display:grid}.pair{grid-template-columns:1fr 1fr;gap:8px;display:grid}input,select{border:1px solid var(--line);background:var(--bg);min-width:0;min-height:36px;color:var(--fg);border-radius:6px;padding:4px 8px;font-size:15px}.status{color:var(--muted);font-size:13px}.actions{justify-content:flex-end;gap:8px;display:flex}.actions button{border:1px solid var(--line);min-width:90px;min-height:40px;color:var(--fg);background:0 0;border-radius:6px;font-size:15px}.actions .primary{background:var(--accent);border-color:var(--accent);color:#fff}
|
|
1
|
+
:root{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light dark;--fg:#1d2329;--muted:#5b6b78;--bg:#fff;--line:#c9d3db;--accent:#1976d2}@media (prefers-color-scheme:dark){:root{--lightningcss-light: ;--lightningcss-dark:initial;--fg:#e8edf2;--muted:#9fb3c4;--bg:#1b2127;--line:#3a4652;--accent:#42a5f5}}body{color:var(--fg);background:var(--bg);margin:0;padding:12px 16px;font-family:Helvetica Neue,Arial,sans-serif;font-size:15px}.chips{flex-wrap:wrap;gap:6px;margin-bottom:12px;display:flex}.chip{border:1px solid var(--line);min-width:52px;min-height:36px;color:var(--fg);background:0 0;border-radius:18px;font-size:14px}.chip.on{background:var(--accent);border-color:var(--accent);color:#fff}.row{grid-template-columns:110px 1fr;align-items:center;gap:8px;margin-bottom:10px;display:grid}.pair{grid-template-columns:1fr 1fr;gap:8px;display:grid}input,select{border:1px solid var(--line);background:var(--bg);min-width:0;min-height:36px;color:var(--fg);border-radius:6px;padding:4px 8px;font-size:15px}.status{color:var(--muted);font-size:13px}.actions{justify-content:flex-end;gap:8px;display:flex}.actions button{border:1px solid var(--line);min-width:90px;min-height:40px;color:var(--fg);background:0 0;border-radius:6px;font-size:15px}.actions .primary{background:var(--accent);border-color:var(--accent);color:#fff}.toggles{gap:6px;margin:4px 0 10px;display:grid}.toggle{align-items:center;gap:10px;min-height:36px;display:flex}.toggle input{width:22px;height:22px;min-height:0}.section-title{color:var(--muted);margin:4px 0 6px;font-size:13px}.picks{gap:6px;margin-bottom:12px;display:grid}.pick{border:1px solid var(--line);min-height:48px;color:var(--fg);text-align:left;background:0 0;border-radius:8px;align-items:center;gap:10px;padding:0 14px;font-size:15px;display:flex}.pick .name{text-overflow:ellipsis;white-space:nowrap;flex:1;overflow:hidden}.pick .reading{color:var(--muted);font-size:13px}.pick.on{background:var(--accent);border-color:var(--accent);color:#fff}.pick.on .reading{color:#fff}.actions.sticky{background:var(--bg);padding:10px 0;position:sticky;bottom:0}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e={path:`environment.wind.directionTrue`,label:``,span:30,spanUnit:`min`,unit:``,decimals:-1},t=6e4,n=6048e5,r=[{path:`environment.wind.directionTrue`,label:`TWD`,title:`True wind direction`},{path:`environment.wind.speedTrue`,label:`TWS`,title:`True wind speed`},{path:`environment.wind.angleTrueWater`,label:`TWA`,title:`True wind angle`},{path:`environment.wind.angleApparent`,label:`AWA`,title:`Apparent wind angle`},{path:`environment.wind.speedApparent`,label:`AWS`,title:`Apparent wind speed`},{path:`navigation.speedOverGround`,label:`SOG`,title:`Speed over ground`},{path:`navigation.speedThroughWater`,label:`STW`,title:`Speed through water`},{path:`navigation.courseOverGroundTrue`,label:`COG`,title:`Course over ground`},{path:`navigation.headingTrue`,label:`HDG`,title:`Heading`},{path:`environment.depth.belowTransducer`,label:`Depth`,title:`Depth`},{path:`environment.outside.pressure`,label:`Baro`,title:`Air pressure`},{path:`environment.outside.temperature`,label:`Air`,title:`Air temperature`},{path:`environment.water.temperature`,label:`Water`,title:`Water temperature`}];function i(e){return e.label?e.label:r.find(t=>t.path===e.path)?.label??e.path.split(`.`).pop()??``}function a(e){let r=e.span*(e.spanUnit===`h`?36e5:6e4);return Math.min(n,Math.max(t,r))}function o(t){let n=(e,t)=>typeof e==`string`?e.trim():t,r=(e,t)=>typeof e==`number`&&Number.isFinite(e)?e:t,i=r(t.span,e.span);return{path:n(t.path,e.path),label:n(t.label,e.label),span:i>0?i:e.span,spanUnit:t.spanUnit===`h`?`h`:`min`,unit:n(t.unit,e.unit),decimals:Math.min(4,Math.round(r(t.decimals,e.decimals)))}}function s(e){let t=e.split(`.`).pop()??``;return!/direction|heading|course|bearing/i.test(t)}export{a,o as i,s as n,i as r,r as t};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{t as e}from"./chunk-U7YORXZ7-BLTBSYrv.js";import{i as t,t as n}from"./config-model-DvTH1O2f.js";import{i as r,o as i,t as a}from"./units-Cn4uhL0-.js";/* empty css */function o(e){return e.replace(/[&<>"]/g,e=>`&#${e.charCodeAt(0)};`)}function s(e){let t=document.getElementById(e);if(!t)throw Error(`missing #${e}`);return t}function c(e){let t=s(e);if(t instanceof HTMLInputElement||t instanceof HTMLSelectElement)return t;throw Error(`#${e} is not a form field`)}function l(e,t,r){let i=n.map(t=>`<button type="button" class="chip${t.path===e.path?` on`:``}" data-path="${t.path}" title="${o(t.title)}">${t.label}</button>`).join(``),a=r.length>0?`History from ${o(r.join(`, `))}: the strip fills at once.`:`No history provider on the server: the strip fills from live data. Install one (for example KIP, signalk-parquet or signalk-to-influxdb2) to see the past window at once.`;return`
|
|
2
2
|
<div class="chips">${i}</div>
|
|
3
3
|
<label class="row"><span>Signal K path</span>
|
|
4
4
|
<input id="path" list="paths" value="${o(e.path)}" placeholder="Type to search…">
|
|
@@ -27,4 +27,4 @@ import{f as e,i as t,o as n,s as r,t as i,u as a}from"./units-D0PJ17NW.js";funct
|
|
|
27
27
|
<div class="actions">
|
|
28
28
|
<button type="button" id="cancel">Cancel</button>
|
|
29
29
|
<button type="button" id="save" class="primary">Save</button>
|
|
30
|
-
</div>`}function u(e,t){let n=c(`path`).value.trim(),r=e.find(e=>e.path===n)?.units,
|
|
30
|
+
</div>`}function u(e,t){let n=c(`path`).value.trim(),r=e.find(e=>e.path===n)?.units,i=r?a[r]??[]:[],l=i.some(e=>e.id===t)?t:``;s(`unit`).innerHTML=[`<option value=""${l===``?` selected`:``}>Server default</option>`,...i.filter((e,t,n)=>n.findIndex(t=>t.symbol===e.symbol)===t).map(e=>`<option value="${e.id}"${e.id===l?` selected`:``}>${o(e.symbol||e.id)}</option>`)].join(``)}function d(){let e=Number(c(`span`).value);return{path:c(`path`).value.trim(),label:c(`label`).value.trim(),span:Number.isFinite(e)&&e>0?e:30,spanUnit:c(`spanUnit`).value,unit:c(`unit`).value,decimals:Number(c(`decimals`).value)}}async function f(){let n=s(`root`),a=await e();n.innerHTML=`<p class="status">Loading…</p>`;let[o,f,p]=await Promise.all([a.state.get(),i(),r()]),m=t(o);n.innerHTML=l(m,f,p),u(f,m.unit);let h=c(`path`),g=()=>{u(f,c(`unit`).value);for(let e of document.querySelectorAll(`.chip`))e.classList.toggle(`on`,e.dataset.path===h.value.trim())};h.addEventListener(`change`,g),h.addEventListener(`input`,g);for(let e of document.querySelectorAll(`.chip`))e.addEventListener(`click`,()=>{h.value=e.dataset.path??``,g()});let _=s(`status`);s(`save`).addEventListener(`click`,()=>{a.state.set(d()).then(()=>a.call(`ui.closePanel`)).catch(e=>{_.textContent=`Save failed: ${e instanceof Error?e.message:String(e)}`})}),s(`cancel`).addEventListener(`click`,()=>{a.call(`ui.closePanel`).catch(()=>void 0)})}f().catch(e=>{let t=document.getElementById(`root`);t&&(t.textContent=`Host connection failed: ${e instanceof Error?e.message:String(e)}`)});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{t as e}from"./chunk-U7YORXZ7-BLTBSYrv.js";import{n as t,t as n}from"./styles-CjtPzFRR.js";import{a as r,n as i,r as a,s as o}from"./units-Cn4uhL0-.js";import{r as s}from"./steer-model-DA5-9SMr.js";import{a as c,i as l,n as u,o as d,r as f,t as p}from"./level-model-0XbpyS2Q.js";var m={card:`rgba(20, 26, 32, 0.78)`,text:`#e8edf2`,muted:`#9fb3c4`,glass:`rgba(255, 255, 255, 0.07)`,glassEdge:`rgba(207, 216, 220, 0.75)`,glint:`rgba(255, 255, 255, 0.28)`,liquid:{ok:{edge:`#1b5e20`,middle:`#66bb6a`,surface:`#a5d6a7`},warn:{edge:`#e65100`,middle:`#fdd835`,surface:`#fff59d`},alarm:{edge:`#7f0000`,middle:`#e53935`,surface:`#ef9a9a`}},mark:{ok:`#66bb6a`,warn:`#fdd835`,alarm:`#e53935`}},h={card:`rgba(8, 0, 0, 0.85)`,text:`#d32f2f`,muted:`#8e2424`,glass:`rgba(120, 20, 20, 0.08)`,glassEdge:`rgba(110, 24, 24, 0.9)`,glint:`rgba(160, 40, 40, 0.18)`,liquid:{ok:{edge:`#0b2a0d`,middle:`#2e5d31`,surface:`#3d7340`},warn:{edge:`#3d2600`,middle:`#7a6a10`,surface:`#8f7d1c`},alarm:{edge:`#3a0000`,middle:`#8e1b1b`,surface:`#b02a2a`}},mark:{ok:`#2e5d31`,warn:`#7a6a10`,alarm:`#b02a2a`}};function g(e,t,n,r,i,a){e.beginPath(),e.moveTo(t+a,n),e.arcTo(t+r,n,t+r,n+i,a),e.arcTo(t+r,n+i,t,n+i,a),e.arcTo(t,n+i,t,n,a),e.arcTo(t,n,t+r,n,a),e.closePath()}function _(e,t,n,r,i=``){let a=n;for(e.font=`${i} ${a}px 'Helvetica Neue', Arial, sans-serif`;a>8&&e.measureText(t).width>r;)--a,e.font=`${i} ${a}px 'Helvetica Neue', Arial, sans-serif`}function v(e,t,n,r,i,a){e.beginPath(),e.moveTo(t-n,i),e.lineTo(t-n,a),e.ellipse(t,a,n,r,0,Math.PI,0,!0),e.lineTo(t+n,i),e.ellipse(t,i,n,r,0,0,Math.PI,!1),e.closePath()}function y(e,t,n,r,i){let a=e.createLinearGradient(t-n,0,t+n,0);return a.addColorStop(0,r),a.addColorStop(.35,i),a.addColorStop(.55,i),a.addColorStop(1,r),a}function b(e,t){return e.bottom-Math.min(1,Math.max(0,t))*(e.bottom-e.top)}function x(e,t,n){let r=n.palette,{cx:i,rx:a,top:o,bottom:s}=t,c=a*.22;if(v(e,i,a,c,o,s),e.fillStyle=r.glass,e.fill(),n.fraction!==void 0){let o=r.liquid[n.level],l=b(t,n.fraction);v(e,i,a,c,l,s),e.fillStyle=y(e,i,a,o.edge,o.middle),e.fill(),e.beginPath(),e.ellipse(i,l,a,c,0,0,Math.PI*2),e.fillStyle=o.surface,e.fill()}let l=e.createLinearGradient(i-a*.7,0,i-a*.35,0);if(l.addColorStop(0,`rgba(255, 255, 255, 0)`),l.addColorStop(.5,r.glint),l.addColorStop(1,`rgba(255, 255, 255, 0)`),e.fillStyle=l,e.fillRect(i-a*.7,o+c,a*.35,s-o-c),e.lineWidth=Math.max(1,a*.04),e.strokeStyle=r.glassEdge,v(e,i,a,c,o,s),e.stroke(),e.beginPath(),e.ellipse(i,o,a,c,0,0,Math.PI*2),e.stroke(),n.kind===`battery`){let t=a*.32,n=t*.22,s=a*.22;v(e,i,t,n,o-s,o),e.fillStyle=y(e,i,t,r.glassEdge,r.text),e.fill(),e.beginPath(),e.ellipse(i,o-s,t,n,0,0,Math.PI*2),e.fillStyle=r.text,e.fill()}}function S(e,t,n){let r=n.palette,{cx:i,rx:a,top:o,bottom:s}=t,c=Math.min(a*.25,8),l=Math.max(1,a*.04);if(g(e,i-a,o,2*a,s-o,c),e.fillStyle=r.glass,e.fill(),n.fraction!==void 0){let l=b(t,n.fraction);e.save(),g(e,i-a,o,2*a,s-o,c),e.clip(),e.fillStyle=r.liquid[n.level].middle,e.fillRect(i-a,l,2*a,s-l),e.restore()}if(e.lineWidth=l,e.strokeStyle=r.glassEdge,g(e,i-a,o,2*a,s-o,c),e.stroke(),n.kind===`battery`){let t=a*.6,n=a*.18;g(e,i-t/2,o-n,t,n,Math.min(n/2,3)),e.fillStyle=r.glassEdge,e.fill()}}function C(e,t,n,r){let i=r.palette;e.clearRect(0,0,t,n),g(e,0,0,t,n,Math.min(10,t/10)),e.fillStyle=i.card,e.fill();let a=Math.max(6,Math.min(t,n)*.06),o=Math.max(10,Math.min(t*.11,n*.06)),s=Math.max(14,Math.min(t*.2,n*.11)),c=Math.max(9,s*.5);e.textAlign=`center`,e.textBaseline=`top`,e.fillStyle=i.muted,_(e,r.title,o,t-2*a),e.fillText(r.title,t/2,a);let l=s+(r.detailText?c*1.2:0)+a,u=a+o*1.5,d=n-l-a*.5,f=Math.min(t*.27,(d-u)*.4),p=f*(r.style===`3d`?.22:0)+(r.kind===`battery`?f*.22:0),m={cx:t/2-f*.15,rx:f,top:u+p,bottom:d-(r.style===`3d`?f*.22:0)};if(m.bottom-m.top<4)return;r.style===`flat`?S(e,m,r):x(e,m,r);let h=m.cx+f+Math.max(3,f*.12),v=e=>b(m,e);e.lineCap=`round`,e.strokeStyle=i.muted,e.lineWidth=Math.max(1,f*.03);for(let t of[.25,.5,.75])e.beginPath(),e.moveTo(h,v(t)),e.lineTo(h+f*.18,v(t)),e.stroke();e.lineWidth=Math.max(2,f*.09);for(let t of r.marks)e.strokeStyle=i.mark[t.level],e.beginPath(),e.moveTo(h,v(t.at)),e.lineTo(h+f*.32,v(t.at)),e.stroke();e.textBaseline=`alphabetic`,e.fillStyle=r.fraction===void 0?i.muted:i.text,_(e,r.valueText,s,t-2*a,`bold`);let y=n-a-(r.detailText?c*1.2:0);e.fillText(r.valueText,t/2,y),r.detailText&&(e.fillStyle=i.muted,_(e,r.detailText,c,t-2*a),e.fillText(r.detailText,t/2,n-a))}var w=12e4,T=5e3;function E(g){let _=p[g],v=document.createElement(`canvas`);document.getElementById(`root`)?.append(v);let y,b=!1,x=0,S,E=!1,D=(e,t,n=!0)=>e&&(!n||t-e.time<w)?e.value:void 0;function O(e){let t=b?h:m,n=y;if(!n?.config.path)return{title:_.noun,fraction:void 0,level:`ok`,valueText:`Not set up`,detailText:``,marks:[],kind:g,style:n?.config.style??`3d`,palette:t};let r=D(n.levelReading,e),a=D(n.detailReading,e,_.detailAges),o=n.config.showDetail&&r!==void 0&&a!==void 0?`${i(_.detail(r,a),n.detail.conversion).toFixed(n.detail.decimals)} ${n.detail.symbol}`.trim():``;return{title:n.title,fraction:r,level:r===void 0?`ok`:u(d(r,n.zones)),valueText:r===void 0?`--`:`${i(r,n.level.conversion).toFixed(n.level.decimals)} ${n.level.symbol}`.trim(),detailText:o,marks:c(n.zones),kind:g,style:n.config.style,palette:t}}function k(){E=!1;let e=v.getBoundingClientRect(),t=window.devicePixelRatio||1,n=Math.max(1,Math.round(e.width*t)),r=Math.max(1,Math.round(e.height*t));(v.width!==n||v.height!==r)&&(v.width=n,v.height=r);let i=v.getContext(`2d`);i&&(i.setTransform(t,0,0,t,0,0),C(i,e.width,e.height,O(Date.now())))}function A(){E||(E=!0,requestAnimationFrame(k))}async function j(e){let t=++x,n=f(await e.state.get()),i=_.matches(n.path)?_.detailPath(n.path):void 0,[c,u,d]=n.path?await Promise.all([r(n.path),i?r(i):void 0,i&&!_.detailAges?o(i):void 0]):[void 0,void 0,void 0];if(t!==x)return;let p={config:n,title:n.label||c?.displayName||_.name(n.path),zones:l(c?.zones),level:a({...c,units:c?.units??`ratio`},`percent`,0),detail:a({...u,units:u?.units??_.detailUnits},void 0,_.detailDecimals)};typeof d==`number`&&Number.isFinite(d)&&(p.detailReading={time:Date.now(),value:d});let m;if(n.path){let r=i?[n.path,i]:[n.path],a=new s;if(m=await e.signalk.subscribe(r,e=>{if(typeof e.value!=`number`||!Number.isFinite(e.value))return;let t={time:a.local(Date.now(),e.timestamp),value:e.value};e.path===n.path?p.levelReading=t:p.detailReading=t,A()}),t!==x){await m().catch(()=>void 0);return}}let h=S;S=m,y=p,A(),await h?.().catch(()=>void 0)}async function M(){let r=await e();t(r),new ResizeObserver(A).observe(v),setInterval(A,T),await r.subscribe([`state.changed`],()=>{j(r).catch(e=>{console.warn(`${g}: settings reload failed`,e)})}),await n(r,e=>{b=e,A()}).catch(e=>{console.warn(`${g}: night mode unavailable`,e)}),await j(r)}M().catch(e=>{console.warn(`${g}: host connection failed`,e)})}export{E as t};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import{t as e}from"./chunk-U7YORXZ7-BLTBSYrv.js";import{n as t,o as n,r}from"./units-Cn4uhL0-.js";import{r as i,t as a}from"./level-model-0XbpyS2Q.js";/* empty css */function o(e){return e.replace(/[&<>"]/g,e=>`&#${e.charCodeAt(0)};`)}function s(e){let t=document.getElementById(e);if(!t)throw Error(`missing #${e}`);return t}function c(e){let t=s(e);if(t instanceof HTMLInputElement)return t;throw Error(`#${e} is not an input`)}var l=r({units:`ratio`},`percent`,0);function u(e,n,r){let i=e.matches(n.path)?n.displayName||e.name(n.path):n.path;return`<button type="button" class="pick${n.path===r?` on`:``}" aria-pressed="${n.path===r}" data-path="${o(n.path)}" title="${o(n.path)}">
|
|
2
|
+
<span class="name">${o(i)}</span>
|
|
3
|
+
<span class="reading">${t(n.value,l.conversion).toFixed(l.decimals)} ${l.symbol}</span>
|
|
4
|
+
</button>`}function d(e,t,n,r){return n.length===0?``:`<div class="section-title">${t}</div>
|
|
5
|
+
<div class="picks">${n.map(t=>u(e,t,r)).join(``)}</div>`}function f(e,t){e.classList.toggle(`on`,t),e.setAttribute(`aria-pressed`,String(t))}function p(e,t,n){return`<div class="chips">${t.map(([t,r])=>`<button type="button" class="chip${t===n?` on`:``}" aria-pressed="${t===n}" data-${e}="${t}">${r}</button>`).join(``)}</div>`}function m(e,t){let n=document.querySelectorAll(`[data-${e}]`);for(let r of n)r.addEventListener(`click`,()=>{for(let e of n)f(e,e===r);t(r.dataset[e]??``)})}async function h(t){let r=a[t],l=s(`root`),u=await e();l.innerHTML=`<p class="status">Loading…</p>`;let[h,g]=await Promise.all([u.state.get(),n()]),_=i(h),v=g.filter(e=>e.units===`ratio`||r.matches(e.path)),y=v.filter(e=>r.matches(e.path)),b=v.filter(e=>!r.matches(e.path)),{path:x,style:S,showDetail:C}=_;l.innerHTML=`
|
|
6
|
+
${d(r,r.plural,y,x)}
|
|
7
|
+
${d(r,`Other levels`,b,x)}
|
|
8
|
+
${v.length===0?`<p class="status">No levels on this server yet.</p>`:``}
|
|
9
|
+
<label class="row"><span>Label</span>
|
|
10
|
+
<input id="label" value="${o(_.label)}" placeholder="Automatic">
|
|
11
|
+
</label>
|
|
12
|
+
<div class="section-title">Look</div>
|
|
13
|
+
${p(`look`,[[`3d`,`3D`],[`flat`,`Flat`]],S)}
|
|
14
|
+
<div class="section-title">${r.detailTitle}</div>
|
|
15
|
+
${p(`detail`,[[`true`,`Show`],[`false`,`Hide`]],String(C))}
|
|
16
|
+
<p class="status">Colours follow the zones set for the path in the server's metadata: green when normal, yellow for alert and warn, red for alarm and emergency.</p>
|
|
17
|
+
<p class="status" id="status"></p>
|
|
18
|
+
<div class="actions sticky">
|
|
19
|
+
<button type="button" id="cancel">Cancel</button>
|
|
20
|
+
<button type="button" id="save" class="primary">Save</button>
|
|
21
|
+
</div>`;for(let e of document.querySelectorAll(`.pick`))e.addEventListener(`click`,()=>{x=e.dataset.path??``;for(let t of document.querySelectorAll(`.pick`))f(t,t===e)});m(`look`,e=>{S=e===`flat`?`flat`:`3d`}),m(`detail`,e=>{C=e===`true`});let w=s(`status`);s(`save`).addEventListener(`click`,()=>{u.state.set({path:x,label:c(`label`).value.trim(),style:S,showDetail:C}).then(()=>u.call(`ui.closePanel`)).catch(e=>{w.textContent=`Save failed: ${e instanceof Error?e.message:String(e)}`})}),s(`cancel`).addEventListener(`click`,()=>{u.call(`ui.closePanel`).catch(()=>void 0)})}function g(e){h(e).catch(e=>{let t=document.getElementById(`root`);t&&(t.textContent=`Host connection failed: ${e instanceof Error?e.message:String(e)}`)})}export{g as t};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e={path:``,label:``,style:`3d`,showDetail:!0};function t(t){let n=typeof t==`object`&&t?t:{};return{path:typeof n.path==`string`?n.path:e.path,label:typeof n.label==`string`?n.label:e.label,style:n.style===`flat`||n.style===`3d`?n.style:e.style,showDetail:typeof n.showDetail==`boolean`?n.showDetail:e.showDetail}}var n=/^tanks\.([^.]+)\.([^.]+)\.currentLevel$/,r=/^electrical\.batteries\.([^.]+)\.capacity\.stateOfCharge$/,i={fuel:`Fuel`,freshWater:`Fresh water`,wasteWater:`Grey water`,blackWater:`Black water`,lubrication:`Oil`,liveWell:`Live well`,baitWell:`Bait well`,ballast:`Ballast`,gas:`Gas`},a={tank:{noun:`Tank`,plural:`Tanks`,matches:e=>n.test(e),name:e=>{let t=n.exec(e);if(!t)return e;let[,r=``,a=``]=t;return`${i[r]??r} ${a}`.trim()},detailPath:e=>e.replace(/\.currentLevel$/,`.capacity`),detail:(e,t)=>e*t,detailUnits:`m3`,detailDecimals:0,detailAges:!1,detailTitle:`Volume left (needs the tank's capacity)`},battery:{noun:`Battery`,plural:`Battery banks`,matches:e=>r.test(e),name:e=>{let t=r.exec(e)?.[1];return t===void 0?e:`Battery ${t}`},detailPath:e=>e.replace(/\.capacity\.stateOfCharge$/,`.voltage`),detail:(e,t)=>t,detailUnits:`V`,detailDecimals:1,detailAges:!0,detailTitle:`Voltage (needs the bank's voltage)`}},o={nominal:0,normal:0,alert:1,warn:2,alarm:3,emergency:4};function s(e){return typeof e==`string`&&e in o}function c(e){if(!Array.isArray(e))return[];let t=[];for(let n of e){if(typeof n!=`object`||!n)continue;let e=n;if(!s(e.state))continue;let r={state:e.state};typeof e.lower==`number`&&(r.lower=e.lower),typeof e.upper==`number`&&(r.upper=e.upper),typeof e.message==`string`&&(r.message=e.message),t.push(r)}return t}function l(e,t){let n=`normal`;for(let r of t)r.lower!==void 0&&e<r.lower||r.upper!==void 0&&e>r.upper||o[r.state]>o[n]&&(n=r.state);return n}function u(e){let t=o[e];return t>=3?`alarm`:t>=1?`warn`:`ok`}function d(e){let t={ok:0,warn:1,alarm:2},n=new Map;for(let r of e){let e=u(r.state);if(e!==`ok`)for(let i of[r.lower,r.upper]){if(i===void 0||i<=0||i>=1)continue;let r=n.get(i);(!r||t[e]>t[r])&&n.set(i,e)}}return[...n].map(([e,t])=>({at:e,level:t})).sort((e,t)=>e.at-t.at)}export{d as a,c as i,u as n,l as o,t as r,a as t};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{t as e}from"./chunk-U7YORXZ7-BLTBSYrv.js";import{n as t,t as n}from"./styles-CjtPzFRR.js";import{a as r,n as i,r as a}from"./units-Cn4uhL0-.js";import{a as o,c as s,d as c,i as l,l as u,n as d,o as f,r as p,s as m,u as h}from"./steer-model-DA5-9SMr.js";var g={card:`rgba(20, 26, 32, 0.78)`,ring:`#cfd8dc`,ringText:`#1d2329`,text:`#e8edf2`,muted:`#9fb3c4`,port:`#e53935`,starboard:`#43a047`,apparent:`#29b6f6`,trueWind:`#ffa726`,shift:`rgba(255, 167, 38, 0.28)`,waypoint:`#ab47bc`,course:`#ec407a`,current:`#26c6da`},_={card:`rgba(8, 0, 0, 0.85)`,ring:`#4a0c0c`,ringText:`#d32f2f`,text:`#d32f2f`,muted:`#8e2424`,port:`#b71c1c`,starboard:`#7f1212`,apparent:`#ff5252`,trueWind:`#d32f2f`,shift:`rgba(211, 47, 47, 0.22)`,waypoint:`#c62828`,course:`#e57373`,current:`#ef9a9a`},v=Math.PI/180,y=35*v,b=`'Helvetica Neue', Arial, sans-serif`;function x(e,t,n){return[e.cx+n*Math.sin(t),e.cy-n*Math.cos(t)]}function S(e){return e-Math.PI/2}function C(e,t){return c(t-e)}function ee(e,t,n){let r=n.palette,i=t.r-t.band/2;e.lineWidth=t.band,e.strokeStyle=r.ring,e.beginPath(),e.arc(t.cx,t.cy,i,0,2*Math.PI),e.stroke(),e.lineWidth=t.band*.2;for(let[n,i,a]of[[r.port,-60*v,-20*v],[r.starboard,20*v,60*v]])e.strokeStyle=n,e.beginPath(),e.arc(t.cx,t.cy,t.r-t.band*.9,S(i),S(a)),e.stroke();let a=n.dial===`compass`?n.heading:0;if(e.strokeStyle=r.ringText,e.fillStyle=r.ringText,e.textAlign=`center`,e.textBaseline=`middle`,e.font=`600 ${Math.max(8,t.band*.5)}px ${b}`,a!==void 0)for(let r=0;r<360;r+=10){let i=r*v-a,o=r%30==0;e.lineWidth=o?2:1;let[s,c]=x(t,i,t.r),[l,u]=x(t,i,t.r-t.band*(o?.28:.18));if(e.beginPath(),e.moveTo(s,c),e.lineTo(l,u),e.stroke(),!o)continue;let d=n.dial===`compass`?w(r):String(r>180?360-r:r),[f,p]=x(t,i,t.r-t.band*.62);e.save(),e.translate(f,p),e.rotate(i),e.fillText(d,0,0),e.restore()}}function w(e){return{0:`N`,90:`E`,180:`S`,270:`W`}[e]??String(e)}function T(e,t,n,r,i){let[a,o]=x(t,n,t.r-t.band);e.strokeStyle=r,e.lineWidth=Math.max(1.5,t.r*.012),e.setLineDash(i?[t.r*.05,t.r*.04]:[]),e.beginPath(),e.moveTo(t.cx,t.cy),e.lineTo(a,o),e.stroke(),e.setLineDash([])}function E(e,t,n){if(!n.shift)return;e.fillStyle=n.palette.shift,e.beginPath(),e.moveTo(t.cx,t.cy);let r=S(n.shift.from);e.arc(t.cx,t.cy,t.r-t.band,r,r+C(n.shift.from,n.shift.to)),e.closePath(),e.fill()}function D(e,t,n,r,i,a){let o=t.r-t.band,s=x(t,n,o*(1-a)),c=t.r*.07,l=x(t,n,o),u=[l[0]-c*Math.cos(n),l[1]-c*Math.sin(n)],d=[l[0]+c*Math.cos(n),l[1]+c*Math.sin(n)];e.fillStyle=r,e.beginPath(),e.moveTo(s[0],s[1]),e.lineTo(u[0],u[1]),e.lineTo(d[0],d[1]),e.closePath(),e.fill();let[f,p]=x(t,n,o-t.r*.07);e.fillStyle=`#10161b`,e.font=`700 ${Math.max(8,t.r*.075)}px ${b}`,e.textAlign=`center`,e.textBaseline=`middle`,e.fillText(i,f,p)}function O(e,t,n,r,i){let[a,o]=x(t,n,t.r-t.band/2),s=t.band*.32;e.fillStyle=r,e.strokeStyle=`#10161b`,e.lineWidth=1.5,e.beginPath(),i===`dot`?e.arc(a,o,s,0,2*Math.PI):(e.moveTo(a+s*Math.sin(n),o-s*Math.cos(n)),e.lineTo(a+s*Math.cos(n),o+s*Math.sin(n)),e.lineTo(a-s*Math.sin(n),o+s*Math.cos(n)),e.lineTo(a-s*Math.cos(n),o-s*Math.sin(n)),e.closePath()),e.fill(),e.stroke()}function k(e,t,n){let r=n.current;if(!r)return;let i=(t.r-t.band)*(.25+.45*Math.min(1,r.fraction)),[a,o]=x(t,r.angle,i),s=t.r*.06;e.strokeStyle=n.palette.current,e.fillStyle=n.palette.current,e.lineWidth=Math.max(2,t.r*.02),e.beginPath(),e.moveTo(t.cx,t.cy),e.lineTo(a,o),e.stroke();let[c,l]=x(t,r.angle,i-s*1.6);e.beginPath(),e.moveTo(a,o),e.lineTo(c+s*Math.cos(r.angle),l+s*Math.sin(r.angle)),e.lineTo(c-s*Math.cos(r.angle),l-s*Math.sin(r.angle)),e.closePath(),e.fill();let[u,d]=x(t,r.angle,i+t.r*.09);e.font=`600 ${Math.max(8,t.r*.075)}px ${b}`,e.textAlign=`center`,e.textBaseline=`middle`,e.fillText(r.text,u,d)}function A(e,t,n){let r=t.r*.38,i=r*.34;e.strokeStyle=n.palette.muted,e.lineWidth=1.5,e.beginPath(),e.moveTo(t.cx,t.cy-r),e.bezierCurveTo(t.cx+i*1.1,t.cy-r*.45,t.cx+i,t.cy+r*.5,t.cx+i*.7,t.cy+r*.8),e.lineTo(t.cx-i*.7,t.cy+r*.8),e.bezierCurveTo(t.cx-i,t.cy+r*.5,t.cx-i*1.1,t.cy-r*.45,t.cx,t.cy-r),e.stroke()}function j(e,t,n){let r=t.band*.45,i=t.cy-t.r;e.fillStyle=n.palette.text,e.beginPath(),e.moveTo(t.cx,i+r),e.lineTo(t.cx-r*.7,i-r*.25),e.lineTo(t.cx+r*.7,i-r*.25),e.closePath(),e.fill()}function M(e,t,n,r,i,a,o,s){e.textAlign=i,e.textBaseline=`top`;let c=a*.5,l=s?r-a-c-2:r,u=s?r-a:r+c+2;e.fillStyle=o.muted,e.font=`600 ${c}px ${b}`,e.fillText(t.unit?`${t.label} ${t.unit}`:t.label,n,l),e.fillStyle=o.text,e.font=`700 ${a}px ${b}`,e.fillText(t.value,n,u)}function te(e,t,n,r){if(n.rudder===void 0)return;let i=t.r*.55,a=Math.max(4,t.r*.05);e.fillStyle=n.palette.ring,e.globalAlpha=.35,e.fillRect(t.cx-i,r,i*2,a),e.globalAlpha=1;let o=Math.max(-1,Math.min(1,n.rudder/y));e.fillStyle=o<0?n.palette.port:n.palette.starboard,e.fillRect(Math.min(t.cx,t.cx+o*i),r,Math.abs(o)*i,a),e.fillStyle=n.palette.text,e.fillRect(t.cx-1,r-2,2,a+4)}function N(e,t,n,r){let i=r.palette;e.clearRect(0,0,t,n),e.fillStyle=i.card,e.beginPath(),e.roundRect(0,0,t,n,8),e.fill();let a=Math.max(4,Math.min(t,n)*.03),o=r.rudder===void 0?0:Math.min(t,n)*.06,s=Math.max(10,Math.min(t,n-o)/2-a),c={cx:t/2,cy:(n-o)/2,r:s,band:s*.16};E(e,c,r),ee(e,c,r),A(e,c,r),r.trueWind!==void 0&&r.closeHauled!==void 0&&(T(e,c,r.trueWind+r.closeHauled,i.port,!0),T(e,c,r.trueWind-r.closeHauled,i.starboard,!0)),r.waypoint!==void 0&&O(e,c,r.waypoint,i.waypoint,`dot`),r.course!==void 0&&O(e,c,r.course,i.course,`diamond`),k(e,c,r),r.trueWind!==void 0&&D(e,c,r.trueWind,i.trueWind,`T`,.22),r.apparent!==void 0&&D(e,c,r.apparent,i.apparent,`A`,.5),j(e,c,r),e.fillStyle=i.text,e.textAlign=`center`,e.textBaseline=`middle`,e.font=`700 ${Math.max(10,s*.2)}px ${b}`,e.fillText(r.heading===void 0?`---`:`${l(r.heading)}°`,c.cx,c.cy);let u=Math.max(10,Math.min(t,n)*.075);M(e,r.topLeft,a,a,`left`,u,i,!1),M(e,r.topRight,t-a,a,`right`,u,i,!1),M(e,r.bottomLeft,a,n-a,`left`,u,i,!0),M(e,r.bottomRight,t-a,n-a,`right`,u,i,!0),te(e,c,r,c.cy+s+a*.6)}var P=1e4,F=Math.PI/180,I=.1,L=.05,R=1,z=1e3,B=new Set([`angleTrueWater`,`angleTrueGround`,`directionTrue`]),V=document.createElement(`canvas`);document.getElementById(`root`)?.append(V);var H,U=!1,W=0,G,K=!1;function q(e,t){return n=>{let r=e.values.get(n);return r&&t-r.time<P?r.value:void 0}}function J(e,t,n){return{label:e,value:t===void 0?`--`:i(t,n.conversion).toFixed(n.decimals),unit:n.symbol}}function Y(e,t){return{label:e,value:t===void 0?`--`:m(t),unit:``}}function X(e){let t=U?_:g,n={label:``,value:``,unit:``},r=H;if(!r)return{dial:`compass`,topLeft:n,topRight:n,bottomLeft:n,bottomRight:n,palette:t};let a=r.config,s=q(r,e),c=s(`heading`),l=s(`angleApparent`),d=u(s,a.trueWind),f=s(`speedOverGround`),p=s(`drift`),m=o(s(`set`),c),v=a.showShift&&c!==void 0?r.shift.range(e):void 0;return{dial:a.dial,heading:c,apparent:l,trueWind:d,shift:v&&c!==void 0?{from:v.from-c,to:v.to-c}:void 0,closeHauled:a.showTackLines?a.closeHauled*F:void 0,waypoint:a.showWaypoint?o(s(`waypoint`),c):void 0,course:a.showCourse&&f!==void 0&&f>=I?o(s(`course`),c):void 0,current:a.showCurrent&&m!==void 0&&p!==void 0&&p>=L?{angle:m,fraction:p/R,text:`${i(p,r.speed.conversion).toFixed(1)} ${r.speed.symbol}`}:void 0,rudder:a.showRudder?s(`rudder`):void 0,topLeft:J(`AWS`,s(`speedApparent`),r.speed),topRight:J(`TWS`,h(s,a.trueWind),r.speed),bottomLeft:Y(`AWA`,l),bottomRight:Y(`TWA`,d),palette:t}}function Z(){K=!1;let e=V.getBoundingClientRect(),t=window.devicePixelRatio||1,n=Math.max(1,Math.round(e.width*t)),r=Math.max(1,Math.round(e.height*t));(V.width!==n||V.height!==r)&&(V.width=n,V.height=r);let i=V.getContext(`2d`);i&&(i.setTransform(t,0,0,t,0,0),N(i,e.width,e.height,X(Date.now())))}function Q(){K||(K=!0,requestAnimationFrame(Z))}function ne(e,t){let n=q(e,t),r=n(`heading`),i=u(n,e.config.trueWind);r!==void 0&&i!==void 0&&e.shift.add(t,c(r+i))}async function $(e){let t=++W,n=f(await e.state.get()),i=s(n.north),o=await r(i.speedApparent);if(t!==W)return;let c={config:n,values:new Map,shift:new d(n.shiftSeconds*1e3),clock:new p,speed:a({...o,units:o?.units??`m/s`},void 0,1)},l=new Map;for(let[e,t]of Object.entries(i))l.set(t,[...l.get(t)??[],e]);let u=await e.signalk.subscribe([...l.keys()],e=>{let t=l.get(e.path);if(!t||typeof e.value!=`number`||!Number.isFinite(e.value))return;let n=Date.now(),r=c.clock.local(n,e.timestamp);for(let n of t)c.values.set(n,{time:r,value:e.value});t.some(e=>B.has(e))&&ne(c,n),Q()});if(t!==W){await u().catch(()=>void 0);return}let m=G;G=u,H=c,Q(),await m?.().catch(()=>void 0)}async function re(){let r=await e();t(r),new ResizeObserver(Q).observe(V),setInterval(Q,z),await r.subscribe([`state.changed`],()=>{$(r).catch(e=>{console.warn(`wind steering: settings reload failed`,e)})}),await n(r,e=>{U=e,Q()}).catch(e=>{console.warn(`wind steering: night mode unavailable`,e)}),await $(r)}re().catch(e=>{console.warn(`wind steering: host connection failed`,e)});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import{t as e}from"./chunk-U7YORXZ7-BLTBSYrv.js";import{o as t,t as n}from"./steer-model-DA5-9SMr.js";/* empty css */var r=[{key:`showTackLines`,label:`Close-hauled lines for both tacks`},{key:`showShift`,label:`Wind shift fan`},{key:`showWaypoint`,label:`Next waypoint`},{key:`showCourse`,label:`Course over ground`},{key:`showCurrent`,label:`Current (set and drift)`},{key:`showRudder`,label:`Rudder angle`}];function i(e){let t=document.getElementById(e);if(!t)throw Error(`missing #${e}`);return t}function a(e){let t=i(e);if(t instanceof HTMLInputElement||t instanceof HTMLSelectElement)return t;throw Error(`#${e} is not a form field`)}function o(e){let t=i(e);if(t instanceof HTMLInputElement)return t;throw Error(`#${e} is not a checkbox`)}function s(e,t,n){return`<select id="${e}">${t.map(([e,t])=>`<option value="${e}"${e===n?` selected`:``}>${t}</option>`).join(``)}</select>`}function c(e){return`
|
|
2
|
+
<label class="row"><span>Dial</span>
|
|
3
|
+
${s(`dial`,[[`compass`,`Compass card, heading up`],[`wind`,`Wind angles, bow up`]],e.dial)}
|
|
4
|
+
</label>
|
|
5
|
+
<label class="row"><span>North</span>
|
|
6
|
+
${s(`north`,[[`true`,`True`],[`magnetic`,`Magnetic`]],e.north)}
|
|
7
|
+
</label>
|
|
8
|
+
<label class="row"><span>True wind</span>
|
|
9
|
+
${s(`trueWind`,[[`water`,`Through water`],[`ground`,`Over ground`]],e.trueWind)}
|
|
10
|
+
</label>
|
|
11
|
+
<label class="row"><span>Close-hauled</span>
|
|
12
|
+
<input id="closeHauled" type="number" min="${n.min}" max="${n.max}" step="1" value="${e.closeHauled}">
|
|
13
|
+
</label>
|
|
14
|
+
<label class="row"><span>Shift window</span>
|
|
15
|
+
<select id="shiftSeconds">
|
|
16
|
+
${[30,60,120,300,600,1800].map(t=>`<option value="${t}"${t===e.shiftSeconds?` selected`:``}>${t<60?`${t} s`:`${t/60} min`}</option>`).join(``)}
|
|
17
|
+
${[30,60,120,300,600,1800].includes(e.shiftSeconds)?``:`<option value="${e.shiftSeconds}" selected>${e.shiftSeconds} s</option>`}
|
|
18
|
+
</select>
|
|
19
|
+
</label>
|
|
20
|
+
<div class="toggles">
|
|
21
|
+
${r.map(t=>`<label class="toggle"><input type="checkbox" id="${t.key}"${e[t.key]?` checked`:``}> ${t.label}</label>`).join(``)}
|
|
22
|
+
</div>
|
|
23
|
+
<p class="status">Close-hauled is the angle off the true wind you sail upwind, ${n.min} to ${n.max} degrees. Wind speeds use the unit set for apparent wind speed in the server's Unit Preferences.</p>
|
|
24
|
+
<p class="status" id="status"></p>
|
|
25
|
+
<div class="actions">
|
|
26
|
+
<button type="button" id="cancel">Cancel</button>
|
|
27
|
+
<button type="button" id="save" class="primary">Save</button>
|
|
28
|
+
</div>`}function l(){let e={dial:a(`dial`).value,north:a(`north`).value,trueWind:a(`trueWind`).value,closeHauled:Number(a(`closeHauled`).value),shiftSeconds:Number(a(`shiftSeconds`).value)};for(let t of r)e[t.key]=o(t.key).checked;return{...t(e)}}async function u(){let n=i(`root`),r=await e();n.innerHTML=`<p class="status">Loading…</p>`,n.innerHTML=c(t(await r.state.get()));let a=i(`status`);i(`save`).addEventListener(`click`,()=>{r.state.set(l()).then(()=>r.call(`ui.closePanel`)).catch(e=>{a.textContent=`Save failed: ${e instanceof Error?e.message:String(e)}`})}),i(`cancel`).addEventListener(`click`,()=>{r.call(`ui.closePanel`).catch(()=>void 0)})}u().catch(e=>{let t=document.getElementById(`root`);t&&(t.textContent=`Host connection failed: ${e instanceof Error?e.message:String(e)}`)});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e={dial:`compass`,north:`true`,trueWind:`water`,closeHauled:45,shiftSeconds:60,showTackLines:!0,showShift:!0,showWaypoint:!0,showCourse:!0,showCurrent:!0,showRudder:!0},t={min:20,max:80},n={min:10,max:1800};function r(e,t,n){return Math.min(n,Math.max(t,e))}function i(i){let a=e,o=(e,t)=>typeof e==`number`&&Number.isFinite(e)?e:t,s=(e,t)=>typeof e==`boolean`?e:t;return{dial:i.dial===`wind`?`wind`:`compass`,north:i.north===`magnetic`?`magnetic`:`true`,trueWind:i.trueWind===`ground`?`ground`:`water`,closeHauled:r(Math.round(o(i.closeHauled,a.closeHauled)),t.min,t.max),shiftSeconds:r(Math.round(o(i.shiftSeconds,a.shiftSeconds)),n.min,n.max),showTackLines:s(i.showTackLines,a.showTackLines),showShift:s(i.showShift,a.showShift),showWaypoint:s(i.showWaypoint,a.showWaypoint),showCourse:s(i.showCourse,a.showCourse),showCurrent:s(i.showCurrent,a.showCurrent),showRudder:s(i.showRudder,a.showRudder)}}var a=2*Math.PI;function o(e){return((e+Math.PI)%a+a)%a-Math.PI}function s(e){return(e%a+a)%a}function c(e){let t=e===`magnetic`;return{heading:t?`navigation.headingMagnetic`:`navigation.headingTrue`,course:t?`navigation.courseOverGroundMagnetic`:`navigation.courseOverGroundTrue`,waypoint:t?`navigation.course.calcValues.bearingMagnetic`:`navigation.course.calcValues.bearingTrue`,set:t?`environment.current.setMagnetic`:`environment.current.setTrue`,headingTrue:`navigation.headingTrue`,speedOverGround:`navigation.speedOverGround`,drift:`environment.current.drift`,rudder:`steering.rudderAngle`,angleApparent:`environment.wind.angleApparent`,speedApparent:`environment.wind.speedApparent`,angleTrueWater:`environment.wind.angleTrueWater`,angleTrueGround:`environment.wind.angleTrueGround`,directionTrue:`environment.wind.directionTrue`,speedTrue:`environment.wind.speedTrue`,speedOverGroundWind:`environment.wind.speedOverGround`}}function l(e,t){if(t===`water`)return e(`angleTrueWater`);let n=e(`angleTrueGround`);if(n!==void 0)return n;let r=e(`directionTrue`),i=e(`headingTrue`);return r===void 0||i===void 0?void 0:o(r-i)}function u(e,t){return t===`ground`?e(`speedOverGroundWind`)??e(`speedTrue`):e(`speedTrue`)}function d(e,t){return e===void 0||t===void 0?void 0:o(e-t)}var f=class{windowMs;samples=[];constructor(e){this.windowMs=e}add(e,t){this.samples.push({time:e,direction:t}),this.prune(e)}clear(){this.samples=[]}prune(e){let t=e-this.windowMs,n=0;for(;n<this.samples.length&&(this.samples[n]?.time??0)<t;)n++;n>0&&this.samples.splice(0,n)}range(e){this.prune(e);let t=this.samples.at(-1);if(!t)return;let n=0,r=0;for(let e of this.samples){let i=o(e.direction-t.direction);i<n&&(n=i),i>r&&(r=i)}return{from:s(t.direction+n),to:s(t.direction+r)}}};function p(e){let t=Math.round(s(e)*180/Math.PI)%360;return String(t).padStart(3,`0`)}function m(e){let t=Math.round(Math.abs(o(e))*180/Math.PI);return t===0||t===180?`${t}°`:`${t}° ${o(e)<0?`P`:`S`}`}var h=6e4,g=class{gaps=[];offset;carried;local(e,t){let n=t===void 0?NaN:Date.parse(t);if(!Number.isFinite(n))return e;let r=e-n,i=Math.floor(e/1e3),a=i-h/1e3;for(;(this.gaps[0]?.second??1/0)<=a;)this.gaps.shift();this.gaps.length===0&&this.offset!==void 0&&(this.carried={second:i,offset:this.offset});let o=this.gaps.at(-1);o?.second===i?o.gap=Math.min(o.gap,r):this.gaps.push({second:i,gap:r});let s=r;for(let e of this.gaps)s=Math.min(s,e.gap);return this.offset=s,this.carried?.second===i&&(s=Math.min(s,this.carried.offset)),n+s}};export{d as a,c,s as d,p as i,l,f as n,i as o,g as r,m as s,t,u};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function e(e){let t,n=()=>{t&&clearTimeout(t),t=void 0};window.addEventListener(`pointerdown`,()=>{n(),t=setTimeout(()=>{e.call(`ui.openConfigPanel`).catch(()=>void 0)},1500)});for(let e of[`pointerup`,`pointercancel`,`pointerleave`])window.addEventListener(e,n)}async function t(e,t){e.hasCapability(`nightMode`)&&(await e.subscribe([`nightMode.changed`],(e,n)=>{t(n?.enabled===!0)}),t((await e.nightMode.get()).enabled))}export{e as n,t};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./chunk-U7YORXZ7-BLTBSYrv.js";import{t as e}from"./level-CwEJ7gss.js";e(`tank`);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./chunk-U7YORXZ7-BLTBSYrv.js";import{t as e}from"./level-config-B0mMmED6.js";e(`tank`);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function e(e){return typeof e==`object`&&!!e}async function t(t){try{let n=await fetch(`/signalk/v1/api/vessels/self/${t.replace(/\./g,`/`)}/meta`,{credentials:`include`});if(!n.ok)return;let r=await n.json();if(!e(r))return;let i={};typeof r.units==`string`&&(i.units=r.units),typeof r.displayName==`string`&&(i.displayName=r.displayName),Array.isArray(r.zones)&&(i.zones=r.zones);let a=r.displayUnits;return e(a)&&(i.displayUnits={},typeof a.targetUnit==`string`&&(i.displayUnits.targetUnit=a.targetUnit),typeof a.displayFormat==`string`&&(i.displayUnits.displayFormat=a.displayFormat)),i}catch{return}}function n(t,r=``,i=[]){if(!e(t))return i;if(`value`in t){if(typeof t.value==`number`){let n=t.meta;i.push({path:r,units:e(n)&&typeof n.units==`string`?n.units:void 0,value:t.value,displayName:e(n)&&typeof n.displayName==`string`?n.displayName:void 0})}return i}for(let[e,a]of Object.entries(t))e!==`meta`&&e!==`$source`&&e!==`timestamp`&&e!==`values`&&n(a,r?`${r}.${e}`:e,i);return i}async function r(){try{let e=await fetch(`/signalk/v1/api/vessels/self`,{credentials:`include`});return e.ok?n(await e.json()).sort((e,t)=>e.path.localeCompare(t.path)):[]}catch{return[]}}async function i(){try{let t=await fetch(`/signalk/v2/api/history/_providers`,{credentials:`include`});if(!t.ok)return[];let n=await t.json();return e(n)?Object.keys(n):[]}catch{return[]}}async function a(e){try{let t=await fetch(`/signalk/v1/api/vessels/self/${e.replace(/\./g,`/`)}/value`,{credentials:`include`});return t.ok?await t.json():void 0}catch{return}}var o=(e,t,n,r=0,i=0)=>({id:e,symbol:t,factor:n,offset:r,add:i}),s={rad:[o(`degree`,`°`,180/Math.PI),o(`radian`,`rad`,1)],"m/s":[o(`kn`,`kn`,1.94384),o(`m/s`,`m/s`,1),o(`km/h`,`km/h`,3.6),o(`mph`,`mph`,2.2369362920544025)],K:[o(`C`,`°C`,1,-273.15),o(`F`,`°F`,9/5,-273.15,32),o(`K`,`K`,1)],Pa:[o(`hPa`,`hPa`,.01),o(`mbar`,`mbar`,.01),o(`bar`,`bar`,1e-5),o(`psi`,`psi`,.0001450376807894691),o(`inHg`,`inHg`,.00029529987601298443),o(`mmHg`,`mmHg`,.0075006168507298),o(`Pa`,`Pa`,1)],m:[o(`m`,`m`,1),o(`meter`,`m`,1),o(`foot`,`ft`,3.280839895013124),o(`fathom`,`fathom`,.5467468562055768),o(`naut-mile`,`nmi`,.0005399568034557236),o(`kilometer`,`km`,.001),o(`mile`,`mi`,.000621371192237334)],"rad/s":[o(`deg/s`,`°/s`,57.29577951308232),o(`rpm`,`rpm`,9.549296585513723),o(`rad/s`,`rad/s`,1)],ratio:[o(`percent`,`%`,100),o(`ratio`,``,1)],Hz:[o(`rpm`,`rpm`,60),o(`hertz`,`Hz`,1)],V:[o(`V`,`V`,1)],A:[o(`A`,`A`,1)],W:[o(`watt`,`W`,1),o(`kW`,`kW`,.001)],m3:[o(`liter`,`l`,1e3),o(`gallon`,`gal`,264.1720512415585),o(`m3`,`m³`,1)],"m3/s":[o(`L/h`,`l/h`,36e5),o(`L/min`,`l/min`,6e4),o(`gal/h`,`gal/h`,264.17205236)]};function c(e,t){return t?(e+(t.offset??0))*t.factor+(t.add??0):e}function l(e){if(e)return/\.(0+)/.exec(e)?.[1]?.length??0}function u(e,t,n){let r=e?.units,i=r?s[r]??[]:[],a=e=>i.find(t=>t.id===e),o=a(t)??a(e?.displayUnits?.targetUnit)??i[0],c=r===`rad`,u=o&&o.id===e?.displayUnits?.targetUnit?l(e.displayUnits.displayFormat):void 0,d=o?.id===`degree`?0:u??1;return{conversion:o,symbol:o?.symbol??r??``,decimals:n>=0?n:d,angular:c}}export{t as a,i,c as n,r as o,u as r,a as s,s as t};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{t as e}from"./chunk-U7YORXZ7-BLTBSYrv.js";import{a as t,i as n,n as r,r as i}from"./config-model-DvTH1O2f.js";import{n as a,t as o}from"./styles-CjtPzFRR.js";import{a as s,n as c,r as l}from"./units-Cn4uhL0-.js";var u=`/signalk/v2/api/history/values`;function d(e,t,n,r){return`${u}?${new URLSearchParams({paths:(r?[`min`,`max`,`average`]:[`average`]).map(t=>`${e}:${t}`).join(`,`),duration:String(Math.ceil(t/1e3)),resolution:String(Math.max(1,Math.round(n/1e3)))}).toString()}`}function f(e,t){let n=n=>e.values.findIndex(e=>e.path===t&&e.method===n),r=n(`average`);if(r<0)return[];let i=n(`min`),a=n(`max`),o=[];for(let t of e.data){let e=Date.parse(t[0]),n=t[r+1];if(Number.isNaN(e)||typeof n!=`number`)continue;let s=i<0?n:t[i+1],c=a<0?n:t[a+1];o.push({time:e,avg:n,min:typeof s==`number`?s:n,max:typeof c==`number`?c:n})}return o}function p(e){if(typeof e!=`object`||!e)return!1;let t=e;return Array.isArray(t.values)&&Array.isArray(t.data)}async function m(e,t,n,r=fetch){for(let i of[!0,!1])try{let a=await r(d(e,t,n,i),{credentials:`include`});if(a.status===404||a.status===501)return[];if(!a.ok)continue;let o=await a.json();if(p(o))return f(o,e)}catch(e){return console.warn(`history query failed`,e),[]}return[]}var h={card:`rgba(20, 26, 32, 0.78)`,text:`#e8edf2`,muted:`#9fb3c4`,bar:`#29b6f6`,avg:`#ef5350`,grid:`rgba(255, 255, 255, 0.22)`},g={card:`rgba(8, 0, 0, 0.85)`,text:`#d32f2f`,muted:`#8e2424`,bar:`#7f1212`,avg:`#ff5252`,grid:`rgba(211, 47, 47, 0.3)`},_=[1,2,5,10,15,20,30,60,120,180,240,360,720,1440],v=4;function y(e){let t=e/6e4,n=_.find(e=>t/e<=v)??Math.ceil(t/v/1440)*1440,r=t>120,i=[];for(let e=n;e<=t+1e-9;e+=n)i.push({age:e*6e4,label:r?`${+(e/60).toFixed(1)}h`:`${e}`});return i}function b(e,t){let n=e.min,r=e.max;if(r-n<t){let e=(r+n)/2;n=e-t/2,r=e+t/2}let i=(r-n)*.05;return{lo:n-i,hi:r+i}}function x(e,t,n,r,i=``){let a=r;for(e.font=`${i} ${a}px 'Helvetica Neue', Arial, sans-serif`;a>7&&e.measureText(t).width>n;)a--,e.font=`${i} ${a}px 'Helvetica Neue', Arial, sans-serif`;return a}function S(e,t,n,r){let i=r.palette,a=Math.max(4,Math.round(t*.05));e.clearRect(0,0,t,n),e.fillStyle=i.card,e.beginPath(),e.roundRect(0,0,t,n,10),e.fill(),e.textBaseline=`alphabetic`;let o=t-2*a,s=Math.max(8,Math.round(Math.min(t/9,n/18)));e.fillStyle=i.muted,e.font=`600 ${s}px 'Helvetica Neue', Arial, sans-serif`,e.textAlign=`left`;let c=a+s;e.fillText(r.label,a,c,o*.7),e.textAlign=`right`,e.fillText(r.symbol,t-a,c,o*.3);let l=x(e,r.value,o,Math.round(Math.min(t/3.2,n/7)),`600`);e.fillStyle=i.text,e.textAlign=`center`,c+=l+2,e.fillText(r.value,t/2,c);let u=x(e,`${r.min} ${r.avg} ${r.max}`,o,s);e.font=`${u}px 'Helvetica Neue', Arial, sans-serif`,c+=u+4,e.textAlign=`left`,e.fillStyle=i.muted,e.fillText(r.min,a,c),e.textAlign=`right`,e.fillText(r.max,t-a,c),e.textAlign=`center`,e.fillStyle=i.avg,e.fillText(r.avg,t/2,c);let d=Math.max(7,s-2);e.font=`${d}px 'Helvetica Neue', Arial, sans-serif`;let f=y(r.windowMs),p=Math.ceil(Math.max(...f.map(t=>e.measureText(t.label).width),0))+3,m=c+4,h=a,g=t-a-p,_=n-a,v=_-m,S=g-h;if(v<10||S<10)return;e.strokeStyle=i.grid,e.lineWidth=1,e.strokeRect(h+.5,m+.5,S-1,v-1);let C=e=>m+e/r.windowMs*v;e.fillStyle=i.muted,e.textAlign=`left`,e.textBaseline=`middle`;for(let t of f){let n=Math.round(C(t.age))+.5;t.age<r.windowMs&&(e.beginPath(),e.moveTo(h,n),e.lineTo(g,n),e.stroke()),e.fillText(t.label,g+3,Math.min(n,_-d/2))}if(!r.stats){r.message&&(e.textAlign=`center`,e.fillStyle=i.muted,e.fillText(r.message,h+S/2,m+v/2,S-4));return}let w=b(r.stats,r.minSpan),T=e=>h+(e-w.lo)/(w.hi-w.lo)*S;e.fillStyle=i.bar;for(let t of r.stats.bands){let n=C(t.ageStart),r=Math.min(_,C(t.ageEnd));if(r<=m)continue;let i=T(t.lo),a=Math.max(i+2,T(t.hi)),o=Math.max(m,n);e.fillRect(i,o,a-i,Math.max(1,r-o)+.5)}let E=Math.round(T(r.stats.avg))+.5;e.strokeStyle=i.avg,e.beginPath(),e.moveTo(E,m),e.lineTo(E,_),e.stroke()}var C=2*Math.PI;function w(e){let t=((e+Math.PI)%C+C)%C;return t===0?Math.PI:t-Math.PI}var T=class{windowMs;bucketMs;angular;buckets=new Map;constructor(e,t,n=240){this.windowMs=e,this.bucketMs=e/n,this.angular=t}get size(){return this.buckets.size}add(e,t){this.merge(e,t,t,t)}merge(e,t,n,r){if(![e,t,n,r].every(Number.isFinite))return;let i=Math.floor(e/this.bucketMs),a=this.buckets.get(i);if(a||(a={index:i,ref:this.angular?r:0,lo:1/0,hi:-1/0,sum:0,sin:0,cos:0,n:0},this.buckets.set(i,a)),this.angular){let e=w(r-a.ref),i=w(t-r),o=w(n-r);a.lo=Math.min(a.lo,e+Math.min(0,i,o)),a.hi=Math.max(a.hi,e+Math.max(0,i,o)),a.sin+=Math.sin(r),a.cos+=Math.cos(r)}else a.lo=Math.min(a.lo,t,r),a.hi=Math.max(a.hi,n,r),a.sum+=r;a.n++}prune(e){let t=Math.floor((e-this.windowMs)/this.bucketMs);for(let e of this.buckets.keys())e<t&&this.buckets.delete(e)}clear(){this.buckets.clear()}stats(e){this.prune(e);let t=[...this.buckets.values()].filter(e=>e.n>0);if(t.length===0)return;let n=0;if(this.angular){let e=0,r=0;for(let n of t){let t=Math.atan2(n.sin,n.cos);e+=Math.sin(t),r+=Math.cos(t)}n=Math.atan2(e,r)}let r=1/0,i=-1/0,a=0,o=[];for(let s of t){let t=this.angular?w(s.ref-n):0,c=s.lo+t,l=s.hi+t;r=Math.min(r,c),i=Math.max(i,l),this.angular||(a+=s.sum/s.n);let u=s.index*this.bucketMs;o.push({ageStart:Math.max(0,e-(u+this.bucketMs)),ageEnd:Math.max(0,e-u),lo:c,hi:l})}return{centre:n,min:r,max:i,avg:this.angular?0:a/t.length,bands:o}}},E=6e4,D=2*Math.PI,O=2,k=document.createElement(`canvas`);document.getElementById(`root`)?.append(k);var A,j=!1,M=0,N,P,F=!1;function I(e,t){if(e===void 0||!Number.isFinite(e))return`--`;let n=e;t.display.angular&&(n=t.signed?w(e):(e%D+D)%D);let r=c(n,t.display.conversion).toFixed(t.display.decimals);return t.display.angular&&!t.signed&&Number(r)>=360?0 .toFixed(t.display.decimals):r}function L(e){let t=j?g:h,n=A;if(!n)return{label:``,value:`--`,symbol:``,min:``,avg:``,max:``,windowMs:1,stats:void 0,minSpan:1,palette:t};let r=n.series.stats(e),a=e=>e===void 0?void 0:(r?.centre??0)+e,o=n.last&&e-n.last.time<E?n.last.value:void 0,s=n.display.conversion?1/Math.abs(n.display.conversion.factor):1;return{label:i(n.config),value:I(o,n),symbol:n.display.symbol,min:r?I(a(r.min),n):``,avg:r?I(a(r.avg),n):``,max:r?I(a(r.max),n):``,windowMs:n.series.windowMs,stats:r,minSpan:O*s,message:n.loading?`Loading…`:`No data`,palette:t}}function R(){F=!1;let e=k.getBoundingClientRect(),t=window.devicePixelRatio||1,n=Math.max(1,Math.round(e.width*t)),r=Math.max(1,Math.round(e.height*t));(k.width!==n||k.height!==r)&&(k.width=n,k.height=r);let i=k.getContext(`2d`);i&&(i.setTransform(t,0,0,t,0,0),S(i,e.width,e.height,L(Date.now())))}function z(){F||(F=!0,requestAnimationFrame(R))}async function B(e,t){let n=await m(e.config.path,e.series.windowMs,e.series.bucketMs);if(t===M){if(n.length>0){e.series.clear();for(let t of n)e.series.merge(t.time,t.min,t.max,t.avg);e.last&&e.series.add(e.last.time,e.last.value)}e.loading=!1,z()}}async function V(e){let i=++M;if(N){let e=N;N=void 0,await e().catch(()=>void 0)}P&&clearInterval(P);let a=n(await e.state.get()),o=await s(a.path);if(i!==M)return;let c=l(o,a.unit||void 0,a.decimals),u={config:a,display:c,series:new T(t(a),c.angular),signed:r(a.path),loading:!0};A=u,z(),N=await e.signalk.subscribe([a.path],e=>{if(typeof e.value!=`number`||!Number.isFinite(e.value))return;let t=Date.now();u.last={time:t,value:e.value},u.series.add(t,e.value),z()}),P=setInterval(z,Math.max(1e3,u.series.bucketMs)),await B(u,i)}async function H(){let t=await e();a(t),new ResizeObserver(z).observe(k),await t.subscribe([`state.changed`],()=>{V(t).catch(e=>{console.warn(`history strip: settings reload failed`,e)})}),document.addEventListener(`visibilitychange`,()=>{document.visibilityState===`visible`&&A&&B(A,M)}),await o(t,e=>{j=e,z()}).catch(e=>{console.warn(`history strip: night mode unavailable`,e)}),await V(t)}H().catch(e=>{console.warn(`history strip: host connection failed`,e)});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>Battery setup</title>
|
|
7
|
+
<script type="module" crossorigin src="./assets/battery-config-CUDy2wrq.js"></script>
|
|
8
|
+
<link rel="modulepreload" crossorigin href="./assets/chunk-U7YORXZ7-BLTBSYrv.js">
|
|
9
|
+
<link rel="modulepreload" crossorigin href="./assets/units-Cn4uhL0-.js">
|
|
10
|
+
<link rel="modulepreload" crossorigin href="./assets/level-model-0XbpyS2Q.js">
|
|
11
|
+
<link rel="modulepreload" crossorigin href="./assets/level-config-B0mMmED6.js">
|
|
12
|
+
<link rel="stylesheet" crossorigin href="./assets/config-DBAHPNIf.css">
|
|
13
|
+
</head>
|
|
14
|
+
<body>
|
|
15
|
+
<div id="root"></div>
|
|
16
|
+
</body>
|
|
17
|
+
</html>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>Battery</title>
|
|
7
|
+
<script type="module" crossorigin src="./assets/battery-BhqU92AD.js"></script>
|
|
8
|
+
<link rel="modulepreload" crossorigin href="./assets/chunk-U7YORXZ7-BLTBSYrv.js">
|
|
9
|
+
<link rel="modulepreload" crossorigin href="./assets/styles-CjtPzFRR.js">
|
|
10
|
+
<link rel="modulepreload" crossorigin href="./assets/units-Cn4uhL0-.js">
|
|
11
|
+
<link rel="modulepreload" crossorigin href="./assets/steer-model-DA5-9SMr.js">
|
|
12
|
+
<link rel="modulepreload" crossorigin href="./assets/level-model-0XbpyS2Q.js">
|
|
13
|
+
<link rel="modulepreload" crossorigin href="./assets/level-CwEJ7gss.js">
|
|
14
|
+
<link rel="stylesheet" crossorigin href="./assets/styles-D6wkrAFq.css">
|
|
15
|
+
</head>
|
|
16
|
+
<body>
|
|
17
|
+
<div id="root"></div>
|
|
18
|
+
</body>
|
|
19
|
+
</html>
|
package/public/config.html
CHANGED
|
@@ -4,9 +4,11 @@
|
|
|
4
4
|
<meta charset="utf-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
6
|
<title>History strip setup</title>
|
|
7
|
-
<script type="module" crossorigin src="./assets/config-
|
|
8
|
-
<link rel="modulepreload" crossorigin href="./assets/
|
|
9
|
-
<link rel="
|
|
7
|
+
<script type="module" crossorigin src="./assets/config-n3H05jK1.js"></script>
|
|
8
|
+
<link rel="modulepreload" crossorigin href="./assets/chunk-U7YORXZ7-BLTBSYrv.js">
|
|
9
|
+
<link rel="modulepreload" crossorigin href="./assets/config-model-DvTH1O2f.js">
|
|
10
|
+
<link rel="modulepreload" crossorigin href="./assets/units-Cn4uhL0-.js">
|
|
11
|
+
<link rel="stylesheet" crossorigin href="./assets/config-DBAHPNIf.css">
|
|
10
12
|
</head>
|
|
11
13
|
<body>
|
|
12
14
|
<div id="root"></div>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>Wind steering setup</title>
|
|
7
|
+
<script type="module" crossorigin src="./assets/steer-config-CxpZ3N7O.js"></script>
|
|
8
|
+
<link rel="modulepreload" crossorigin href="./assets/chunk-U7YORXZ7-BLTBSYrv.js">
|
|
9
|
+
<link rel="modulepreload" crossorigin href="./assets/steer-model-DA5-9SMr.js">
|
|
10
|
+
<link rel="stylesheet" crossorigin href="./assets/config-DBAHPNIf.css">
|
|
11
|
+
</head>
|
|
12
|
+
<body>
|
|
13
|
+
<div id="root"></div>
|
|
14
|
+
</body>
|
|
15
|
+
</html>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>Wind steering</title>
|
|
7
|
+
<script type="module" crossorigin src="./assets/steer-BhT70PR7.js"></script>
|
|
8
|
+
<link rel="modulepreload" crossorigin href="./assets/chunk-U7YORXZ7-BLTBSYrv.js">
|
|
9
|
+
<link rel="modulepreload" crossorigin href="./assets/styles-CjtPzFRR.js">
|
|
10
|
+
<link rel="modulepreload" crossorigin href="./assets/units-Cn4uhL0-.js">
|
|
11
|
+
<link rel="modulepreload" crossorigin href="./assets/steer-model-DA5-9SMr.js">
|
|
12
|
+
<link rel="stylesheet" crossorigin href="./assets/styles-D6wkrAFq.css">
|
|
13
|
+
</head>
|
|
14
|
+
<body>
|
|
15
|
+
<div id="root"></div>
|
|
16
|
+
</body>
|
|
17
|
+
</html>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>Tank setup</title>
|
|
7
|
+
<script type="module" crossorigin src="./assets/tank-config-C6CTiHir.js"></script>
|
|
8
|
+
<link rel="modulepreload" crossorigin href="./assets/chunk-U7YORXZ7-BLTBSYrv.js">
|
|
9
|
+
<link rel="modulepreload" crossorigin href="./assets/units-Cn4uhL0-.js">
|
|
10
|
+
<link rel="modulepreload" crossorigin href="./assets/level-model-0XbpyS2Q.js">
|
|
11
|
+
<link rel="modulepreload" crossorigin href="./assets/level-config-B0mMmED6.js">
|
|
12
|
+
<link rel="stylesheet" crossorigin href="./assets/config-DBAHPNIf.css">
|
|
13
|
+
</head>
|
|
14
|
+
<body>
|
|
15
|
+
<div id="root"></div>
|
|
16
|
+
</body>
|
|
17
|
+
</html>
|
package/public/tank.html
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>Tank</title>
|
|
7
|
+
<script type="module" crossorigin src="./assets/tank-Gyfo7ABV.js"></script>
|
|
8
|
+
<link rel="modulepreload" crossorigin href="./assets/chunk-U7YORXZ7-BLTBSYrv.js">
|
|
9
|
+
<link rel="modulepreload" crossorigin href="./assets/styles-CjtPzFRR.js">
|
|
10
|
+
<link rel="modulepreload" crossorigin href="./assets/units-Cn4uhL0-.js">
|
|
11
|
+
<link rel="modulepreload" crossorigin href="./assets/steer-model-DA5-9SMr.js">
|
|
12
|
+
<link rel="modulepreload" crossorigin href="./assets/level-model-0XbpyS2Q.js">
|
|
13
|
+
<link rel="modulepreload" crossorigin href="./assets/level-CwEJ7gss.js">
|
|
14
|
+
<link rel="stylesheet" crossorigin href="./assets/styles-D6wkrAFq.css">
|
|
15
|
+
</head>
|
|
16
|
+
<body>
|
|
17
|
+
<div id="root"></div>
|
|
18
|
+
</body>
|
|
19
|
+
</html>
|
package/public/widget.html
CHANGED
|
@@ -4,9 +4,12 @@
|
|
|
4
4
|
<meta charset="utf-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
6
|
<title>History strip</title>
|
|
7
|
-
<script type="module" crossorigin src="./assets/widget-
|
|
8
|
-
<link rel="modulepreload" crossorigin href="./assets/
|
|
9
|
-
<link rel="
|
|
7
|
+
<script type="module" crossorigin src="./assets/widget-CH38Wyvl.js"></script>
|
|
8
|
+
<link rel="modulepreload" crossorigin href="./assets/chunk-U7YORXZ7-BLTBSYrv.js">
|
|
9
|
+
<link rel="modulepreload" crossorigin href="./assets/config-model-DvTH1O2f.js">
|
|
10
|
+
<link rel="modulepreload" crossorigin href="./assets/styles-CjtPzFRR.js">
|
|
11
|
+
<link rel="modulepreload" crossorigin href="./assets/units-Cn4uhL0-.js">
|
|
12
|
+
<link rel="stylesheet" crossorigin href="./assets/styles-D6wkrAFq.css">
|
|
10
13
|
</head>
|
|
11
14
|
<body>
|
|
12
15
|
<div id="root"></div>
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{a as e,c as t,d as n,f as r,l as i,n as a,r as o,u as s}from"./units-D0PJ17NW.js";var c=`/signalk/v2/api/history/values`;function l(e,t,n,r){return`${c}?${new URLSearchParams({paths:(r?[`min`,`max`,`average`]:[`average`]).map(t=>`${e}:${t}`).join(`,`),duration:String(Math.ceil(t/1e3)),resolution:String(Math.max(1,Math.round(n/1e3)))}).toString()}`}function u(e,t){let n=n=>e.values.findIndex(e=>e.path===t&&e.method===n),r=n(`average`);if(r<0)return[];let i=n(`min`),a=n(`max`),o=[];for(let t of e.data){let e=Date.parse(t[0]),n=t[r+1];if(Number.isNaN(e)||typeof n!=`number`)continue;let s=i<0?n:t[i+1],c=a<0?n:t[a+1];o.push({time:e,avg:n,min:typeof s==`number`?s:n,max:typeof c==`number`?c:n})}return o}function d(e){if(typeof e!=`object`||!e)return!1;let t=e;return Array.isArray(t.values)&&Array.isArray(t.data)}async function f(e,t,n,r=fetch){for(let i of[!0,!1])try{let a=await r(l(e,t,n,i),{credentials:`include`});if(a.status===404||a.status===501)return[];if(!a.ok)continue;let o=await a.json();if(d(o))return u(o,e)}catch(e){return console.warn(`history query failed`,e),[]}return[]}var p={card:`rgba(20, 26, 32, 0.78)`,text:`#e8edf2`,muted:`#9fb3c4`,bar:`#29b6f6`,avg:`#ef5350`,grid:`rgba(255, 255, 255, 0.22)`},m={card:`rgba(8, 0, 0, 0.85)`,text:`#d32f2f`,muted:`#8e2424`,bar:`#7f1212`,avg:`#ff5252`,grid:`rgba(211, 47, 47, 0.3)`},h=[1,2,5,10,15,20,30,60,120,180,240,360,720,1440],g=4;function _(e){let t=e/6e4,n=h.find(e=>t/e<=g)??Math.ceil(t/g/1440)*1440,r=t>120,i=[];for(let e=n;e<=t+1e-9;e+=n)i.push({age:e*6e4,label:r?`${+(e/60).toFixed(1)}h`:`${e}`});return i}function v(e,t){let n=e.min,r=e.max;if(r-n<t){let e=(r+n)/2;n=e-t/2,r=e+t/2}let i=(r-n)*.05;return{lo:n-i,hi:r+i}}function y(e,t,n,r,i=``){let a=r;for(e.font=`${i} ${a}px 'Helvetica Neue', Arial, sans-serif`;a>7&&e.measureText(t).width>n;)a--,e.font=`${i} ${a}px 'Helvetica Neue', Arial, sans-serif`;return a}function b(e,t,n,r){let i=r.palette,a=Math.max(4,Math.round(t*.05));e.clearRect(0,0,t,n),e.fillStyle=i.card,e.beginPath(),e.roundRect(0,0,t,n,10),e.fill(),e.textBaseline=`alphabetic`;let o=t-2*a,s=Math.max(8,Math.round(Math.min(t/9,n/18)));e.fillStyle=i.muted,e.font=`600 ${s}px 'Helvetica Neue', Arial, sans-serif`,e.textAlign=`left`;let c=a+s;e.fillText(r.label,a,c,o*.7),e.textAlign=`right`,e.fillText(r.symbol,t-a,c,o*.3);let l=y(e,r.value,o,Math.round(Math.min(t/3.2,n/7)),`600`);e.fillStyle=i.text,e.textAlign=`center`,c+=l+2,e.fillText(r.value,t/2,c);let u=y(e,`${r.min} ${r.avg} ${r.max}`,o,s);e.font=`${u}px 'Helvetica Neue', Arial, sans-serif`,c+=u+4,e.textAlign=`left`,e.fillStyle=i.muted,e.fillText(r.min,a,c),e.textAlign=`right`,e.fillText(r.max,t-a,c),e.textAlign=`center`,e.fillStyle=i.avg,e.fillText(r.avg,t/2,c);let d=Math.max(7,s-2);e.font=`${d}px 'Helvetica Neue', Arial, sans-serif`;let f=_(r.windowMs),p=Math.ceil(Math.max(...f.map(t=>e.measureText(t.label).width),0))+3,m=c+4,h=a,g=t-a-p,b=n-a,x=b-m,S=g-h;if(x<10||S<10)return;e.strokeStyle=i.grid,e.lineWidth=1,e.strokeRect(h+.5,m+.5,S-1,x-1);let C=e=>m+e/r.windowMs*x;e.fillStyle=i.muted,e.textAlign=`left`,e.textBaseline=`middle`;for(let t of f){let n=Math.round(C(t.age))+.5;t.age<r.windowMs&&(e.beginPath(),e.moveTo(h,n),e.lineTo(g,n),e.stroke()),e.fillText(t.label,g+3,Math.min(n,b-d/2))}if(!r.stats){r.message&&(e.textAlign=`center`,e.fillStyle=i.muted,e.fillText(r.message,h+S/2,m+x/2,S-4));return}let w=v(r.stats,r.minSpan),T=e=>h+(e-w.lo)/(w.hi-w.lo)*S;e.fillStyle=i.bar;for(let t of r.stats.bands){let n=C(t.ageStart),r=Math.min(b,C(t.ageEnd));if(r<=m)continue;let i=T(t.lo),a=Math.max(i+2,T(t.hi)),o=Math.max(m,n);e.fillRect(i,o,a-i,Math.max(1,r-o)+.5)}let E=Math.round(T(r.stats.avg))+.5;e.strokeStyle=i.avg,e.beginPath(),e.moveTo(E,m),e.lineTo(E,b),e.stroke()}var x=2*Math.PI;function S(e){let t=((e+Math.PI)%x+x)%x;return t===0?Math.PI:t-Math.PI}var C=class{windowMs;bucketMs;angular;buckets=new Map;constructor(e,t,n=240){this.windowMs=e,this.bucketMs=e/n,this.angular=t}get size(){return this.buckets.size}add(e,t){this.merge(e,t,t,t)}merge(e,t,n,r){if(![e,t,n,r].every(Number.isFinite))return;let i=Math.floor(e/this.bucketMs),a=this.buckets.get(i);if(a||(a={index:i,ref:this.angular?r:0,lo:1/0,hi:-1/0,sum:0,sin:0,cos:0,n:0},this.buckets.set(i,a)),this.angular){let e=S(r-a.ref),i=S(t-r),o=S(n-r);a.lo=Math.min(a.lo,e+Math.min(0,i,o)),a.hi=Math.max(a.hi,e+Math.max(0,i,o)),a.sin+=Math.sin(r),a.cos+=Math.cos(r)}else a.lo=Math.min(a.lo,t,r),a.hi=Math.max(a.hi,n,r),a.sum+=r;a.n++}prune(e){let t=Math.floor((e-this.windowMs)/this.bucketMs);for(let e of this.buckets.keys())e<t&&this.buckets.delete(e)}clear(){this.buckets.clear()}stats(e){this.prune(e);let t=[...this.buckets.values()].filter(e=>e.n>0);if(t.length===0)return;let n=0;if(this.angular){let e=0,r=0;for(let n of t){let t=Math.atan2(n.sin,n.cos);e+=Math.sin(t),r+=Math.cos(t)}n=Math.atan2(e,r)}let r=1/0,i=-1/0,a=0,o=[];for(let s of t){let t=this.angular?S(s.ref-n):0,c=s.lo+t,l=s.hi+t;r=Math.min(r,c),i=Math.max(i,l),this.angular||(a+=s.sum/s.n);let u=s.index*this.bucketMs;o.push({ageStart:Math.max(0,e-(u+this.bucketMs)),ageEnd:Math.max(0,e-u),lo:c,hi:l})}return{centre:n,min:r,max:i,avg:this.angular?0:a/t.length,bands:o}}},w=1500,T=6e4,E=2*Math.PI,D=2,O=document.createElement(`canvas`);document.getElementById(`root`)?.append(O);var k,A=!1,j=0,M,N,P=!1;function F(e,t){if(e===void 0||!Number.isFinite(e))return`--`;let n=e;t.display.angular&&(n=t.signed?S(e):(e%E+E)%E);let r=a(n,t.display.conversion).toFixed(t.display.decimals);return t.display.angular&&!t.signed&&Number(r)>=360?0 .toFixed(t.display.decimals):r}function I(e){let t=A?m:p,n=k;if(!n)return{label:``,value:`--`,symbol:``,min:``,avg:``,max:``,windowMs:1,stats:void 0,minSpan:1,palette:t};let r=n.series.stats(e),a=e=>e===void 0?void 0:(r?.centre??0)+e,o=n.last&&e-n.last.time<T?n.last.value:void 0,s=n.display.conversion?1/Math.abs(n.display.conversion.factor):1;return{label:i(n.config),value:F(o,n),symbol:n.display.symbol,min:r?F(a(r.min),n):``,avg:r?F(a(r.avg),n):``,max:r?F(a(r.max),n):``,windowMs:n.series.windowMs,stats:r,minSpan:D*s,message:n.loading?`Loading…`:`No data`,palette:t}}function L(){P=!1;let e=O.getBoundingClientRect(),t=window.devicePixelRatio||1,n=Math.max(1,Math.round(e.width*t)),r=Math.max(1,Math.round(e.height*t));(O.width!==n||O.height!==r)&&(O.width=n,O.height=r);let i=O.getContext(`2d`);i&&(i.setTransform(t,0,0,t,0,0),b(i,e.width,e.height,I(Date.now())))}function R(){P||(P=!0,requestAnimationFrame(L))}async function z(e,t){let n=await f(e.config.path,e.series.windowMs,e.series.bucketMs);if(t===j){if(n.length>0){e.series.clear();for(let t of n)e.series.merge(t.time,t.min,t.max,t.avg);e.last&&e.series.add(e.last.time,e.last.value)}e.loading=!1,R()}}async function B(r){let i=++j;if(M){let e=M;M=void 0,await e().catch(()=>void 0)}N&&clearInterval(N);let a=s(await r.state.get()),c=await e(a.path);if(i!==j)return;let l=o(c,a.unit||void 0,a.decimals),u={config:a,display:l,series:new C(n(a),l.angular),signed:t(a.path),loading:!0};k=u,R(),M=await r.signalk.subscribe([a.path],e=>{if(typeof e.value!=`number`||!Number.isFinite(e.value))return;let t=Date.now();u.last={time:t,value:e.value},u.series.add(t,e.value),R()}),N=setInterval(R,Math.max(1e3,u.series.bucketMs)),await z(u,i)}function V(e){let t,n=()=>{t&&clearTimeout(t),t=void 0};window.addEventListener(`pointerdown`,()=>{n(),t=setTimeout(()=>{e.call(`ui.openConfigPanel`).catch(()=>void 0)},w)});for(let e of[`pointerup`,`pointercancel`,`pointerleave`])window.addEventListener(e,n)}async function H(e){e.hasCapability(`nightMode`)&&(await e.subscribe([`nightMode.changed`],(e,t)=>{A=t?.enabled===!0,R()}),A=(await e.nightMode.get()).enabled,R())}async function U(){let e=await r();V(e),new ResizeObserver(R).observe(O),await e.subscribe([`state.changed`],()=>{B(e).catch(e=>{console.warn(`history strip: settings reload failed`,e)})}),document.addEventListener(`visibilitychange`,()=>{document.visibilityState===`visible`&&k&&z(k,j)}),await H(e).catch(e=>{console.warn(`history strip: night mode unavailable`,e)}),await B(e)}U().catch(e=>{console.warn(`history strip: host connection failed`,e)});
|
|
File without changes
|