{"id":43140914,"url":"https://github.com/jonasclaes/go-thermal-printer","last_synced_at":"2026-01-31T22:15:58.079Z","repository":{"id":308732135,"uuid":"1033449357","full_name":"jonasclaes/go-thermal-printer","owner":"jonasclaes","description":"Thermal printer web API written in Go, with the EPSON TM-T88II in mind.","archived":false,"fork":false,"pushed_at":"2025-09-20T16:21:03.000Z","size":71,"stargazers_count":2,"open_issues_count":0,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-11-05T17:02:13.893Z","etag":null,"topics":["epson","golang","rest-api","thermal-printer","tm-t88ii"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jonasclaes.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-08-06T20:43:53.000Z","updated_at":"2025-09-20T16:20:52.000Z","dependencies_parsed_at":"2025-08-07T16:05:34.718Z","dependency_job_id":null,"html_url":"https://github.com/jonasclaes/go-thermal-printer","commit_stats":null,"previous_names":["jonasclaes/go-thermal-printer"],"tags_count":19,"template":false,"template_full_name":null,"purl":"pkg:github/jonasclaes/go-thermal-printer","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonasclaes%2Fgo-thermal-printer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonasclaes%2Fgo-thermal-printer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonasclaes%2Fgo-thermal-printer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonasclaes%2Fgo-thermal-printer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jonasclaes","download_url":"https://codeload.github.com/jonasclaes/go-thermal-printer/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonasclaes%2Fgo-thermal-printer/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28957399,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-31T18:30:42.805Z","status":"ssl_error","status_checked_at":"2026-01-31T18:30:19.593Z","response_time":128,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["epson","golang","rest-api","thermal-printer","tm-t88ii"],"created_at":"2026-01-31T22:15:57.475Z","updated_at":"2026-01-31T22:15:58.074Z","avatar_url":"https://github.com/jonasclaes.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n# go-thermal-printer\n\nLightweight, concurrent-friendly REST API for printing to ESC/POS thermal printers (currently verified only on EPSON TM-T88II; other ESC/POS models may work but are untested). Supports raw ESC/POS bytes, template rendering with helpers (bold, underline, italics, font B), status querying, and containerized deployment.\n\n![License](https://img.shields.io/badge/license-MIT-green)\n![Go Version](https://img.shields.io/github/go-mod/go-version/jonasclaes/go-thermal-printer)\n![Status](https://img.shields.io/badge/api-v1-blue)\n\n\u003c/div\u003e\n\n## ✨ Features\n\n- RESTful API (Gin) with versioned routes under `/api/v1`\n- Print raw ESC/POS byte payloads (Base64 friendly for JSON transport)\n- Print using text templates with variable substitution\n- Built-in template functions: `bold`, `underline`, `italic` / `italics`, `fontb`\n- Safe sequential hardware access via internal print/status worker \u0026 channels\n- Query printer status (printer, offline, error, paper) via dedicated endpoint\n- Configurable via TOML + `CONFIG_PATH` environment variable override\n- Docker \u0026 docker-compose ready\n- Graceful context-based timeouts for print/status ops\n\n## 🖨 Supported Printer\n\n| Model | Status | Notes |\n|-------|--------|-------|\n| EPSON TM-T88II | ✅ Verified | Development \u0026 runtime testing performed on this model |\n| Other ESC/POS printers | ? (Untested) | May work if they implement standard ESC/POS command set \u0026 status bytes |\n\nIf you successfully run another model, consider opening an issue so it can be listed.\n\n## 🔢 Versioning \u0026 Releases\n\nSemantic Versioning (MAJOR.MINOR.PATCH):\n\n- Backwards-compatible additions increase MINOR\n- Bug fixes increase PATCH\n- Breaking API / contract changes increase MAJOR\n\n\"Latest\" references the most recent published (non pre-release) GitHub Release tag (e.g. `v1.2.3`). Pre-releases (like `v1.3.0-rc1`) are not treated as \"latest\" for documentation examples.\n\nSuggested Docker pull pattern (after first release is published):\n```bash\n# Exact version\ndocker pull ghcr.io/jonasclaes/go-thermal-printer:v1.2.3\n# Track latest stable\ndocker pull ghcr.io/jonasclaes/go-thermal-printer:latest\n```\n\nUntil a first stable release is cut, treat `main` (or the feature branch you build from) as potentially unstable.\n\n## 🧱 Architecture Overview\n\n```\n┌────────────┐   HTTP (Gin)    ┌────────────────┐        ┌────────────────────┐\n│  Client    │ ──────────────▶ │  Controllers   │ ─────▶ │  PrinterService    │\n└────────────┘                 └────────────────┘        │ (timeout wrapper)  │\n                                                 ▲                       └─────────┬──────────┘\n                                                 │                                 │ channels\n                                                 │                       ┌─────────▼──────────┐\n                                                 │                       │   PrintService     │\n                                                 │                       │  (worker goroutine)│\n                                                 │                       ├─────────┬──────────┤\n                                                 │                       │ printQ   │ statusQ │\n                                                 │                       └────┬─────┴────┬────┘\n                                                 │                            │          │\n                                                 │                            ▼          ▼\n                                                 │                        ESC/POS    ESC/POS\n                                                 │                        serial hw  status cmds\n                                                 │\n                                        Templates (text/template + helper funcs)\n```\n\n## 📦 Endpoints\n\n### Authentication: API Key\n\nAll API endpoints require an API key for access. Include the `X-Api-Key` header in your requests:\n\n```http\nX-Api-Key: \u003cyour-api-key-here\u003e\n```\n\nThe API key is configured in your TOML file under the `[server]` section as `api_key`.\n\nExample `config.toml`:\n\n```toml\n[server]\nhost = \"127.0.0.1\"\nport = 8080\napi_key = \"your-secret-key\"\n```\n\nIf the header is missing or invalid, requests will be rejected with an authentication error.\n\n\nBase URL: `http://\u003chost\u003e:\u003cport\u003e` (default `http://127.0.0.1:8080`)\n\n| Method | Path | Description |\n|--------|------|-------------|\n| GET | `/health` | Liveness probe |\n| GET | `/api/v1/printer/status` | Returns raw status bytes (printer/offline/error/paper) |\n| POST | `/api/v1/printer/print` | Print raw ESC/POS payload (JSON) |\n| POST | `/api/v1/printer/print-template` | Render \u0026 print template file with variables |\n\n### Request / Response Examples\n\nHealth:\n```http\nGET /health\n200 OK\n```\n\nPrinter status:\n```http\nGET /api/v1/printer/status\nX-Api-Key: \u003cyour-api-key-here\u003e\n200 OK\n{\n   \"printerStatus\": 0,\n   \"offlineStatus\": 0,\n   \"errorStatus\": 0,\n   \"continuousPaperStatus\": 0\n}\n```\n\nRaw print (data is Base64 in example):\n```http\nPOST /api/v1/printer/print\nX-Api-Key: \u003cyour-api-key-here\u003e\nContent-Type: application/json\n\n{\n   \"data\": \"G3QT1Qo=\"\n}\n```\n\nTemplate print:\n```http\nPOST /api/v1/printer/print-template\nX-Api-Key: \u003cyour-api-key-here\u003e\nContent-Type: application/json\n\n{\n   \"templateFile\": \"templates/receipt.tmpl\",\n   \"variables\": {\n      \"storeName\": \"Coffee \u0026 More\",\n      \"date\": \"2025-08-07\",\n      \"time\": \"14:30:25\",\n      \"orderNumber\": \"67890\",\n      \"items\": {\"Coffee\": 3.50, \"Sandwich\": 8.75},\n      \"subtotal\": 12.25,\n      \"tax\": 0.98,\n      \"total\": 13.23,\n      \"customerName\": \"Jane Smith\"\n   }\n}\n```\n\n## 🧪 Template System\n\nTemplates are standard Go `text/template` files. Example (`templates/receipt.tmpl`):\n\n```gotemplate\n{{ bold .storeName }}\\n\nOrder: {{ .orderNumber }}\\n\n{{ underline \"Items\" }}\\n\n{{ range $name, $price := .items -}}\n{{$name}}  {{$price}}\\n\n{{ end -}}\n\nSubtotal: {{ .subtotal }}\\n\nTax: {{ .tax }}\\n\nTOTAL: {{ bold (printf \"%.2f\" .total) }}\\n\nThank you {{ italic .customerName }}!\\n\n```\n\nCustom helpers wrap ESC/POS commands, producing styled output directly.\n\n## ⚙️ Configuration\n\nEnvironment variable:\n- `CONFIG_PATH` (default: `config.toml` if present, else falls back to embedded defaults)\n\nTOML structure:\n```toml\n[server]\nhost = \"127.0.0.1\"\nport = 8080\n\n[printer]\nport = \"/dev/ttyUSB0\"   # e.g. Linux /dev/ttyUSB0, macOS /dev/tty.usbserial*, Windows COM3\nbaud_rate = 19200\ndata_bits = 8\nstop_bits = 1            # 1 or 2\nparity = 0               # 0=None,1=Odd,2=Even,3=Mark,4=Space\n```\n\n### Selecting the Serial Port\n\nList ports (Linux):\n```bash\nls /dev/ttyUSB* /dev/ttyACM* 2\u003e/dev/null\n```\nGrant permissions (temporary):\n```bash\nsudo chmod a+rw /dev/ttyUSB0\n```\nPersistent approach: add your user to `dialout` (Linux):\n```bash\nsudo usermod -aG dialout $USER\n```\n\n## 🚀 Quick Start (Local)\n\n```bash\ngit clone https://github.com/jonasclaes/go-thermal-printer.git\ncd go-thermal-printer\ncp config.example.toml config.toml\n# adjust printer.port etc.\ngo run ./cmd/go-thermal-printer\n```\n\nTest health:\n```bash\ncurl http://localhost:8080/health\n```\n\nPrint using `requests.http` (REST Client / curl). Ensure Base64 content decodes to valid ESC/POS.\n\n## 🐳 Docker\n\nBuild image:\n```bash\ndocker build -t go-thermal-printer .\n```\n\nRun (Linux example):\n```bash\ndocker run --rm \\\n   --device /dev/ttyUSB0 \\\n   -p 8080:8080 \\\n   -v $(pwd)/config.docker.toml:/app/config.toml \\\n   -v $(pwd)/templates:/app/templates:ro \\\n   -e CONFIG_PATH=/app/config.toml \\\n   go-thermal-printer\n```\n\ndocker-compose:\n```bash\ndocker compose up --build\n```\n\nNote: container must access the serial device (`--device`). On macOS with USB adapters inside Docker Desktop, device passthrough may vary.\n\n## 🔐 Security Considerations\n\n- Expose only on trusted networks; unauthenticated print endpoints can be abused\n- Consider adding an API key / auth proxy in front (future enhancement)\n- Validate template variables if coming from untrusted clients\n\n## 🧵 Concurrency Model\n\nAll serial I/O is funneled through a single worker goroutine (`PrintService.worker`) using channels:\n- `printQueue` (buffered) for print jobs\n- `statusQueue` for status requests\nThis ensures commands never interleave on the serial line.\n\n## ⏱ Timeouts\n\nEach public operation (print/status) wraps requests with a 10s context timeout in `PrinterService`. Adjust there if needed.\n\n## 🧰 Development\n\nRun with live reload (suggested tool [air] or [fresh]) – not included by default. Minimal flow:\n```bash\ngo build ./...\ngo test ./...\ngo run ./cmd/go-thermal-printer\n```\n\n## 🐛 Troubleshooting\n\n| Symptom | Cause | Fix |\n|---------|-------|-----|\n| Permission denied opening port | User lacks group / device perms | Add user to `dialout`, adjust udev rules |\n| Garbled characters | Wrong baud / code page | Match printer settings; ensure `baud_rate` correct |\n| Nothing prints | Wrong port or cable | Verify port exists; try different USB adapter |\n| Status always zero | Printer not replying / flow control | Confirm printer supports status commands \u0026 cable supports bi-directional comm |\n| Template styles not applied | Printer resets unexpectedly | Ensure initialization not overridden mid-print |\n\nEnable verbose serial debugging by wrapping `serial.Open` with logging (not yet built-in – PRs welcome).\n\n## 🛣 Roadmap (Ideas)\n\n- [ ] Authentication / API key middleware\n- [ ] Structured logging (zap / zerolog)\n- [ ] Metrics (Prometheus endpoint)\n- [ ] Graceful shutdown \u0026 port close on SIGTERM\n- [ ] Support for images / QR codes\n- [ ] Hot reload of templates\n- [ ] Pluggable transport (network printers / USB raw)\n\n## 🤝 Contributing\n\nFork, create a feature branch, open a PR. Please include:\n- Description \u0026 rationale\n- Tests if adding logic\n- Updated docs / examples\n\n## 📄 License\n\nMIT – see `LICENSE`.\n\n## 🙋 FAQ\n\nQ: How do I generate raw ESC/POS bytes?\nA: Use a library or record printer output; encode the bytes (Base64) for JSON. You can also craft templates using helper functions.\n\nQ: Can I run multiple printers?\nA: Not yet; would require managing multiple `PrintService` instances bound to different serial ports (future enhancement).\n\nQ: Does it support Windows?\nA: Should, provided the serial library can open `COMx` ports. Adjust config accordingly.\n\n---\n\nQuestions or feature requests? Open an issue.\n\n\u003c/div\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjonasclaes%2Fgo-thermal-printer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjonasclaes%2Fgo-thermal-printer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjonasclaes%2Fgo-thermal-printer/lists"}