commit e798cde7a1d14a6f2fa0e0bbf16fd82446bed828 Author: Dorian Date: Mon Jul 20 22:51:54 2026 +0100 feat: PineVoice Improv-BLE WiFi provisioning page Local Web Bluetooth page that provisions the Pine64 PineVoice speaker over the Improv BLE protocol. Waits for the AUTHORIZED state before sending credentials (per the Improv spec), uses write-with-response since the firmware declares the RPC characteristic write-only, and logs the device state/error sequence for diagnostics. Co-Authored-By: Claude Fable 5 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e43b0f9 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.DS_Store diff --git a/README.md b/README.md new file mode 100644 index 0000000..0bd532e --- /dev/null +++ b/README.md @@ -0,0 +1,49 @@ +# PineVoice WiFi Setup Tool + +Local Improv-BLE provisioning page for the Pine64 PineVoice smart speaker. +Serves from localhost so Web Bluetooth works and no VPN/proxy can interfere. + +## Run + +```sh +python3 -m http.server 8377 --bind 127.0.0.1 -d ~/Projects/pine +``` + +Then open in Chrome (needs Web Bluetooth — Chrome/Edge, not Safari/Firefox). + +## Use + +1. Put the speaker in provisioning mode: ring LED blinking yellow + (hold the • dot button 15 s to reset if needed). +2. Enter SSID + password, click **Connect PineVoice**, pick *PineVoice* in the + Bluetooth chooser. +3. If the log says *authorization required*, **press the centre button** on the + speaker. The page waits for this and only sends credentials once the device + reports `authorized`. Authorization lapses after ~1 min, so don't linger. +4. Success: log shows `✓ PROVISIONED`, speaker plays a chime, LED breathes dim + magenta. Failure: speaker plays a failure sound within ~20 s and the log + shows the reason. + +## After WiFi: add to Home Assistant + +Settings → Devices & services → discovered **Wyoming Protocol** device → +Configure. If not auto-discovered: Add integration → Wyoming Protocol → +host = speaker IP, port `10700`. Requires an Assist pipeline with +Whisper (STT) and Piper (TTS). Wake word: "Hey Jarvis", or press the center button. + +## The gotchas this tool fixes + +**Authorization ordering.** Per the [Improv BLE spec](https://www.improv-wifi.com/ble/), +a device in `AUTHORIZATION_REQUIRED` (`0x01`) rejects a `WIFI_SETTINGS` RPC with +error `0x04`. The client must wait for the state to become `AUTHORIZED` (`0x02`) +— i.e. for the physical button press — before writing. Sending immediately looks +like a successful write (the device ACKs the GATT write) but provisioning never +starts. + +**Write-with-response.** The PineVoice firmware declares its Improv RPC characteristic as +write-WITH-response only (`GATT_CHRC_PROP_WRITE` in +`pinevoice_fw_e907/app/src/improv/improv.c`). Anything that sends credentials +as an unacknowledged BLE Write Command is **silently ignored** — no error, no +state change. This page uses `writeValueWithResponse` explicitly. + +Firmware source: diff --git a/ha-stack.yml b/ha-stack.yml new file mode 100644 index 0000000..fb7c45b --- /dev/null +++ b/ha-stack.yml @@ -0,0 +1,27 @@ +# Home Assistant + Wyoming voice pipeline for the PineVoice speakers. +# Deploy as a Portainer stack (inside the Umbrel Portainer DinD). +# /data/ paths persist across reboots (map to ~/umbrel/app-data/portainer/data/docker/). +services: + homeassistant: + image: homeassistant/home-assistant:2026.7.1 + restart: unless-stopped + ports: + - "8123:8123" + environment: + - TZ=Europe/London + volumes: + - /data/homeassistant:/config + + whisper: + image: rhasspy/wyoming-whisper:3.4.1 + restart: unless-stopped + command: --model base-int8 --language en + volumes: + - /data/wyoming-whisper:/data + + piper: + image: rhasspy/wyoming-piper:2.2.2 + restart: unless-stopped + command: --voice en_GB-alba-medium + volumes: + - /data/wyoming-piper:/data diff --git a/index.html b/index.html new file mode 100644 index 0000000..6c92a60 --- /dev/null +++ b/index.html @@ -0,0 +1,223 @@ + + + + + +PineVoice WiFi Setup (local) + + + +
+

PineVoice WiFi Setup

+

Improv-over-BLE, served from localhost. Speaker LED must be blinking yellow.

+ + + + + +
Ready. Click the button, then pick “PineVoice” in the Bluetooth popup.
+
+ + +