signalk-openwakeword 0.1.0 → 0.2.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 CHANGED
@@ -1,89 +1,143 @@
1
1
  # signalk-openwakeword
2
2
 
3
- > **Status: ALPHA** This SignalK Wyoming system is 100% vibecoded slop. I don't have the right hardware yet to test it, so I'm putting it out there for people to test in the meantime. It _should_ work. File issues for anything that doestn.
4
-
5
- Wake word detection for Signal K boats. This plugin runs
6
- [rhasspy/wyoming-openwakeword](https://github.com/rhasspy/wyoming-openwakeword)
7
- as a managed container and exposes it as a [Wyoming
8
- protocol](https://github.com/rhasspy/wyoming) service on your network, so
9
- voice satellites (cockpit Pis, the `signalk-wyoming` local satellite, or any
10
- Wyoming client such as Home Assistant) can stream microphone audio to it and
11
- get "wake word heard" events back. It is one of the
12
- [signalk-wyoming](https://github.com/hoeken/signalk-wyoming) voice family:
13
- `signalk-wyoming` (orchestrator + webapp), `signalk-whisper` (speech to
14
- text), `signalk-piper` (text to speech), and this plugin (wake words).
3
+ > **Status: ALPHA.** This SignalK Wyoming system is 100% vibecoded slop. I
4
+ > don't have the right hardware yet to test it, so I'm putting it out there
5
+ > for people to test in the meantime. It _should_ work. File issues for
6
+ > anything that doesn't.
7
+
8
+ ## What is this?
9
+
10
+ Wake word detection for [Signal K](https://signalk.org) the part of a
11
+ voice assistant that listens for "okay nabu" (or "hey jarvis", "alexa", …)
12
+ so the rest of the stack knows when to start paying attention. The plugin
13
+ runs [openWakeWord](https://github.com/rhasspy/wyoming-openwakeword) as a
14
+ background service and takes care of everything around it: starting it in a
15
+ container (via the
16
+ [signalk-container](https://www.npmjs.com/package/signalk-container)
17
+ plugin), checking that it stays healthy, and telling the rest of the voice
18
+ stack where to find it. You never have to touch docker or podman yourself.
19
+
20
+ It is the wake-word building block of the
21
+ [signalk-wyoming voice-assistant family](https://github.com/hoeken/signalk-wyoming)
22
+ — install it together with the `signalk-wyoming` orchestrator,
23
+ `signalk-whisper` (speech to text), and `signalk-piper` (text to speech) to
24
+ get voice commands on your boat. Because it speaks the standard
25
+ [Wyoming protocol](https://github.com/rhasspy/wyoming), it also works as a
26
+ standalone wake word server for other software such as Home Assistant.
15
27
 
16
28
  **Fully offline:** all wake word models ship inside the container image.
17
- First start pulls the ~90 MB image and nothing else — no model downloads, no
18
- surprises at sea.
29
+ First start pulls the ~90 MB image and nothing else — no model downloads,
30
+ no surprises at sea.
19
31
 
20
32
  ## Requirements
21
33
 
22
- - Signal K server ≥ 2.x on Node.js 24
23
- - The [signalk-container](https://www.npmjs.com/package/signalk-container)
24
- plugin with a working podman or docker runtime
34
+ - Signal K server ≥ 2.x on **Node 24+**
35
+ - The **signalk-container** plugin with a working podman or docker runtime
25
36
  - ~384 MB RAM headroom for the container (the default memory cap)
26
37
  - amd64 or arm64 (no armv7 — upstream stopped publishing it)
27
38
 
28
39
  ## Install
29
40
 
30
- Install **signalk-openwakeword** from the Signal K App Store (plus
31
- signalk-container if you don't have it), enable it in Plugin Config, and the
32
- plugin pulls and starts `rhasspy/wyoming-openwakeword` pinned to a tested
33
- release. The plugin reports `ready` only once the service answers a Wyoming
34
- `describe` request — proof the detector is actually up, not just the
35
- container.
41
+ Install **signalk-openwakeword** from the Signal K App Store (or
42
+ `npm install signalk-openwakeword` in your server directory), enable it in
43
+ Plugin Config, and enable the signalk-container plugin if you have not
44
+ already. The plugin pulls and starts a pinned, tested release of the
45
+ service and reports `ready` once the detector actually answers not just
46
+ once the container is up.
36
47
 
37
48
  ## Configuration
38
49
 
39
- | Setting | Default | Notes |
40
- | ---------------------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
41
- | `wakeWords` | `["okay_nabu"]` | Wake words your satellites use. Built-ins: `okay_nabu`, `hey_jarvis`, `hey_mycroft`, `alexa`, `hey_rhasspy`. **Not** passed to the container — Wyoming clients select models per connection (`Detect` event); this list is validated against the running service and shared with the orchestrator. ⚠️ upstream 2.0.0 renamed `ok_nabu` → `okay_nabu`; the old name silently matches nothing. |
42
- | `threshold` | `0.5` | Wake probability threshold (0–1). Lower = more sensitive, more false wakes. |
43
- | `triggerLevel` | `1` | Activations above the threshold before a detection fires. Raise it in noisy cabins. |
44
- | `port` | `10400` | Host TCP port the Wyoming service is published on. |
45
- | `bind` | `0.0.0.0` | Host interface for the published port. See [Security](#security). |
46
- | `imageTag` | `auto` | `auto` pins the tested upstream release (currently `2.1.0`); set an explicit Docker tag to override. |
47
- | `memoryLimit` | `384m` | Container memory cap (swap disabled). |
48
- | `restartPolicy` | `unless-stopped` | Container restart policy. |
49
- | `advanced.refractorySeconds` | _(unset)_ | `--refractory-seconds` minimum seconds between re-detections (upstream default 2.0). |
50
- | `advanced.customModels` | `false` | Mounts the shared signalk-container data directory at `/data` and passes `--custom-model-dir /data/custom`. See [Custom wake words](#custom-wake-words). |
51
- | `advanced.advertiseHost` | _(unset)_ | Overrides the host part of the advertised `tcp://` URI (containerized Signal K, multi-NIC hosts). |
50
+ The plugin ships a graphical configuration panel (Server → Plugin Config →
51
+ openWakeWord) with a live container status card, a one-click image update
52
+ check/apply, a version dropdown fed by Docker Hub, wake word checkboxes
53
+ driven by the models the running service actually advertises, and all the
54
+ settings below with inline warnings when a selected wake word is missing
55
+ or the service is open to the network. On servers without custom-panel
56
+ support you get a plain settings form with the same options.
57
+
58
+ | Setting | Default | Notes |
59
+ | ---------------------------- | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
60
+ | `wakeWords` | `["okay_nabu"]` | Wake words your satellites use. See [Wake words & sensitivity](#wake-words--sensitivity). |
61
+ | `threshold` | `0.5` | Wake probability threshold (0–1). Lower = more sensitive, more false wakes. |
62
+ | `triggerLevel` | `1` | Activations above the threshold before a detection fires. Raise it in noisy cabins. |
63
+ | `port` | `10400` | Host TCP port the Wyoming service is published on. |
64
+ | `bind` | `0.0.0.0` | Host interface for the published port. See [Security](#security). |
65
+ | `imageTag` | `auto` | `auto` runs the pinned, tested upstream release (**2.1.0**) and follows plugin updates. Set an explicit Docker tag to pin something else. |
66
+ | `memoryLimit` | `384m` | Container memory cap (swap disabled). |
67
+ | `restartPolicy` | `unless-stopped` | Container restart policy. |
68
+ | `advanced.refractorySeconds` | _(unset)_ | Minimum seconds between re-detections (upstream default 2.0). |
69
+ | `advanced.customModels` | `false` | Load your own model files. See [Custom wake words](#custom-wake-words). |
70
+ | `advanced.advertiseHost` | _(unset)_ | Overrides the host part of the advertised `tcp://` URI (containerized Signal K, multi-NIC hosts). |
71
+
72
+ ### Wake words & sensitivity
73
+
74
+ The built-in wake words are `okay_nabu` (default), `hey_jarvis`,
75
+ `hey_mycroft`, `alexa`, and `hey_rhasspy`. The `wakeWords` list is what
76
+ your satellites will listen for — it is validated against the running
77
+ service and shared with the `signalk-wyoming` orchestrator, and the config
78
+ panel warns you if a selected word is not available (note: upstream 2.0.0
79
+ renamed `ok_nabu` to `okay_nabu`; the old name silently matches nothing).
80
+
81
+ Tuning for your boat:
82
+
83
+ - **Missing wakes** (it doesn't hear you): lower `threshold` a little, e.g.
84
+ `0.4`.
85
+ - **False wakes** (it triggers on engine noise or conversation): raise
86
+ `threshold`, or raise `triggerLevel` to `2`–`3` so a single borderline
87
+ hit isn't enough.
88
+ - **Double triggers** from one utterance: set
89
+ `advanced.refractorySeconds` (upstream default is 2 seconds between
90
+ re-detections).
91
+
92
+ ## Custom wake words
93
+
94
+ Want your boat to answer to its own name? Enable
95
+ `advanced.customModels`, then drop openWakeWord `.tflite` model files into
96
+ the `custom/` folder inside the **signalk-container** plugin's data
97
+ directory (`<signalk>/plugin-config-data/signalk-container/custom/` —
98
+ create `custom/` if it doesn't exist; note this is signalk-container's
99
+ directory, shared by all plugins that use it, not this plugin's own) and
100
+ restart the plugin. If Signal K itself runs in a container with a
101
+ volume-backed data directory, put the files in the corresponding location
102
+ inside that volume. A `_v1.0`-style filename suffix is stripped for the
103
+ model id. A webapp upload UI is planned for v1.x; the manual drop works
104
+ today.
52
105
 
53
106
  ## Using it from other software
54
107
 
55
- The service speaks plain Wyoming over TCP at `tcp://<boat-server>:10400`.
56
- The `signalk-wyoming` orchestrator discovers it automatically via the
57
- `wyoming-service` PropertyValues convention:
108
+ Once ready, the service is a plain Wyoming wake word server at
109
+ `tcp://<boat-server>:10400`:
58
110
 
59
- ```js
60
- { plugin: 'signalk-openwakeword', type: 'wake', uri: 'tcp://127.0.0.1:10400', status: 'ready' }
61
- ```
111
+ - **signalk-wyoming** discovers it automatically — nothing to configure.
112
+ - **Remote satellites** (cockpit Pis running `wyoming-satellite`) point at
113
+ it with `--wake-uri tcp://<boat-server>:10400 --wake-word-name okay_nabu`.
114
+ - **Home Assistant** (or any other Wyoming client) can use it via the
115
+ Wyoming integration at the same URI — no Signal K required on the client
116
+ side.
62
117
 
63
- Standalone use works too: point Home Assistant's Wyoming integration, a
64
- `wyoming-satellite` (`--wake-uri tcp://<boat-server>:10400
65
- --wake-word-name okay_nabu`), or any other Wyoming client at the URI — no
66
- Signal K required on the client side.
118
+ ## HTTP API
119
+
120
+ | Endpoint | Access | Purpose |
121
+ | ----------------------------------------------------- | ---------------------- | ---------------------------------------------------------------------------------------------- |
122
+ | `GET /plugins/signalk-openwakeword/api/status` | any authenticated user | Current state: `{ status, uri, tag, containerState, lastHealth, info }` |
123
+ | `GET /plugins/signalk-openwakeword/api/versions` | any authenticated user | Available image versions from Docker Hub (feeds the config panel; works while plugin disabled) |
124
+ | `GET /plugins/signalk-openwakeword/api/update/check` | admin | Check whether a newer image is available |
125
+ | `POST /plugins/signalk-openwakeword/api/update/apply` | admin | Pull and switch to the newer image |
67
126
 
68
127
  ## Health & notifications
69
128
 
70
- - Readiness and liveness are protocol-native: a periodic Wyoming `describe`
71
- ping must return a valid `info` response. Three consecutive failures set a
72
- plugin error, emit `wyoming-service` status `error`, and raise the
73
- `notifications.voice.openwakeword` notification with `state: 'alarm'`
74
- (method `['visual']` deliberately not `'sound'`, so notification-to-speech
75
- bridges don't narrate voice-stack outages). Recovery clears it back to
76
- `normal`.
77
- - Plugin status shows what's happening at each stage (image pull, waiting
78
- for the service, running).
79
- - `GET /plugins/signalk-openwakeword/api/status` returns
80
- `{ status, uri, tag, containerState, lastHealth, info }` (readonly access).
81
- `GET /api/update/check` / `POST /api/update/apply` (admin) manage container
82
- image updates.
129
+ The plugin checks the service every 30 seconds. If it stops answering,
130
+ after three consecutive failures (about 90 seconds) it raises the Signal K
131
+ notification **`notifications.voice.openwakeword`** with `state: "alarm"`
132
+ and shows an error in Plugin Config. When the service answers again
133
+ everything clears back to normal automatically no action needed.
134
+
135
+ The alarm is visual-only by design: plugins that read notifications aloud
136
+ won't try to _speak_ the voice stack's own failure.
83
137
 
84
138
  ## Security
85
139
 
86
- **Wyoming has no authentication.** Unlike whisper/piper (loopback-only by
140
+ Wyoming has **no authentication.** Unlike whisper/piper (loopback-only by
87
141
  default), the wake word service must be reachable from your LAN so remote
88
142
  satellites can stream audio to it — that is why `bind` defaults to
89
143
  `0.0.0.0`. The exposure is modest (openWakeWord only ever sees audio a
@@ -93,35 +147,10 @@ satellite port like a baby monitor. On marina wifi, put the boat network
93
147
  behind a firewall or VLAN, or use WireGuard between boat segments. If every
94
148
  satellite runs on the server itself, set `bind` to `127.0.0.1`.
95
149
 
96
- ## Custom wake words
97
-
98
- Enable `advanced.customModels`, then drop openWakeWord `.tflite` model files
99
- into the `custom/` folder inside the **signalk-container** plugin's data
100
- directory (`<signalk>/plugin-config-data/signalk-container/custom/` — create
101
- `custom/` if it doesn't exist) and restart the plugin. The mount is provided
102
- by signalk-container's `signalkDataMount`, which always maps `/data` to
103
- signalk-container's _own_ data directory (shared by every plugin that uses
104
- it), **not** to this plugin's `plugin-config-data/signalk-openwakeword/`
105
- directory. If Signal K itself runs in a container with a volume-backed data
106
- directory, put the files in the corresponding location inside that volume.
107
- The container loads them via `--custom-model-dir /data/custom`; a `_v1.0`
108
- style filename suffix is stripped for the model id. A webapp upload UI is
109
- planned for v1.x; the manual drop works today.
110
-
111
150
  ## Development
112
151
 
113
- ```bash
114
- npm install
115
- npm run build # tsc → dist/
116
- npm test # typecheck + vitest (mock Wyoming server, fake container manager)
117
- npm run ci-lint # eslint + prettier --check
118
- ```
119
-
120
- Tests never touch docker: the container manager is faked and the Wyoming
121
- endpoint is the scriptable `signalk-wyoming/mock` server (a devDependency
122
- on the [signalk-wyoming](https://github.com/hoeken/signalk-wyoming)
123
- package). Production code has no runtime dependency on it — the plugin
124
- embeds its own ~150-line `describe` client.
152
+ See [DEVELOPERS.md](DEVELOPERS.md) for the code layout, build/test
153
+ commands, architecture notes, and the service-discovery contract.
125
154
 
126
155
  ## License
127
156
 
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAKL,KAAK,UAAU,EAChB,MAAM,0BAA0B,CAAC;AAalC,OAAO,EAEL,KAAK,YAAY,EACjB,KAAK,UAAU,EAChB,MAAM,cAAc,CAAC;AAEtB,MAAM,WAAW,SAAU,SAAQ,UAAU;IAC3C,iBAAiB,CACf,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,GAC/B,IAAI,CAAC;IACR,iBAAiB,CAAC,IAAI;QAAE,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,GAAG,SAAS,CAAC;CAC/E;AAID,MAAM,WAAW,YAAa,SAAQ,UAAU;IAC9C,4DAA4D;IAC5D,MAAM,CAAC,CAAC,KAAK,EAAE,UAAU,GAAG,WAAW,GAAG,UAAU,CAAC;CACtD;AAED,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,MAAM,CAAC;IACrB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,kBAAkB,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,IAAI,CAAC;CACpD;AAED;;;GAGG;AACH,MAAM,CAAC,OAAO,UAAU,YAAY,CAClC,GAAG,EAAE,SAAS,EACd,MAAM,GAAE,OAAO,CAAC,YAAY,CAAM,GACjC,kBAAkB,CAoHpB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAML,KAAK,UAAU,EAChB,MAAM,0BAA0B,CAAC;AAalC,OAAO,EAEL,KAAK,YAAY,EACjB,KAAK,UAAU,EAChB,MAAM,cAAc,CAAC;AAEtB,MAAM,WAAW,SAAU,SAAQ,UAAU;IAC3C,iBAAiB,CACf,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,GAC/B,IAAI,CAAC;IACR,iBAAiB,CAAC,IAAI;QAAE,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,GAAG,SAAS,CAAC;CAC/E;AAOD,MAAM,WAAW,YAAa,SAAQ,UAAU;IAC9C,4DAA4D;IAC5D,MAAM,CAAC,CAAC,KAAK,EAAE,UAAU,GAAG,WAAW,GAAG,UAAU,CAAC;CACtD;AAED,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,MAAM,CAAC;IACrB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,kBAAkB,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,IAAI,CAAC;CACpD;AAED;;;GAGG;AACH,MAAM,CAAC,OAAO,UAAU,YAAY,CAClC,GAAG,EAAE,SAAS,EACd,MAAM,GAAE,OAAO,CAAC,YAAY,CAAM,GACjC,kBAAkB,CAgJpB"}
package/dist/index.js CHANGED
@@ -6,9 +6,11 @@
6
6
  * advertises the service on the shared `wyoming-service` PropertyValues
7
7
  * convention, and keeps a protocol-native health loop running.
8
8
  */
9
- import { ManagedContainer, startSafely, errMsg, } from "signalk-container-helper";
9
+ import { ManagedContainer, startSafely, errMsg, fetchWithTimeout, } from "signalk-container-helper";
10
10
  import { buildContainerConfig, CONFIG_SCHEMA, CONTAINER_NAME, IMAGE, isSemverTag, PLUGIN_ID, PLUGIN_NAME, resolveTag, withDefaults, } from "./config.js";
11
11
  import { ServiceRunner, } from "./service.js";
12
+ /** Docker Hub tag listing backing the config panel's version dropdown. */
13
+ const TAGS_URL = `https://hub.docker.com/v2/repositories/${IMAGE}/tags/?page_size=25`;
12
14
  /**
13
15
  * Plugin factory. The optional `timing` parameter is a test seam — the
14
16
  * Signal K loader only ever passes `app`.
@@ -115,7 +117,44 @@ export default function createPlugin(app, timing = {}) {
115
117
  res.status(500).json({ error: errMsg(err) });
116
118
  }
117
119
  }));
120
+ // Version-dropdown feed for the config panel. Deliberately not
121
+ // guarded by the running flag: the operator picks a tag while the
122
+ // plugin is still disabled, and the route only reaches out on demand.
123
+ readonlyRouter.get("/api/versions", async (_req, res) => {
124
+ try {
125
+ const response = await fetchWithTimeout(TAGS_URL, {
126
+ timeoutMs: 10_000,
127
+ });
128
+ if (!response.ok) {
129
+ res
130
+ .status(502)
131
+ .json({ error: `Docker Hub answered HTTP ${response.status}` });
132
+ return;
133
+ }
134
+ const body = (await response.json());
135
+ const versions = (Array.isArray(body.results) ? body.results : [])
136
+ .map((r) => (typeof r?.name === "string" ? r.name : ""))
137
+ .filter(isSemverTag)
138
+ .sort(compareSemverDesc)
139
+ .map((tag) => ({ tag }));
140
+ res.json({ versions });
141
+ }
142
+ catch (err) {
143
+ res.status(502).json({ error: errMsg(err) });
144
+ }
145
+ });
118
146
  },
119
147
  };
120
148
  }
149
+ /** Numeric-descending compare for the plain x.y.z tags isSemverTag admits. */
150
+ function compareSemverDesc(a, b) {
151
+ const pa = a.split(".").map(Number);
152
+ const pb = b.split(".").map(Number);
153
+ for (let i = 0; i < 3; i += 1) {
154
+ const d = (pb[i] ?? 0) - (pa[i] ?? 0);
155
+ if (d !== 0)
156
+ return d;
157
+ }
158
+ return 0;
159
+ }
121
160
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,17 +1,18 @@
1
1
  {
2
2
  "name": "signalk-openwakeword",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Wyoming openWakeWord wake word detection service for Signal K — runs rhasspy/wyoming-openwakeword in a managed container.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
8
8
  "files": [
9
9
  "dist",
10
+ "public",
10
11
  "README.md",
11
12
  "LICENSE"
12
13
  ],
13
14
  "scripts": {
14
- "build": "tsc",
15
+ "build": "tsc && webpack",
15
16
  "test": "npm run typecheck:test && vitest run",
16
17
  "test:watch": "vitest",
17
18
  "typecheck": "tsc --noEmit",
@@ -25,7 +26,7 @@
25
26
  "prepublishOnly": "npm run build && npm test"
26
27
  },
27
28
  "lint-staged": {
28
- "*.ts": [
29
+ "*.{ts,jsx}": [
29
30
  "eslint --fix",
30
31
  "prettier --write"
31
32
  ],
@@ -33,6 +34,7 @@
33
34
  },
34
35
  "keywords": [
35
36
  "signalk-node-server-plugin",
37
+ "signalk-plugin-configurator",
36
38
  "signalk-category-utility",
37
39
  "wyoming",
38
40
  "openwakeword",
@@ -56,21 +58,28 @@
56
58
  "node": ">=24"
57
59
  },
58
60
  "dependencies": {
59
- "signalk-container-helper": "^0.1.0"
61
+ "signalk-container-helper": "^0.2.1"
60
62
  },
61
63
  "devDependencies": {
64
+ "@babel/core": "^7.29.0",
65
+ "@babel/preset-react": "^7.28.5",
62
66
  "@eslint/js": "^10.0.1",
63
67
  "@signalk/server-api": "^2.30.0",
64
68
  "@types/node": "^24.0.0",
69
+ "babel-loader": "^10.1.1",
65
70
  "eslint": "^10.2.0",
66
71
  "eslint-config-prettier": "^10.1.8",
67
72
  "globals": "^17.4.0",
68
73
  "husky": "^9.1.7",
69
74
  "lint-staged": "^16.4.0",
70
75
  "prettier": "^3.8.1",
76
+ "react": "^19.2.4",
77
+ "react-dom": "^19.2.4",
71
78
  "signalk-wyoming": "^0.1.0",
72
79
  "typescript": "^5.5.0",
73
80
  "typescript-eslint": "^8.58.0",
74
- "vitest": "^4.1.10"
81
+ "vitest": "^4.1.10",
82
+ "webpack": "^5.105.4",
83
+ "webpack-cli": "^7.0.2"
75
84
  }
76
85
  }
package/public/172.mjs ADDED
@@ -0,0 +1 @@
1
+ export const __webpack_esm_id__=172;export const __webpack_esm_ids__=[172];export const __webpack_esm_modules__={172(e,t,r){r.d(t,{default:()=>S}),r.r(t);var n=r(231);function a(e,t={}){const{stableCount:r=5,preCount:n=3}=t,a=e.filter(e=>"number"==typeof e.pr),o=e.filter(e=>"number"!=typeof e.pr);return{prVersions:a,stableVersions:o.filter(e=>!e.prerelease).slice(0,r),preVersions:o.filter(e=>e.prerelease).slice(0,n)}}const o={root:{fontFamily:'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',color:"#333",padding:"16px 0"},sectionTitle:{fontSize:13,fontWeight:600,color:"#888",textTransform:"uppercase",letterSpacing:"0.05em",marginBottom:10,marginTop:24},btn:{display:"inline-flex",alignItems:"center",gap:8,padding:"8px 16px",border:"none",borderRadius:6,fontSize:13,fontWeight:600,cursor:"pointer"},btnPrimary:{background:"#3b82f6",color:"#fff"},btnSecondary:{background:"#f1f5f9",color:"#475569",border:"1px solid #e2e8f0"},btnWarn:{background:"#f59e0b",color:"#fff"},btnDanger:{background:"#ef4444",color:"#fff"},btnDisabled:{opacity:.5,cursor:"not-allowed"},btnSmall:{padding:"4px 12px",fontSize:12},status:{marginTop:8,fontSize:12,minHeight:18},card:{display:"flex",alignItems:"center",gap:14,padding:"14px 18px",background:"#f8f9fa",border:"1px solid #e0e0e0",borderRadius:10,marginBottom:12},cardIcon:{width:44,height:44,borderRadius:10,display:"flex",alignItems:"center",justifyContent:"center",fontSize:20,fontWeight:700,flexShrink:0},cardInfo:{flex:1},cardTitle:{fontSize:15,fontWeight:600,color:"#333"},cardMeta:{fontSize:12,color:"#888"},stateIndicator:{width:10,height:10,borderRadius:"50%",flexShrink:0},fieldRow:{display:"flex",alignItems:"center",gap:12,marginBottom:10},label:{fontSize:13,fontWeight:500,color:"#555",width:180,flexShrink:0},select:{padding:"6px 10px",borderRadius:6,border:"1px solid #ccc",fontSize:13,background:"#fff",color:"#333",minWidth:200},input:{padding:"6px 10px",borderRadius:6,border:"1px solid #ccc",fontSize:13,background:"#fff",color:"#333",width:200},inputSmall:{padding:"6px 10px",borderRadius:6,border:"1px solid #ccc",fontSize:13,background:"#fff",color:"#333",width:80},checkbox:{width:16,height:16,accentColor:"#3b82f6"},hint:{fontSize:11,color:"#aaa",marginLeft:8},link:{color:"#3b82f6",textDecoration:"none",fontSize:13,fontWeight:600},textarea:{padding:"6px 10px",borderRadius:6,border:"1px solid #ccc",fontSize:13,fontFamily:"monospace",background:"#fff",color:"#333",width:"100%",minHeight:70,boxSizing:"border-box",resize:"vertical"},empty:{textAlign:"center",padding:"30px 16px",color:"#999",fontSize:13},tag:{display:"inline-block",padding:"2px 8px",borderRadius:4,fontSize:10,fontWeight:600,marginLeft:8},tagPre:{background:"#fef3c7",color:"#92400e"},tagLatest:{background:"#dcfce7",color:"#166534"},warnBanner:{padding:"12px 16px",background:"#fef2f2",border:"1px solid #fecaca",borderRadius:10,marginBottom:12,fontSize:13,color:"#991b1b",lineHeight:1.5},warnBannerTitle:{fontWeight:700,marginBottom:4},infoBanner:{padding:"12px 16px",background:"#fffbeb",border:"1px solid #fde68a",borderRadius:10,marginBottom:12,fontSize:13,color:"#92400e",lineHeight:1.5},bannerCode:{display:"block",marginTop:8,padding:"8px 10px",background:"#fff",border:"1px solid #fecaca",borderRadius:6,fontFamily:"monospace",fontSize:12,color:"#7f1d1d",whiteSpace:"pre-wrap",wordBreak:"break-word"},statsGrid:{display:"grid",gridTemplateColumns:"repeat(auto-fit, minmax(140px, 1fr))",gap:10,marginBottom:12},statCard:{padding:"12px 16px",background:"#f8f9fa",border:"1px solid #e0e0e0",borderRadius:10,textAlign:"center"},statValue:{fontSize:22,fontWeight:700,color:"#333"},statLabel:{fontSize:11,color:"#888",marginTop:2}},l={ok:"#10b981",warn:"#f59e0b",error:"#ef4444"};function i(e){const{runningTag:t,tagKind:r,currentVersion:n,latestVersion:a,updateAvailable:o,reason:l,fromCache:i,lastSuccessfulCheckAt:s}=e??{};return"offline"===l?i&&s?`Offline — last checked ${function(e){try{const t=new Date(e).getTime(),r=Math.max(0,Math.floor((Date.now()-t)/1e3));if(r<5)return"just now";if(r<60)return`${r}s ago`;const n=Math.floor(r/60);if(n<60)return`${n}m ago`;const a=Math.floor(n/60);return a<24?`${a}h ago`:`${Math.floor(a/24)}d ago`}catch{return e}}(s)}: ${o?"update available":"up to date"}`:"Offline — never checked yet":"newer-version"===l?`Update available: ${n} → ${a}`:"digest-drift"===l?`Image rebuild available for :${t}${a?` (latest stable: ${a})`:""}`:"up-to-date"===l?"floating"===r&&a?`Up to date with :${t} (latest stable: ${a})`:`Up to date (${n||t})`:"older-than-pinned"===l?`Pinned to ${n}; latest stable is ${a}`:"error"===l?`Check error: ${e?.error||"unknown"}`:`State: ${l||"unknown"}`}function s({children:e}){return n.createElement("div",{style:o.sectionTitle},e)}function c({children:e,color:t}){return n.createElement("span",{style:{...o.hint,...t?{color:t}:{}}},e)}function d({label:e,hint:t,hintColor:r,children:a}){return n.createElement("div",{style:o.fieldRow},n.createElement("span",{style:o.label},e),a,null!=t&&""!==t&&n.createElement(c,{color:r},t))}function u({state:e="ok",color:t,title:r,style:a}){return n.createElement("div",{style:{...o.stateIndicator,background:t??l[e],...a},title:r})}function p({icon:e,iconBackground:t,iconColor:r,title:a,meta:i,state:s="ok",stateTitle:c,link:d,children:p}){const m="error"===s&&!t;return n.createElement("div",{style:o.card},n.createElement("div",{style:{...o.cardIcon,background:m?"#fef2f2":t??"#3b82f6",color:m?l.error:r??"#fff"}},e),n.createElement("div",{style:o.cardInfo},n.createElement("div",{style:o.cardTitle},a),null!=i&&n.createElement("div",{style:o.cardMeta},i)),p,d&&n.createElement("a",{href:d.href,target:"_blank",rel:"noopener noreferrer",style:o.link},d.label),n.createElement(u,{state:s,title:c}))}function m({title:e,defaultOpen:t=!1,children:r}){const[a,l]=(0,n.useState)(t);return n.createElement("div",null,n.createElement("button",{type:"button","aria-expanded":a,style:{...o.sectionTitle,cursor:"pointer",userSelect:"none",display:"flex",alignItems:"center",gap:6,width:"100%",textAlign:"left",background:"none",border:"none",padding:0},onClick:()=>l(!a)},n.createElement("span",{style:{fontSize:10,transition:"transform 0.15s",transform:a?"rotate(90deg)":"rotate(0deg)"}},"▶"),e),a&&n.createElement("div",{style:{marginBottom:16}},r))}const g={primary:o.btnPrimary,secondary:o.btnSecondary,warn:o.btnWarn,danger:o.btnDanger};function f({variant:e="primary",small:t=!1,busy:r=!1,busyLabel:a,disabled:l=!1,onClick:i,title:s,style:c,children:d}){const u=l||r;return n.createElement("button",{type:"button",style:{...o.btn,...g[e],...t?o.btnSmall:{},...u?o.btnDisabled:{},...c},onClick:i,disabled:u,title:s},r?a??d:d)}function b({message:e,error:t=!1,style:r}){return e?n.createElement("div",{style:{...o.status,color:t?l.error:l.ok,marginTop:16,...r}},e):null}const h=[{tag:"latest",label:"latest (recommended)"}];function y({value:e,onChange:t,versions:r,floatingOptions:i=h,stableCount:s,preCount:d,loading:u=!1,error:p,onRefresh:m,disabled:g=!1,style:b}){const y={stableCount:s,preCount:d},{prVersions:k,stableVersions:v,preVersions:w}=a(r,y),E=function(e,t,r=["latest"],n={}){return e?function(e,t=["latest"],r={}){const{prVersions:n,stableVersions:o,preVersions:l}=a(e,r);return new Set([...t,...l.map(e=>e.tag),...o.map(e=>e.tag),...n.map(e=>e.tag)])}(t,r,n).has(e)?null:e:null}(e,r,i.map(e=>e.tag),y);return n.createElement(n.Fragment,null,n.createElement("select",{style:{...o.select,...b},value:e,onChange:e=>t(e.target.value),disabled:g},i.map(e=>n.createElement("option",{key:e.tag,value:e.tag},e.label??e.tag)),w.map(e=>n.createElement("option",{key:e.tag,value:e.tag},e.tag," (pre-release)")),v.map((e,t)=>n.createElement("option",{key:e.tag,value:e.tag},e.tag,0===t?" (current stable)":"")),k.length>0&&n.createElement("optgroup",{label:"PR test images"},k.map(e=>n.createElement("option",{key:e.tag,value:e.tag},e.tag,e.title?` — ${e.title}`:""))),E&&n.createElement("option",{value:E},E," (running)")),u&&n.createElement(c,null,"loading releases..."),m&&n.createElement(f,{small:!0,style:{padding:"4px 10px",fontSize:11},onClick:m,title:"Refresh available versions"},"↻"),p&&n.createElement(c,{color:l.error},p))}function k({checkUrl:e,applyUrl:t,tag:r,onApplied:a,updateLabel:o="Update",disabled:s=!1,style:c}){const d=function(e){const{checkUrl:t,applyUrl:r}=e,[a,o]=(0,n.useState)(!1),[l,i]=(0,n.useState)(!1),[s,c]=(0,n.useState)(null),[d,u]=(0,n.useState)("");return{checking:a,applying:l,result:s,error:d,check:(0,n.useCallback)(async()=>{o(!0),u("");try{const e=await fetch(t),r=await e.json().catch(()=>null);if(e.ok&&r&&"object"==typeof r)c(r);else{const t=r?.error??`HTTP ${e.status}`;u(`Check failed: ${t}`)}}catch(e){u(`Check failed: ${e instanceof Error?e.message:String(e)}`)}o(!1)},[t]),apply:(0,n.useCallback)(async e=>{let t;i(!0),u("");try{const n=await fetch(r,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e?{tag:e}:{})}),a=await n.json().catch(()=>null);n.ok?(t={ok:!0,tag:a?.tag},c(null)):(t={ok:!1,error:a?.error??`HTTP ${n.status}`},u(`Update failed: ${t.error}`))}catch(e){t={ok:!1,error:e instanceof Error?e.message:String(e)},u(`Update failed: ${t.error}`)}return i(!1),t},[r])}}({checkUrl:e,applyUrl:t}),[u,p]=(0,n.useState)("");return n.createElement("div",{style:{display:"flex",alignItems:"center",gap:10,marginBottom:12,...c}},d.result?.updateAvailable?n.createElement(n.Fragment,null,n.createElement("span",{style:{fontSize:13}},i(d.result)),n.createElement(f,{small:!0,variant:"warn",busy:d.applying,busyLabel:"Updating...",disabled:s||d.checking,onClick:()=>{(async()=>{p("");const e=await d.apply(r);e.ok&&(p(e.tag?`Updated to ${e.tag}.`:"Updated."),a?.(e.tag))})()},title:"Pull the image and recreate the container"},o)):d.result?n.createElement("span",{style:{fontSize:12,color:"#888"}},i(d.result)):n.createElement(f,{small:!0,variant:"secondary",busy:d.checking,busyLabel:"Checking...",disabled:s||d.applying,onClick:()=>{d.check()}},"Check for updates"),u&&n.createElement("span",{style:{fontSize:12,color:l.ok}},u),d.error&&n.createElement("span",{style:{fontSize:12,color:l.error}},d.error))}const v="/plugins/signalk-openwakeword",w={imageTag:"auto",bind:"0.0.0.0",wakeWords:["okay_nabu"],threshold:.5,triggerLevel:1,memoryLimit:"384m",restartPolicy:"unless-stopped"},E=["okay_nabu","hey_jarvis","hey_mycroft","alexa","hey_rhasspy"];function S({configuration:e,save:t}){const r=e||{},a=r.advanced||{},[i,c]=(0,n.useState)(()=>{const e=Array.isArray(r.wakeWords)?r.wakeWords.filter(e=>"string"==typeof e&&""!==e):[];return e.length>0?e:[...w.wakeWords]}),[u,g]=(0,n.useState)(""),[h,S]=(0,n.useState)(String("number"==typeof r.threshold?r.threshold:w.threshold)),[x,C]=(0,n.useState)(String("number"==typeof r.triggerLevel?r.triggerLevel:w.triggerLevel)),[$,z]=(0,n.useState)(String(r.port??10400)),[T,_]=(0,n.useState)("127.0.0.1"===r.bind?"127.0.0.1":w.bind),[A,R]=(0,n.useState)(r.imageTag||w.imageTag),[W,I]=(0,n.useState)(r.memoryLimit||w.memoryLimit),[L,U]=(0,n.useState)(r.restartPolicy||w.restartPolicy),[B,H]=(0,n.useState)("number"==typeof a.refractorySeconds?String(a.refractorySeconds):""),[N,P]=(0,n.useState)(!0===a.customModels),[j,V]=(0,n.useState)("string"==typeof a.advertiseHost?a.advertiseHost:""),[M,D]=(0,n.useState)(""),{status:F,loading:O,refresh:G}=function(e,t={}){const{intervalMs:r=5e3}=t,[a,o]=(0,n.useState)(null),[l,i]=(0,n.useState)(!0),s=(0,n.useRef)(0),c=(0,n.useRef)(t.fallback??null),d=(0,n.useCallback)(async t=>{const r=++s.current;let n=c.current;try{const r=await fetch(e,{signal:t}),a=await r.json().catch(()=>null);a&&"object"==typeof a&&(n=a)}catch{}r===s.current&&(o(n),i(!1))},[e]);return(0,n.useEffect)(()=>{let e=!1,t=null;const n=new AbortController,a=async()=>{await d(n.signal),!e&&r>0&&(t=setTimeout(a,r))};return a(),()=>{e=!0,n.abort(),t&&clearTimeout(t)}},[d,r]),{status:a,loading:l,refresh:()=>d()}}(`${v}/api/status`,{fallback:{status:"not_running"}}),K=function(e){const[t,r]=(0,n.useState)([]),[a,o]=(0,n.useState)(""),[l,i]=(0,n.useState)(!1),s=(0,n.useRef)(0),c=(0,n.useCallback)(async()=>{const t=++s.current;i(!0);try{const n=await fetch(e),a=await n.json().catch(()=>null),l=function(e,t){if(!e)return{versions:null,versionsError:"⚠ Could not reach GitHub — showing last known versions, retry"};const r=t&&"object"==typeof t?t:{},n=Array.isArray(r)?r:Array.isArray(r.versions)?r.versions:[],a=!Array.isArray(r)&&r.sources&&"object"==typeof r.sources?r.sources:{};let o="";return"rate-limited"===a.prImages?o="⚠ GitHub rate-limited — PR test images temporarily unavailable, retry shortly":"rate-limited"===a.releases?o="⚠ GitHub rate-limited — some versions temporarily unavailable, retry shortly":"error"!==a.prImages&&"error"!==a.releases||(o="⚠ Could not fetch some versions from GitHub, retry"),{versions:n,versionsError:o}}(n.ok,a);if(t!==s.current)return;null!==l.versions&&r(l.versions),o(l.versionsError)}catch{if(t!==s.current)return;o("⚠ Offline — showing last known versions")}i(!1)},[e]);return(0,n.useEffect)(()=>{c()},[c]),{versions:t,versionsError:a,loading:l,refresh:c}}(`${v}/api/versions`),q=F&&"string"==typeof F.status?F.status:"not_running",J="ready"===q?"ok":"starting"===q?"warn":"error",Q=function(e){const t=[];if(!e||!Array.isArray(e.wake))return t;for(const r of e.wake)if(r&&Array.isArray(r.models))for(const e of r.models)e&&"string"==typeof e.name&&""!==e.name&&t.push(e.name);return t}(F&&F.info),X=O?"Checking...":"ready"===q?`rhasspy/wyoming-openwakeword:${F.tag} at ${F.uri}`+(Q.length>0?` — ${Q.length} wake word model${1===Q.length?"":"s"}`:""):"starting"===q?"Starting — wake word models ship in the image, nothing to download":"error"===q?"Not answering"+(F&&F.uri?` at ${F.uri}`:""):"Not running",Y=Q.length>0?Q:E,Z=[...Y,...i.filter(e=>!Y.includes(e))],ee="ready"===q&&Q.length>0?i.filter(e=>!Q.includes(e)):[],te=()=>{const e=u.trim();g(""),""===e||i.includes(e)||c(t=>[...t,e])};return n.createElement("div",{style:o.root},n.createElement(s,null,"openWakeWord status"),n.createElement(p,{icon:"O",iconBackground:"ready"===q?"#7c3aed":void 0,title:"openWakeWord (Wyoming)",meta:X,state:J,stateTitle:q}),"not_running"!==q&&"stopped"!==q&&n.createElement(k,{checkUrl:`${v}/api/update/check`,applyUrl:`${v}/api/update/apply`,tag:A,onApplied:()=>{G()}}),n.createElement(s,null,"Settings"),n.createElement(d,{label:"Wake words",hint:ee.length>0?`not available on the running service: ${ee.join(", ")} (2.0.0 renamed ok_nabu to okay_nabu)`:"what satellites can listen for; clients select models per connection",hintColor:ee.length>0?l.warn:void 0},n.createElement("div",{style:{display:"flex",flexWrap:"wrap",gap:"6px 16px"}},Z.map(e=>n.createElement("label",{key:e,style:{display:"inline-flex",alignItems:"center",gap:6,fontSize:13}},n.createElement("input",{style:o.checkbox,type:"checkbox",checked:i.includes(e),onChange:()=>(e=>{c(t=>t.includes(e)?t.filter(t=>t!==e):[...t,e])})(e)}),e)))),n.createElement(d,{label:"Add wake word",hint:"a model name not listed above (e.g. a custom model, before its first start)"},n.createElement("input",{style:{...o.input,width:180},value:u,onChange:e=>g(e.target.value),onKeyDown:e=>{"Enter"===e.key&&(e.preventDefault(),te())},placeholder:"model_name"}),n.createElement(f,{variant:"secondary",onClick:te},"Add")),n.createElement(d,{label:"Detection threshold",hint:"0–1; lower = more sensitive but more false wakes"},n.createElement("input",{style:{...o.input,width:90},type:"number",min:"0",max:"1",step:"0.05",value:h,onChange:e=>S(e.target.value)})),n.createElement(d,{label:"Trigger level",hint:"activations above the threshold before a detection fires; raise in noisy cabins"},n.createElement("input",{style:{...o.input,width:90},type:"number",min:"1",step:"1",value:x,onChange:e=>C(e.target.value)})),n.createElement(d,{label:"Image version"},n.createElement(y,{value:A,onChange:R,versions:K.versions,floatingOptions:[{tag:"auto",label:"auto (pinned release, recommended)"}],loading:K.loading,error:K.versionsError,onRefresh:K.refresh})),n.createElement(d,{label:"Host port",hint:"satellites connect to tcp://<boat-server>:<this port>"},n.createElement("input",{style:{...o.input,width:90},type:"number",value:$,onChange:e=>z(e.target.value)})),n.createElement(d,{label:"Bind address",hint:"0.0.0.0"===T?"LAN-reachable — required for remote satellites; Wyoming has no authentication, firewall/VLAN on marina wifi":"only satellites on this machine can reach the service",hintColor:"0.0.0.0"===T?l.warn:void 0},n.createElement("select",{style:o.select,value:T,onChange:e=>_(e.target.value)},n.createElement("option",{value:"0.0.0.0"},"0.0.0.0 (all interfaces)"),n.createElement("option",{value:"127.0.0.1"},"127.0.0.1 (this machine only)"))),n.createElement(m,{title:"Advanced"},n.createElement(d,{label:"Refractory seconds",hint:"optional minimum seconds between re-detections; upstream default 2.0"},n.createElement("input",{style:{...o.input,width:90},type:"number",step:"0.5",value:B,onChange:e=>H(e.target.value)})),n.createElement(d,{label:"Custom wake word models",hint:"load .tflite files from plugin-config-data/signalk-container/custom/ (--custom-model-dir)"},n.createElement("input",{style:o.checkbox,type:"checkbox",checked:N,onChange:e=>P(e.target.checked)})),n.createElement(d,{label:"Advertise host",hint:"optional host override for the advertised tcp:// URI (containerized Signal K, multi-NIC)"},n.createElement("input",{style:{...o.input,width:180},value:j,onChange:e=>V(e.target.value),placeholder:"automatic"})),n.createElement(d,{label:"Memory limit",hint:'hard cap, e.g. "384m" or "1g"'},n.createElement("input",{style:{...o.input,width:90},value:W,onChange:e=>I(e.target.value)})),n.createElement(d,{label:"Restart policy"},n.createElement("select",{style:o.select,value:L,onChange:e=>U(e.target.value)},n.createElement("option",{value:"unless-stopped"},"unless-stopped"),n.createElement("option",{value:"always"},"always"),n.createElement("option",{value:"no"},"no")))),n.createElement("div",{style:{marginTop:24}},n.createElement(f,{onClick:()=>{const e={customModels:N},n=Number(B);""!==B&&Number.isFinite(n)&&(e.refractorySeconds=n),""!==j.trim()&&(e.advertiseHost=j.trim());const a=Number(h),o=Number(x),l=Number($);t({...r,wakeWords:i.length>0?i:[...w.wakeWords],threshold:""!==h&&Number.isFinite(a)?a:w.threshold,triggerLevel:""!==x&&Number.isFinite(o)?o:w.triggerLevel,port:""!==$&&Number.isFinite(l)?l:10400,bind:T,imageTag:A,memoryLimit:W,restartPolicy:L,advanced:e}),D("Saved. Signal K restarts the plugin with the new configuration.")}},"Save Configuration")),n.createElement(b,{message:M}))}}};
package/public/540.mjs ADDED
@@ -0,0 +1,2 @@
1
+ /*! For license information please see 540.mjs.LICENSE.txt */
2
+ export const __webpack_esm_id__=540;export const __webpack_esm_ids__=[540];export const __webpack_esm_modules__={869(e,t){var n=Symbol.for("react.transitional.element"),r=Symbol.for("react.portal"),o=Symbol.for("react.fragment"),u=Symbol.for("react.strict_mode"),c=Symbol.for("react.profiler"),s=Symbol.for("react.consumer"),i=Symbol.for("react.context"),a=Symbol.for("react.forward_ref"),f=Symbol.for("react.suspense"),l=Symbol.for("react.memo"),p=Symbol.for("react.lazy"),y=Symbol.for("react.activity"),d=Symbol.iterator,_={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},h=Object.assign,m={};function b(e,t,n){this.props=e,this.context=t,this.refs=m,this.updater=n||_}function v(){}function S(e,t,n){this.props=e,this.context=t,this.refs=m,this.updater=n||_}b.prototype.isReactComponent={},b.prototype.setState=function(e,t){if("object"!=typeof e&&"function"!=typeof e&&null!=e)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,e,t,"setState")},b.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,"forceUpdate")},v.prototype=b.prototype;var E=S.prototype=new v;E.constructor=S,h(E,b.prototype),E.isPureReactComponent=!0;var w=Array.isArray;function g(){}var k={H:null,A:null,T:null,S:null},H=Object.prototype.hasOwnProperty;function j(e,t,r){var o=r.ref;return{$$typeof:n,type:e,key:t,ref:void 0!==o?o:null,props:r}}function R(e){return"object"==typeof e&&null!==e&&e.$$typeof===n}var C=/\/+/g;function $(e,t){return"object"==typeof e&&null!==e&&null!=e.key?(n=""+e.key,r={"=":"=0",":":"=2"},"$"+n.replace(/[=:]/g,function(e){return r[e]})):t.toString(36);var n,r}function x(e,t,o,u,c){var s=typeof e;"undefined"!==s&&"boolean"!==s||(e=null);var i,a,f=!1;if(null===e)f=!0;else switch(s){case"bigint":case"string":case"number":f=!0;break;case"object":switch(e.$$typeof){case n:case r:f=!0;break;case p:return x((f=e._init)(e._payload),t,o,u,c)}}if(f)return c=c(e),f=""===u?"."+$(e,0):u,w(c)?(o="",null!=f&&(o=f.replace(C,"$&/")+"/"),x(c,t,o,"",function(e){return e})):null!=c&&(R(c)&&(i=c,a=o+(null==c.key||e&&e.key===c.key?"":(""+c.key).replace(C,"$&/")+"/")+f,c=j(i.type,a,i.props)),t.push(c)),1;f=0;var l,y=""===u?".":u+":";if(w(e))for(var _=0;_<e.length;_++)f+=x(u=e[_],t,o,s=y+$(u,_),c);else if("function"==typeof(_=null===(l=e)||"object"!=typeof l?null:"function"==typeof(l=d&&l[d]||l["@@iterator"])?l:null))for(e=_.call(e),_=0;!(u=e.next()).done;)f+=x(u=u.value,t,o,s=y+$(u,_++),c);else if("object"===s){if("function"==typeof e.then)return x(function(e){switch(e.status){case"fulfilled":return e.value;case"rejected":throw e.reason;default:switch("string"==typeof e.status?e.then(g,g):(e.status="pending",e.then(function(t){"pending"===e.status&&(e.status="fulfilled",e.value=t)},function(t){"pending"===e.status&&(e.status="rejected",e.reason=t)})),e.status){case"fulfilled":return e.value;case"rejected":throw e.reason}}throw e}(e),t,o,u,c);throw t=String(e),Error("Objects are not valid as a React child (found: "+("[object Object]"===t?"object with keys {"+Object.keys(e).join(", ")+"}":t)+"). If you meant to render a collection of children, use an array instead.")}return f}function T(e,t,n){if(null==e)return e;var r=[],o=0;return x(e,r,"","",function(e){return t.call(n,e,o++)}),r}function A(e){if(-1===e._status){var t=e._result;(t=t()).then(function(t){0!==e._status&&-1!==e._status||(e._status=1,e._result=t)},function(t){0!==e._status&&-1!==e._status||(e._status=2,e._result=t)}),-1===e._status&&(e._status=0,e._result=t)}if(1===e._status)return e._result.default;throw e._result}var O="function"==typeof reportError?reportError:function(e){if("object"==typeof window&&"function"==typeof window.ErrorEvent){var t=new window.ErrorEvent("error",{bubbles:!0,cancelable:!0,message:"object"==typeof e&&null!==e&&"string"==typeof e.message?String(e.message):String(e),error:e});if(!window.dispatchEvent(t))return}else if("object"==typeof process&&"function"==typeof process.emit)return void process.emit("uncaughtException",e);console.error(e)},I={map:T,forEach:function(e,t,n){T(e,function(){t.apply(this,arguments)},n)},count:function(e){var t=0;return T(e,function(){t++}),t},toArray:function(e){return T(e,function(e){return e})||[]},only:function(e){if(!R(e))throw Error("React.Children.only expected to receive a single React element child.");return e}};t.Activity=y,t.Children=I,t.Component=b,t.Fragment=o,t.Profiler=c,t.PureComponent=S,t.StrictMode=u,t.Suspense=f,t.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE=k,t.__COMPILER_RUNTIME={__proto__:null,c:function(e){return k.H.useMemoCache(e)}},t.cache=function(e){return function(){return e.apply(null,arguments)}},t.cacheSignal=function(){return null},t.cloneElement=function(e,t,n){if(null==e)throw Error("The argument must be a React element, but you passed "+e+".");var r=h({},e.props),o=e.key;if(null!=t)for(u in void 0!==t.key&&(o=""+t.key),t)!H.call(t,u)||"key"===u||"__self"===u||"__source"===u||"ref"===u&&void 0===t.ref||(r[u]=t[u]);var u=arguments.length-2;if(1===u)r.children=n;else if(1<u){for(var c=Array(u),s=0;s<u;s++)c[s]=arguments[s+2];r.children=c}return j(e.type,o,r)},t.createContext=function(e){return(e={$$typeof:i,_currentValue:e,_currentValue2:e,_threadCount:0,Provider:null,Consumer:null}).Provider=e,e.Consumer={$$typeof:s,_context:e},e},t.createElement=function(e,t,n){var r,o={},u=null;if(null!=t)for(r in void 0!==t.key&&(u=""+t.key),t)H.call(t,r)&&"key"!==r&&"__self"!==r&&"__source"!==r&&(o[r]=t[r]);var c=arguments.length-2;if(1===c)o.children=n;else if(1<c){for(var s=Array(c),i=0;i<c;i++)s[i]=arguments[i+2];o.children=s}if(e&&e.defaultProps)for(r in c=e.defaultProps)void 0===o[r]&&(o[r]=c[r]);return j(e,u,o)},t.createRef=function(){return{current:null}},t.forwardRef=function(e){return{$$typeof:a,render:e}},t.isValidElement=R,t.lazy=function(e){return{$$typeof:p,_payload:{_status:-1,_result:e},_init:A}},t.memo=function(e,t){return{$$typeof:l,type:e,compare:void 0===t?null:t}},t.startTransition=function(e){var t=k.T,n={};k.T=n;try{var r=e(),o=k.S;null!==o&&o(n,r),"object"==typeof r&&null!==r&&"function"==typeof r.then&&r.then(g,O)}catch(e){O(e)}finally{null!==t&&null!==n.types&&(t.types=n.types),k.T=t}},t.unstable_useCacheRefresh=function(){return k.H.useCacheRefresh()},t.use=function(e){return k.H.use(e)},t.useActionState=function(e,t,n){return k.H.useActionState(e,t,n)},t.useCallback=function(e,t){return k.H.useCallback(e,t)},t.useContext=function(e){return k.H.useContext(e)},t.useDebugValue=function(){},t.useDeferredValue=function(e,t){return k.H.useDeferredValue(e,t)},t.useEffect=function(e,t){return k.H.useEffect(e,t)},t.useEffectEvent=function(e){return k.H.useEffectEvent(e)},t.useId=function(){return k.H.useId()},t.useImperativeHandle=function(e,t,n){return k.H.useImperativeHandle(e,t,n)},t.useInsertionEffect=function(e,t){return k.H.useInsertionEffect(e,t)},t.useLayoutEffect=function(e,t){return k.H.useLayoutEffect(e,t)},t.useMemo=function(e,t){return k.H.useMemo(e,t)},t.useOptimistic=function(e,t){return k.H.useOptimistic(e,t)},t.useReducer=function(e,t,n){return k.H.useReducer(e,t,n)},t.useRef=function(e){return k.H.useRef(e)},t.useState=function(e){return k.H.useState(e)},t.useSyncExternalStore=function(e,t,n){return k.H.useSyncExternalStore(e,t,n)},t.useTransition=function(){return k.H.useTransition()},t.version="19.2.8"},540(e,t,n){e.exports=n(869)}};
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @license React
3
+ * react.production.js
4
+ *
5
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
6
+ *
7
+ * This source code is licensed under the MIT license found in the
8
+ * LICENSE file in the root directory of this source tree.
9
+ */
@@ -0,0 +1 @@
1
+ var e={980(){}};const o={};function t(r){const s=o[r];if(void 0!==s)return s.exports;const n=o[r]={exports:{}};return e[r](n,n.exports,t),n.exports}t.m=e,t.c=o,t.f={},t.e=e=>Promise.all(Object.keys(t.f).reduce((o,r)=>(t.f[r](e,o),o),[])),t.u=e=>e+".mjs",t.o=(e,o)=>Object.prototype.hasOwnProperty.call(e,o),(()=>{t.S={};const e={},o={};t.I=(r,s)=>{s||(s=[]);let n=o[r];if(n||(n=o[r]={}),s.indexOf(n)>=0)return;if(s.push(n),e[r])return e[r];t.o(t.S,r)||(t.S[r]={});const i=t.S[r],c="signalk-openwakeword",_=[];return"default"===r&&((e,o,r,s)=>{const n=i[e]=i[e]||{},_=n[o];(!_||!_.loaded&&(1!=!_.eager?s:c>_.from))&&(n[o]={get:()=>t.e(540).then(()=>()=>t(540)),from:c,eager:!1})})("react","19.2.8"),e[r]=_.length?Promise.all(_).then(()=>e[r]=1):1}})(),(()=>{const e={792:0},o=o=>{let{__webpack_esm_ids__:r,__webpack_esm_modules__:s,__webpack_esm_runtime__:n}=o;var i,c,_=0;for(i in s)t.o(s,i)&&(t.m[i]=s[i]);for(n&&n(t);_<r.length;_++)c=r[_],t.o(e,c)&&e[c]&&e[c][0](),e[r[_]]=0};t.f.j=(r,s)=>{let n=t.o(e,r)?e[r]:void 0;if(0!==n)if(n)s.push(n[1]);else{let i=import("./"+t.u(r)).then(o,o=>{throw 0!==e[r]&&(e[r]=void 0),o});i=Promise.race([i,new Promise(o=>n=e[r]=[o])]),s.push(n[1]=i)}}})(),t(980);
@@ -0,0 +1 @@
1
+ var e={623(e,r,t){const n={"./PluginConfigurationPanel":()=>t.e(172).then(()=>()=>t(172))},o=(e,r)=>(t.R=r,r=t.o(n,e)?n[e]():Promise.resolve().then(()=>{throw new Error('Module "'+e+'" does not exist in container.')}),t.R=void 0,r),i=(e,r)=>{if(!t.S)return;const n="default",o=t.S[n];if(o&&o!==e)throw new Error("Container initialization failed as it has already been initialized with a different share scope");return t.S[n]=e,t.I(n,r)};t.d(r,{get:()=>o,init:()=>i})}};const r={};function t(n){const o=r[n];if(void 0!==o)return o.exports;const i=r[n]={exports:{}};return e[n](i,i.exports,t),i.exports}t.m=e,t.c=r,t.d=(e,r)=>{if(Array.isArray(r))for(var n=0;n<r.length;){var o=r[n++],i=r[n++];t.o(e,o)?0===i&&n++:0===i?Object.defineProperty(e,o,{enumerable:!0,value:r[n++]}):Object.defineProperty(e,o,{enumerable:!0,get:i})}else for(var o in r)t.o(r,o)&&!t.o(e,o)&&Object.defineProperty(e,o,{enumerable:!0,get:r[o]})},t.f={},t.e=e=>Promise.all(Object.keys(t.f).reduce((r,n)=>(t.f[n](e,r),r),[])),t.u=e=>e+".mjs",t.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),t.r=e=>{Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},(()=>{t.S={};const e={},r={};t.I=(n,o)=>{o||(o=[]);let i=r[n];if(i||(i=r[n]={}),o.indexOf(i)>=0)return;if(o.push(i),e[n])return e[n];t.o(t.S,n)||(t.S[n]={});const s=t.S[n],a="signalk-openwakeword",u=[];return"default"===n&&((e,r,n,o)=>{const i=s[e]=s[e]||{},u=i[r];(!u||!u.loaded&&(1!=!u.eager?o:a>u.from))&&(i[r]={get:()=>t.e(540).then(()=>()=>t(540)),from:a,eager:!1})})("react","19.2.8"),e[n]=u.length?Promise.all(u).then(()=>e[n]=1):1}})(),(()=>{var e=e=>{var r=e=>e.split(".").map(e=>+e==e?+e:e),t=/^([^-+]+)?(?:-([^+]+))?(?:\+(.+))?$/.exec(e),n=t[1]?r(t[1]):[];return t[2]&&(n.length++,n.push.apply(n,r(t[2]))),t[3]&&(n.push([]),n.push.apply(n,r(t[3]))),n},r=e=>{var t=e[0],n="";if(1===e.length)return"*";if(t+.5){n+=0==t?">=":-1==t?"<":1==t?"^":2==t?"~":t>0?"=":"!=";for(var o=1,i=1;i<e.length;i++)o--,n+="u"==(typeof(a=e[i]))[0]?"-":(o>0?".":"")+(o=2,a);return n}var s=[];for(i=1;i<e.length;i++){var a=e[i];s.push(0===a?"not("+u()+")":1===a?"("+u()+" || "+u()+")":2===a?s.pop()+" "+s.pop():r(a))}return u();function u(){return s.pop().replace(/^\((.+)\)$/,"$1")}},n=(r,t)=>{if(0 in r){t=e(t);var o=r[0],i=o<0;i&&(o=-o-1);for(var s=0,a=1,u=!0;;a++,s++){var f,l,c=a<r.length?(typeof r[a])[0]:"";if(s>=t.length||"o"==(l=(typeof(f=t[s]))[0]))return!u||("u"==c?a>o&&!i:""==c!=i);if("u"==l){if(!u||"u"!=c)return!1}else if(u)if(c==l)if(a<=o){if(f!=r[a])return!1}else{if(i?f>r[a]:f<r[a])return!1;f!=r[a]&&(u=!1)}else if("s"!=c&&"n"!=c){if(i||a<=o)return!1;u=!1,a--}else{if(a<=o||l<c!=i)return!1;u=!1}else"s"!=c&&"n"!=c&&(u=!1,a--)}}var p=[],d=p.pop.bind(p);for(s=1;s<r.length;s++){var h=r[s];p.push(1==h?d()|d():2==h?d()&d():h?n(h,t):!d())}return!!d()};const o=(r,t,n)=>{const o=n?(e=>Object.keys(e).reduce((r,t)=>(e[t].eager&&(r[t]=e[t]),r),{}))(r[t]):r[t];return Object.keys(o).reduce((r,t)=>!r||!o[r].loaded&&((r,t)=>{r=e(r),t=e(t);for(var n=0;;){if(n>=r.length)return n<t.length&&"u"!=(typeof t[n])[0];var o=r[n],i=(typeof o)[0];if(n>=t.length)return"u"==i;var s=t[n],a=(typeof s)[0];if(i!=a)return"o"==i&&"n"==a||"s"==a||"u"==i;if("o"!=i&&"u"!=i&&o!=s)return o<s;n++}})(r,t)?t:r,0)},i=(e,r,t)=>t?t():((e,r)=>(e=>{throw new Error(e)})("Shared module "+r+" doesn't exist in shared scope "+e))(e,r),s=(e=>function(r,n,o,i,s){const a=t.I(r);return a?.then&&!o?a.then(e.bind(e,r,t.S[r],n,!1,i,s)):e(r,t.S[r],n,o,i,s)})((e,s,a,u,f,l)=>{if(!((e,r)=>e&&t.o(e,r))(s,a))return i(e,a,l);const c=o(s,a,u);return n(f,c)||(d=((e,t,n,o)=>"Unsatisfied version "+n+" from "+(n&&e[t][n].from)+" of shared singleton module "+t+" (required "+r(o)+")")(s,a,c,f),"undefined"!=typeof console&&console.warn&&console.warn(d)),(p=s[a][c]).loaded=1,p.get();var p,d}),a={},u={231:()=>s("default","react",!1,[1,19],()=>t.e(540).then(()=>()=>t(540)))},f={172:[231]},l={};t.f.consumes=(e,r)=>{t.o(f,e)&&f[e].forEach(e=>{if(t.o(a,e))return r.push(a[e]);if(!l[e]){const n=r=>{a[e]=0,t.m[e]=n=>{delete t.c[e],n.exports=r()}};l[e]=!0;const o=r=>{delete a[e],t.m[e]=n=>{throw delete t.c[e],r}};try{const t=u[e]();t.then?r.push(a[e]=t.then(n).catch(o)):n(t)}catch(e){o(e)}}})}})(),(()=>{const e={393:0},r=r=>{let{__webpack_esm_ids__:n,__webpack_esm_modules__:o,__webpack_esm_runtime__:i}=r;var s,a,u=0;for(s in o)t.o(o,s)&&(t.m[s]=o[s]);for(i&&i(t);u<n.length;u++)a=n[u],t.o(e,a)&&e[a]&&e[a][0](),e[n[u]]=0};t.f.j=(n,o)=>{let i=t.o(e,n)?e[n]:void 0;if(0!==i)if(i)o.push(i[1]);else{let s=import("./"+t.u(n)).then(r,r=>{throw 0!==e[n]&&(e[n]=void 0),r});s=Promise.race([s,new Promise(r=>i=e[n]=[r])]),o.push(i[1]=s)}}})();let n=t(623);const o=n.get,i=n.init;export{o as get,i as init};