signalk-history-widgets 0.3.0 → 0.5.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 +46 -6
- package/docs/screenshots/clock.png +0 -0
- package/docs/screenshots/config.png +0 -0
- package/docs/screenshots/depth.png +0 -0
- package/docs/screenshots/levels-small.png +0 -0
- package/package.json +2 -2
- package/plugin/index.js +44 -7
- package/plugin/index.js.map +1 -1
- package/public/assets/{battery-BhqU92AD.js → battery-BDxrVXxL.js} +1 -1
- package/public/assets/{battery-config-CUDy2wrq.js → battery-config-DjFsEE6z.js} +1 -1
- package/public/assets/clock-BM6oWw1K.js +1 -0
- package/public/assets/clock-config-CEXnt2BC.js +13 -0
- package/public/assets/clock-model-D8TrsRP6.js +1 -0
- package/public/assets/config-CGQo6w5J.js +24 -0
- package/public/assets/{config-model-DvTH1O2f.js → config-model-BkGUzGeC.js} +1 -1
- package/public/assets/depth-Dgg5mbjJ.js +1 -0
- package/public/assets/depth-config-CXV_1Ma9.js +9 -0
- package/public/assets/depth-model-DGmfV7Y9.js +1 -0
- package/public/assets/level-ClsHlQuP.js +1 -0
- package/public/assets/{level-config-B0mMmED6.js → level-config-BUp6HxOr.js} +7 -7
- package/public/assets/level-model-CINplXoI.js +1 -0
- package/public/assets/series-BYD_m2Iw.js +1 -0
- package/public/assets/signalk-BOmj4E6B.js +1 -0
- package/public/assets/steer-DVy1Yeda.js +1 -0
- package/public/assets/{tank-Gyfo7ABV.js → tank-BH_5b7m0.js} +1 -1
- package/public/assets/{tank-config-C6CTiHir.js → tank-config-CQ5sfnUH.js} +1 -1
- package/public/assets/units-nn6bpgTR.js +1 -0
- package/public/assets/widget-CmMhtUz9.js +1 -0
- package/public/battery-config.html +5 -4
- package/public/battery.html +5 -4
- package/public/clock-config.html +16 -0
- package/public/clock.html +17 -0
- package/public/config.html +4 -3
- package/public/depth-config.html +17 -0
- package/public/depth.html +19 -0
- package/public/steer.html +3 -2
- package/public/tank-config.html +5 -4
- package/public/tank.html +5 -4
- package/public/widget.html +5 -3
- package/public/assets/config-n3H05jK1.js +0 -30
- package/public/assets/level-CwEJ7gss.js +0 -1
- package/public/assets/level-model-0XbpyS2Q.js +0 -1
- package/public/assets/steer-BhT70PR7.js +0 -1
- package/public/assets/units-Cn4uhL0-.js +0 -1
- package/public/assets/widget-CH38Wyvl.js +0 -1
package/README.md
CHANGED
|
@@ -10,8 +10,9 @@ moves.
|
|
|
10
10
|
|
|
11
11
|

|
|
12
12
|
|
|
13
|
-
The package also brings a **Wind steering** dial
|
|
14
|
-
**Battery charge** widgets, described
|
|
13
|
+
The package also brings a **Wind steering** dial, **Tank level** and
|
|
14
|
+
**Battery charge** widgets, a **Clock** and a **Depth profile**, described
|
|
15
|
+
[below](#wind-steering) and [further down](#depth-profile).
|
|
15
16
|
|
|
16
17
|
## Use
|
|
17
18
|
|
|
@@ -22,7 +23,7 @@ The package also brings a **Wind steering** dial and **Tank level** and
|
|
|
22
23
|
(wide)**.
|
|
23
24
|
3. Pick the value: a quick button (TWD, TWS, TWA, AWA, AWS, SOG, STW, COG,
|
|
24
25
|
HDG, depth, air pressure, air and water temperature) or any numeric Signal K
|
|
25
|
-
path.
|
|
26
|
+
path. Tap the time span, from 5 minutes to 24 hours.
|
|
26
27
|
|
|
27
28
|
For several values, place several strips next to each other. Press and hold a
|
|
28
29
|
placed strip to change it or remove it.
|
|
@@ -79,8 +80,9 @@ hidden rather than shown stale. It reads standard Signal K paths:
|
|
|
79
80
|
|
|
80
81
|
A tank's level or a battery bank's state of charge as a glass cylinder or, if
|
|
81
82
|
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
|
|
83
|
-
the units set in the server's Unit Preferences.
|
|
83
|
+
its capacity also shows the volume left, and a battery bank its voltage and
|
|
84
|
+
current, in the units set in the server's Unit Preferences. A green arrow up
|
|
85
|
+
means the bank is charging, a red arrow down that it is discharging. The colour follows the zones
|
|
84
86
|
set for the level in the server's metadata (`meta.zones`): green when the
|
|
85
87
|
level is normal, yellow in an alert or warn zone, red in an alarm or
|
|
86
88
|
emergency zone. Marks beside the bar show where the zones begin, so you see
|
|
@@ -88,14 +90,52 @@ how far the level is from the next one. Without zones the bar stays green.
|
|
|
88
90
|
|
|
89
91
|

|
|
90
92
|
|
|
93
|
+
Both come in a small size too (1x1), with the reading beside the bar:
|
|
94
|
+
|
|
95
|
+

|
|
96
|
+
|
|
91
97
|
Press and hold the widget to pick another tank or bank. The panel lists every
|
|
92
98
|
`tanks.*.*.currentLevel` (or every
|
|
93
99
|
`electrical.batteries.*.capacity.stateOfCharge`) on the server with its
|
|
94
100
|
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
|
|
101
|
+
flat) and whether to show the volume or voltage and current as buttons, so it is set up
|
|
96
102
|
with a few taps and no keyboard. A level older than two minutes shows as
|
|
97
103
|
`--`.
|
|
98
104
|
|
|
105
|
+
## Clock
|
|
106
|
+
|
|
107
|
+
The time, as large as the widget allows, with the date underneath. It shows
|
|
108
|
+
this screen's time zone, UTC, or the boat's time zone as the server reports
|
|
109
|
+
it (`environment.time.timezoneRegion`, or `environment.time.timezoneOffset`).
|
|
110
|
+
While the server reports no usable boat zone, a clock set to boat time shows
|
|
111
|
+
this screen's own zone instead and says so above the time.
|
|
112
|
+
A screen whose clock is more than two seconds off the GPS time
|
|
113
|
+
(`navigation.datetime`) follows the GPS, which keeps a display that booted
|
|
114
|
+
without a network right.
|
|
115
|
+
|
|
116
|
+

|
|
117
|
+
|
|
118
|
+
Press and hold the clock to choose the zone, 24 or 12 hours, and whether to
|
|
119
|
+
show seconds and the date, all with buttons.
|
|
120
|
+
|
|
121
|
+
## Depth profile
|
|
122
|
+
|
|
123
|
+
The depth over the last minutes or hours like an echo sounder: time runs
|
|
124
|
+
left to right with now at the right edge, depth runs down from the selected
|
|
125
|
+
reference, and the seabed is filled in below each moment's shallowest reading
|
|
126
|
+
(for history, the provider's minimum; a provider that gives only averages
|
|
127
|
+
shows the average, so the seabed can look deeper than the shallowest depth
|
|
128
|
+
recorded). The current depth sits top left in the unit set in the server's Unit
|
|
129
|
+
Preferences. Like the strip, it is seeded from the History API: with a
|
|
130
|
+
history provider on the server it is full as soon as it opens, without one
|
|
131
|
+
it fills from live data as the window passes.
|
|
132
|
+
|
|
133
|
+

