Reverse-engineer DWARF II telescope API and build open-source Go client

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
This commit is contained in:
Jacquin Antoine
2026-07-12 15:18:56 +02:00
commit 814a836c5a
54 changed files with 35973 additions and 0 deletions

View File

@ -0,0 +1,71 @@
syntax = "proto3";
option go_package = "github.com/antitbone/dwarfctl/proto;pb";
message ReqSetTime {
uint64 timestamp = 1;
double timezone_offset = 2;
}
message ReqSetTimezone {
string timezone = 1;
}
message ReqSetMtpMode {
int32 mode = 1;
}
message ReqSetCpuMode {
int32 mode = 1;
}
message ReqsetMasterLock {
bool lock = 1;
}
message ReqGetDeviceActivateInfo {
int32 issuer = 1;
}
message ResDeviceActivateInfo {
int32 activate_state = 1;
int32 activate_process_state = 2;
string request_param = 3;
}
message ReqDeviceActivateWriteFile {
string request_param = 1;
}
message ResDeviceActivateWriteFile {
int32 code = 1;
string request_param = 2;
}
message ReqDeviceActivateSuccessfull {
string request_param = 1;
}
message ResDeviceActivateSuccessfull {
int32 code = 1;
int32 activate_state = 2;
}
message ReqDisableDeviceActivate {
string request_param = 1;
}
message ResDisableDeviceActivate {
int32 code = 1;
int32 activate_state = 2;
}
message ReqSetLocation {
double latitude = 1;
double longitude = 2;
double altitude = 3;
string country_region = 4;
string province = 5;
string city = 6;
string district = 7;
bool enable = 8;
}