- New `dwarfctl interactive` command: persistent WS connection with
real-time slew commands on a single session (no reconnect per command).
Decodes DeviceAttitude notifications (cmd 15295) when available.
- Fixed coordinate mapping: 90°=UP (toward sky), 270°=DOWN (toward ground),
confirmed by live testing on DWARF II in alt-az config.
- Monitor now decodes attitude notifications inline.
- Note: IMU attitude (15295) is NOT pushed during free slew — only during
calibration/EQ solving. Position feedback requires astro calibration.
💘 Generated with Crush
Assisted-by: Crush:glm-5.2
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
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
# 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:
# 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)