A cross-platform .NET 8.0 print service that connects to a web application via SignalR and provides remote printing. Automatically detects the operating system and uses Windows Print (PowerShell/WMI) on Windows or CUPS (lpstat, lp) on Linux/macOS.
Web App (BasicWeigh, etc.)
|
+-- SignalR Hub (/scaleHub)
|
+-- Web Print Service (this)
|
+-- SumatraPDF (silent PDF printing)
|
+-- Physical Printer (USB, Network, Shared)
The service needs a silent PDF printing tool to send tickets to printers. It auto-detects in this order:
- SumatraPDF (recommended) — auto-installed via
wingetif not found - PDFtoPrinter.exe — if placed in the service directory
- Fallback — uses
Start-Process -Verb PrintTo(may open a dialog, not recommended for unattended use)
To manually install SumatraPDF:
winget install SumatraPDF.SumatraPDF
Or download from https://www.sumatrapdfreader.org/free-pdf-reader
Web App (BasicWeigh, etc.)
|
+-- SignalR Hub (/scaleHub)
|
+-- Web Print Service (this)
|
+-- CUPS (lpstat, lp, lpoptions)
|
+-- Physical Printer (USB, Network, IPP)
- Cross-Platform — automatically uses Windows Print or CUPS based on the OS
- Windows Integration — uses PowerShell
Get-Printer,Start-Process,Out-Printerto manage and print to local and network printers - CUPS Integration — uses
lpstat,lp,lpoptionsto manage and print to local printers on Linux/macOS - SignalR Connection — connects to any web app's SignalR hub (outbound only, works behind firewalls)
- Printer Discovery — announces available CUPS or Windows printers to the web app on connect/reconnect
- PDF Printing — downloads ticket PDFs from the web app and prints locally
- Test Print — send a test page to any connected printer from the web UI
- Swagger API — local REST API for configuration, testing, and diagnostics
- SQLite Settings — persistent configuration stored locally (no appsettings.json edits needed)
- Forever Retry — exponential backoff, never gives up reconnecting
# Windows
cd WebPrintService
dotnet run
# Linux / Raspberry Pi
sudo apt-get install cups
dotnet runReturns service health, print system type, and printer count.
Response:
{
"status": "ok",
"printSystem": "Windows",
"printSystemAvailable": true,
"printerCount": 3,
"printers": [
{ "printerId": "HP LaserJet", "displayName": "HP LaserJet", "status": "idle", "enabled": true, "isDefault": true }
]
}Returns API documentation as JSON including all endpoints, parameters, response formats, and SignalR methods.
List all printers with their status.
Response:
[
{
"printerId": "HP_LaserJet_Pro",
"displayName": "HP LaserJet Pro",
"status": "idle",
"isDefault": true,
"enabled": true
}
]Get detailed status of a specific printer.
Example: GET /api/printers/HP_LaserJet_Pro/status
Response:
{
"printerId": "HP_LaserJet_Pro",
"status": "printer HP_LaserJet_Pro is idle. enabled since Mon 24 Mar 2026 08:00:00 AM CDT"
}Send a test page to a printer. No request body needed.
Example: POST /api/printers/HP_LaserJet_Pro/test
Response:
{
"success": true,
"message": "Sent to HP_LaserJet_Pro"
}Get current service settings.
Response:
{
"id": 1,
"serviceId": "default",
"serverUrl": "http://localhost:5110",
"signalRHub": "/scaleHub"
}Update service settings. Triggers a reconnect to the web app.
Request:
{
"serviceId": "office-printer",
"serverUrl": "http://192.168.1.100:5110"
}Response:
{
"success": true,
"message": "Settings saved. Service restarting..."
}| Direction | Method | Description |
|---|---|---|
| Service -> Hub | JoinPrintGroup(serviceId) |
Join the PrintClients group |
| Service -> Hub | PrintServiceReady(announcement) |
Announce printers on connect |
| Service -> Hub | PrinterListResponse(data) |
Respond to printer list request |
| Service -> Hub | PrintResult(result) |
Report print job result |
| Service -> Hub | TestPrintResult(result) |
Report test print result |
| Hub -> Service | PrintTicket(data) |
Print a ticket PDF |
| Hub -> Service | GetPrinterList |
Request printer list |
| Hub -> Service | TestPrint(printerId) |
Send test page to printer |
| Hub -> Service | ReloadConfig |
Restart the service |
Uses PowerShell commands:
Get-Printer— enumerate all local and network printersStart-Process -Verb PrintTo— print PDFs (or SumatraPDF if installed)Out-Printer— print text filesrundll32 shimgvw.dll— print images- Optional: SumatraPDF for better PDF printing
Uses CUPS command-line tools:
lpstat -p -d— enumerate printers and defaultlp -d <printer>— print fileslpoptions -p <printer>— get printer options/description- CUPS web interface at https://localhost:631
SSH into the Pi and run:
git clone https://github.com/GTMichelli-Dev/web-print-service.git /tmp/wps
bash /tmp/wps/deploy/install.sh https://basicscale.scaledata.net
rm -rf /tmp/wpsWith options:
git clone https://github.com/GTMichelli-Dev/web-print-service.git /tmp/wps
bash /tmp/wps/deploy/install.sh https://basicscale.scaledata.net \
--service-id office --port 5230
rm -rf /tmp/wpsFor private repos, git will prompt for credentials. You can also use a deploy key or GitHub token.
Options:
| Option | Default | Description |
|---|---|---|
--service-id <id> |
default |
Unique ID for this service instance |
--port <port> |
5230 |
API/Swagger port |
--branch <branch> |
master |
Git branch to install |
--install-dir <path> |
/opt/web-print-service |
Install location |
What the script does automatically:
- Detects Pi architecture (arm64, armv7l, x64)
- Installs CUPS (printer system)
- Installs .NET 8 SDK and runtime permanently (skips download on future updates)
- Downloads latest source from GitHub
- Builds for the Pi's architecture
- Installs to
/opt/web-print-service - Preserves existing database on updates
- Registers and starts as a systemd service
Prerequisites: Just internet access and git (pre-installed on Raspberry Pi OS). No .NET, no CUPS — the script installs everything. The .NET SDK is installed permanently so future updates skip the download.
To update: Run the same command again. The script stops the service, updates files, preserves your database, and restarts.
After install:
- Swagger:
http://<pi-ip>:5230/swagger - CUPS Admin:
http://<pi-ip>:631 - Logs:
sudo journalctl -u web-print-service -f - Restart:
sudo systemctl restart web-print-service
Option A — Automated (as a Windows Service):
Run PowerShell as Administrator:
.\deploy\deploy-to-windows.ps1 -WebServerUrl "https://basicscale.scaledata.net"
# With options
.\deploy\deploy-to-windows.ps1 -WebServerUrl "https://basicscale.scaledata.net" `
-ServiceId "office" -Port 5230| Parameter | Default | Description |
|---|---|---|
-WebServerUrl |
(required) | BasicWeigh web server URL |
-ServiceId |
default |
Unique ID for this instance |
-Port |
5230 |
API/Swagger port |
-InstallDir |
C:\Services\WebPrintService |
Install location |
-ServiceName |
WebPrintService |
Windows service name |
What the script does: Installs .NET if needed, builds, installs to C:\Services\WebPrintService, preserves existing database, registers as a Windows Service with auto-restart on failure.
After install:
- Swagger:
http://localhost:5230/swagger - Manage:
services.msc> "Web Print Service" - Restart:
Restart-Service WebPrintService
Option B — Manual (for development):
cd WebPrintService
dotnet runWindows printers are auto-detected — no additional setup needed.
Initial config via appsettings.json:
{
"Print": {
"ServerUrl": "https://your-server",
"Port": "5230"
}
}After first run, use the API to update settings (persists to SQLite):
curl -X PUT http://localhost:5230/api/settings \
-H "Content-Type: application/json" \
-d '{"serverUrl": "https://your-server", "serviceId": "office-printer"}'