feat: moonraker websocket status worker#25
Merged
Conversation
Adds a long-running asyncio worker that holds a persistent WebSocket connection to each configured Moonraker printer, consumes status notifications, and updates PrintQueue entries in the database. This finally makes server-side print-end detection work without relying on an open browser tab. Architecture: - core/services/moonraker_ws.py: one websocket per printer with exponential reconnect backoff. Subscribes to print_stats, virtual_sdcard, display_status, heater_bed, extruder. Supports Moonraker oneshot-token auth via /access/oneshot_token when an API key is configured; connects unauthenticated otherwise. - core/services/printer_status_sync.py: pure sync event -> DB logic. Handles notify_history_changed (primary job-end trigger) and notify_status_update (progress + fallback terminal detection). Progress writes are throttled to PROGRESS_WRITE_INTERVAL (5s) per entry to keep SQLite happy. - core/management/commands/moonraker_worker.py: the process entry point. Enables SQLite WAL + busy_timeout, runs an asyncio.TaskGroup with a reconciler task that polls PrinterProfile every WORKER_RELOAD_INTERVAL seconds (default 10s) and spawns/cancels/ restarts per-printer tasks on add/remove/change — live reload, no container restart needed on printer config changes. Model & view changes: - PrintQueue gains progress, status_updated_at, last_error fields (migration 0016). - QueueCheckPrinterStatusView no longer talks to Moonraker directly. It reads the worker-populated DB fields and exposes a worker_stale flag when status_updated_at is older than 60s (or absent). Response shape stays backwards compatible for the existing polling.js. - moonraker.get_job_status() now reuses a shared map_klipper_state() helper so the WS worker and the REST client stay in sync. Deployment: - New docker-compose service `worker` using the same release image, entrypoint overridden to skip migrate/collectstatic (web runs them). - requirements.txt: add websockets>=14,<16. Tests (65 total across affected modules): - test_services_printer_status_sync: history_changed transitions, throttled progress writes, terminal state via notify_status_update, last_error population. - test_services_moonraker_ws: build_ws_url and fetch_oneshot_token helpers. - test_management_moonraker_worker: reconciler spawn/cancel/restart with a fake task group (IsolatedAsyncioTestCase). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds a server-side Moonraker WebSocket worker that keeps printer status/progress up to date in the DB, enabling reliable print-end detection without relying on browser polling.
Changes:
- Introduces a long-running
moonraker_workermanagement command that maintains per-printer WebSocket connections and live-reloads printer config changes. - Adds
PrintQueue.progress,PrintQueue.status_updated_at, andPrintQueue.last_errorfields plus sync event→DB logic (printer_status_sync). - Updates the queue status polling endpoint to read from DB fields and emit a
worker_stalehint instead of calling Moonraker directly.
Reviewed changes
Copilot reviewed 11 out of 14 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| requirements.txt | Adds websockets dependency needed for the Moonraker WS client. |
| docker-compose.yml | Adds a dedicated worker service to run the management command. |
| core/views/queue.py | Changes /api/queue/<pk>/check-status/ to serve worker-populated DB status/progress (+ staleness). |
| core/models/queue.py | Adds progress, status_updated_at, last_error fields to PrintQueue. |
| core/migrations/0016_printqueue_progress_fields.py | Migration adding the new PrintQueue fields. |
| core/services/moonraker.py | Adds shared map_klipper_state() helper for consistent state normalization. |
| core/services/moonraker_ws.py | Implements the Moonraker JSON-RPC WebSocket client + oneshot token helper. |
| core/services/printer_status_sync.py | Adds sync notification handling + throttled progress writes + terminal transitions. |
| core/management/commands/moonraker_worker.py | Adds the asyncio TaskGroup worker/reconciler process entry point and SQLite WAL tuning. |
| core/tests/test_services_printer_status_sync.py | Unit tests for event→DB transition logic and progress throttling. |
| core/tests/test_services_moonraker_ws.py | Unit tests for WS URL building and oneshot-token fetching behavior. |
| core/tests/test_management_moonraker_worker.py | Tests for reconciler behavior (spawn/cancel/restart). |
Preview image readyBy tag: By digest: Commit: |
- moonraker_worker._main(): cancel all per-printer tasks in addition to the reconciler on shutdown. Without this, the TaskGroup context manager would hang waiting for printer tasks that never observe the stop_event, so SIGTERM/SIGINT wouldn't terminate the worker promptly. The active map is now shared between _main and the reconciler so _main can see and cancel spawned tasks. - moonraker_ws.MoonrakerWebSocketClient: reset the reconnect backoff to RECONNECT_INITIAL_DELAY after every successful subscribe. Previously a transient outage could permanently inflate the reconnect sleep up to RECONNECT_MAX_DELAY. Stored as an instance attribute so _connect_and_listen can reset it from the happy path. - printer_status_sync._transition_to_review(): clear last_error on successful completion so a stale failure from a previous retry doesn't leak into a later successful one. Added a regression test. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a long-running asyncio worker that holds a persistent WebSocket connection to each configured Moonraker printer, consumes status notifications, and updates
PrintQueueentries in the database. This finally fixes the original problem: server-side print-end detection now works without relying on an open browser tab.Builds on the `PrinterBackend` abstraction from #24.
Architecture
Model & View Changes
Deployment
Test Plan
Out of Scope
🤖 Generated with Claude Code