# callpipe control protocol (Pi ⇄ phone) The phone app hosts a **WebSocket server** on the LAN (default port **8090**). The Pi (or any client, e.g. `wscat`) connects in and drives the cellular call. **No audio** crosses this channel — audio is Bluetooth HFP between the phone and the Pi. This is purely call *control* + *state*. Endpoint: `ws://:8090/` (any path is accepted) ## Commands (client → phone) | JSON | Action | |------|--------| | `{"cmd":"dial","number":"+9607…","sim":0}` | Place outgoing call. `sim` optional (0-based index from the `sims` event); omit for default SIM. | | `{"cmd":"answer"}` | Answer the ringing call. | | `{"cmd":"hangup"}` | End the active call (or reject if still ringing). | | `{"cmd":"reject"}` | Reject the ringing call. | | `{"cmd":"state"}` | Ask for the current call snapshot. | | `{"cmd":"sims"}` | Ask for the SIM list. | | `{"cmd":"ping"}` | Liveness check → `pong`. | ## Events (phone → client) | JSON | Meaning | |------|---------| | `{"event":"hello","app":"callpipe","proto":1}` | Sent on connect. | | `{"event":"sims","sims":[{"index":0,"label":"Ooredoo"},{"index":1,"label":"Dhiraagu"}]}` | SIM list (sent on connect + on request). | | `{"event":"state","state":"ACTIVE","number":"+9607…","direction":"OUT"}` | Call state. Pushed on every change + on connect. | | `{"event":"ack","cmd":"dial"}` | Command accepted. | | `{"event":"error","cmd":"dial","msg":"…"}` | Command failed. | | `{"event":"pong"}` | Reply to `ping`. | `state` values: `IDLE`, `CONNECTING`, `DIALING`, `RINGING`, `ACTIVE`, `HOLDING`, `DISCONNECTING`, `DISCONNECTED`. `direction`: `IN`, `OUT`, or `null`. ## Quick test (before the Pi is ready) ```sh # phone IP is 10.0.1.239 (adb wireless). Install app, open it once so the # foreground service + default-dialer role are granted. npm i -g wscat # or: nix-shell -p nodePackages.wscat wscat -c ws://10.0.1.239:8090/ > {"cmd":"sims"} > {"cmd":"dial","number":"+9607xxxxxx"} > {"cmd":"hangup"} ``` You should see `hello`, `sims`, and live `state` events stream back.