# WebSocket Protocol ## Connection After BLE authentication, the app connects via WebSocket for all telescope control. ``` URL: ws://:9900/?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` 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 |