|
|
134
|
+
|
|
135
|
+
It follows the depth below the surface when the server has it, else below
|
|
136
|
+
the transducer or the keel. Press and hold it to pick one of those yourself
|
|
137
|
+
or another window, from 5 minutes to 24 hours, all with buttons.
|
|
138
|
+
|
|
99
139
|
## Requirements
|
|
100
140
|
|
|
101
141
|
- Signal K server 2.22.1 or newer (History API v2) on Node 24 or newer
|
|
Binary file
|
|
Binary file
|
|
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, wind steering
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "History strip, wind steering, tank level, clock and depth profile 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, tank levels and battery charge coloured by their zones, a clock in local, UTC or boat time, and the depth over time like an echo sounder",
|
|
5
5
|
"author": "Dirk Wahrheit",
|
|
6
6
|
"main": "plugin/index.js",
|
|
7
7
|
"type": "module",
|
package/plugin/index.js
CHANGED
|
@@ -39,6 +39,15 @@ var PLUGIN_ID = "signalk-history-widgets";
|
|
|
39
39
|
* admins only: every user of the chartplotter loads these.
|
|
40
40
|
*/
|
|
41
41
|
var ASSET_BASE = `/plotterext/${PLUGIN_ID}`;
|
|
42
|
+
var depth = (id, title, size) => ({
|
|
43
|
+
id,
|
|
44
|
+
title,
|
|
45
|
+
type: "iframe",
|
|
46
|
+
url: `${ASSET_BASE}/depth.html`,
|
|
47
|
+
size,
|
|
48
|
+
configPanel: "depth-config",
|
|
49
|
+
lifecycle: "whileEnabled"
|
|
50
|
+
});
|
|
42
51
|
var strip = (id, title, size) => ({
|
|
43
52
|
id,
|
|
44
53
|
title,
|
|
@@ -48,13 +57,14 @@ var strip = (id, title, size) => ({
|
|
|
48
57
|
configPanel: "strip-config",
|
|
49
58
|
lifecycle: "whileEnabled"
|
|
50
59
|
});
|
|
51
|
-
|
|
60
|
+
/** A level widget; the small one shares its page and panel. */
|
|
61
|
+
var level = (id, title, size = "1x2", page = id) => ({
|
|
52
62
|
id,
|
|
53
63
|
title,
|
|
54
64
|
type: "iframe",
|
|
55
|
-
url: `${ASSET_BASE}/${
|
|
56
|
-
size
|
|
57
|
-
configPanel: `${
|
|
65
|
+
url: `${ASSET_BASE}/${page}.html`,
|
|
66
|
+
size,
|
|
67
|
+
configPanel: `${page}-config`,
|
|
58
68
|
lifecycle: "whileEnabled"
|
|
59
69
|
});
|
|
60
70
|
var levelPanel = (id, title) => ({
|
|
@@ -68,7 +78,7 @@ var levelPanel = (id, title) => ({
|
|
|
68
78
|
function buildManifest(version) {
|
|
69
79
|
return {
|
|
70
80
|
name: "History Widgets",
|
|
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,
|
|
81
|
+
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, tank levels and battery charge coloured by their zones, a clock in local, UTC or boat time, and the depth over time like an echo sounder.",
|
|
72
82
|
version,
|
|
73
83
|
apiVersion: "1",
|
|
74
84
|
requires: [
|
|
@@ -90,7 +100,20 @@ function buildManifest(version) {
|
|
|
90
100
|
lifecycle: "whileEnabled"
|
|
91
101
|
},
|
|
92
102
|
level("tank", "Tank level"),
|
|
93
|
-
level("
|
|
103
|
+
level("tank-small", "Tank level (small)", "1x1", "tank"),
|
|
104
|
+
level("battery", "Battery charge"),
|
|
105
|
+
level("battery-small", "Battery charge (small)", "1x1", "battery"),
|
|
106
|
+
{
|
|
107
|
+
id: "clock",
|
|
108
|
+
title: "Clock",
|
|
109
|
+
type: "iframe",
|
|
110
|
+
url: `${ASSET_BASE}/clock.html`,
|
|
111
|
+
size: "2x1",
|
|
112
|
+
configPanel: "clock-config",
|
|
113
|
+
lifecycle: "whileEnabled"
|
|
114
|
+
},
|
|
115
|
+
depth("depth", "Depth profile", "2x1"),
|
|
116
|
+
depth("depth-large", "Depth profile (large)", "2x2")
|
|
94
117
|
],
|
|
95
118
|
panels: [
|
|
96
119
|
{
|
|
@@ -108,7 +131,21 @@ function buildManifest(version) {
|
|
|
108
131
|
lifecycle: "onOpen"
|
|
109
132
|
},
|
|
110
133
|
levelPanel("tank", "Tank level"),
|
|
111
|
-
levelPanel("battery", "Battery charge")
|
|
134
|
+
levelPanel("battery", "Battery charge"),
|
|
135
|
+
{
|
|
136
|
+
id: "clock-config",
|
|
137
|
+
title: "Clock",
|
|
138
|
+
type: "iframe",
|
|
139
|
+
url: `${ASSET_BASE}/clock-config.html`,
|
|
140
|
+
lifecycle: "onOpen"
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
id: "depth-config",
|
|
144
|
+
title: "Depth profile",
|
|
145
|
+
type: "iframe",
|
|
146
|
+
url: `${ASSET_BASE}/depth-config.html`,
|
|
147
|
+
lifecycle: "onOpen"
|
|
148
|
+
}
|
|
112
149
|
]
|
|
113
150
|
};
|
|
114
151
|
}
|
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\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":"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"}
|
|
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 depth = (id: string, title: string, size: string) => ({\n id,\n title,\n type: 'iframe',\n url: `${ASSET_BASE}/depth.html`,\n size,\n configPanel: 'depth-config',\n lifecycle: 'whileEnabled'\n})\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/** A level widget; the small one shares its page and panel. */\nconst level = (id: string, title: string, size = '1x2', page = id) => ({\n id,\n title,\n type: 'iframe',\n url: `${ASSET_BASE}/${page}.html`,\n size,\n configPanel: `${page}-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, tank levels and battery charge coloured by their zones, a clock in local, UTC or boat time, and the depth over time like an echo sounder.',\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('tank-small', 'Tank level (small)', '1x1', 'tank'),\n level('battery', 'Battery charge'),\n level('battery-small', 'Battery charge (small)', '1x1', 'battery'),\n {\n id: 'clock',\n title: 'Clock',\n type: 'iframe',\n url: `${ASSET_BASE}/clock.html`,\n size: '2x1',\n configPanel: 'clock-config',\n lifecycle: 'whileEnabled'\n },\n depth('depth', 'Depth profile', '2x1'),\n depth('depth-large', 'Depth profile (large)', '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 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 id: 'clock-config',\n title: 'Clock',\n type: 'iframe',\n url: `${ASSET_BASE}/clock-config.html`,\n lifecycle: 'onOpen'\n },\n {\n id: 'depth-config',\n title: 'Depth profile',\n type: 'iframe',\n url: `${ASSET_BASE}/depth-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:\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":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import"./chunk-U7YORXZ7-BLTBSYrv.js";import{t as e}from"./level-
|
|
1
|
+
import"./chunk-U7YORXZ7-BLTBSYrv.js";import{t as e}from"./level-ClsHlQuP.js";e(`battery`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import"./chunk-U7YORXZ7-BLTBSYrv.js";import{t as e}from"./level-config-
|
|
1
|
+
import"./chunk-U7YORXZ7-BLTBSYrv.js";import{t as e}from"./level-config-BUp6HxOr.js";e(`battery`);
|
|
@@ -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,i,n as a,o,r as s,s as c,t as l}from"./clock-model-D8TrsRP6.js";import{i as u}from"./signalk-BOmj4E6B.js";var d={card:`rgba(20, 26, 32, 0.78)`,text:`#e8edf2`,muted:`#9fb3c4`},f={card:`rgba(8, 0, 0, 0.85)`,text:`#d32f2f`,muted:`#8e2424`},p=`'Helvetica Neue', Arial, sans-serif`;function m(e,t,n){let r=Math.floor(n);for(;r>7&&e(r)>t;)r--;return r}function h(e,t,n){if(n(e))return e;let r=t&&e.endsWith(t)?t:``,i=e.slice(0,e.length-r.length);for(let e=i.length-1;e>0;e--){let t=`${i.slice(0,e).trimEnd()}…${r?` ${r}`:``}`;if(n(t))return t}for(let e=(r||i).length-1;e>0;e--){let t=`${(r||i).slice(0,e).trimEnd()}…`;if(n(t))return t}return`…`}function g(e,t,n,r,i=``){let a=m(n=>(e.font=`${i} ${n}px ${p}`,e.measureText(t).width),n,r);return e.font=`${i} ${a}px ${p}`,a}function _(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)*.06),o=t-2*a,s=Math.max(9,Math.min(n*.14,t*.08));e.textAlign=`center`,e.textBaseline=`alphabetic`;let c=g(e,r.title,o,s),l=h(r.title,r.keep??``,t=>e.measureText(t).width<=o);e.fillStyle=i.muted,e.fillText(l,t/2,a+c);let u=r.date?s:0,d=a+c,f=n-a-(r.date?u*1.3:0),p=g(e,r.time,o,(f-d)*.9,`600`);e.fillStyle=i.text,e.fillText(r.time,t/2,(d+f)/2+p*.36),r.date&&(g(e,r.date,o,u),e.fillStyle=i.muted,e.fillText(r.date,t/2,n-a))}var v=document.createElement(`canvas`);document.getElementById(`root`)?.append(v);var y=o({}),b=!1,x=new s,S=new l,C=!1;function w(){C=!1;let e=v.getBoundingClientRect(),t=window.devicePixelRatio||1,n=Math.max(1,Math.round(e.width*t)),a=Math.max(1,Math.round(e.height*t));(v.width!==n||v.height!==a)&&(v.width=n,v.height=a);let o=v.getContext(`2d`);if(!o)return;o.setTransform(t,0,0,t,0,0);let s=i(Date.now()+x.correction,y,S.zone());_(o,e.width,e.height,{title:r(y,s),keep:s.fallback?s.zoneName:void 0,time:s.time,date:y.showDate?s.date:``,palette:b?f:d})}function T(){C||(C=!0,requestAnimationFrame(w))}function E(){T();let e=Date.now()+x.correction;setTimeout(E,1e3-e%1e3+20)}async function D(e){t(e),new ResizeObserver(T).observe(v);let r=c(()=>e.state.get().then(e=>{y=o(e),T()}).catch(e=>{console.warn(`clock: settings unavailable`,e)}));await e.subscribe([`state.changed`],r),await n(e,e=>{b=e,T()}).catch(e=>{console.warn(`clock: night mode unavailable`,e)}),E(),r();for(let e of[a.region,a.offset])u(e).then(t=>{S.stored(e,t),T()});await e.signalk.subscribe(Object.values(a),e=>{e.path===a.gpsTime?x.read(Date.now(),e.value):S.live(e.path,e.value),T()})}e().then(D).catch(e=>{console.warn(`clock: host connection failed`,e)});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import{t as e}from"./chunk-U7YORXZ7-BLTBSYrv.js";import{a as t,i as n,n as r,o as i,t as a}from"./clock-model-D8TrsRP6.js";import{i as o}from"./signalk-BOmj4E6B.js";/* empty css */var s=[{key:`zone`,title:`Time zone`,options:[[`local`,`This screen`],[`utc`,`UTC`],[`boat`,`Boat`]]},{key:`hours`,title:`Hours`,options:[[`24`,`24 h`],[`12`,`12 h`]]},{key:`seconds`,title:`Seconds`,options:[[!0,`Show`],[!1,`Hide`]]},{key:`showDate`,title:`Date`,options:[[!0,`Show`],[!1,`Hide`]]}];function c(e){let t=document.getElementById(e);if(!t)throw Error(`missing #${e}`);return t}function l(){let e=c(`label`);return e instanceof HTMLInputElement?e.value.trim():``}function u(e){return e.replace(/[&<>"]/g,e=>`&#${e.charCodeAt(0)};`)}async function d(){let d=c(`root`),f=await e();d.innerHTML=`<p class="status">Loading…</p>`;let p=i(await f.state.get()),m=new a,h=()=>{let e={...p,label:l()},r=n(Date.now(),e,m.zone()),i=p.showDate?`, ${u(r.date)}`:``;return`${u(t(e,r))}: ${u(r.time)}${i}`},g=()=>{c(`preview`).innerHTML=h()},_=()=>{d.innerHTML=`
|
|
2
|
+
${s.map(e=>`
|
|
3
|
+
<div class="row"><span>${e.title}</span><div class="chips">${e.options.map(([t,n],r)=>`<button type="button" class="chip${p[e.key]===t?` on`:``}" data-key="${e.key}" data-index="${r}">${n}</button>`).join(``)}</div></div>`).join(``)}
|
|
4
|
+
<label class="row"><span>Label</span>
|
|
5
|
+
<input id="label" type="text" placeholder="Automatic" value="${u(p.label)}">
|
|
6
|
+
</label>
|
|
7
|
+
<p class="status" id="preview"></p>
|
|
8
|
+
<p class="status">Boat is the zone set on the server, the same on every screen.</p>
|
|
9
|
+
<p class="status" id="status"></p>
|
|
10
|
+
<div class="actions">
|
|
11
|
+
<button type="button" id="cancel">Cancel</button>
|
|
12
|
+
<button type="button" id="save" class="primary">Save</button>
|
|
13
|
+
</div>`,g(),c(`label`).addEventListener(`input`,g);for(let e of d.querySelectorAll(`.chip`))e.addEventListener(`click`,()=>{let t=s.find(t=>t.key===e.dataset.key),n=t?.options[Number(e.dataset.index)];t&&n&&(Object.assign(p,{[t.key]:n[0]}),p.label=l(),_())});c(`save`).addEventListener(`click`,()=>{p.label=l(),f.state.set({...p}).then(()=>f.call(`ui.closePanel`)).catch(e=>{c(`status`).textContent=`Save failed: ${e instanceof Error?e.message:String(e)}`})}),c(`cancel`).addEventListener(`click`,()=>{f.call(`ui.closePanel`).catch(()=>void 0)})};_();let v=setInterval(g,1e3);window.addEventListener(`pagehide`,()=>{clearInterval(v)});for(let e of[r.region,r.offset])o(e).then(t=>{m.stored(e,t),g()})}d().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={zone:`local`,hours:`24`,seconds:!1,showDate:!0,label:``};function t(t){let n=e,r=(e,t)=>typeof e==`boolean`?e:t;return{zone:t.zone===`utc`||t.zone===`boat`?t.zone:n.zone,hours:t.hours===`12`?`12`:n.hours,seconds:r(t.seconds,n.seconds),showDate:r(t.showDate,n.showDate),label:typeof t.label==`string`?t.label.trim():n.label}}var n={gpsTime:`navigation.datetime`,region:`environment.time.timezoneRegion`,offset:`environment.time.timezoneOffset`};function r(e){try{return new Intl.DateTimeFormat(`en`,{timeZone:e}),!0}catch{return!1}}function i(e,t){if(typeof e==`string`&&e&&r(e))return{region:e};if(typeof t==`number`&&Number.isFinite(t)){let e=Math.abs(Math.trunc(t)),n=Math.floor(e/100)*60+e%100;if(e%100<60&&n<=840)return{minutes:t<0?-n:n}}}var a=class{values=new Map;live(e,t){this.values.set(e,t)}stored(e,t){this.values.has(e)||this.values.set(e,t)}zone(){return i(this.values.get(n.region),this.values.get(n.offset))}},o=class{correction=0;last;read(e,t){let n=typeof t==`string`?Date.parse(t):NaN;if(!Number.isFinite(n))return this.correction;let r=this.last;if(this.last={arrival:e,gps:n},!r)return this.correction;let i=n-r.gps;if(i>0&&i<=6e4&&Math.abs(i-(e-r.arrival))<=1e3){let t=n-e;this.correction=Math.abs(t)>2e3?t:0}return this.correction}};function s(e){if(e===0)return`UTC`;let t=Math.abs(e),n=Math.floor(t/60),r=t%60;return`UTC${e<0?`-`:`+`}${n}${r?`:${String(r).padStart(2,`0`)}`:``}`}function c(e,t,n,r){let i,a=e,o,c=t.zone===`boat`&&n===void 0;return t.zone===`utc`?(i=`UTC`,o=`UTC`):t.zone===`boat`&&n&&`region`in n?(i=n.region,o=`Boat time, ${n.region.split(`/`).pop()?.replace(/_/g,` `)??n.region}`):t.zone===`boat`&&n&&`minutes`in n?(i=`UTC`,a=e+n.minutes*6e4,o=`Boat time, ${s(n.minutes)}`):o=c?`Local time (no boat zone)`:`Local time`,{time:new Intl.DateTimeFormat(r,{timeZone:i,hour:`2-digit`,minute:`2-digit`,second:t.seconds?`2-digit`:void 0,hourCycle:t.hours===`12`?`h12`:`h23`}).format(a),date:new Intl.DateTimeFormat(r,{timeZone:i,weekday:`short`,day:`numeric`,month:`short`,year:`numeric`}).format(a),zoneName:o,fallback:c}}function l(e,t){return e.label?t.fallback?`${e.label} · ${t.zoneName}`:e.label:t.zoneName}function u(e){let t=!1,n=!1,r=()=>{t?n=!0:(t=!0,e().finally(()=>{t=!1,n&&(n=!1,r())}))};return r}export{l as a,c as i,n,t as o,o as r,u as s,a as t};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import{t as e}from"./chunk-U7YORXZ7-BLTBSYrv.js";import{a as t,i as n,o as r,t as i}from"./config-model-BkGUzGeC.js";import{r as a,t as o}from"./signalk-BOmj4E6B.js";import{t as s}from"./units-nn6bpgTR.js";/* empty css */function c(e){return e.replace(/[&<>"]/g,e=>`&#${e.charCodeAt(0)};`)}function l(e){let t=document.getElementById(e);if(!t)throw Error(`missing #${e}`);return t}function u(e){let t=l(e);if(t instanceof HTMLInputElement||t instanceof HTMLSelectElement)return t;throw Error(`#${e} is not a form field`)}function d(e,n,a){let o=i.map(t=>`<button type="button" class="chip${t.path===e.path?` on`:``}" data-path="${t.path}" title="${c(t.title)}">${t.label}</button>`).join(``),s=a.length>0?`History from ${c(a.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
|
+
<div class="chips">${o}</div>
|
|
3
|
+
<label class="row"><span>Signal K path</span>
|
|
4
|
+
<input id="path" list="paths" value="${c(e.path)}" placeholder="Type to search…">
|
|
5
|
+
<datalist id="paths">${n.map(e=>`<option value="${c(e.path)}">`).join(``)}</datalist>
|
|
6
|
+
</label>
|
|
7
|
+
<label class="row"><span>Label</span>
|
|
8
|
+
<input id="label" value="${c(e.label)}" placeholder="Automatic">
|
|
9
|
+
</label>
|
|
10
|
+
<div class="row"><span id="span-title">Time span</span>
|
|
11
|
+
<div class="chips" role="group" aria-labelledby="span-title">${t(e).map(t=>`<button type="button" class="chip${r({...e,...t})===r(e)?` on`:``}" aria-pressed="${r({...e,...t})===r(e)}" data-span="${t.span}" data-span-unit="${t.spanUnit}">${t.label}</button>`).join(``)}</div>
|
|
12
|
+
</div>
|
|
13
|
+
<label class="row"><span>Units</span><select id="unit"></select></label>
|
|
14
|
+
<label class="row"><span>Decimals</span>
|
|
15
|
+
<select id="decimals">
|
|
16
|
+
${[-1,0,1,2,3].map(t=>`<option value="${t}"${t===e.decimals?` selected`:``}>${t<0?`Automatic`:t}</option>`).join(``)}
|
|
17
|
+
</select>
|
|
18
|
+
</label>
|
|
19
|
+
<p class="status">${s}</p>
|
|
20
|
+
<p class="status" id="status"></p>
|
|
21
|
+
<div class="actions">
|
|
22
|
+
<button type="button" id="cancel">Cancel</button>
|
|
23
|
+
<button type="button" id="save" class="primary">Save</button>
|
|
24
|
+
</div>`}function f(e,t){let n=u(`path`).value.trim(),r=e.find(e=>e.path===n)?.units,i=r?s[r]??[]:[],a=i.some(e=>e.id===t)?t:``;l(`unit`).innerHTML=[`<option value=""${a===``?` 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===a?` selected`:``}>${c(e.symbol||e.id)}</option>`)].join(``)}function p(){let e=document.querySelector(`[data-span].on`);return{path:u(`path`).value.trim(),label:u(`label`).value.trim(),span:Number(e?.dataset.span),spanUnit:e?.dataset.spanUnit,unit:u(`unit`).value,decimals:Number(u(`decimals`).value)}}async function m(){let t=l(`root`),r=await e();t.innerHTML=`<p class="status">Loading…</p>`;let[i,s,c]=await Promise.all([r.state.get(),a(),o()]),m=n(i);t.innerHTML=d(m,s,c),f(s,m.unit);let h=u(`path`),g=()=>{f(s,u(`unit`).value);for(let e of document.querySelectorAll(`[data-path]`))e.classList.toggle(`on`,e.dataset.path===h.value.trim())};h.addEventListener(`change`,g),h.addEventListener(`input`,g);for(let e of document.querySelectorAll(`[data-path]`))e.addEventListener(`click`,()=>{h.value=e.dataset.path??``,g()});let _=document.querySelectorAll(`[data-span]`);for(let e of _)e.addEventListener(`click`,()=>{for(let t of _)t.classList.toggle(`on`,t===e),t.setAttribute(`aria-pressed`,String(t===e))});let v=l(`status`);l(`save`).addEventListener(`click`,()=>{r.state.set(p()).then(()=>r.call(`ui.closePanel`)).catch(e=>{v.textContent=`Save failed: ${e instanceof Error?e.message:String(e)}`})}),l(`cancel`).addEventListener(`click`,()=>{r.call(`ui.closePanel`).catch(()=>void 0)})}m().catch(e=>{let t=document.getElementById(`root`);t&&(t.textContent=`Host connection failed: ${e instanceof Error?e.message:String(e)}`)});
|
|
@@ -1 +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
|
+
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)}var c=(e,t)=>({span:e,spanUnit:t,label:`${e} ${t}`}),l=[c(5,`min`),c(15,`min`),c(30,`min`),c(1,`h`),c(3,`h`),c(6,`h`),c(12,`h`),c(24,`h`)];function u(e){let t=a(e);if(l.some(n=>a({...e,...n})===t))return[...l];let n=c(e.span,e.spanUnit);return[...l,n].sort((t,n)=>a({...e,...t})-a({...e,...n}))}export{u as a,o as i,s as n,a as o,i as r,r as t};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{t as e}from"./chunk-U7YORXZ7-BLTBSYrv.js";import{a as t,i as n,o as r,r as i,t as a}from"./depth-model-DGmfV7Y9.js";import{o,s,t as c}from"./series-BYD_m2Iw.js";import{n as l,t as u}from"./styles-CjtPzFRR.js";import{a as d,n as f}from"./signalk-BOmj4E6B.js";import{n as p,r as m}from"./units-nn6bpgTR.js";var h={card:`rgba(20, 26, 32, 0.78)`,waterTop:`rgba(41, 182, 246, 0.30)`,waterBottom:`rgba(13, 71, 161, 0.55)`,seabed:`#8d6e63`,seabedEdge:`#d7ccc8`,text:`#e8edf2`,muted:`#9fb3c4`,grid:`rgba(255, 255, 255, 0.22)`},g={card:`rgba(8, 0, 0, 0.85)`,waterTop:`rgba(40, 0, 0, 0.4)`,waterBottom:`rgba(60, 0, 0, 0.6)`,seabed:`#4a0c0c`,seabedEdge:`#b71c1c`,text:`#d32f2f`,muted:`#8e2424`,grid:`rgba(211, 47, 47, 0.3)`},_=`'Helvetica Neue', Arial, sans-serif`;function v(e,t){let n=Number.isInteger(t)?0:t*10===Math.round(t*10)?1:2;return e.toFixed(n)}function y(e,t,n,r){let a=r.palette;e.clearRect(0,0,t,n),e.fillStyle=a.card,e.beginPath(),e.roundRect(0,0,t,n,10),e.fill();let s=Math.max(4,Math.round(Math.min(t,n)*.05)),c=Math.max(8,Math.round(Math.min(t/18,n/9))),l=r.bands.reduce((e,t)=>Math.max(e,t.hi),0),u=i(l),d=[];for(let e=0;e<=u.max+u.step/1e3;e+=u.step)d.push(e);e.font=`${c}px ${_}`;let f=Math.ceil(Math.max(...d.map(t=>e.measureText(v(t,u.step)).width)))+4,p=s,m=t-s-f,h=s,g=n-s-c-2,y=m-p,b=g-h;if(y<10||b<10)return;let x=e=>m-e/r.windowMs*y,S=e=>h+Math.max(0,e)/u.max*b,C=e.createLinearGradient(0,h,0,g);C.addColorStop(0,a.waterTop),C.addColorStop(1,a.waterBottom),e.fillStyle=C,e.fillRect(p,h,y,b),e.fillStyle=a.seabed;for(let t of r.bands){let n=Math.max(p,x(t.ageEnd)),r=Math.min(m,x(t.ageStart));r<=p||e.fillRect(n,S(t.lo),r-n+.5,g-S(t.lo))}e.fillStyle=a.seabedEdge;for(let t of r.bands){let n=Math.max(p,x(t.ageEnd)),r=Math.min(m,x(t.ageStart));r<=p||e.fillRect(n,S(t.lo)-1,r-n+.5,2)}e.strokeStyle=a.grid,e.lineWidth=1,e.fillStyle=a.muted,e.textAlign=`left`,e.textBaseline=`middle`;let w=Math.ceil(c*1.3/(b/(d.length-1)));d.forEach((t,n)=>{let r=Math.round(S(t))+.5;t>0&&t<u.max&&(e.beginPath(),e.moveTo(p,r),e.lineTo(m,r),e.stroke()),n%w===0&&e.fillText(v(t,u.step),m+4,Math.min(Math.max(r,h+c/2),g))}),e.textAlign=`center`,e.textBaseline=`alphabetic`;for(let t of o(r.windowMs)){let i=Math.round(x(t.age))+.5;t.age<r.windowMs&&(e.beginPath(),e.moveTo(i,h),e.lineTo(i,g),e.stroke()),e.fillText(/h$/.test(t.label)?t.label:`${t.label} min`,Math.max(i,p+2*c),n-s)}e.strokeRect(p+.5,h+.5,y-1,b-1),e.save(),e.shadowColor=`rgba(0, 0, 0, 0.8)`,e.shadowBlur=4,e.textAlign=`left`,e.fillStyle=a.muted,e.font=`600 ${c}px ${_}`,e.fillText(r.title,p+s,h+s+c,y-2*s);let T=Math.round(Math.min(b*.38,y/4));e.font=`600 ${T}px ${_}`,e.fillStyle=a.text;let E=h+s+c+T;e.fillText(r.value,p+s,E);let D=e.measureText(r.value).width;e.font=`${c}px ${_}`,e.fillStyle=a.muted,e.fillText(r.symbol,p+s+D+4,E),e.restore(),r.bands.length===0&&(e.textAlign=`center`,e.fillText(r.message,p+y/2,h+b*.75,y-4))}var b=6e4,x=1e4,S=document.createElement(`canvas`);document.getElementById(`root`)?.append(S);var C,w=!1,T=0,E,D,O=!1;function k(e){let n=w?g:h,r=C;if(!r)return{title:`Depth`,value:`--`,symbol:``,windowMs:1,bands:[],message:``,palette:n};let i=r.display.conversion,a=(r.series.stats(e)?.bands??[]).map(e=>({...e,lo:p(e.lo,i),hi:p(e.hi,i)})),o=r.last&&e-r.last.time<b?r.last.value:void 0;return{title:t(r.path),value:o===void 0?`--`:p(o,i).toFixed(r.display.decimals),symbol:r.display.symbol,windowMs:r.series.windowMs,bands:a,message:r.loading?`Loading…`:`No depth yet`,palette:n}}function A(){O=!1;let e=S.getBoundingClientRect(),t=window.devicePixelRatio||1,n=Math.max(1,Math.round(e.width*t)),r=Math.max(1,Math.round(e.height*t));(S.width!==n||S.height!==r)&&(S.width=n,S.height=r);let i=S.getContext(`2d`);i&&(i.setTransform(t,0,0,t,0,0),y(i,e.width,e.height,k(Date.now())))}function j(){O||(O=!0,requestAnimationFrame(A))}async function M(e){let t=await s(e.path,e.series.windowMs,e.series.bucketMs);if(C===e){for(let n of t)e.series.merge(n.time,n.min,n.max,n.avg);e.loading=!1,j()}}async function N(){let e=await Promise.all(a.map(e=>d(e.path)));return new Set(a.filter((t,n)=>typeof e[n]==`number`).map(e=>e.path))}async function P(e){let t=++T,i=r(await e.state.get()),a=n(i,i.path?new Set:await N()),o=await f(a);if(t!==T)return;let s={path:a,display:m({...o,units:o?.units??`m`},void 0,-1),series:new c(i.minutes*6e4,!1),loading:!0},l=await e.signalk.subscribe([a],e=>{if(typeof e.value!=`number`||!Number.isFinite(e.value))return;let t=Date.now();s.last={time:t,value:e.value},s.series.add(t,e.value),j()});if(t!==T){await l().catch(()=>void 0);return}let u=E;clearInterval(D),E=l,C=s,j(),D=setInterval(j,Math.max(1e3,s.series.bucketMs));let d=M(s);u?.().catch(()=>void 0),await d}function F(e){P(e).catch(t=>{console.warn(`depth profile: loading settings failed, retrying`,t);let n=T;setTimeout(()=>{T===n&&F(e)},x)})}async function I(){let t=await e();l(t),new ResizeObserver(j).observe(S),await t.subscribe([`state.changed`],()=>{F(t)}),document.addEventListener(`visibilitychange`,()=>{document.visibilityState===`visible`&&C&&M(C)}),await u(t,e=>{w=e,j()}).catch(e=>{console.warn(`depth profile: night mode unavailable`,e)}),F(t)}I().catch(e=>{console.warn(`depth profile: host connection failed`,e)});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import{t as e}from"./chunk-U7YORXZ7-BLTBSYrv.js";import{i as t,n,o as r,s as i,t as a}from"./depth-model-DGmfV7Y9.js";import{i as o,n as s}from"./signalk-BOmj4E6B.js";import{n as c,r as l}from"./units-nn6bpgTR.js";/* empty css */function u(e){return e.replace(/[&<>"]/g,e=>`&#${e.charCodeAt(0)};`)}function d(e){let t=document.getElementById(e);if(!t)throw Error(`missing #${e}`);return t}async function f(){let e=new Map;return await Promise.all(a.map(async t=>{let[n,r]=await Promise.all([o(t.path),s(t.path)]);if(typeof n!=`number`||!Number.isFinite(n))return;let i=l({...r,units:r?.units??`m`},void 0,-1);e.set(t.path,`${c(n,i.conversion).toFixed(i.decimals)} ${i.symbol}`)})),e}async function p(){let o=d(`root`),s=await e();o.innerHTML=`<p class="status">Loading…</p>`;let[c,l]=await Promise.all([s.state.get(),f()]),p=r(c),m=t({...p,path:``},new Set(l.keys())),h=a.find(e=>e.path===m)?.label.toLowerCase()??``,g=()=>{let e=[{path:``,label:`Automatic (${h})`},...a.map(e=>({path:e.path,label:`${e.label} ${l.get(e.path)??`(none)`}`}))];o.innerHTML=`
|
|
2
|
+
<div class="row"><span>Depth</span><div class="chips">${e.map((e,t)=>`<button type="button" class="chip${e.path===p.path?` on`:``}" data-depth="${t}">${u(e.label)}</button>`).join(``)}</div></div>
|
|
3
|
+
<div class="row"><span>Window</span><div class="chips">${n.map(e=>`<button type="button" class="chip${e===p.minutes?` on`:``}" data-minutes="${e}">${i(e)}</button>`).join(``)}</div></div>
|
|
4
|
+
<p class="status">Automatic follows the depth below the surface when the server has it, else below the transducer or the keel.</p>
|
|
5
|
+
<p class="status" id="status"></p>
|
|
6
|
+
<div class="actions">
|
|
7
|
+
<button type="button" id="cancel">Cancel</button>
|
|
8
|
+
<button type="button" id="save" class="primary">Save</button>
|
|
9
|
+
</div>`;for(let t of o.querySelectorAll(`[data-depth]`))t.addEventListener(`click`,()=>{p.path=e[Number(t.dataset.depth)]?.path??``,g()});for(let e of o.querySelectorAll(`[data-minutes]`))e.addEventListener(`click`,()=>{p.minutes=Number(e.dataset.minutes),g()});d(`save`).addEventListener(`click`,()=>{s.state.set({...p}).then(()=>s.call(`ui.closePanel`)).catch(e=>{d(`status`).textContent=`Save failed: ${e instanceof Error?e.message:String(e)}`})}),d(`cancel`).addEventListener(`click`,()=>{s.call(`ui.closePanel`).catch(()=>void 0)})};g()}p().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=[{path:`environment.depth.belowSurface`,label:`Below surface`},{path:`environment.depth.belowTransducer`,label:`Below transducer`},{path:`environment.depth.belowKeel`,label:`Below keel`}],t=[5,15,30,60,180,360,720,1440],n={path:``,minutes:30};function r(r){return{path:e.some(e=>e.path===r.path)?String(r.path):``,minutes:t.find(e=>e===r.minutes)??n.minutes}}function i(t,n){return t.path?t.path:e.find(e=>n.has(e.path))?.path??e[1].path}function a(t){let n=e.find(e=>e.path===t);return n?`Depth ${n.label.toLowerCase()}`:`Depth`}function o(e){return e<60?`${e} min`:`${e/60} h`}var s=4;function c(e){let t=Math.max(e*1.15,1),n=t/s,r=10**Math.floor(Math.log10(n)),i=[1,2,2.5,5,10].map(e=>e*r).find(e=>e>=n)??10*r;return{max:Math.ceil(t/i)*i,step:i}}export{a,i,t as n,r as o,c as r,o as s,e 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{i as r,n as i}from"./signalk-BOmj4E6B.js";import{r as a}from"./steer-model-DA5-9SMr.js";import{a as o,i as s,n as c,o as l,r as u,s as d,t as f}from"./level-model-CINplXoI.js";import{n as p,r as m}from"./units-nn6bpgTR.js";var h={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`}},g={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 _(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 v(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 y(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 b(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 x(e,t){return e.bottom-Math.min(1,Math.max(0,t))*(e.bottom-e.top)}function S(e,t,n){let r=n.palette,{cx:i,rx:a,top:o,bottom:s}=t,c=a*.22;if(y(e,i,a,c,o,s),e.fillStyle=r.glass,e.fill(),n.fraction!==void 0){let o=r.liquid[n.level],l=x(t,n.fraction);y(e,i,a,c,l,s),e.fillStyle=b(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,y(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;y(e,i,t,n,o-s,o),e.fillStyle=b(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 C(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(_(e,i-a,o,2*a,s-o,c),e.fillStyle=r.glass,e.fill(),n.fraction!==void 0){let l=x(t,n.fraction);e.save(),_(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,_(e,i-a,o,2*a,s-o,c),e.stroke(),n.kind===`battery`){let t=a*.6,n=a*.18;_(e,i-t/2,o-n,t,n,Math.min(n/2,3)),e.fillStyle=r.glassEdge,e.fill()}}function w(e,t,n,r,i,a,o){let s=t.direction===`idle`?0:i*.7,c=s?i*.3:0;v(e,t.text,i,a-s-c,`bold`);let l=n-(s+c+e.measureText(t.text).width)/2;if(e.fillStyle=t.direction===`charging`?o.mark.ok:t.direction===`discharging`?o.mark.alarm:o.muted,s){let n=r-i*.8,a=t.direction===`charging`?n:r,o=t.direction===`charging`?r:n;e.beginPath(),e.moveTo(l+s/2,a),e.lineTo(l+s,o),e.lineTo(l,o),e.closePath(),e.fill()}e.textAlign=`left`,e.fillText(t.text,l+s+c,r),e.textAlign=`center`}function T(e,t,n,r){let i=r.palette;e.clearRect(0,0,t,n),_(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=t>=n*.8,s=Math.max(10,Math.min(t*.11,n*(o?.1:.06))),c=Math.max(14,o?Math.min(t*.17,n*.2):Math.min(t*.2,n*.11)),l=Math.max(9,c*.5);e.textAlign=`center`,e.textBaseline=`top`,e.fillStyle=i.muted,v(e,r.title,s,t-2*a),e.fillText(r.title,t/2,a);let u=l*1.2,d=(+!!r.detailText+ +!!r.flow)*u,f=o?0:c+d+a,p=a+s*1.5,m=n-f-a*.5,h=Math.min(o?t*.15:t*.27,(m-p)*.4),g=h*(r.style===`3d`?.22:0)+(r.kind===`battery`?h*.22:0),y={cx:o?a+h:t/2-h*.15,rx:h,top:p+g,bottom:m-(r.style===`3d`?h*.22:0)};if(y.bottom-y.top<4)return;r.style===`flat`?C(e,y,r):S(e,y,r);let b=y.cx+h+Math.max(3,h*.12),T=e=>x(y,e);e.lineCap=`round`,e.strokeStyle=i.muted,e.lineWidth=Math.max(1,h*.03);for(let t of[.25,.5,.75])e.beginPath(),e.moveTo(b,T(t)),e.lineTo(b+h*.18,T(t)),e.stroke();e.lineWidth=Math.max(2,h*.09);for(let t of r.marks)e.strokeStyle=i.mark[t.level],e.beginPath(),e.moveTo(b,T(t.at)),e.lineTo(b+h*.32,T(t.at)),e.stroke();let E=o?b+h*.32+a*.5:a,D=t-a-E,O=E+D/2,k=c+d,A=o?(p+m+k)/2:n-a;e.textBaseline=`alphabetic`,e.fillStyle=r.fraction===void 0?i.muted:i.text,v(e,r.valueText,c,D,`bold`);let j=A-d;e.fillText(r.valueText,O,j),r.detailText&&(j+=u,e.fillStyle=i.muted,v(e,r.detailText,l,D),e.fillText(r.detailText,O,j)),r.flow&&w(e,r.flow,O,j+u,l,D,i)}var E=12e4,D=5e3;function O(_){let v=f[_],y=document.createElement(`canvas`);document.getElementById(`root`)?.append(y);let b,x=!1,S=0,C,w=!1,O=(e,t,n=!0)=>e&&(!n||t-e.time<E)?e.value:void 0;function k(e){let t=x?g:h,n=b;if(!n?.config.path)return{title:v.noun,fraction:void 0,level:`ok`,valueText:`Not set up`,detailText:``,marks:[],kind:_,style:n?.config.style??`3d`,palette:t};let r=O(n.levelReading,e),i=O(n.detailReading,e,v.detailAges),a=n.config.showDetail&&r!==void 0&&i!==void 0?`${p(v.detail(r,i),n.detail.conversion).toFixed(n.detail.decimals)} ${n.detail.symbol}`.trim():``,o=n.config.showDetail?O(n.flowReading,e):void 0,s=o===void 0?void 0:{text:`${p(Math.abs(o),n.flow.conversion).toFixed(n.flow.decimals)} ${n.flow.symbol}`.trim(),direction:c(o)};return{title:n.title,fraction:r,level:r===void 0?`ok`:u(d(r,n.zones)),valueText:r===void 0?`--`:`${p(r,n.level.conversion).toFixed(n.level.decimals)} ${n.level.symbol}`.trim(),detailText:a,flow:s,marks:l(n.zones),kind:_,style:n.config.style,palette:t}}function A(){w=!1;let e=y.getBoundingClientRect(),t=window.devicePixelRatio||1,n=Math.max(1,Math.round(e.width*t)),r=Math.max(1,Math.round(e.height*t));(y.width!==n||y.height!==r)&&(y.width=n,y.height=r);let i=y.getContext(`2d`);i&&(i.setTransform(t,0,0,t,0,0),T(i,e.width,e.height,k(Date.now())))}function j(){w||(w=!0,requestAnimationFrame(A))}async function M(e){let t=++S,n=s(await e.state.get()),c=v.matches(n.path),l=c?v.detailPath(n.path):void 0,u=c?v.flowPath?.(n.path):void 0,[d,f,p,h]=n.path?await Promise.all([i(n.path),l?i(l):void 0,l&&!v.detailAges?r(l):void 0,u?i(u):void 0]):[void 0,void 0,void 0,void 0];if(t!==S)return;let g={config:n,title:n.label||d?.displayName||v.name(n.path),zones:o(d?.zones),level:m({...d,units:d?.units??`ratio`},`percent`,0),detail:m({...f,units:f?.units??v.detailUnits},void 0,v.detailDecimals),flow:m({...h,units:h?.units??`A`},void 0,1)};typeof p==`number`&&Number.isFinite(p)&&(g.detailReading={time:Date.now(),value:p});let _;if(n.path){let r=[n.path,l,u].filter(e=>e!==void 0),i=new a;if(_=await e.signalk.subscribe(r,e=>{if(typeof e.value!=`number`||!Number.isFinite(e.value))return;let t={time:i.local(Date.now(),e.timestamp),value:e.value};e.path===n.path?g.levelReading=t:e.path===u?g.flowReading=t:g.detailReading=t,j()}),t!==S){await _().catch(()=>void 0);return}}let y=C;C=_,b=g,j(),await y?.().catch(()=>void 0)}async function N(){let r=await e();t(r),new ResizeObserver(j).observe(y),setInterval(j,D),await r.subscribe([`state.changed`],()=>{M(r).catch(e=>{console.warn(`${_}: settings reload failed`,e)})}),await n(r,e=>{x=e,j()}).catch(e=>{console.warn(`${_}: night mode unavailable`,e)}),await M(r)}N().catch(e=>{console.warn(`${_}: host connection failed`,e)})}export{O as t};
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import{t as e}from"./chunk-U7YORXZ7-BLTBSYrv.js";import{
|
|
2
|
-
<span class="name">${o(
|
|
3
|
-
<span class="reading">${t
|
|
1
|
+
import{t as e}from"./chunk-U7YORXZ7-BLTBSYrv.js";import{r as t}from"./signalk-BOmj4E6B.js";import{i as n,t as r}from"./level-model-CINplXoI.js";import{n as i,r as a}from"./units-nn6bpgTR.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=a({units:`ratio`},`percent`,0);function u(e,t,n){let r=e.matches(t.path)?t.displayName||e.name(t.path):t.path;return`<button type="button" class="pick${t.path===n?` on`:``}" aria-pressed="${t.path===n}" data-path="${o(t.path)}" title="${o(t.path)}">
|
|
2
|
+
<span class="name">${o(r)}</span>
|
|
3
|
+
<span class="reading">${i(t.value,l.conversion).toFixed(l.decimals)} ${l.symbol}</span>
|
|
4
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(
|
|
6
|
-
${d(
|
|
7
|
-
${d(
|
|
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(i){let a=r[i],l=s(`root`),u=await e();l.innerHTML=`<p class="status">Loading…</p>`;let[h,g]=await Promise.all([u.state.get(),t()]),_=n(h),v=g.filter(e=>e.units===`ratio`||a.matches(e.path)),y=v.filter(e=>a.matches(e.path)),b=v.filter(e=>!a.matches(e.path)),{path:x,style:S,showDetail:C}=_;l.innerHTML=`
|
|
6
|
+
${d(a,a.plural,y,x)}
|
|
7
|
+
${d(a,`Other levels`,b,x)}
|
|
8
8
|
${v.length===0?`<p class="status">No levels on this server yet.</p>`:``}
|
|
9
9
|
<label class="row"><span>Label</span>
|
|
10
10
|
<input id="label" value="${o(_.label)}" placeholder="Automatic">
|
|
11
11
|
</label>
|
|
12
12
|
<div class="section-title">Look</div>
|
|
13
13
|
${p(`look`,[[`3d`,`3D`],[`flat`,`Flat`]],S)}
|
|
14
|
-
<div class="section-title">${
|
|
14
|
+
<div class="section-title">${a.detailTitle}</div>
|
|
15
15
|
${p(`detail`,[[`true`,`Show`],[`false`,`Hide`]],String(C))}
|
|
16
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
17
|
<p class="status" id="status"></p>
|
|
@@ -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,flowPath:e=>e.replace(/\.capacity\.stateOfCharge$/,`.current`),detailTitle:`Voltage and current (needs the bank's readings)`}};function o(e){return e>=.05?`charging`:e<=-.05?`discharging`:`idle`}var s={nominal:0,normal:0,alert:1,warn:2,alarm:3,emergency:4};function c(e){return typeof e==`string`&&e in s}function l(e){if(!Array.isArray(e))return[];let t=[];for(let n of e){if(typeof n!=`object`||!n)continue;let e=n;if(!c(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 u(e,t){let n=`normal`;for(let r of t)r.lower!==void 0&&e<r.lower||r.upper!==void 0&&e>r.upper||s[r.state]>s[n]&&(n=r.state);return n}function d(e){let t=s[e];return t>=3?`alarm`:t>=1?`warn`:`ok`}function f(e){let t={ok:0,warn:1,alarm:2},n=new Map;for(let r of e){let e=d(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{l as a,t as i,o as n,f as o,d as r,u as s,a as t};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function e(e,t,n,r){return`/signalk/v2/api/history/values?${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 t(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 n(e){if(typeof e!=`object`||!e)return!1;let t=e;return Array.isArray(t.values)&&Array.isArray(t.data)}async function r(r,i,a,o=fetch){for(let s of[!0,!1])try{let c=await o(e(r,i,a,s),{credentials:`include`});if(c.status===404||c.status===501)return[];if(!c.ok)continue;let l=await c.json();if(n(l))return t(l,r)}catch(e){return console.warn(`history query failed`,e),[]}return[]}var i={card:`rgba(20, 26, 32, 0.78)`,text:`#e8edf2`,muted:`#9fb3c4`,bar:`#29b6f6`,avg:`#ef5350`,grid:`rgba(255, 255, 255, 0.22)`},a={card:`rgba(8, 0, 0, 0.85)`,text:`#d32f2f`,muted:`#8e2424`,bar:`#7f1212`,avg:`#ff5252`,grid:`rgba(211, 47, 47, 0.3)`},o=[1,2,5,10,15,20,30,60,120,180,240,360,720,1440],s=4;function c(e){let t=e/6e4,n=o.find(e=>t/e<=s)??Math.ceil(t/s/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 l(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 u(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 d(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 d=a+s;e.fillText(r.label,a,d,o*.7),e.textAlign=`right`,e.fillText(r.symbol,t-a,d,o*.3);let f=u(e,r.value,o,Math.round(Math.min(t/3.2,n/7)),`600`);e.fillStyle=i.text,e.textAlign=`center`,d+=f+2,e.fillText(r.value,t/2,d);let p=u(e,`${r.min} ${r.avg} ${r.max}`,o,s);e.font=`${p}px 'Helvetica Neue', Arial, sans-serif`,d+=p+4,e.textAlign=`left`,e.fillStyle=i.muted,e.fillText(r.min,a,d),e.textAlign=`right`,e.fillText(r.max,t-a,d),e.textAlign=`center`,e.fillStyle=i.avg,e.fillText(r.avg,t/2,d);let m=Math.max(7,s-2);e.font=`${m}px 'Helvetica Neue', Arial, sans-serif`;let h=c(r.windowMs),g=Math.ceil(Math.max(...h.map(t=>e.measureText(t.label).width),0))+3,_=d+4,v=a,y=t-a-g,b=n-a,x=b-_,S=y-v;if(x<10||S<10)return;e.strokeStyle=i.grid,e.lineWidth=1,e.strokeRect(v+.5,_+.5,S-1,x-1);let C=e=>_+e/r.windowMs*x;e.fillStyle=i.muted,e.textAlign=`left`,e.textBaseline=`middle`;for(let t of h){let n=Math.round(C(t.age))+.5;t.age<r.windowMs&&(e.beginPath(),e.moveTo(v,n),e.lineTo(y,n),e.stroke()),e.fillText(t.label,y+3,Math.min(n,b-m/2))}if(!r.stats){r.message&&(e.textAlign=`center`,e.fillStyle=i.muted,e.fillText(r.message,v+S/2,_+x/2,S-4));return}let w=l(r.stats,r.minSpan),T=e=>v+(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<=_)continue;let i=T(t.lo),a=Math.max(i+2,T(t.hi)),o=Math.max(_,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,_),e.lineTo(E,b),e.stroke()}var f=2*Math.PI;function p(e){let t=((e+Math.PI)%f+f)%f;return t===0?Math.PI:t-Math.PI}var m=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=p(r-a.ref),i=p(t-r),o=p(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?p(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}}};export{d as a,a as i,p as n,c as o,i as r,r as s,m as t};
|
|
@@ -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){let t=await fetch(`/signalk/v1/api/vessels/self/${e.replace(/\./g,`/`)}/value`,{credentials:`include`});if(t.status>=500)throw Error(`${e}: HTTP ${t.status}`);if(t.ok)return await t.json()}async function o(e){try{return await a(e)}catch{return}}export{a,o as i,t as n,r,i 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{n as r}from"./signalk-BOmj4E6B.js";import{a as i,c as a,d as o,i as s,l as c,n as l,o as u,r as d,s as f,u as p}from"./steer-model-DA5-9SMr.js";import{n as m,r as h}from"./units-nn6bpgTR.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 o(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 N(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 te(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,c=Math.max(10,Math.min(t,n-o)/2-a),l={cx:t/2,cy:(n-o)/2,r:c,band:c*.16};E(e,l,r),ee(e,l,r),A(e,l,r),r.trueWind!==void 0&&r.closeHauled!==void 0&&(T(e,l,r.trueWind+r.closeHauled,i.port,!0),T(e,l,r.trueWind-r.closeHauled,i.starboard,!0)),r.waypoint!==void 0&&O(e,l,r.waypoint,i.waypoint,`dot`),r.course!==void 0&&O(e,l,r.course,i.course,`diamond`),k(e,l,r),r.trueWind!==void 0&&D(e,l,r.trueWind,i.trueWind,`T`,.22),r.apparent!==void 0&&D(e,l,r.apparent,i.apparent,`A`,.5),j(e,l,r),e.fillStyle=i.text,e.textAlign=`center`,e.textBaseline=`middle`,e.font=`700 ${Math.max(10,c*.2)}px ${b}`,e.fillText(r.heading===void 0?`---`:`${s(r.heading)}°`,l.cx,l.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),N(e,l,r,l.cy+c+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?`--`:m(t,n.conversion).toFixed(n.decimals),unit:n.symbol}}function Y(e,t){return{label:e,value:t===void 0?`--`:f(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,o=q(r,e),s=o(`heading`),l=o(`angleApparent`),u=c(o,a.trueWind),d=o(`speedOverGround`),f=o(`drift`),h=i(o(`set`),s),v=a.showShift&&s!==void 0?r.shift.range(e):void 0;return{dial:a.dial,heading:s,apparent:l,trueWind:u,shift:v&&s!==void 0?{from:v.from-s,to:v.to-s}:void 0,closeHauled:a.showTackLines?a.closeHauled*F:void 0,waypoint:a.showWaypoint?i(o(`waypoint`),s):void 0,course:a.showCourse&&d!==void 0&&d>=I?i(o(`course`),s):void 0,current:a.showCurrent&&h!==void 0&&f!==void 0&&f>=L?{angle:h,fraction:f/R,text:`${m(f,r.speed.conversion).toFixed(1)} ${r.speed.symbol}`}:void 0,rudder:a.showRudder?o(`rudder`):void 0,topLeft:J(`AWS`,o(`speedApparent`),r.speed),topRight:J(`TWS`,p(o,a.trueWind),r.speed),bottomLeft:Y(`AWA`,l),bottomRight:Y(`TWA`,u),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),te(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=c(n,e.config.trueWind);r!==void 0&&i!==void 0&&e.shift.add(t,o(r+i))}async function $(e){let t=++W,n=u(await e.state.get()),i=a(n.north),o=await r(i.speedApparent);if(t!==W)return;let s={config:n,values:new Map,shift:new l(n.shiftSeconds*1e3),clock:new d,speed:h({...o,units:o?.units??`m/s`},void 0,1)},c=new Map;for(let[e,t]of Object.entries(i))c.set(t,[...c.get(t)??[],e]);let f=await e.signalk.subscribe([...c.keys()],e=>{let t=c.get(e.path);if(!t||typeof e.value!=`number`||!Number.isFinite(e.value))return;let n=Date.now(),r=s.clock.local(n,e.timestamp);for(let n of t)s.values.set(n,{time:r,value:e.value});t.some(e=>B.has(e))&&ne(s,n),Q()});if(t!==W){await f().catch(()=>void 0);return}let p=G;G=f,H=s,Q(),await p?.().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)});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import"./chunk-U7YORXZ7-BLTBSYrv.js";import{t as e}from"./level-
|
|
1
|
+
import"./chunk-U7YORXZ7-BLTBSYrv.js";import{t as e}from"./level-ClsHlQuP.js";e(`tank`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import"./chunk-U7YORXZ7-BLTBSYrv.js";import{t as e}from"./level-config-
|
|
1
|
+
import"./chunk-U7YORXZ7-BLTBSYrv.js";import{t as e}from"./level-config-BUp6HxOr.js";e(`tank`);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e=(e,t,n,r=0,i=0)=>({id:e,symbol:t,factor:n,offset:r,add:i}),t={rad:[e(`degree`,`°`,180/Math.PI),e(`radian`,`rad`,1)],"m/s":[e(`kn`,`kn`,1.94384),e(`m/s`,`m/s`,1),e(`km/h`,`km/h`,3.6),e(`mph`,`mph`,2.2369362920544025)],K:[e(`C`,`°C`,1,-273.15),e(`F`,`°F`,9/5,-273.15,32),e(`K`,`K`,1)],Pa:[e(`hPa`,`hPa`,.01),e(`mbar`,`mbar`,.01),e(`bar`,`bar`,1e-5),e(`psi`,`psi`,.0001450376807894691),e(`inHg`,`inHg`,.00029529987601298443),e(`mmHg`,`mmHg`,.0075006168507298),e(`Pa`,`Pa`,1)],m:[e(`m`,`m`,1),e(`meter`,`m`,1),e(`foot`,`ft`,3.280839895013124),e(`fathom`,`fathom`,.5467468562055768),e(`naut-mile`,`nmi`,.0005399568034557236),e(`kilometer`,`km`,.001),e(`mile`,`mi`,.000621371192237334)],"rad/s":[e(`deg/s`,`°/s`,57.29577951308232),e(`rpm`,`rpm`,9.549296585513723),e(`rad/s`,`rad/s`,1)],ratio:[e(`percent`,`%`,100),e(`ratio`,``,1)],Hz:[e(`rpm`,`rpm`,60),e(`hertz`,`Hz`,1)],V:[e(`V`,`V`,1)],A:[e(`A`,`A`,1)],W:[e(`watt`,`W`,1),e(`kW`,`kW`,.001)],m3:[e(`liter`,`l`,1e3),e(`gallon`,`gal`,264.1720512415585),e(`m3`,`m³`,1)],"m3/s":[e(`L/h`,`l/h`,36e5),e(`L/min`,`l/min`,6e4),e(`gal/h`,`gal/h`,264.17205236)]};function n(e,t){return t?(e+(t.offset??0))*t.factor+(t.add??0):e}function r(e){if(e)return/\.(0+)/.exec(e)?.[1]?.length??0}function i(e,n,i){let a=e?.units,o=a?t[a]??[]:[],s=e=>o.find(t=>t.id===e),c=s(n)??s(e?.displayUnits?.targetUnit)??o[0],l=a===`rad`,u=c&&c.id===e?.displayUnits?.targetUnit?r(e.displayUnits.displayFormat):void 0,d=c?.id===`degree`?0:u??1;return{conversion:c,symbol:c?.symbol??a??``,decimals:i>=0?i:d,angular:l}}export{n,i as r,t};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{t as e}from"./chunk-U7YORXZ7-BLTBSYrv.js";import{i as t,n,o as r,r as i}from"./config-model-BkGUzGeC.js";import{a,i as o,n as s,r as c,s as l,t as u}from"./series-BYD_m2Iw.js";import{n as d,t as f}from"./styles-CjtPzFRR.js";import{n as p}from"./signalk-BOmj4E6B.js";import{n as m,r as h}from"./units-nn6bpgTR.js";var g=6e4,_=2*Math.PI,v=2,y=document.createElement(`canvas`);document.getElementById(`root`)?.append(y);var b,x=!1,S=0,C,w,T=!1;function E(e,t){if(e===void 0||!Number.isFinite(e))return`--`;let n=e;t.display.angular&&(n=t.signed?s(e):(e%_+_)%_);let r=m(n,t.display.conversion).toFixed(t.display.decimals);return t.display.angular&&!t.signed&&Number(r)>=360?0 .toFixed(t.display.decimals):r}function D(e){let t=x?o:c,n=b;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,s=n.last&&e-n.last.time<g?n.last.value:void 0,l=n.display.conversion?1/Math.abs(n.display.conversion.factor):1;return{label:i(n.config),value:E(s,n),symbol:n.display.symbol,min:r?E(a(r.min),n):``,avg:r?E(a(r.avg),n):``,max:r?E(a(r.max),n):``,windowMs:n.series.windowMs,stats:r,minSpan:v*l,message:n.loading?`Loading…`:`No data`,palette:t}}function O(){T=!1;let e=y.getBoundingClientRect(),t=window.devicePixelRatio||1,n=Math.max(1,Math.round(e.width*t)),r=Math.max(1,Math.round(e.height*t));(y.width!==n||y.height!==r)&&(y.width=n,y.height=r);let i=y.getContext(`2d`);i&&(i.setTransform(t,0,0,t,0,0),a(i,e.width,e.height,D(Date.now())))}function k(){T||(T=!0,requestAnimationFrame(O))}async function A(e,t){let n=await l(e.config.path,e.series.windowMs,e.series.bucketMs);if(t===S){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,k()}}async function j(e){let i=++S;if(C){let e=C;C=void 0,await e().catch(()=>void 0)}w&&clearInterval(w);let a=t(await e.state.get()),o=await p(a.path);if(i!==S)return;let s=h(o,a.unit||void 0,a.decimals),c=r(a),l={config:a,display:s,series:new u(c,s.angular),signed:n(a.path),loading:!0};b=l,k(),C=await e.signalk.subscribe([a.path],e=>{if(typeof e.value!=`number`||!Number.isFinite(e.value))return;let t=Date.now();l.last={time:t,value:e.value},l.series.add(t,e.value),k()}),w=setInterval(k,Math.max(1e3,l.series.bucketMs)),await A(l,i)}async function M(){let t=await e();d(t),new ResizeObserver(k).observe(y),await t.subscribe([`state.changed`],()=>{j(t).catch(e=>{console.warn(`history strip: settings reload failed`,e)})}),document.addEventListener(`visibilitychange`,()=>{document.visibilityState===`visible`&&b&&A(b,S)}),await f(t,e=>{x=e,k()}).catch(e=>{console.warn(`history strip: night mode unavailable`,e)}),await j(t)}M().catch(e=>{console.warn(`history strip: host connection failed`,e)});
|
|
@@ -4,11 +4,12 @@
|
|
|
4
4
|
<meta charset="utf-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
6
|
<title>Battery setup</title>
|
|
7
|
-
<script type="module" crossorigin src="./assets/battery-config-
|
|
7
|
+
<script type="module" crossorigin src="./assets/battery-config-DjFsEE6z.js"></script>
|
|
8
8
|
<link rel="modulepreload" crossorigin href="./assets/chunk-U7YORXZ7-BLTBSYrv.js">
|
|
9
|
-
<link rel="modulepreload" crossorigin href="./assets/
|
|
10
|
-
<link rel="modulepreload" crossorigin href="./assets/level-model-
|
|
11
|
-
<link rel="modulepreload" crossorigin href="./assets/
|
|
9
|
+
<link rel="modulepreload" crossorigin href="./assets/signalk-BOmj4E6B.js">
|
|
10
|
+
<link rel="modulepreload" crossorigin href="./assets/level-model-CINplXoI.js">
|
|
11
|
+
<link rel="modulepreload" crossorigin href="./assets/units-nn6bpgTR.js">
|
|
12
|
+
<link rel="modulepreload" crossorigin href="./assets/level-config-BUp6HxOr.js">
|
|
12
13
|
<link rel="stylesheet" crossorigin href="./assets/config-DBAHPNIf.css">
|
|
13
14
|
</head>
|
|
14
15
|
<body>
|
package/public/battery.html
CHANGED
|
@@ -4,13 +4,14 @@
|
|
|
4
4
|
<meta charset="utf-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
6
|
<title>Battery</title>
|
|
7
|
-
<script type="module" crossorigin src="./assets/battery-
|
|
7
|
+
<script type="module" crossorigin src="./assets/battery-BDxrVXxL.js"></script>
|
|
8
8
|
<link rel="modulepreload" crossorigin href="./assets/chunk-U7YORXZ7-BLTBSYrv.js">
|
|
9
9
|
<link rel="modulepreload" crossorigin href="./assets/styles-CjtPzFRR.js">
|
|
10
|
-
<link rel="modulepreload" crossorigin href="./assets/
|
|
10
|
+
<link rel="modulepreload" crossorigin href="./assets/signalk-BOmj4E6B.js">
|
|
11
11
|
<link rel="modulepreload" crossorigin href="./assets/steer-model-DA5-9SMr.js">
|
|
12
|
-
<link rel="modulepreload" crossorigin href="./assets/level-model-
|
|
13
|
-
<link rel="modulepreload" crossorigin href="./assets/
|
|
12
|
+
<link rel="modulepreload" crossorigin href="./assets/level-model-CINplXoI.js">
|
|
13
|
+
<link rel="modulepreload" crossorigin href="./assets/units-nn6bpgTR.js">
|
|
14
|
+
<link rel="modulepreload" crossorigin href="./assets/level-ClsHlQuP.js">
|
|
14
15
|
<link rel="stylesheet" crossorigin href="./assets/styles-D6wkrAFq.css">
|
|
15
16
|
</head>
|
|
16
17
|
<body>
|
|
@@ -0,0 +1,16 @@
|
|
|
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>Clock setup</title>
|
|
7
|
+
<script type="module" crossorigin src="./assets/clock-config-CEXnt2BC.js"></script>
|
|
8
|
+
<link rel="modulepreload" crossorigin href="./assets/chunk-U7YORXZ7-BLTBSYrv.js">
|
|
9
|
+
<link rel="modulepreload" crossorigin href="./assets/clock-model-D8TrsRP6.js">
|
|
10
|
+
<link rel="modulepreload" crossorigin href="./assets/signalk-BOmj4E6B.js">
|
|
11
|
+
<link rel="stylesheet" crossorigin href="./assets/config-DBAHPNIf.css">
|
|
12
|
+
</head>
|
|
13
|
+
<body>
|
|
14
|
+
<div id="root"></div>
|
|
15
|
+
</body>
|
|
16
|
+
</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>Clock</title>
|
|
7
|
+
<script type="module" crossorigin src="./assets/clock-BM6oWw1K.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/clock-model-D8TrsRP6.js">
|
|
11
|
+
<link rel="modulepreload" crossorigin href="./assets/signalk-BOmj4E6B.js">
|
|
12
|
+
<link rel="stylesheet" crossorigin href="./assets/styles-D6wkrAFq.css">
|
|
13
|
+
</head>
|
|
14
|
+
<body>
|
|
15
|
+
<div id="root"></div>
|
|
16
|
+
</body>
|
|
17
|
+
</html>
|
package/public/config.html
CHANGED
|
@@ -4,10 +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-
|
|
7
|
+
<script type="module" crossorigin src="./assets/config-CGQo6w5J.js"></script>
|
|
8
8
|
<link rel="modulepreload" crossorigin href="./assets/chunk-U7YORXZ7-BLTBSYrv.js">
|
|
9
|
-
<link rel="modulepreload" crossorigin href="./assets/config-model-
|
|
10
|
-
<link rel="modulepreload" crossorigin href="./assets/
|
|
9
|
+
<link rel="modulepreload" crossorigin href="./assets/config-model-BkGUzGeC.js">
|
|
10
|
+
<link rel="modulepreload" crossorigin href="./assets/signalk-BOmj4E6B.js">
|
|
11
|
+
<link rel="modulepreload" crossorigin href="./assets/units-nn6bpgTR.js">
|
|
11
12
|
<link rel="stylesheet" crossorigin href="./assets/config-DBAHPNIf.css">
|
|
12
13
|
</head>
|
|
13
14
|
<body>
|
|
@@ -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>Depth profile setup</title>
|
|
7
|
+
<script type="module" crossorigin src="./assets/depth-config-CXV_1Ma9.js"></script>
|
|
8
|
+
<link rel="modulepreload" crossorigin href="./assets/chunk-U7YORXZ7-BLTBSYrv.js">
|
|
9
|
+
<link rel="modulepreload" crossorigin href="./assets/depth-model-DGmfV7Y9.js">
|
|
10
|
+
<link rel="modulepreload" crossorigin href="./assets/signalk-BOmj4E6B.js">
|
|
11
|
+
<link rel="modulepreload" crossorigin href="./assets/units-nn6bpgTR.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>Depth profile</title>
|
|
7
|
+
<script type="module" crossorigin src="./assets/depth-Dgg5mbjJ.js"></script>
|
|
8
|
+
<link rel="modulepreload" crossorigin href="./assets/chunk-U7YORXZ7-BLTBSYrv.js">
|
|
9
|
+
<link rel="modulepreload" crossorigin href="./assets/depth-model-DGmfV7Y9.js">
|
|
10
|
+
<link rel="modulepreload" crossorigin href="./assets/series-BYD_m2Iw.js">
|
|
11
|
+
<link rel="modulepreload" crossorigin href="./assets/styles-CjtPzFRR.js">
|
|
12
|
+
<link rel="modulepreload" crossorigin href="./assets/signalk-BOmj4E6B.js">
|
|
13
|
+
<link rel="modulepreload" crossorigin href="./assets/units-nn6bpgTR.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/steer.html
CHANGED
|
@@ -4,11 +4,12 @@
|
|
|
4
4
|
<meta charset="utf-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
6
|
<title>Wind steering</title>
|
|
7
|
-
<script type="module" crossorigin src="./assets/steer-
|
|
7
|
+
<script type="module" crossorigin src="./assets/steer-DVy1Yeda.js"></script>
|
|
8
8
|
<link rel="modulepreload" crossorigin href="./assets/chunk-U7YORXZ7-BLTBSYrv.js">
|
|
9
9
|
<link rel="modulepreload" crossorigin href="./assets/styles-CjtPzFRR.js">
|
|
10
|
-
<link rel="modulepreload" crossorigin href="./assets/
|
|
10
|
+
<link rel="modulepreload" crossorigin href="./assets/signalk-BOmj4E6B.js">
|
|
11
11
|
<link rel="modulepreload" crossorigin href="./assets/steer-model-DA5-9SMr.js">
|
|
12
|
+
<link rel="modulepreload" crossorigin href="./assets/units-nn6bpgTR.js">
|
|
12
13
|
<link rel="stylesheet" crossorigin href="./assets/styles-D6wkrAFq.css">
|
|
13
14
|
</head>
|
|
14
15
|
<body>
|
package/public/tank-config.html
CHANGED
|
@@ -4,11 +4,12 @@
|
|
|
4
4
|
<meta charset="utf-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
6
|
<title>Tank setup</title>
|
|
7
|
-
<script type="module" crossorigin src="./assets/tank-config-
|
|
7
|
+
<script type="module" crossorigin src="./assets/tank-config-CQ5sfnUH.js"></script>
|
|
8
8
|
<link rel="modulepreload" crossorigin href="./assets/chunk-U7YORXZ7-BLTBSYrv.js">
|
|
9
|
-
<link rel="modulepreload" crossorigin href="./assets/
|
|
10
|
-
<link rel="modulepreload" crossorigin href="./assets/level-model-
|
|
11
|
-
<link rel="modulepreload" crossorigin href="./assets/
|
|
9
|
+
<link rel="modulepreload" crossorigin href="./assets/signalk-BOmj4E6B.js">
|
|
10
|
+
<link rel="modulepreload" crossorigin href="./assets/level-model-CINplXoI.js">
|
|
11
|
+
<link rel="modulepreload" crossorigin href="./assets/units-nn6bpgTR.js">
|
|
12
|
+
<link rel="modulepreload" crossorigin href="./assets/level-config-BUp6HxOr.js">
|
|
12
13
|
<link rel="stylesheet" crossorigin href="./assets/config-DBAHPNIf.css">
|
|
13
14
|
</head>
|
|
14
15
|
<body>
|
package/public/tank.html
CHANGED
|
@@ -4,13 +4,14 @@
|
|
|
4
4
|
<meta charset="utf-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
6
|
<title>Tank</title>
|
|
7
|
-
<script type="module" crossorigin src="./assets/tank-
|
|
7
|
+
<script type="module" crossorigin src="./assets/tank-BH_5b7m0.js"></script>
|
|
8
8
|
<link rel="modulepreload" crossorigin href="./assets/chunk-U7YORXZ7-BLTBSYrv.js">
|
|
9
9
|
<link rel="modulepreload" crossorigin href="./assets/styles-CjtPzFRR.js">
|
|
10
|
-
<link rel="modulepreload" crossorigin href="./assets/
|
|
10
|
+
<link rel="modulepreload" crossorigin href="./assets/signalk-BOmj4E6B.js">
|
|
11
11
|
<link rel="modulepreload" crossorigin href="./assets/steer-model-DA5-9SMr.js">
|
|
12
|
-
<link rel="modulepreload" crossorigin href="./assets/level-model-
|
|
13
|
-
<link rel="modulepreload" crossorigin href="./assets/
|
|
12
|
+
<link rel="modulepreload" crossorigin href="./assets/level-model-CINplXoI.js">
|
|
13
|
+
<link rel="modulepreload" crossorigin href="./assets/units-nn6bpgTR.js">
|
|
14
|
+
<link rel="modulepreload" crossorigin href="./assets/level-ClsHlQuP.js">
|
|
14
15
|
<link rel="stylesheet" crossorigin href="./assets/styles-D6wkrAFq.css">
|
|
15
16
|
</head>
|
|
16
17
|
<body>
|
package/public/widget.html
CHANGED
|
@@ -4,11 +4,13 @@
|
|
|
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-
|
|
7
|
+
<script type="module" crossorigin src="./assets/widget-CmMhtUz9.js"></script>
|
|
8
8
|
<link rel="modulepreload" crossorigin href="./assets/chunk-U7YORXZ7-BLTBSYrv.js">
|
|
9
|
-
<link rel="modulepreload" crossorigin href="./assets/config-model-
|
|
9
|
+
<link rel="modulepreload" crossorigin href="./assets/config-model-BkGUzGeC.js">
|
|
10
|
+
<link rel="modulepreload" crossorigin href="./assets/series-BYD_m2Iw.js">
|
|
10
11
|
<link rel="modulepreload" crossorigin href="./assets/styles-CjtPzFRR.js">
|
|
11
|
-
<link rel="modulepreload" crossorigin href="./assets/
|
|
12
|
+
<link rel="modulepreload" crossorigin href="./assets/signalk-BOmj4E6B.js">
|
|
13
|
+
<link rel="modulepreload" crossorigin href="./assets/units-nn6bpgTR.js">
|
|
12
14
|
<link rel="stylesheet" crossorigin href="./assets/styles-D6wkrAFq.css">
|
|
13
15
|
</head>
|
|
14
16
|
<body>
|
|
@@ -1,30 +0,0 @@
|
|
|
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
|
-
<div class="chips">${i}</div>
|
|
3
|
-
<label class="row"><span>Signal K path</span>
|
|
4
|
-
<input id="path" list="paths" value="${o(e.path)}" placeholder="Type to search…">
|
|
5
|
-
<datalist id="paths">${t.map(e=>`<option value="${o(e.path)}">`).join(``)}</datalist>
|
|
6
|
-
</label>
|
|
7
|
-
<label class="row"><span>Label</span>
|
|
8
|
-
<input id="label" value="${o(e.label)}" placeholder="Automatic">
|
|
9
|
-
</label>
|
|
10
|
-
<div class="row"><span>Time span</span>
|
|
11
|
-
<div class="pair">
|
|
12
|
-
<input id="span" type="number" min="1" step="1" value="${e.span}">
|
|
13
|
-
<select id="spanUnit">
|
|
14
|
-
<option value="min"${e.spanUnit===`min`?` selected`:``}>minutes</option>
|
|
15
|
-
<option value="h"${e.spanUnit===`h`?` selected`:``}>hours</option>
|
|
16
|
-
</select>
|
|
17
|
-
</div>
|
|
18
|
-
</div>
|
|
19
|
-
<label class="row"><span>Units</span><select id="unit"></select></label>
|
|
20
|
-
<label class="row"><span>Decimals</span>
|
|
21
|
-
<select id="decimals">
|
|
22
|
-
${[-1,0,1,2,3].map(t=>`<option value="${t}"${t===e.decimals?` selected`:``}>${t<0?`Automatic`:t}</option>`).join(``)}
|
|
23
|
-
</select>
|
|
24
|
-
</label>
|
|
25
|
-
<p class="status">${a}</p>
|
|
26
|
-
<p class="status" id="status"></p>
|
|
27
|
-
<div class="actions">
|
|
28
|
-
<button type="button" id="cancel">Cancel</button>
|
|
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,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)}`)});
|
|
@@ -1 +0,0 @@
|
|
|
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};
|
|
@@ -1 +0,0 @@
|
|
|
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};
|
|
@@ -1 +0,0 @@
|
|
|
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)});
|
|
@@ -1 +0,0 @@
|
|
|
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};
|
|
@@ -1 +0,0 @@
|
|
|
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)});
|