Complete reverse-engineering of the DWARFLAB Android app (v3.4.0) protocol
and implementation of a working CLI tool to control DWARF II telescopes.
Analysis (from APK decompilation with jadx):
- Extracted 17 protobuf definitions (382 messages) from embedded descriptors
- Mapped all 323 WebSocket command IDs across 16 modules
- Documented the full protocol: BLE discovery, WebSocket control (port 9900),
RTSP preview, WsPacket envelope (proto v2.3)
- Documented the Android UI structure (screens, navigation, shooting modes)
- Key discovery: telescope responds with type=3 (reply), not type=1 (response),
and several commands are fire-and-forget (RGB, camera open/close)
dwarfctl Go client:
- Protobuf bindings generated from extracted .proto files (397 messages)
- WebSocket transport layer with request-response matching and notification fan-out
- Typed API covering cameras, motors, astrophotography, focus, tracking, system, power
- Cobra CLI with 30+ subcommands and --debug traffic logging
- 57 unit tests (transport round-trip, command routing, proto encoding)
- Validated on real hardware: state, photo, motor slew (all directions/speeds),
focus, RGB, time/location sync all confirmed working
💘 Generated with Crush
Assisted-by: Crush:glm-5.2
123 lines
3.3 KiB
Markdown
123 lines
3.3 KiB
Markdown
# dwarfctl — Open-source DWARF telescope control CLI
|
|
|
|
A Go command-line tool for controlling **DWARF II** (and upcoming DWARF III)
|
|
smart telescopes over Wi-Fi via their WebSocket API.
|
|
|
|
Built from reverse-engineered protocol specs (see `../analysis/`).
|
|
|
|
## Quick start
|
|
|
|
```bash
|
|
make build
|
|
./dwarfctl --ip 192.168.1.100 state
|
|
```
|
|
|
|
## Prerequisites
|
|
|
|
The telescope must be connected to your Wi-Fi (or your phone joined to the
|
|
telescope's AP hotspot). Once you can ping the telescope's IP, `dwarfctl` can
|
|
reach it.
|
|
|
|
Find the IP from:
|
|
- The official DWARFLAB app's Settings → My Device
|
|
- Your router's DHCP table (hostname like `DWARFxxxx`)
|
|
- The BLE handshake (see `../analysis/API_REFERENCE.md`)
|
|
|
|
## Usage examples
|
|
|
|
```bash
|
|
# Device state
|
|
dwarfctl --ip 192.168.1.100 state
|
|
|
|
# Camera
|
|
dwarfctl camera open --cam tele
|
|
dwarfctl camera photo --cam tele
|
|
dwarfctl camera photo --cam wide --raw
|
|
dwarfctl camera burst start --cam tele
|
|
dwarfctl camera exp --cam tele 156 # 1/4s exposure
|
|
dwarfctl camera gain --cam tele 60
|
|
dwarfctl camera params --cam tele
|
|
|
|
# Motor / slew
|
|
dwarfctl motor slew 90 0.5 # angle 90°, half speed
|
|
dwarfctl motor stop 0 # stop RA motor
|
|
dwarfctl motor goto 0 45.0 5.0 # RA motor to 45° at speed 5
|
|
|
|
# Astrophotography
|
|
dwarfctl astro calibrate start
|
|
dwarfctl astro goto-dso 10.6847 41.2687 "M31 Andromeda"
|
|
dwarfctl astro goto-solar 3 # Earth = index 3
|
|
dwarfctl astro stack start
|
|
dwarfctl astro eq-solve start # polar alignment
|
|
|
|
# Focus
|
|
dwarfctl focus auto
|
|
dwarfctl focus step 1 # 1=out, 0=in
|
|
dwarfctl focus astro-af start
|
|
|
|
# Tracking
|
|
dwarfctl track start 640 360 100 100 0 # bbox at center of tele cam
|
|
dwarfctl track stop
|
|
|
|
# System
|
|
dwarfctl system sync-time
|
|
dwarfctl system set-location 48.8566 2.3522 35
|
|
|
|
# Power
|
|
dwarfctl power rgb-on
|
|
dwarfctl power rgb-off
|
|
dwarfctl power reboot
|
|
dwarfctl power off
|
|
|
|
# Monitor notifications (live status events)
|
|
dwarfctl monitor
|
|
```
|
|
|
|
## Architecture
|
|
|
|
```
|
|
dwarfctl/
|
|
├── proto/dwarf.proto # unified proto3 definitions (397 messages)
|
|
├── proto/dwarf.pb.go # generated Go bindings (24948 lines)
|
|
├── internal/
|
|
│ ├── transport/client.go # WebSocket client + WsPacket envelope
|
|
│ └── api/
|
|
│ ├── commands.go # 323 command IDs + module routing
|
|
│ └── client.go # typed Telescope API (camera/motor/astro/...)
|
|
└── cmd/dwarfctl/main.go # cobra CLI
|
|
```
|
|
|
|
### Protocol summary
|
|
|
|
| Layer | Transport | Port |
|
|
|-------|-----------|------|
|
|
| Control | WebSocket (binary protobuf) | 9900 |
|
|
| Preview | RTSP (not implemented here) | 554 |
|
|
|
|
Every command is a serialized `WsPacket{major=2, minor=3, device_id,
|
|
module_id, cmd, type, data, client_id}`. The `module_id` is derived from `cmd`
|
|
by range. See `../analysis/API_REFERENCE.md` for details.
|
|
|
|
## Regenerating protos
|
|
|
|
If the proto definitions change:
|
|
|
|
```bash
|
|
# From the APK analysis directory:
|
|
python3 ../analysis/extract_protos.py ../extracted/jadx/.../proto ../analysis/protos
|
|
|
|
# Merge into unified proto:
|
|
cd dwarfctl/proto
|
|
python3 merge.py . dwarf.proto
|
|
protoc --go_out=. --go_opt=paths=source_relative dwarf.proto
|
|
```
|
|
|
|
## Roadmap
|
|
|
|
- [ ] BLE discovery + handshake (DwarfPing/DwarfEcho)
|
|
- [ ] RTSP preview viewer
|
|
- [ ] Interactive REPL mode
|
|
- [ ] Schedule plan management
|
|
- [ ] OTA firmware update
|
|
- [ ] Full Notify event parsing (typed)
|