diff --git a/app-catalog/catalog.json b/app-catalog/catalog.json index d42cc11d..4688db41 100644 --- a/app-catalog/catalog.json +++ b/app-catalog/catalog.json @@ -373,7 +373,7 @@ { "id": "pine", "title": "Pine", - "version": "1.0.0", + "version": "1.1.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.", "icon": "/assets/img/app-icons/pine.svg", "author": "Archipelago", diff --git a/apps/pine/manifest.yml b/apps/pine/manifest.yml index ee0cf4f6..9387384c 100644 --- a/apps/pine/manifest.yml +++ b/apps/pine/manifest.yml @@ -1,14 +1,15 @@ app: id: pine name: Pine - version: "1.0.0" + version: "1.1.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. category: home # The user-facing launcher (app_id + container both "pine", matching the # runtime references + the live container so the orchestrator adopts it). A - # tiny nginx that serves the setup / status page for the voice stack. The two - # Wyoming engines (pine-whisper, pine-piper) are internal stack members. + # tiny nginx that serves the "Connect Pine to WiFi" provisioner page for the + # voice stack. The two Wyoming engines (pine-whisper, pine-piper) are internal + # stack members. container_name: pine container: @@ -16,6 +17,15 @@ app: pull_policy: if-not-present network: archy-net network_aliases: [pine] + # The provisioner uses Web Bluetooth (Improv-over-BLE) to push WiFi creds to + # the PineVoice speaker. navigator.bluetooth only exists in a SECURE CONTEXT + # (https or localhost), so the launcher terminates TLS with a self-signed + # cert — otherwise the "Connect Pine to WiFi" button is inert on the LAN. + # Idempotent: kept as-is when crt+key already exist. Mirrors the netbird + # secure-context fix (#15). + generated_certs: + - crt: /var/lib/archipelago/pine/tls.crt + key: /var/lib/archipelago/pine/tls.key dependencies: - app_id: pine-whisper @@ -27,7 +37,7 @@ app: security: # cap-drop=ALL is applied by the orchestrator. nginx (master as root, drops - # workers) binds :80 inside the container — needs the worker-drop caps + + # workers) binds :443 inside the container — needs the worker-drop caps + # NET_BIND_SERVICE for the privileged port. capabilities: [CHOWN, DAC_OVERRIDE, SETGID, SETUID, NET_BIND_SERVICE] readonly_root: false @@ -36,10 +46,22 @@ app: ports: - host: 10380 - container: 80 + container: 443 protocol: tcp volumes: + - type: bind + source: /var/lib/archipelago/pine/nginx.conf + target: /etc/nginx/conf.d/default.conf + options: [ro] + - type: bind + source: /var/lib/archipelago/pine/tls.crt + target: /etc/nginx/tls.crt + options: [ro] + - type: bind + source: /var/lib/archipelago/pine/tls.key + target: /etc/nginx/tls.key + options: [ro] - type: bind source: /var/lib/archipelago/pine/index.html target: /usr/share/nginx/html/index.html @@ -47,12 +69,19 @@ app: environment: [] - # The setup / status page, written to the host before create and served - # read-only. Web-Bluetooth WiFi provisioning of the speaker only works from a - # secure/localhost context (not this LAN page), so the page documents that - # flow rather than embedding it — the live provisioner lives in the `pine` - # Gitea repo (index.html), run from a laptop on localhost. files: + - path: /var/lib/archipelago/pine/nginx.conf + overwrite: true + content: | + server { + listen 443 ssl; + server_name _; + ssl_certificate /etc/nginx/tls.crt; + ssl_certificate_key /etc/nginx/tls.key; + root /usr/share/nginx/html; + index index.html; + location / { try_files $uri $uri/ /index.html; } + } - path: /var/lib/archipelago/pine/index.html overwrite: true content: | @@ -61,26 +90,42 @@ app:
-A private voice assistant that runs on your node.
-Connect your speaker — everything stays on your node.
Pine gives Home Assistant a local voice: your PineVoice speaker - hears you, Whisper turns speech into text on this node, and - Piper speaks the reply back — nothing leaves your home.
- -host.containers.internal:10300host.containers.internal:10200<speaker-ip>:10700:10300:10200pine repo on a laptop:
- python3 -m http.server 8377 --bind 127.0.0.1, then
- open http://localhost:8377 in Chrome.✓ PROVISIONED and the
- speaker chimes.host.containers.internal:10300 (Whisper) and
- :10200 (Piper).<speaker-ip>:10700.After WiFi joins, add the speaker to Home Assistant: + Settings → Devices & services → Add Wyoming Protocol, host = + the speaker’s IP, port 10700, then pick an Assist pipeline using + Whisper + Piper. Wake word: “Hey Jarvis.”