# PrintSpy ![Build Status](https://img.shields.io/github/actions/workflow/status/ccmpbll/printspy/build.yaml) ![Docker Image Size](https://img.shields.io/docker/image-size/ccmpbll/printspy/latest) ![Docker Pulls](https://img.shields.io/docker/pulls/ccmpbll/printspy.svg) ![License](https://img.shields.io/badge/License-AGPL--3.0-blue.svg) A self-hosted dashboard for monitoring multiple 3D printers — OctoPrint and PrusaLink — from a single web interface. This was built for my own personal use and the way I want to interact with my printers, as well as an ongoing experiment in agentic coding. > **Still Under Development** — expect rough edges, breaking changes, and evolving APIs. Feedback and contributions welcome. ## What it does Each printer gets a row in the dashboard: webcam/snapshot, progress/ETA, temps, and smart plug power state/control. Updates push live via SSE. Everything's configured through the settings page — no config files, no restart needed. ## Screenshots ![Dashboard](docs/screenshots/printspy-dashboard.png)
File Manager & Print History ![File Manager](docs/screenshots/printspy-files.png) ![Print History](docs/screenshots/printspy-history.png)
## Supported platforms - **PrusaLink** — supported, but only tested on MK4S and Core One - **OctoPrint** — technically supported, but nothing to test against at this point Plugin architecture — new platforms should be straightforward to add. ## Features ### Core (both platforms) - Real-time dashboard updates via SSE, no manual refresh - Print control — pause, resume, cancel - File Manager per printer — browse every file on its storage, one-click reprint, download, or delete, thumbnail and material/tool previews for every file (native on OctoPrint, cached from the file's own metadata on PrusaLink) - Print history per printer, with configurable retention (days, 0 = keep forever) and real thumbnails per entry - Pushover notifications — print started/checkpoints/complete/failed/error, each independently configurable: priority, sound, image source (camera/thumbnail/none), and custom title/message templates - Smart plug power control via a directly-configured Tasmota device, independent of any platform plugin, assignable to any printer — HTTP-direct (default) or MQTT (push-based, opt-in per plug) - Auto-off after idle timeout and thermal runaway protection (second layer on top of firmware protection) for printers with an assigned smart plug - Camera feed via [printspy-cam](https://github.com/ccmpbll/printspy-cam) (ESP32-CAM firmware), assignable to any printer — snapshot/live/plate-thumbnail toggle buttons under each card's image, with an optional global setting to hide the whole section for a printer while it's unreachable - Multi-user login with per-account passwords, no roles/tiers - Config backup/restore as YAML - Printer reordering, optional free-text "model" field, dark mode, responsive layout ### OctoPrint - Auto-detects camera stack (MJPEG or camera-streamer), printer name, and installed plugins - Native smart plug power control + energy monitoring, auto-detected via the Tasmota or PSU Control plugin - Layer progress display (needs the DisplayLayerProgress plugin) - Live webcam streaming, with a snapshot/live toggle ### PrusaLink - Snapshot only — no live stream from the printer's own camera (assign a printspy-cam for a live feed) - No native power control over the API — use a directly-configured smart plug instead - Keepalive ping — keeps idle printers' wifi up; paused while the printer is off - File upload from the dashboard, with an optional "print immediately" - PrusaLink printers with an assigned smart plug are polled only once a minute while offline with every plug off - Slicer print-host target — point PrusaSlicer/OrcaSlicer's "Send to printer" (PrusaLink mode) at PrintSpy, pinned to one specific printer; "Upload" relays automatically once the printer's online, "Upload and Print" also powers it on first if it's off — no manual step either way - Print history enriched with real per-print metadata — material, filament used/cost, layer height, duration vs. estimate, and per-tool breakdown for multi-material prints — parsed directly from the file's own slicer metadata, since none of this is exposed over PrusaLink's API ## Quick start ```bash docker run -d \ --name printspy \ -p 8080:8080 \ -v printspy-data:/data \ ccmpbll/printspy:latest ``` Open `http://:8080` — first run prompts you to create a login. Once in, click the settings gear and add your first printer. You'll need the printer's URL and OctoPrint API key or Prusalink user/pass. ### Docker Compose ```yaml services: printspy: image: ccmpbll/printspy:latest ports: - "8080:8080" volumes: - printspy-data:/data restart: unless-stopped volumes: printspy-data: ``` ## Configuration All printer management is done through the web UI — open the settings page to add, edit, reorder, and remove printers. No config files needed. ### Login First run redirects to a setup page to create the first account. Add or remove additional users from Settings → Users. No roles or permission tiers — every account has full access. ### Smart plugs OctoPrint printers with the Tasmota or PSU Control plugin installed get power control automatically — nothing to configure. For everything else (PrusaLink or an OctoPrint printer without the plugin), add a Tasmota device directly under Settings → Smart Plugs and assign it to a printer. Plugs are managed independently of printers, so deleting a printer unassigns its plug instead of deleting it. Each plug can use either connection mode: - **Direct HTTP** (default) — polled over the network on each printer's own poll interval, no extra setup - **MQTT** — push-based via a broker instead of polling; configure the broker under Settings → MQTT Broker (host:port, optional TLS/username/password), then set the plug's mode to MQTT and enter its Tasmota topic Both modes can be mixed freely across plugs. ### Cameras Any printer type can get a webcam feed by assigning a [printspy-cam](https://github.com/ccmpbll/printspy-cam) device under Settings → Cameras — useful for PrusaLink, which has no webcam support of its own. Assigning a camera overrides whatever webcam a printer's own plugin would otherwise show. Cameras are managed independently of printers, so deleting a printer unassigns its camera instead of deleting it. ### Slicer print targets Create a target under Settings → Slicer print targets, pinned to one printer. In PrusaSlicer or OrcaSlicer, add a physical printer using the **PrusaLink** host type and set: - **Host:** the URL shown on the target, `http://:/ingest/