Architecture Overview¶
Design Philosophy¶
This SDK is built on several core principles:
- Separation of Concerns - Each component has a single, well-defined responsibility
- Composition over Inheritance - Flexibility through component assembly rather than class hierarchies
- BLE-First Strategy - Prioritize BLE commands for reliability; HTTP only when necessary
- Dual-Mode Support - Offline mode (BLE only) by default, with optional online mode (BLE+WiFi)
This SDK implements the OpenGoPro protocol specifications and reuses OpenGoPro's protobuf definitions and constants. For detailed protocol documentation, refer to the official OpenGoPro documentation.
Layered Architecture¶
┌─────────────────────────────────────────────────────────────┐
│ Application Layer │
├─────────────────────────────────────────────────────────────┤
│ MultiCameraManager (multi_camera.py) │
│ - Batch operations across multiple cameras │
│ - Concurrent execution with configurable parallelism │
│ - Per-camera error isolation │
└─────────────────────────────────────────────────────────────┘
↓ manages
┌─────────────────────────────────────────────────────────────┐
│ Client Layer │
├─────────────────────────────────────────────────────────────┤
│ GoProClient (client.py) │
│ - Unified API for camera control │
│ - Offline/Online mode management │
│ - COHN credential persistence │
│ - Assembles command and connection components │
└─────────────────────────────────────────────────────────────┘
↓ delegates to
┌─────────────────────────────────────────────────────────────┐
│ Command Layer │
├─────────────────────────────────────────────────────────────┤
│ BleCommands │ HttpCommands │ MediaCommands │
│ - Shutter control │ - Preview stream │ - File listing │
│ - Date/time sync │ - Camera state │ - Download │
│ - Tag highlight │ - Settings │ - Delete │
│ - Load preset │ - Digital zoom │ - Metadata │
│ - Sleep │ - Reboot │ - Turbo mode │
│ - WiFi management │ - Keep alive │ │
│ - COHN config │ │ WebcamCommands │
│ │ │ - USB webcam mode │
└─────────────────────────────────────────────────────────────┘
↓ uses
┌─────────────────────────────────────────────────────────────┐
│ Connection Layer │
├─────────────────────────────────────────────────────────────┤
│ BleConnectionManager │ HttpConnectionManager │
│ - BLE connect/disconnect │ - HTTPS session management │
│ - Response fragmentation │ - SSL context (COHN certs) │
│ - Notification handling │ - Basic auth credentials │
│ - Async response queue │ - Connection pooling │
├───────────────────────────┴──────────────────────────────────┤
│ BleScanner │ HealthCheckMixin │
│ - Device discovery │ - Connection health monitoring │
│ - Streaming scan API │ - Auto-reconnect logic │
└─────────────────────────────────────────────────────────────┘
↓ uses
┌─────────────────────────────────────────────────────────────┐
│ Configuration & Utilities │
├─────────────────────────────────────────────────────────────┤
│ CohnConfigManager │ TimeoutConfig │ StateParser │
│ - TinyDB storage │ - BLE timeouts │ - Raw → enum │
│ - CRUD operations │ - HTTP timeouts │ - Status helpers │
│ - Per-camera creds │ - WiFi timeouts │ │
│ │ - COHN timeouts │ Exceptions │
│ │ │ - Custom hierarchy│
└─────────────────────────────────────────────────────────────┘
↓ built on
┌─────────────────────────────────────────────────────────────┐
│ External Dependencies │
├─────────────────────────────────────────────────────────────┤
│ bleak │ aiohttp │ open-gopro │
│ - BLE operations │ - HTTP client │ - Protobuf defs │
│ - Cross-platform │ - Async I/O │ - Constants │
│ │ - SSL support │ - Parsers │
└─────────────────────────────────────────────────────────────┘
Core Components¶
GoProClient¶
The main entry point providing a unified interface for camera control.
Key Design Decisions:
- Offline mode by default - BLE-only operation for scenarios without WiFi
- Composition pattern - Holds instances of command interfaces and connection managers
- Delegation pattern - High-level methods delegate to specialized command classes
- BLE-first strategy - Control commands prefer BLE for reliability
class GoProClient(HealthCheckMixin):
def __init__(self, target: str, offline_mode: bool = True, ...):
# Connection managers
self.ble = BleConnectionManager(target, timeout_config)
self.http = HttpConnectionManager(target, timeout_config)
# Command interfaces (composition)
self.ble_commands = BleCommands(self.ble)
self.http_commands = HttpCommands(self.http)
self.media_commands = MediaCommands(self.http)
self.webcam_commands = WebcamCommands(self.http)
Command Routing Strategy:
| Command Type | Protocol | Reason |
|---|---|---|
| Shutter control | BLE | More reliable, works offline |
| Date/time sync | BLE | More reliable, works offline |
| Tag highlight | BLE | More reliable, works offline |
| Load preset | BLE | More reliable, works offline |
| Sleep | BLE | More reliable, works offline |
| Preview stream | HTTP | Requires network for UDP stream |
| Media operations | HTTP | Large data transfer |
| Camera state query | HTTP | Comprehensive status data |
| Webcam mode | HTTP | USB mode control |
Connection Layer¶
BleConnectionManager¶
Handles low-level BLE communication with response fragmentation.
Key Features:
- Direct use of `bleak` library for cross-platform BLE
- Response queue for async command handling
- BLE packet fragmentation/reassembly (Open GoPro protocol)
- Notification callback handling
BLE Protocol Details:
# GoPro BLE packet header types
HEADER_TYPE_GENERAL = 0 # 5-bit length, max 31 bytes
HEADER_TYPE_EXT_13 = 1 # 13-bit length, max 8191 bytes
HEADER_TYPE_EXT_16 = 2 # 16-bit length, max 65535 bytes
HttpConnectionManager¶
Manages HTTPS connections for COHN (Camera on Home Network) mode.
Key Features:
- SSL context with camera's self-signed certificate
- HTTP Basic authentication
- Connection pooling via `aiohttp.ClientSession`
- Lazy connection (connects on first request)
# COHN connection setup
ssl_context = ssl.create_default_context()
ssl_context.load_verify_locations(cadata=credentials.certificate)
auth = aiohttp.BasicAuth(credentials.username, credentials.password)
BleScanner¶
Discovers nearby GoPro cameras via BLE advertising.
Key Features:
- Streaming scan API with `AsyncIterator`
- Idle timeout for early termination
- Target count for batch discovery
- Serial number extraction from device names
async for devices in BleScanner.scan_devices_stream(duration=8.0):
for dev in devices:
print(f"Found: {dev['name']} ({dev['serial']})")
Command Layer¶
BleCommands¶
Implements commands executed via BLE, using simple byte commands and protobuf messages.
Simple Commands (CmdId-based):
# Format: [cmd_id, param_len, ...params]
command_data = bytes([CmdId.SET_SHUTTER, 0x01, shutter_value])
await self.ble.write(GoProBleUUID.CQ_COMMAND, command_data)
Protobuf Commands (FeatureId + ActionId):
# COHN configuration, WiFi management, etc.
command_data = bytes([feature_id, action_id]) + proto_message.SerializeToString()
await self.ble.write(GoProBleUUID.CQ_COMMAND, command_data)
HttpCommands¶
Implements commands executed via HTTP REST API.
Key Features:
- Automatic retry decorator for transient failures
- JSON response parsing
- Error handling with custom exceptions
@with_http_retry(max_retries=3)
async def get_camera_state(self) -> dict[str, Any]:
async with self.http.get("gopro/camera/state") as resp:
return await resp.json()
MediaCommands¶
Handles media file operations.
Key Features:
- File listing with `MediaFile` dataclass
- Streaming download with progress callback
- Turbo transfer mode support
WebcamCommands¶
Controls USB webcam mode.
Key Features:
- Resolution and FOV configuration
- Protocol selection (TS/RTSP)
- Status monitoring
Configuration Layer¶
CohnConfigManager¶
Persists COHN credentials using TinyDB.
Storage Format:
{
"camera_id": "1332",
"ip_address": "192.168.1.100",
"username": "gopro",
"password": "xxx",
"certificate": "-----BEGIN CERTIFICATE-----..."
}
TimeoutConfig¶
Centralized timeout configuration with sensible defaults.
@dataclass
class TimeoutConfig:
# BLE timeouts
ble_connect_timeout: float = 20.0
ble_response_timeout: float = 5.0
# HTTP timeouts
http_request_timeout: float = 30.0
http_download_timeout: float = 300.0
# WiFi timeouts
wifi_connect_configured_timeout: float = 15.0
wifi_provision_timeout: float = 60.0
# COHN timeouts
cohn_provision_timeout: float = 60.0
Exception Hierarchy¶
CustomGoProError (base)
├── BleConnectionError
│ └── BleTimeoutError
├── HttpConnectionError
├── CohnNotConfiguredError
└── CohnConfigurationError
Operating Modes¶
Offline Mode (Default)¶
BLE-only operation for scenarios without WiFi or when WiFi is unreliable.
Supported Features:
- Recording control (start/stop)
- Date/time synchronization
- Tag highlight
- Load preset / preset group
- Sleep
Not Supported:
- Preview stream (requires UDP)
- Media download (requires HTTP)
- Camera state queries (HTTP only)
- Webcam mode (USB)
Online Mode¶
BLE + WiFi/COHN for full functionality.
Connection Flow:
1. BLE connect
2. WiFi configuration (if credentials provided)
3. COHN credential retrieval/refresh
4. HTTP session initialization (lazy)
Dynamic Mode Switching:
async with GoProClient("1332") as client: # Starts in offline mode
await client.start_recording() # Works via BLE
# Switch to online when needed
await client.switch_to_online_mode(wifi_ssid="MyWiFi", wifi_password="pass")
await client.start_preview() # Now available
Multi-Camera Management¶
`MultiCameraManager` provides coordinated control of multiple cameras.
Key Features:
- Configurable concurrency (`max_concurrent`)
- Per-camera error isolation
- Batch operations with aggregated results
- Camera status tracking
async with MultiCameraManager(["1332", "1333", "1334"]) as manager:
await manager.connect_all()
# Execute on all cameras
results = await manager.execute_all(lambda c: c.start_recording())
# Get all statuses
statuses = manager.get_camera_status()
Data Flow Examples¶
BLE Command Execution¶
GoProClient.start_recording()
└─> BleCommands.set_shutter(True)
└─> Build command: [0x01, 0x01, 0x01]
└─> BleConnectionManager.write(CQ_COMMAND, data)
└─> BleakClient.write_gatt_char()
└─> BleConnectionManager.wait_for_response()
└─> Response queue.get()
└─> Parse response: [cmd_id, status_code]
HTTP Command Execution¶
GoProClient.get_camera_state()
└─> HttpCommands.get_camera_state()
└─> HttpConnectionManager.get("gopro/camera/state")
└─> aiohttp session request
└─> SSL verification with COHN cert
└─> Basic auth header
└─> Parse JSON response
└─> Return state dict
COHN Configuration Flow¶
GoProClient.open() [online mode]
└─> BleConnectionManager.connect()
└─> [Optional] BleCommands.connect_to_wifi(ssid, password)
└─> Check CohnConfigManager for existing credentials
└─> If not exists:
└─> BleCommands.configure_cohn()
└─> REQUEST_CREATE_COHN_CERT via BLE
└─> Wait for COHN_PROVISIONED state
└─> REQUEST_GET_COHN_CERT via BLE
└─> REQUEST_GET_COHN_STATUS via BLE
└─> CohnConfigManager.save(credentials)
└─> Else:
└─> Refresh IP address from camera
└─> HttpConnectionManager.set_credentials()
Extension Points¶
Adding New BLE Commands¶
- Add command method to `BleCommands`
- Use `CmdId` constants or define protobuf messages
- Handle response parsing
- Add convenience method to `GoProClient`
# In ble_commands.py
async def new_command(self, param: int) -> None:
command_data = bytes([CmdId.NEW_CMD, 0x01, param])
await self.ble.write(GoProBleUUID.CQ_COMMAND, command_data)
response = await self.ble.wait_for_response()
# Parse response...
Adding New HTTP Commands¶
- Add command method to `HttpCommands`
- Use `@with_http_retry` decorator
- Add to `GoProClient` with offline mode check
# In http_commands.py
@with_http_retry(max_retries=3)
async def new_command(self) -> dict[str, Any]:
async with self.http.get("gopro/new/endpoint") as resp:
return await resp.json()
# In client.py
async def new_command(self) -> dict[str, Any]:
self._require_online_mode("New command")
return await self.http_commands.new_command()
Custom Configuration Storage¶
Extend `CohnConfigManager` for alternative storage:
class RedisConfigManager(CohnConfigManager):
def save(self, camera_id: str, credentials: CohnCredentials) -> None:
# Save to Redis
pass
def load(self, camera_id: str) -> CohnCredentials | None:
# Load from Redis
pass
Performance Considerations¶
Connection Management¶
- BLE connection reuse - Single connection per camera session
- HTTP session pooling - `aiohttp.ClientSession` manages connection pool
- Lazy HTTP connection - Only connects when first HTTP request is made
Concurrency¶
- All I/O is async - Non-blocking operations throughout
- Multi-camera parallelism - `asyncio.gather()` with semaphore control
- Response queue - Efficient BLE notification dispatching
Timeout Tuning¶
`TimeoutConfig` allows fine-tuning based on network conditions: