feat(pine): 1.3.0 manifests — openwakeword member + live node status on the launcher page
Demo images / Build & push demo images (push) Failing after 1m40s
Demo images / Build & push demo images (push) Failing after 1m40s
pine-openwakeword manifest (wyoming-openwakeword 2.1.0, :10400, /custom model dir for the future Yo Archy model). Pine 1.3.0: launcher page gains a live node-status card fed by /api/pine/status via a same-origin nginx proxy, and copy for the new intents / Claude fallback / mesh announcements. Catalogs, app-session config, drift ids regenerated; release catalog embeds 56 manifests (unsigned until the ceremony). Framework PT test plan in docs/. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
e074a117d2
commit
184257390d
@@ -0,0 +1,70 @@
|
||||
app:
|
||||
id: pine-openwakeword
|
||||
name: Pine Wake Word (openWakeWord)
|
||||
version: "2.1.0"
|
||||
description: Wyoming-protocol openWakeWord wake-word engine. Internal Pine voice-assistant stack member — lets Assist pipelines run wake-word detection on the node (groundwork for the custom "Yo Archy" wake word; stock models like "ok nabu" ship with the image).
|
||||
category: home
|
||||
|
||||
# Hyphen name matches the runtime references (stack member table / startup
|
||||
# order) so the orchestrator adopts a matching running container instead of
|
||||
# recreating it.
|
||||
container_name: pine-openwakeword
|
||||
|
||||
container:
|
||||
image: docker.io/rhasspy/wyoming-openwakeword:2.1.0
|
||||
pull_policy: if-not-present
|
||||
network: archy-net
|
||||
network_aliases: [pine-openwakeword]
|
||||
# The image entrypoint binds tcp://0.0.0.0:10400. Preload the stock
|
||||
# "ok nabu" model; /custom is where a trained custom model (yo_archy)
|
||||
# drops in later — the engine picks up new .tflite files on restart.
|
||||
custom_args: ["--preload-model", "ok_nabu", "--custom-model-dir", "/custom"]
|
||||
|
||||
dependencies:
|
||||
- storage: 512Mi
|
||||
|
||||
resources:
|
||||
memory_limit: 512Mi
|
||||
|
||||
security:
|
||||
# cap-drop=ALL is applied by the orchestrator. A plain Python Wyoming server
|
||||
# on an unprivileged port needs no added capabilities.
|
||||
capabilities: []
|
||||
readonly_root: false
|
||||
no_new_privileges: true
|
||||
network_policy: isolated
|
||||
|
||||
ports:
|
||||
# Published so Home Assistant (on the pasta net) can reach the engine via
|
||||
# host.containers.internal:10400 (the Wyoming integration endpoint).
|
||||
- host: 10400
|
||||
container: 10400
|
||||
protocol: tcp
|
||||
|
||||
volumes:
|
||||
- type: bind
|
||||
source: /var/lib/archipelago/pine-openwakeword
|
||||
target: /custom
|
||||
options: [rw]
|
||||
|
||||
environment: []
|
||||
|
||||
health_check:
|
||||
type: tcp
|
||||
endpoint: localhost:10400
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 30s
|
||||
|
||||
metadata:
|
||||
author: Rhasspy / Home Assistant
|
||||
icon: /assets/img/app-icons/pine.svg
|
||||
website: https://github.com/rhasspy/wyoming-openwakeword
|
||||
repo: https://github.com/rhasspy/wyoming-openwakeword
|
||||
license: MIT
|
||||
tags:
|
||||
- home
|
||||
- voice
|
||||
- wake-word
|
||||
- wyoming
|
||||
+59
-5
@@ -1,8 +1,8 @@
|
||||
app:
|
||||
id: pine
|
||||
name: Pine
|
||||
version: "1.2.0"
|
||||
description: A private voice assistant for your home. Pine runs speech-to-text (Whisper) and text-to-speech (Piper) on your own node and pairs with a PineVoice satellite speaker, so Home Assistant Assist works locally with nothing sent to the cloud.
|
||||
version: "1.3.0"
|
||||
description: A private voice assistant for your home. Pine runs speech-to-text (Whisper), text-to-speech (Piper) and wake-word detection (openWakeWord) on your own node and pairs with a PineVoice satellite speaker, so Home Assistant Assist works locally with nothing sent to the cloud. Ask it about your node — block height, sync, peers, Lightning balance — and, when a Claude API key is set, anything else.
|
||||
category: home
|
||||
|
||||
# The user-facing launcher (app_id + container both "pine", matching the
|
||||
@@ -30,6 +30,7 @@ app:
|
||||
dependencies:
|
||||
- app_id: pine-whisper
|
||||
- app_id: pine-piper
|
||||
- app_id: pine-openwakeword
|
||||
- storage: 128Mi
|
||||
|
||||
resources:
|
||||
@@ -92,6 +93,15 @@ app:
|
||||
ssl_certificate_key /etc/nginx/tls.key;
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
# Live node facts for the status card — proxied to the node's
|
||||
# public status tier so the (https) page can fetch same-origin.
|
||||
location = /node-status {
|
||||
proxy_pass http://host.containers.internal:80/api/pine/status;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_connect_timeout 5s;
|
||||
proxy_read_timeout 10s;
|
||||
}
|
||||
location / { try_files $uri $uri/ /index.html; }
|
||||
}
|
||||
- path: /var/lib/archipelago/pine/index.html
|
||||
@@ -156,9 +166,16 @@ app:
|
||||
<div class="card status">
|
||||
<div class="row"><b>Whisper</b><span>speech-to-text ready on <code>:10300</code></span></div>
|
||||
<div class="row"><b>Piper</b><span>text-to-speech ready on <code>:10200</code></span></div>
|
||||
<div class="row"><b>Wake word</b><span>“Hey Jarvis” on the speaker (openWakeWord on <code>:10400</code>)</span></div>
|
||||
<div class="row"><b>Speaker</b><span>put it in pairing mode — ring LED blinking yellow</span></div>
|
||||
</div>
|
||||
|
||||
<div class="card status">
|
||||
<div class="row"><b>Node</b><span id="ns-node">checking…</span></div>
|
||||
<div class="row"><b>Bitcoin</b><span id="ns-btc">—</span></div>
|
||||
<div class="row"><b>Peers</b><span id="ns-peers">—</span></div>
|
||||
</div>
|
||||
|
||||
<div class="card insecure" id="insecure">
|
||||
This page isn’t running over HTTPS, so the browser blocks Bluetooth.
|
||||
Open it via its <b>https://…:10380</b> address (accept the self-signed
|
||||
@@ -177,9 +194,13 @@ app:
|
||||
<p class="ha">After WiFi joins, one manual step remains — pair the
|
||||
speaker in Home Assistant: <b>Settings → Devices & services →
|
||||
Add Wyoming Protocol</b>, host = the speaker’s IP, port
|
||||
<b>10700</b>. Whisper, Piper and the Assist pipeline are wired up
|
||||
automatically when Pine installs. Wake word: <b>“Hey Jarvis.”</b>
|
||||
You can also ask node things like <i>“what’s the block height?”</i></p>
|
||||
<b>10700</b>. Whisper, Piper, openWakeWord and the Assist pipeline
|
||||
are wired up automatically when Pine installs. Wake word:
|
||||
<b>“Hey Jarvis.”</b> Ask node things like <i>“what’s the block
|
||||
height?”</i>, <i>“how many peers?”</i>, <i>“is the node
|
||||
synced?”</i> or <i>“what’s my lightning balance?”</i> — and when a
|
||||
Claude API key is set on the node, anything else gets answered by
|
||||
Claude. New mesh messages are announced on the speaker too.</p>
|
||||
<p class="ha">Troubleshooting: if it hears you (LED reacts) but answers
|
||||
are silent, unplug and replug the speaker — an interrupted answer can
|
||||
wedge its audio output until it reboots.</p>
|
||||
@@ -304,6 +325,39 @@ app:
|
||||
try { device?.gatt?.disconnect(); } catch {}
|
||||
} finally { btn.disabled = false; }
|
||||
});
|
||||
|
||||
// Live node status card — public tier of /api/pine/status via the
|
||||
// same-origin /node-status proxy. Best-effort: failures just show
|
||||
// "unavailable" and retry on the next tick.
|
||||
const nsNode = document.getElementById("ns-node");
|
||||
const nsBtc = document.getElementById("ns-btc");
|
||||
const nsPeers = document.getElementById("ns-peers");
|
||||
async function refreshNodeStatus() {
|
||||
try {
|
||||
const r = await fetch("/node-status", { cache: "no-store" });
|
||||
if (!r.ok) throw new Error(String(r.status));
|
||||
const s = await r.json();
|
||||
const up = Math.floor((s.uptime_seconds || 0) / 3600);
|
||||
nsNode.textContent = `Archipelago ${s.version || "?"} — up ${up}h`;
|
||||
if (s.bitcoin && s.bitcoin.height != null) {
|
||||
const pct = s.bitcoin.sync_percent;
|
||||
nsBtc.textContent = `block ${s.bitcoin.height}` +
|
||||
(pct != null ? (pct >= 99.99 ? " — synced" : ` — ${pct}% synced`) : "");
|
||||
} else {
|
||||
nsBtc.textContent = "not running";
|
||||
}
|
||||
const btcPeers = s.bitcoin && s.bitcoin.peers != null ? s.bitcoin.peers : "?";
|
||||
const meshPeers = s.mesh ? s.mesh.peers : 0;
|
||||
nsPeers.textContent = `${btcPeers} bitcoin` +
|
||||
(s.mesh && s.mesh.enabled ? `, ${meshPeers} mesh` : "");
|
||||
} catch {
|
||||
nsNode.textContent = "node status unavailable";
|
||||
nsBtc.textContent = "—";
|
||||
nsPeers.textContent = "—";
|
||||
}
|
||||
}
|
||||
refreshNodeStatus();
|
||||
setInterval(refreshNodeStatus, 30000);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user