signalk-piper 0.0.1 → 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 +88 -8
- package/dist/config.d.ts +127 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +208 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +43 -0
- package/dist/index.js.map +1 -0
- package/dist/service.d.ts +142 -0
- package/dist/service.d.ts.map +1 -0
- package/dist/service.js +596 -0
- package/dist/service.js.map +1 -0
- package/dist/wyoming.d.ts +29 -0
- package/dist/wyoming.d.ts.map +1 -0
- package/dist/wyoming.js +124 -0
- package/dist/wyoming.js.map +1 -0
- package/package.json +63 -5
- package/public/172.mjs +1 -0
- package/public/540.mjs +2 -0
- package/public/540.mjs.LICENSE.txt +9 -0
- package/public/main.mjs +1 -0
- package/public/remoteEntry.js +1 -0
- package/index.js +0 -23
package/README.md
CHANGED
|
@@ -1,15 +1,95 @@
|
|
|
1
1
|
# signalk-piper
|
|
2
2
|
|
|
3
|
-
> **Status:
|
|
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 doesn't.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
**signalk-piper gives your Signal K server a voice.** It runs [Piper](https://github.com/rhasspy/wyoming-piper), a fast, natural-sounding text-to-speech engine, in a container that the plugin installs, configures, and monitors for you. Once a voice is downloaded, everything runs fully offline — no cloud, no internet needed at sea.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
|
10
|
-
|
|
|
11
|
-
|
|
|
12
|
-
| [signalk-
|
|
7
|
+
It is the speaking half of the [signalk-wyoming](https://github.com/hoeken/signalk-wyoming) voice assistant family: install it alongside the orchestrator and the plugins below for spoken announcements and a full on-boat voice assistant. It also works standalone — anything that speaks the [Wyoming protocol](https://github.com/rhasspy/wyoming), including Home Assistant, can use it directly.
|
|
8
|
+
|
|
9
|
+
| Plugin | Role |
|
|
10
|
+
| ---------------------------------------------------------------------- | --------------------------------------------------- |
|
|
11
|
+
| [signalk-wyoming](https://github.com/hoeken/signalk-wyoming) | Orchestrator — family spec and discussion live here |
|
|
12
|
+
| [signalk-whisper](https://github.com/hoeken/signalk-whisper) | Speech-to-text |
|
|
13
|
+
| **signalk-piper** | Text-to-speech |
|
|
14
|
+
| [signalk-openwakeword](https://github.com/hoeken/signalk-openwakeword) | Wake word detection |
|
|
15
|
+
|
|
16
|
+
## Requirements
|
|
17
|
+
|
|
18
|
+
- The **[signalk-container](https://www.npmjs.com/package/signalk-container)** plugin with a working podman or docker runtime — install and enable it first.
|
|
19
|
+
- **Node.js ≥ 24** (Signal K server ≥ 2.x).
|
|
20
|
+
- **RAM:** about 150 MB while a voice is loaded; the container is capped at 512 MB by default. Piper plus the orchestrator run comfortably alongside Signal K on a Pi 4 with 2 GB.
|
|
21
|
+
- **Disk:** roughly 400 MB for the container image, plus ~60 MB per voice. Images are available for `linux/amd64` and `linux/arm64` (no 32-bit armv7).
|
|
22
|
+
|
|
23
|
+
## Install
|
|
24
|
+
|
|
25
|
+
1. Install and enable **signalk-container** (Appstore → Available).
|
|
26
|
+
2. Install **signalk-piper** from the App Store (search "piper").
|
|
27
|
+
3. Enable it in Server → Plugin Config.
|
|
28
|
+
|
|
29
|
+
### First start needs internet
|
|
30
|
+
|
|
31
|
+
On first start the container downloads your chosen voice (**~60 MB**) from HuggingFace. The plugin status shows _"Starting — first start downloads ~60 MB per voice"_ until the service is actually up and answering. The voice is stored in the plugin's data directory, so it survives restarts, container recreation, and image updates.
|
|
32
|
+
|
|
33
|
+
**Do the first start at the dock, not at sea.** After the download, everything runs fully offline. The same applies to every voice you plan to use: each one downloads on first use, so pre-download any voice you rely on while you still have internet.
|
|
34
|
+
|
|
35
|
+
## Configuration
|
|
36
|
+
|
|
37
|
+
The plugin ships a graphical configuration panel (Server → Plugin Config → Piper TTS) with a live container status card, a one-click image update check/apply, a version dropdown fed by Docker Hub, and all the settings below — with inline warnings if you pick an unsupported voice or open the service to the network. On servers without custom-panel support you get a plain settings form with the same options.
|
|
38
|
+
|
|
39
|
+
| Setting | Default | Description |
|
|
40
|
+
| -------------------------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
41
|
+
| `voice` | `en_US-lessac-medium` | The Piper voice to load and serve. See [Choosing a voice](#choosing-a-voice). |
|
|
42
|
+
| `imageTag` | `auto` | Version of the `rhasspy/wyoming-piper` image to run. `auto` runs the pinned, tested release (**2.3.1**) and follows plugin updates; set an explicit tag to pin a version. |
|
|
43
|
+
| `port` | `10200` | Host port for the Wyoming TTS service. Leave it alone unless it collides with something else. |
|
|
44
|
+
| `advanced.bind` | `127.0.0.1` | `127.0.0.1`: only Signal K can reach the service (recommended). `0.0.0.0`: share it with other machines on the network (see [Security](#security)). |
|
|
45
|
+
| `advanced.memoryLimit` | `512m` | Hard memory cap for the container. |
|
|
46
|
+
| `advanced.restartPolicy` | `unless-stopped` | Container restart policy (`no` / `unless-stopped` / `always`). |
|
|
47
|
+
| `advanced.updateVoices` | `false` | Refresh the list of available voices from HuggingFace each time the container starts (needs internet; off by default for offline-first boats). |
|
|
48
|
+
| `advanced.sentenceSilence` | _(unset)_ | Seconds of silence added after each sentence. |
|
|
49
|
+
| `advanced.lengthScale` | _(unset)_ | Speech speed tuning; values above 1 slow speech down. |
|
|
50
|
+
|
|
51
|
+
### Choosing a voice
|
|
52
|
+
|
|
53
|
+
Browse and listen to voices at [rhasspy/piper-voices](https://huggingface.co/rhasspy/piper-voices). **Stick to `-medium` or `-high` quality voices** — voices ending in `-low` or `-x_low` use a lower sample rate (16 kHz) that the voice assistant family doesn't support; the plugin warns if you pick one but won't stop you. Each voice is a ~60 MB download on first use.
|
|
54
|
+
|
|
55
|
+
## Using the service
|
|
56
|
+
|
|
57
|
+
The service is a standard Wyoming TTS endpoint at `tcp://<host>:10200`.
|
|
58
|
+
|
|
59
|
+
- **With signalk-wyoming:** nothing to set up — the orchestrator discovers the service automatically and uses it for all spoken output.
|
|
60
|
+
- **With Home Assistant** (standalone): set `advanced.bind` to `0.0.0.0`, then add a _Wyoming Protocol_ integration pointing at `tcp://<boat-ip>:10200`.
|
|
61
|
+
- **Any other Wyoming client** works the same way. Clients may request a different voice per request; a voice that was never downloaded triggers a download mid-request, so pre-download the voices you use.
|
|
62
|
+
|
|
63
|
+
## Status, health, and alarms
|
|
64
|
+
|
|
65
|
+
The plugin doesn't just start the container and hope — it checks that the service really answers before reporting ready, and keeps checking every 30 seconds after that.
|
|
66
|
+
|
|
67
|
+
If the service stops answering (three failed checks in a row, about 90 seconds), the plugin raises the Signal K notification **`notifications.voice.piper`** (`state: "alarm"`) and shows an error in Plugin Config. When the service comes back, the alarm clears itself (`state: "normal"`) — no action needed. Any dashboard or alerting setup that watches Signal K notifications can subscribe to this path.
|
|
68
|
+
|
|
69
|
+
## HTTP API
|
|
70
|
+
|
|
71
|
+
The plugin serves a small REST API under `/plugins/signalk-piper`:
|
|
72
|
+
|
|
73
|
+
| Endpoint | Method | Access | Returns |
|
|
74
|
+
| ------------------- | ------ | -------- | ------------------------------------------------------------------------------------------- |
|
|
75
|
+
| `/api/status` | GET | readonly | `{ status, uri, tag, containerState, lastHealth, info }` — live service and container state |
|
|
76
|
+
| `/api/versions` | GET | readonly | Available `rhasspy/wyoming-piper` releases from Docker Hub, newest first |
|
|
77
|
+
| `/api/update/check` | GET | admin | Whether a newer container image is available |
|
|
78
|
+
| `/api/update/apply` | POST | admin | Pulls and switches to a new image version |
|
|
79
|
+
|
|
80
|
+
## Security
|
|
81
|
+
|
|
82
|
+
Wyoming has **no authentication** — anyone who can reach the port can use the service. The default `bind: 127.0.0.1` keeps it reachable only from the Signal K host, which is right for almost everyone; leave it that way unless another machine genuinely needs TTS. If you do set `bind: 0.0.0.0` (e.g. to share with Home Assistant), firewall the port at the boat router or keep it on a trusted network. See the [family spec's security section](https://github.com/hoeken/signalk-wyoming) for the full picture — the satellite microphone ports are the truly sensitive ones; a TTS port mostly risks someone making your boat talk.
|
|
83
|
+
|
|
84
|
+
## Troubleshooting
|
|
85
|
+
|
|
86
|
+
- **Stuck on "Starting"** — the first start of a voice is a ~60 MB download and can take a while on a slow connection; the plugin waits up to 10 minutes. No internet, no voice: connect and restart the plugin.
|
|
87
|
+
- **`notifications.voice.piper` alarm** — the container stopped answering. Check the container status in the config panel or `/api/status`; the alarm clears automatically once the service recovers.
|
|
88
|
+
- **Speech too fast/slow or sentences run together** — tune `advanced.lengthScale` and `advanced.sentenceSilence`.
|
|
89
|
+
|
|
90
|
+
## Development
|
|
91
|
+
|
|
92
|
+
Build instructions, architecture notes, and the internals (readiness gating, health checks, discovery emissions, the config panel) live in [DEVELOPMENT.md](DEVELOPMENT.md).
|
|
13
93
|
|
|
14
94
|
## License
|
|
15
95
|
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration schema, defaults and container-config construction for
|
|
3
|
+
* signalk-piper.
|
|
4
|
+
*
|
|
5
|
+
* Signal K does NOT seed schema defaults into the config it hands to
|
|
6
|
+
* `start()` — `resolveSettings()` merges user config over `DEFAULT_SETTINGS`.
|
|
7
|
+
* `buildContainerConfig()` must stay a pure, deterministic function of
|
|
8
|
+
* (settings, tag): signalk-container diffs the produced config against the
|
|
9
|
+
* live container to decide whether a recreate is needed.
|
|
10
|
+
*/
|
|
11
|
+
import type { ContainerConfig } from "signalk-container-helper";
|
|
12
|
+
/** Upstream image (repo without tag). */
|
|
13
|
+
export declare const IMAGE = "rhasspy/wyoming-piper";
|
|
14
|
+
/** Pinned upstream release that `imageTag: "auto"` resolves to. */
|
|
15
|
+
export declare const PINNED_TAG = "2.3.1";
|
|
16
|
+
/** Wyoming TCP port piper listens on inside the container (image default). */
|
|
17
|
+
export declare const DEFAULT_PORT = 10200;
|
|
18
|
+
/** Approximate first-start download per voice (README + status hint). */
|
|
19
|
+
export declare const DOWNLOAD_HINT = "~60 MB per voice";
|
|
20
|
+
export interface PiperAdvancedSettings {
|
|
21
|
+
/**
|
|
22
|
+
* "127.0.0.1": Signal K-only networking via signalkAccessiblePorts.
|
|
23
|
+
* "0.0.0.0": publish the port on all interfaces (LAN-reachable).
|
|
24
|
+
*/
|
|
25
|
+
bind: "127.0.0.1" | "0.0.0.0";
|
|
26
|
+
memoryLimit: string;
|
|
27
|
+
restartPolicy: "no" | "unless-stopped" | "always";
|
|
28
|
+
updateVoices: boolean;
|
|
29
|
+
sentenceSilence?: number;
|
|
30
|
+
lengthScale?: number;
|
|
31
|
+
}
|
|
32
|
+
export interface PiperSettings {
|
|
33
|
+
imageTag: string;
|
|
34
|
+
voice: string;
|
|
35
|
+
port: number;
|
|
36
|
+
advanced: PiperAdvancedSettings;
|
|
37
|
+
}
|
|
38
|
+
export declare const DEFAULT_SETTINGS: PiperSettings;
|
|
39
|
+
/** Merge raw plugin config (possibly partial/undefined) over the defaults. */
|
|
40
|
+
export declare function resolveSettings(raw: unknown): PiperSettings;
|
|
41
|
+
/** `imageTag: "auto"` → the pinned upstream release; anything else verbatim. */
|
|
42
|
+
export declare function resolveTag(requested: string): string;
|
|
43
|
+
/** True for plain numeric semver tags like "2.3.1" (update-check filter). */
|
|
44
|
+
export declare function isSemverTag(tag: string): boolean;
|
|
45
|
+
/** True for 16 kHz voices, which the v1 family spec excludes (22.05 kHz only). */
|
|
46
|
+
export declare function isLowSampleRateVoice(voice: string): boolean;
|
|
47
|
+
/**
|
|
48
|
+
* `docker run` arguments appended to the image ENTRYPOINT (which already
|
|
49
|
+
* bakes `--uri tcp://0.0.0.0:10200 --data-dir /data`).
|
|
50
|
+
*/
|
|
51
|
+
export declare function buildCommand(settings: PiperSettings): string[];
|
|
52
|
+
/** Pure (settings, tag) → ContainerConfig. */
|
|
53
|
+
export declare function buildContainerConfig(settings: PiperSettings, tag: string): ContainerConfig;
|
|
54
|
+
/** JSON schema for the Signal K plugin-config UI. */
|
|
55
|
+
export declare const CONFIG_SCHEMA: {
|
|
56
|
+
readonly type: "object";
|
|
57
|
+
readonly properties: {
|
|
58
|
+
readonly voice: {
|
|
59
|
+
readonly type: "string";
|
|
60
|
+
readonly title: "Voice";
|
|
61
|
+
readonly default: string;
|
|
62
|
+
readonly description: string;
|
|
63
|
+
};
|
|
64
|
+
readonly imageTag: {
|
|
65
|
+
readonly type: "string";
|
|
66
|
+
readonly title: "Image tag";
|
|
67
|
+
readonly default: string;
|
|
68
|
+
readonly description: string;
|
|
69
|
+
};
|
|
70
|
+
readonly port: {
|
|
71
|
+
readonly type: "number";
|
|
72
|
+
readonly title: "Port";
|
|
73
|
+
readonly default: number;
|
|
74
|
+
readonly description: string;
|
|
75
|
+
};
|
|
76
|
+
readonly advanced: {
|
|
77
|
+
readonly type: "object";
|
|
78
|
+
readonly title: "Advanced";
|
|
79
|
+
readonly properties: {
|
|
80
|
+
readonly bind: {
|
|
81
|
+
readonly type: "string";
|
|
82
|
+
readonly title: "Bind address";
|
|
83
|
+
readonly enum: readonly ["127.0.0.1", "0.0.0.0"];
|
|
84
|
+
readonly default: "127.0.0.1" | "0.0.0.0";
|
|
85
|
+
readonly description: string;
|
|
86
|
+
};
|
|
87
|
+
readonly memoryLimit: {
|
|
88
|
+
readonly type: "string";
|
|
89
|
+
readonly title: "Memory limit";
|
|
90
|
+
readonly default: string;
|
|
91
|
+
readonly description: "Hard container memory cap (swap disabled), e.g. \"512m\" or \"1g\".";
|
|
92
|
+
};
|
|
93
|
+
readonly restartPolicy: {
|
|
94
|
+
readonly type: "string";
|
|
95
|
+
readonly title: "Restart policy";
|
|
96
|
+
readonly enum: readonly ["no", "unless-stopped", "always"];
|
|
97
|
+
readonly default: "no" | "unless-stopped" | "always";
|
|
98
|
+
readonly description: "Container restart policy.";
|
|
99
|
+
};
|
|
100
|
+
readonly updateVoices: {
|
|
101
|
+
readonly type: "boolean";
|
|
102
|
+
readonly title: "Update voices list on start";
|
|
103
|
+
readonly default: boolean;
|
|
104
|
+
readonly description: string;
|
|
105
|
+
};
|
|
106
|
+
readonly sentenceSilence: {
|
|
107
|
+
readonly type: "number";
|
|
108
|
+
readonly title: "Sentence silence (seconds)";
|
|
109
|
+
readonly description: "Optional seconds of silence appended after each sentence.";
|
|
110
|
+
};
|
|
111
|
+
readonly lengthScale: {
|
|
112
|
+
readonly type: "number";
|
|
113
|
+
readonly title: "Length scale";
|
|
114
|
+
readonly description: "Optional phoneme length scale; values above 1 slow speech down.";
|
|
115
|
+
};
|
|
116
|
+
};
|
|
117
|
+
};
|
|
118
|
+
};
|
|
119
|
+
};
|
|
120
|
+
export declare const UI_SCHEMA: {
|
|
121
|
+
readonly advanced: {
|
|
122
|
+
readonly "ui:options": {
|
|
123
|
+
readonly collapsible: true;
|
|
124
|
+
};
|
|
125
|
+
};
|
|
126
|
+
};
|
|
127
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAEhE,yCAAyC;AACzC,eAAO,MAAM,KAAK,0BAA0B,CAAC;AAE7C,mEAAmE;AACnE,eAAO,MAAM,UAAU,UAAU,CAAC;AAElC,8EAA8E;AAC9E,eAAO,MAAM,YAAY,QAAQ,CAAC;AAElC,yEAAyE;AACzE,eAAO,MAAM,aAAa,qBAAqB,CAAC;AAEhD,MAAM,WAAW,qBAAqB;IACpC;;;OAGG;IACH,IAAI,EAAE,WAAW,GAAG,SAAS,CAAC;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,IAAI,GAAG,gBAAgB,GAAG,QAAQ,CAAC;IAClD,YAAY,EAAE,OAAO,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,qBAAqB,CAAC;CACjC;AAED,eAAO,MAAM,gBAAgB,EAAE,aAU9B,CAAC;AAEF,8EAA8E;AAC9E,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,GAAG,aAAa,CAyB3D;AAED,gFAAgF;AAChF,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,6EAA6E;AAC7E,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEhD;AAED,kFAAkF;AAClF,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAE3D;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,aAAa,GAAG,MAAM,EAAE,CAkB9D;AAED,8CAA8C;AAC9C,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,aAAa,EACvB,GAAG,EAAE,MAAM,GACV,eAAe,CAyBjB;AAED,qDAAqD;AACrD,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmFhB,CAAC;AAEX,eAAO,MAAM,SAAS;;;;;;CAIZ,CAAC"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration schema, defaults and container-config construction for
|
|
3
|
+
* signalk-piper.
|
|
4
|
+
*
|
|
5
|
+
* Signal K does NOT seed schema defaults into the config it hands to
|
|
6
|
+
* `start()` — `resolveSettings()` merges user config over `DEFAULT_SETTINGS`.
|
|
7
|
+
* `buildContainerConfig()` must stay a pure, deterministic function of
|
|
8
|
+
* (settings, tag): signalk-container diffs the produced config against the
|
|
9
|
+
* live container to decide whether a recreate is needed.
|
|
10
|
+
*/
|
|
11
|
+
/** Upstream image (repo without tag). */
|
|
12
|
+
export const IMAGE = "rhasspy/wyoming-piper";
|
|
13
|
+
/** Pinned upstream release that `imageTag: "auto"` resolves to. */
|
|
14
|
+
export const PINNED_TAG = "2.3.1";
|
|
15
|
+
/** Wyoming TCP port piper listens on inside the container (image default). */
|
|
16
|
+
export const DEFAULT_PORT = 10200;
|
|
17
|
+
/** Approximate first-start download per voice (README + status hint). */
|
|
18
|
+
export const DOWNLOAD_HINT = "~60 MB per voice";
|
|
19
|
+
export const DEFAULT_SETTINGS = {
|
|
20
|
+
imageTag: "auto",
|
|
21
|
+
voice: "en_US-lessac-medium",
|
|
22
|
+
port: DEFAULT_PORT,
|
|
23
|
+
advanced: {
|
|
24
|
+
bind: "127.0.0.1",
|
|
25
|
+
memoryLimit: "512m",
|
|
26
|
+
restartPolicy: "unless-stopped",
|
|
27
|
+
updateVoices: false,
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
/** Merge raw plugin config (possibly partial/undefined) over the defaults. */
|
|
31
|
+
export function resolveSettings(raw) {
|
|
32
|
+
const cfg = isObject(raw) ? raw : {};
|
|
33
|
+
const adv = isObject(cfg.advanced) ? cfg.advanced : {};
|
|
34
|
+
const settings = {
|
|
35
|
+
imageTag: str(cfg.imageTag) ?? DEFAULT_SETTINGS.imageTag,
|
|
36
|
+
voice: str(cfg.voice) ?? DEFAULT_SETTINGS.voice,
|
|
37
|
+
port: num(cfg.port) ?? DEFAULT_SETTINGS.port,
|
|
38
|
+
advanced: {
|
|
39
|
+
bind: adv.bind === "0.0.0.0" ? "0.0.0.0" : "127.0.0.1",
|
|
40
|
+
memoryLimit: str(adv.memoryLimit) ?? DEFAULT_SETTINGS.advanced.memoryLimit,
|
|
41
|
+
restartPolicy: adv.restartPolicy === "no" || adv.restartPolicy === "always"
|
|
42
|
+
? adv.restartPolicy
|
|
43
|
+
: DEFAULT_SETTINGS.advanced.restartPolicy,
|
|
44
|
+
updateVoices: adv.updateVoices === true,
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
const sentenceSilence = num(adv.sentenceSilence);
|
|
48
|
+
if (sentenceSilence !== undefined) {
|
|
49
|
+
settings.advanced.sentenceSilence = sentenceSilence;
|
|
50
|
+
}
|
|
51
|
+
const lengthScale = num(adv.lengthScale);
|
|
52
|
+
if (lengthScale !== undefined)
|
|
53
|
+
settings.advanced.lengthScale = lengthScale;
|
|
54
|
+
return settings;
|
|
55
|
+
}
|
|
56
|
+
/** `imageTag: "auto"` → the pinned upstream release; anything else verbatim. */
|
|
57
|
+
export function resolveTag(requested) {
|
|
58
|
+
return requested === "auto" ? PINNED_TAG : requested;
|
|
59
|
+
}
|
|
60
|
+
/** True for plain numeric semver tags like "2.3.1" (update-check filter). */
|
|
61
|
+
export function isSemverTag(tag) {
|
|
62
|
+
return /^\d+\.\d+\.\d+$/.test(tag);
|
|
63
|
+
}
|
|
64
|
+
/** True for 16 kHz voices, which the v1 family spec excludes (22.05 kHz only). */
|
|
65
|
+
export function isLowSampleRateVoice(voice) {
|
|
66
|
+
return voice.endsWith("-low") || voice.endsWith("-x_low");
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* `docker run` arguments appended to the image ENTRYPOINT (which already
|
|
70
|
+
* bakes `--uri tcp://0.0.0.0:10200 --data-dir /data`).
|
|
71
|
+
*/
|
|
72
|
+
export function buildCommand(settings) {
|
|
73
|
+
const command = ["--voice", settings.voice];
|
|
74
|
+
if (settings.advanced.updateVoices)
|
|
75
|
+
command.push("--update-voices");
|
|
76
|
+
if (settings.advanced.sentenceSilence !== undefined) {
|
|
77
|
+
command.push("--sentence-silence", String(settings.advanced.sentenceSilence));
|
|
78
|
+
}
|
|
79
|
+
if (settings.advanced.lengthScale !== undefined) {
|
|
80
|
+
command.push("--length-scale", String(settings.advanced.lengthScale));
|
|
81
|
+
}
|
|
82
|
+
if (settings.port !== DEFAULT_PORT) {
|
|
83
|
+
// --uri is last-wins in upstream argparse, so this overrides the baked-in
|
|
84
|
+
// port and makes the container actually listen on the configured port.
|
|
85
|
+
command.push("--uri", `tcp://0.0.0.0:${settings.port}`);
|
|
86
|
+
}
|
|
87
|
+
return command;
|
|
88
|
+
}
|
|
89
|
+
/** Pure (settings, tag) → ContainerConfig. */
|
|
90
|
+
export function buildContainerConfig(settings, tag) {
|
|
91
|
+
const config = {
|
|
92
|
+
image: IMAGE,
|
|
93
|
+
tag,
|
|
94
|
+
command: buildCommand(settings),
|
|
95
|
+
// Voice models download into /data on first start; the Signal K data
|
|
96
|
+
// mount survives container recreation.
|
|
97
|
+
signalkDataMount: "/data",
|
|
98
|
+
restart: settings.advanced.restartPolicy,
|
|
99
|
+
resources: {
|
|
100
|
+
memory: settings.advanced.memoryLimit,
|
|
101
|
+
memorySwap: settings.advanced.memoryLimit,
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
if (settings.advanced.bind === "0.0.0.0") {
|
|
105
|
+
// LAN exposure (e.g. sharing the service with Home Assistant): explicit
|
|
106
|
+
// all-interfaces publish. Never combined with signalkAccessiblePorts —
|
|
107
|
+
// the manager would overwrite/drop the manual binding.
|
|
108
|
+
config.ports = { [String(settings.port)]: `0.0.0.0:${settings.port}` };
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
// Default: loopback-only on bare metal, container networking when
|
|
112
|
+
// Signal K itself runs in a container. Only Signal K needs to reach it.
|
|
113
|
+
config.signalkAccessiblePorts = [settings.port];
|
|
114
|
+
}
|
|
115
|
+
return config;
|
|
116
|
+
}
|
|
117
|
+
/** JSON schema for the Signal K plugin-config UI. */
|
|
118
|
+
export const CONFIG_SCHEMA = {
|
|
119
|
+
type: "object",
|
|
120
|
+
properties: {
|
|
121
|
+
voice: {
|
|
122
|
+
type: "string",
|
|
123
|
+
title: "Voice",
|
|
124
|
+
default: DEFAULT_SETTINGS.voice,
|
|
125
|
+
description: "Piper voice to load and serve (e.g. en_US-lessac-medium). Use 22.05 kHz " +
|
|
126
|
+
"voices only — -medium and -high qualities. Voices ending in -low or " +
|
|
127
|
+
"-x_low are 16 kHz and outside the signalk-wyoming v1 audio contract. " +
|
|
128
|
+
"Browse voices at https://huggingface.co/rhasspy/piper-voices. The " +
|
|
129
|
+
"voice (~60 MB) is downloaded on first start.",
|
|
130
|
+
},
|
|
131
|
+
imageTag: {
|
|
132
|
+
type: "string",
|
|
133
|
+
title: "Image tag",
|
|
134
|
+
default: DEFAULT_SETTINGS.imageTag,
|
|
135
|
+
description: `Docker image tag for ${IMAGE}. "auto" runs the pinned, tested ` +
|
|
136
|
+
`release (${PINNED_TAG}) and follows plugin updates. Set an explicit ` +
|
|
137
|
+
"tag only to pin a specific upstream version.",
|
|
138
|
+
},
|
|
139
|
+
port: {
|
|
140
|
+
type: "number",
|
|
141
|
+
title: "Port",
|
|
142
|
+
default: DEFAULT_SETTINGS.port,
|
|
143
|
+
description: "Host port for the Wyoming TTS service. Leave at 10200 unless it " +
|
|
144
|
+
"collides with something else on this machine.",
|
|
145
|
+
},
|
|
146
|
+
advanced: {
|
|
147
|
+
type: "object",
|
|
148
|
+
title: "Advanced",
|
|
149
|
+
properties: {
|
|
150
|
+
bind: {
|
|
151
|
+
type: "string",
|
|
152
|
+
title: "Bind address",
|
|
153
|
+
enum: ["127.0.0.1", "0.0.0.0"],
|
|
154
|
+
default: DEFAULT_SETTINGS.advanced.bind,
|
|
155
|
+
description: "127.0.0.1 (recommended): only Signal K can reach the service. " +
|
|
156
|
+
"0.0.0.0 publishes the port on all interfaces so other machines " +
|
|
157
|
+
"(e.g. a Home Assistant box) can use it — Wyoming has no " +
|
|
158
|
+
"authentication, so firewall accordingly.",
|
|
159
|
+
},
|
|
160
|
+
memoryLimit: {
|
|
161
|
+
type: "string",
|
|
162
|
+
title: "Memory limit",
|
|
163
|
+
default: DEFAULT_SETTINGS.advanced.memoryLimit,
|
|
164
|
+
description: 'Hard container memory cap (swap disabled), e.g. "512m" or "1g".',
|
|
165
|
+
},
|
|
166
|
+
restartPolicy: {
|
|
167
|
+
type: "string",
|
|
168
|
+
title: "Restart policy",
|
|
169
|
+
enum: ["no", "unless-stopped", "always"],
|
|
170
|
+
default: DEFAULT_SETTINGS.advanced.restartPolicy,
|
|
171
|
+
description: "Container restart policy.",
|
|
172
|
+
},
|
|
173
|
+
updateVoices: {
|
|
174
|
+
type: "boolean",
|
|
175
|
+
title: "Update voices list on start",
|
|
176
|
+
default: DEFAULT_SETTINGS.advanced.updateVoices,
|
|
177
|
+
description: "Download the latest voices.json from HuggingFace at container " +
|
|
178
|
+
"start (needs internet; off by default for offline-first boats).",
|
|
179
|
+
},
|
|
180
|
+
sentenceSilence: {
|
|
181
|
+
type: "number",
|
|
182
|
+
title: "Sentence silence (seconds)",
|
|
183
|
+
description: "Optional seconds of silence appended after each sentence.",
|
|
184
|
+
},
|
|
185
|
+
lengthScale: {
|
|
186
|
+
type: "number",
|
|
187
|
+
title: "Length scale",
|
|
188
|
+
description: "Optional phoneme length scale; values above 1 slow speech down.",
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
export const UI_SCHEMA = {
|
|
195
|
+
advanced: {
|
|
196
|
+
"ui:options": { collapsible: true },
|
|
197
|
+
},
|
|
198
|
+
};
|
|
199
|
+
function isObject(v) {
|
|
200
|
+
return v !== null && typeof v === "object" && !Array.isArray(v);
|
|
201
|
+
}
|
|
202
|
+
function str(v) {
|
|
203
|
+
return typeof v === "string" && v.length > 0 ? v : undefined;
|
|
204
|
+
}
|
|
205
|
+
function num(v) {
|
|
206
|
+
return typeof v === "number" && Number.isFinite(v) ? v : undefined;
|
|
207
|
+
}
|
|
208
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* signalk-piper — Piper text-to-speech (Wyoming protocol) as a Signal K
|
|
3
|
+
* managed container plugin.
|
|
4
|
+
*
|
|
5
|
+
* Runs rhasspy/wyoming-piper via the signalk-container plugin, gates
|
|
6
|
+
* readiness on a real Wyoming `describe`/`info` exchange, health-checks the
|
|
7
|
+
* service, and advertises it on the `wyoming-service` PropertyValues
|
|
8
|
+
* convention for the signalk-wyoming orchestrator (or any other consumer).
|
|
9
|
+
*/
|
|
10
|
+
import type { Plugin, ServerAPI } from "@signalk/server-api";
|
|
11
|
+
export default function createPlugin(app: ServerAPI): Plugin;
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAK7D,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,GAAG,EAAE,SAAS,GAAG,MAAM,CAyC3D"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* signalk-piper — Piper text-to-speech (Wyoming protocol) as a Signal K
|
|
3
|
+
* managed container plugin.
|
|
4
|
+
*
|
|
5
|
+
* Runs rhasspy/wyoming-piper via the signalk-container plugin, gates
|
|
6
|
+
* readiness on a real Wyoming `describe`/`info` exchange, health-checks the
|
|
7
|
+
* service, and advertises it on the `wyoming-service` PropertyValues
|
|
8
|
+
* convention for the signalk-wyoming orchestrator (or any other consumer).
|
|
9
|
+
*/
|
|
10
|
+
import { CONFIG_SCHEMA, UI_SCHEMA } from "./config.js";
|
|
11
|
+
import { PLUGIN_ID, PiperService } from "./service.js";
|
|
12
|
+
export default function createPlugin(app) {
|
|
13
|
+
// One PiperService per server process: Express routers cannot be
|
|
14
|
+
// deregistered, so routes must keep pointing at a live object across
|
|
15
|
+
// plugin stop/start cycles.
|
|
16
|
+
let service = null;
|
|
17
|
+
let lastRawConfig;
|
|
18
|
+
const getService = () => {
|
|
19
|
+
service ??= new PiperService(app);
|
|
20
|
+
return service;
|
|
21
|
+
};
|
|
22
|
+
const plugin = {
|
|
23
|
+
id: PLUGIN_ID,
|
|
24
|
+
name: "Piper TTS (Wyoming)",
|
|
25
|
+
description: "Piper text-to-speech in a managed container (Wyoming protocol) — " +
|
|
26
|
+
"spoken announcements for the signalk-wyoming voice assistant family, " +
|
|
27
|
+
"or standalone for Home Assistant and other Wyoming clients.",
|
|
28
|
+
schema: () => CONFIG_SCHEMA,
|
|
29
|
+
uiSchema: () => UI_SCHEMA,
|
|
30
|
+
start(config) {
|
|
31
|
+
lastRawConfig = config;
|
|
32
|
+
getService().start(config);
|
|
33
|
+
},
|
|
34
|
+
stop() {
|
|
35
|
+
return service?.stop() ?? Promise.resolve();
|
|
36
|
+
},
|
|
37
|
+
registerWithRouter(router) {
|
|
38
|
+
getService().registerRoutes(router, () => lastRawConfig);
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
return plugin;
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,SAAS,EAAE,YAAY,EAAmB,MAAM,cAAc,CAAC;AAExE,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,GAAc;IACjD,iEAAiE;IACjE,qEAAqE;IACrE,4BAA4B;IAC5B,IAAI,OAAO,GAAwB,IAAI,CAAC;IACxC,IAAI,aAAsB,CAAC;IAE3B,MAAM,UAAU,GAAG,GAAiB,EAAE;QACpC,OAAO,KAAK,IAAI,YAAY,CAAC,GAA4B,CAAC,CAAC;QAC3D,OAAO,OAAO,CAAC;IACjB,CAAC,CAAC;IAEF,MAAM,MAAM,GAAW;QACrB,EAAE,EAAE,SAAS;QACb,IAAI,EAAE,qBAAqB;QAC3B,WAAW,EACT,mEAAmE;YACnE,uEAAuE;YACvE,6DAA6D;QAE/D,MAAM,EAAE,GAAG,EAAE,CAAC,aAAkC;QAChD,QAAQ,EAAE,GAAG,EAAE,CAAC,SAA8B;QAE9C,KAAK,CAAC,MAAc;YAClB,aAAa,GAAG,MAAM,CAAC;YACvB,UAAU,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAC7B,CAAC;QAED,IAAI;YACF,OAAO,OAAO,EAAE,IAAI,EAAE,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QAC9C,CAAC;QAED,kBAAkB,CAAC,MAAM;YACvB,UAAU,EAAE,CAAC,cAAc,CACzB,MAA+B,EAC/B,GAAG,EAAE,CAAC,aAAa,CACpB,CAAC;QACJ,CAAC;KACF,CAAC;IAEF,OAAO,MAAM,CAAC;AAChB,CAAC"}
|