The DWARF 3 Alpaca server bridges ASCOM Alpaca clients to a DWARF 3 smart telescope. It multiplexes several device-facing transports (websocket, HTTP/JSON, FTP, RTSP, BLE) behind a FastAPI application that exposes Alpaca Telescope/0, Camera/0, Focuser/0, and FilterWheel/0 endpoints.
+------------------+ UDP 32227 +------------------+ WS/HTTP/FTP/RTSP/BLE +---------------+
| Alpaca Clients | <---------> | Alpaca Server | <--------------------> | DWARF 3 Unit |
| (NINA, Voyager) | HTTP | (this project) | | |
+------------------+ +--------+---------+ +---------------+
|
v
DwarfSession
|
+-----------+--------+------+-----------+---------+
| | | | |
DwarfWsClient DwarfHttpClient DwarfFtpClient DwarfRtspClient DwarfBleProvisioner
| Area | Responsibilities |
|---|---|
src/dwarf_alpaca/server.py |
Builds the FastAPI app, registers Alpaca routers, configures structured logging, and coordinates the discovery service lifecycle. |
src/dwarf_alpaca/discovery.py |
UDP discovery responder that advertises device metadata and the correct server URL. |
src/dwarf_alpaca/cli.py |
Command-line interface for serve, start, guide, and provision. Handles BLE provisioning, connectivity checks, master-lock preflight, and logging. |
src/dwarf_alpaca/devices/ |
Alpaca routers for telescope, camera, focuser, and filter wheel. Each router translates Alpaca verbs to methods on DwarfSession and manages Alpaca state caching. |
src/dwarf_alpaca/dwarf/session.py |
Central orchestrator maintaining connections, state, caches, filter definitions, and background tasks (temperature monitor, FTP polling). |
src/dwarf_alpaca/dwarf/*.py |
Transport clients (ws_client, http_client, ftp_client, rtsp_client), BLE workflow, exposure resolver, and persistent state helpers. |
src/dwarf_alpaca/config |
Pydantic settings with environment/YAML overrides. |
src/dwarf_alpaca/management |
Alpaca management endpoints and health checks. |
tests/ |
pytest suite with fixtures for discovery, CLI flows, and device endpoint behaviour. |
DwarfSession wraps the transport clients and acts as the shared façade consumed by device routers:
- Lazily connects to the DWARF websocket and acquires the master lock, guarding access with async locks and reference counts per logical device.
- Tracks telescope slews, manual axis rates, joystick activity, and recent go-to targets to validate exposures.
- Manages camera state: exposure parameters, active capture task, FTP/album baselines, filter selection, gain tables, and temperature readings.
- Processes websocket notifications (focus, temperature, system status) and exposes cached values for Alpaca GET requests without extra network churn.
- Hosts background tasks (e.g., temperature polling loop) and ensures they are restarted on reconnect.
- Provides domain-level methods (
telescope_slew_to_coordinates,camera_start_exposure,set_filter_position,focuser_move, etc.) that precisely match Alpaca device needs.
- Telescope (
devices/telescope.py) – ImplementsITelescopeV3verbs. Handles connection toggles, slews, tracking, manual axis motion, UTC adjustments, and error translation for DWARF astro commands. - Camera (
devices/camera.py) – Maps Alpaca camera verbs to DWARF capture flow: gain/exposure index management, dark library validation, astro capture start/stop, and image delivery via FTP/album downloads. Also exposes sensor metadata, temperature, and state transitions. - Focuser (
devices/focuser.py) – Supports relative moves, halts, and connection state tied to DWARF focus notifications. Includes safety around concurrent motion tasks and range limits. - Filter wheel (
devices/filterwheel.py) – Loads filter definitions during startup, synchronises positions, applies IR-cut toggles, and exposes focus offsets for Alpaca clients. Integrates withpreload_filters()in the FastAPI lifespan hook. - Shared utilities (
devices/utils.py) – Common Alpaca response helpers, parameter coercion, and request context binding for consistent logging.
DwarfWsClient– Maintains a persistent websocket connection, correlates requests/responses, dispatches notifications, and implements ping keep-alives. RaisesDwarfCommandErrorfor non-zero DWARF error codes.DwarfHttpClient– Wraps DWARF REST endpoints with retry/backoff (album listing, parameter configs, mount status, exposure triggers).DwarfFtpClient– Polls the DWARF FTP server for new captures, supports astro and photo modes, and downloads final assets.DwarfRtspClient– (Optional) streams live-view frames using PyAV/aiortc, buffering recent frames for previews.exposure.ExposureResolver– Builds lookup tables from DWARF parameter configs, translating requested exposure durations and gains into firmware-specific indices.StateStore– Persists STA IP, Wi-Fi credentials, and failure history tovar/connectivity.json.DwarfBleProvisioner– Executes the BLE provisioning handshake, issuing GATT commands to set SSID/password and listen for STA IP notifications.
- Boot
- CLI loads settings, optionally merging YAML profiles.
startcommand provisions Wi-Fi (if requested), updates settings with saved STA IP, and runs a preflight session acquisition to ensure hardware access.run_serverbuilds the FastAPI app, preloads filters, configures structured logging, and starts the UDP discovery responder.
- Discovery
- UDP listener waits on
(discovery_interface, discovery_port)for Alpaca probes and replies with JSON describing the server, advertised host, and four logical devices.
- UDP listener waits on
- Connection lifecycle
- When an Alpaca client toggles
/connectedto true, the corresponding router callssession.acquire(<device>), incrementing reference counts and lazily connecting transports. Disconnects release the device and shut down transports when counts reach zero.
- When an Alpaca client toggles
- Slew & tracking
- Alpaca slews invoke
session.telescope_slew_to_coordinates, which halts manual motions, issuesReqGotoDSO, records the target, and spawns tasks to monitor completion. Abort and manual axis motions map to joystick vector commands with safety limits.
- Alpaca slews invoke
- Focusing
- Relative focus moves choose between single-step and continuous DWARF focus commands based on delta magnitude while listening for
ResNotifyFocusnotifications to update absolute position.
- Relative focus moves choose between single-step and continuous DWARF focus commands based on delta magnitude while listening for
- Filtering
- Filter names derive from DWARF parameter configs. Selecting a slot triggers IR-cut toggles and
ReqSetFeatureParamsinvocations while caching focus offsets per slot.
- Filter names derive from DWARF parameter configs. Selecting a slot triggers IR-cut toggles and
- Exposure pipeline
- Camera exposures ensure the camera is in manual mode, map durations/gain to indices, optionally warm up RTSP, check the dark library, and start astro capture via websocket. Results are pulled from FTP/album, decoded to numpy arrays, and stored in
CameraStatefor AlpacaImageArray/ImageBytesresponses.
- Camera exposures ensure the camera is in manual mode, map durations/gain to indices, optionally warm up RTSP, check the dark library, and start astro capture via websocket. Results are pulled from FTP/album, decoded to numpy arrays, and stored in
- Telemetry
- Temperature notifications update
CameraState.temperature_c; periodic polling keeps readings fresh. Telescope and camera states maintain timestamps to satisfy Alpaca GET semantics.
- Temperature notifications update
dwarf-alpaca guidecreates aDwarfBleProvisionerwith adapter/password hints.- BLE GATT operations push SSID/password, wait for STA association, and parse the reported IP.
- On success
StateStoreupdatesvar/connectivity.jsonwith the STA IP and cached credentials. Futurestartinvocations reuse this state automatically.
- State –
var/connectivity.jsonstores STA mode, IP, password cache, and last error. Additional runtime artefacts (last parameters, album baselines) live in memory insideDwarfSession. - Logging – Global logging uses
structlogJSON processors.cli.startadds a rotating file handler invar/logs/dwarf-alpaca-start.log. HTTP access logs flow through custom middleware.
FastAPI,uvicorn[standard]– HTTP server & ASGI runner.structlog– Structured logging.alpyca– Alpaca constants and helper structures.httpx– Async HTTP client for DWARF REST endpoints.bleak– Cross-platform BLE provisioning.numpy,PyAV,aiortc,opencv-python-headless– Image and stream handling.protobuf– Generated DWARF protocol messages.pytest,pytest-asyncio,ruff– Development tooling.
- Unit tests simulate websocket responses and FTP/HTTP interactions to validate session logic and Alpaca responses.
- CLI tests run
dwarf-alpacacommands via subprocess fixtures and assert logging/state outputs. - UDP discovery tests fire synthetic probes and verify payload contents and advertised hosts.
- Future work: hardware-in-the-loop regression tests once CI access to a DWARF unit is available.
- Stream live RA/Dec/Alt/Az feedback from firmware notifications instead of simulated motion when telemetry is present.
- Integrate RTSP preview frames into Alpaca camera endpoints for faster focus and plate solving.
- Expose additional Alpaca actions (e.g., park, pulseguiding, autofocus) as the DWARF API is reverse engineered.
- Harden TLS / authentication story for remote observatories and multi-user deployments.
- Build structured observability (metrics/tracing) for long-running sessions.