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:
139
dwarfctl/proto/voiceassistant.proto
Normal file
139
dwarfctl/proto/voiceassistant.proto
Normal file
@ -0,0 +1,139 @@
|
||||
syntax = "proto3";
|
||||
option go_package = "github.com/antitbone/dwarfctl/proto;pb";
|
||||
import "base.proto";
|
||||
import "notify.proto";
|
||||
import "task_center.proto";
|
||||
|
||||
enum VoiceCommandType {
|
||||
VOICE_CMD_UNKNOWN = 0;
|
||||
VOICE_CMD_GET_STATUS = 1;
|
||||
VOICE_CMD_TAKE_PHOTO = 2;
|
||||
VOICE_CMD_START_RECORD = 3;
|
||||
VOICE_CMD_STOP_RECORD = 4;
|
||||
VOICE_CMD_START_TIMELAPSE = 5;
|
||||
VOICE_CMD_STOP_TIMELAPSE = 6;
|
||||
VOICE_CMD_START_BURST = 7;
|
||||
VOICE_CMD_STOP_BURST = 8;
|
||||
VOICE_CMD_START_ASTRO = 9;
|
||||
VOICE_CMD_STOP_ASTRO = 10;
|
||||
VOICE_CMD_START_SENTRY = 11;
|
||||
VOICE_CMD_STOP_SENTRY = 12;
|
||||
VOICE_CMD_MOVE = 13;
|
||||
VOICE_CMD_GOTO_TARGET = 14;
|
||||
VOICE_CMD_CALIBRATION = 15;
|
||||
VOICE_CMD_AUTO_FOCUS = 16;
|
||||
VOICE_CMD_STOP_FOCUS = 17;
|
||||
VOICE_CMD_STOP_ALL = 18;
|
||||
}
|
||||
|
||||
message ReqVoiceCommand {
|
||||
// oneof params
|
||||
VoiceCommandType command_type = 1;
|
||||
int32 shooting_mode = 2;
|
||||
VoicePhotoParams photo_params = 10;
|
||||
VoiceRecordParams record_params = 11;
|
||||
VoiceTimelapseParams timelapse_params = 12;
|
||||
VoiceBurstParams burst_params = 13;
|
||||
VoiceAstroParams astro_params = 14;
|
||||
VoiceSentryParams sentry_params = 15;
|
||||
VoiceMoveParams move_params = 16;
|
||||
VoiceGotoParams goto_params = 17;
|
||||
VoiceCalibrationParams calibration_params = 18;
|
||||
VoiceFocusParams focus_params = 19;
|
||||
}
|
||||
|
||||
message VoicePhotoParams {
|
||||
int32 camera_type = 1;
|
||||
}
|
||||
|
||||
message VoiceRecordParams {
|
||||
int32 camera_type = 1;
|
||||
int32 duration_seconds = 2;
|
||||
}
|
||||
|
||||
message VoiceTimelapseParams {
|
||||
int32 camera_type = 1;
|
||||
int32 interval_seconds = 2;
|
||||
int32 duration_seconds = 3;
|
||||
}
|
||||
|
||||
message VoiceBurstParams {
|
||||
int32 camera_type = 1;
|
||||
int32 count = 2;
|
||||
}
|
||||
|
||||
message VoiceAstroParams {
|
||||
string target_name = 1;
|
||||
double ra = 2;
|
||||
double dec = 3;
|
||||
int32 index = 4;
|
||||
double lon = 5;
|
||||
double lat = 6;
|
||||
bool auto_goto = 7;
|
||||
bool force_start = 8;
|
||||
}
|
||||
|
||||
message VoiceSentryParams {
|
||||
int32 type = 1;
|
||||
}
|
||||
|
||||
message VoiceMoveParams {
|
||||
double azimuth_angle = 1;
|
||||
double altitude_angle = 2;
|
||||
int32 speed = 3;
|
||||
}
|
||||
|
||||
message VoiceGotoParams {
|
||||
string target_name = 1;
|
||||
double ra = 2;
|
||||
double dec = 3;
|
||||
int32 index = 4;
|
||||
double lon = 5;
|
||||
double lat = 6;
|
||||
int32 shooting_mode = 7;
|
||||
}
|
||||
|
||||
message VoiceCalibrationParams {
|
||||
double lon = 1;
|
||||
double lat = 2;
|
||||
}
|
||||
|
||||
message VoiceFocusParams {
|
||||
bool is_infinity = 1;
|
||||
}
|
||||
|
||||
message ResVoiceCommand {
|
||||
// oneof result
|
||||
int32 code = 1;
|
||||
string message = 2;
|
||||
VoiceCommandType command_type = 3;
|
||||
VoiceStatusResult status_result = 10;
|
||||
VoiceOperationResult operation_result = 11;
|
||||
}
|
||||
|
||||
message AstroShootingProgress {
|
||||
string target_name = 1;
|
||||
int32 captured_count = 2;
|
||||
int32 total_count = 3;
|
||||
int32 stacked_count = 4;
|
||||
int32 progress_percentage = 5;
|
||||
int64 elapsed_time = 6;
|
||||
int64 remaining_time = 7;
|
||||
}
|
||||
|
||||
message VoiceStatusResult {
|
||||
ResGetDeviceStateInfo device_state_info = 1;
|
||||
AstroShootingProgress astro_progress = 2;
|
||||
}
|
||||
|
||||
message VoiceOperationResult {
|
||||
bool success = 1;
|
||||
string detail_message = 2;
|
||||
}
|
||||
|
||||
message ResNotifyVoiceAssistant {
|
||||
VoiceCommandType command_type = 1;
|
||||
notify.OperationState state = 2;
|
||||
string message = 4;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user