Files
dwarf_protocol/03_websocket_protocol.md
Jacquin Antoine be87e8a4ca Add complete DWARFLAB protocol documentation
- Architecture overview, BLE and WebSocket protocols
- All 19 modules with command IDs validated against smali source
- BLE packet format with CRC-16/MODBUS pseudocode
- Complete WsCmd index (~300 commands)
- Connection flow with pseudocode examples
- Methodology section explaining APK decompilation process
2026-06-13 00:21:19 +02:00

141 lines
5.0 KiB
Markdown

# WebSocket Protocol
## Connection
After BLE authentication, the app connects via WebSocket for all telescope control.
```
URL: ws://<device_ip>:9900/?client_id=<client_id>
Port: 9900 (primary), 4409/4410 (secondary)
Scheme: ws:// (unencrypted, NOT wss://)
```
The device IP is obtained from `ResGetconfig.ip` (if already on WiFi) or `ResSta.ip` (after STA configuration).
The `client_id` is stored in MMKV under key `"data_default_client_id"`, or generated if not present.
Default device IP: `192.168.88.1`
## WsPacket Format
Every WebSocket message is a serialized `BaseProto.WsPacket` protobuf:
```protobuf
message WsPacket {
int32 majorVersion = 1; // Always 2
int32 minorVersion = 2; // Always 3
int32 deviceId = 3; // Target device ID (see DeviceType)
int32 moduleId = 4; // Module ID (0-18, see module table)
int32 cmd = 5; // Command ID (see WsCmd index)
int32 type = 6; // Message type: 0=request, 1=response, 2=notification, 3=reply
bytes data = 7; // Protobuf-encoded payload for the specific module
string clientId = 8; // App client identifier
}
```
### Pseudocode: Sending a Command
```python
import protobuf
def send_command(websocket, device_id, module_id, cmd, payload_bytes, client_id):
"""Send a command via WebSocket."""
packet = WsPacket(
majorVersion=2,
minorVersion=3,
deviceId=device_id,
moduleId=module_id,
cmd=cmd,
type=0, # request
data=payload_bytes,
clientId=client_id
)
websocket.send(packet.SerializeToString())
def send_track_start(websocket, device_id, client_id, x, y, w, h, cam_id):
"""Example: Start tracking."""
track_req = TrackProto.ReqStartTrack(x=x, y=y, w=w, h=h, camId=cam_id)
send_command(websocket, device_id, module_id=7, cmd=14800,
payload_bytes=track_req.SerializeToString(), client_id=client_id)
```
## Message Types
| type | Name | Description |
|------|------|-------------|
| 0 | request | App → Device command |
| 1 | response | Device → App response to a request |
| 2 | notification | Device → App spontaneous notification |
| 3 | reply | Device → App acknowledgement |
## Keepalive: DwarfPing / DwarfEcho
After WebSocket connection, the app periodically sends `DwarfPing` and the device responds with `DwarfEcho`.
```protobuf
message DwarfPing {
int32 vocaltype = 1; // VT_PING = 1
int64 timestamp = 2; // Unix timestamp
bytes magic = 3; // Session magic bytes
repeated DwarfPing vocals = 4;
repeated NifAP mutes = 5;
}
message DwarfEcho {
int32 vocaltype = 1; // VT_ECHO = 2
int64 timestamp = 2; // Unix timestamp
bytes magic = 3; // Session magic bytes
int64 tsPing = 4; // Echo of the ping timestamp
bytes macAddress = 5; // Device MAC address
BleProto model = 6; // Device model info
string sn = 7; // Serial number
string name = 8; // Device name
string psw = 9; // Password
string fwVersion = 10; // Firmware version
string wsScheme = 11; // WebSocket scheme ("ws")
int32 session = 12; // Session ID
BleProto ap = 13; // AP info
BleProto sta = 14; // STA info
}
```
> **Note**: DwarfEcho contains complete device info (firmware, MAC, SSID, IP, etc.).
## Response Routing
The client uses `WsRequestHandle` to dispatch responses:
1. Parse `WsPacket` from received bytes
2. Extract `cmd` (command ID)
3. Look up registered handlers in a `HashMap<Integer, List>` for this cmd
4. Execute callbacks with `WsPacket.getData()` (deserialized payload)
## Typical Exchange
```
App Device
│ │
│ WsPacket(type=0, moduleId=1, │
│ cmd=10000, data=ReqOpenCamera) │
│ ─────────────────────────────────────► │
│ │
│ WsPacket(type=1, cmd=10000, │
│ data=ComResponse{code=0}) │
│ ◄───────────────────────────────────── │
│ │
│ WsPacket(type=0, moduleId=7, │
│ cmd=14800, data=ReqStartTrack) │
│ ─────────────────────────────────────► │
│ │
│ WsPacket(type=2, cmd=15200, │
│ data=NotifyProto{...}) │
│ ◄───────────────────────────────────── │
│ (spontaneous notification) │
```
## Protocol Version
| Field | Value | Description |
|-------|-------|-------------|
| `WS_MAJOR_VERSION_NUMBER` | 2 | Current major version |
| `WS_MINOR_VERSION_NUMBER` | 3 | Current minor version |