A Python library to manage a Zebra printer fleet and an API for ZPL print requests.
Project description
zebra_day
Build, Deploy, Run, Monitor, Teardown
# Build & Install (development mode)
pip install -e ".[dev]"
# Activate environment (for development)
source zday_activate # Sets up env, installs package, enables tab completion
# Your prompt will change to: (zday) $
# Deactivate environment
source zday_deactivate # Restores original PATH and prompt
# Run Tests
pytest -v
# Run Linting (requires pip install -e ".[lint]")
ruff check zebra_day tests
ruff format --check zebra_day tests
mypy zebra_day --ignore-missing-imports
# CLI Commands
zday --help # Show all commands
zday bootstrap # First-time setup: scan for printers
zday gui start # Start web UI in background
zday gui stop # Stop web UI
zday gui status # Check if web UI is running
# Health Checks (use https:// if certificates are configured, http:// otherwise)
curl https://localhost:8118/healthz # Basic health check
curl https://localhost:8118/readyz # Readiness check (printer mgr initialized)
# API Documentation
# Visit https://localhost:8118/docs for interactive OpenAPI docs
# Visit https://localhost:8118/redoc for alternative API documentation
| * auto discovery * of networked printers | ui configurable printer fleet details | zpl template drafting & live ui preview |
| monitor printer fleet status in one dashboard | simple and powerful python package offers ability to include barcode label printing in other s/w systems | fast and straight forward deployment and maintaince |
| directly access each printers admin console | integrate with other systems (Salesforce, AWS) | simple print API endpoints (commercial alternatives are quite expensive, and often offer less) |
For The Impatient
-
* Verify there are zebra printers connected & powered up to the same network that the PC you are installing this s/w to is connected.
python --version # should be 3.10+ # advisable to run in some kind of venv
pip install zebra_day
# You should load a fresh env / open a fresh terminal so the package is available
# First-time setup: scan network for printers and initialize configuration
zday bootstrap
# Start the web UI (runs in background)
zday gui start
# Web UI available at http://0.0.0.0:8118
# Or start in foreground (for debugging)
zday gui start --foreground
# Check status
zday gui status
zday info
# Stop the server
zday gui stop
CLI Reference
The zday CLI provides a comprehensive interface for managing your Zebra printer fleet.
Global Flags
| Flag | Description |
|---|---|
--json / -j |
Emit machine-readable JSON output (applies to all commands) |
--help |
Show help for any command or subcommand |
# Get help on any command
zday --help
zday gui --help
zday printer --help
# JSON output for any command
zday --json info
zday --json printer list --live
zday -j dynamo status
# Core commands
zday info # Show version, config paths, server status
zday status # Show printer fleet status, service health
zday bootstrap # First-time setup: scan network, initialize config
# GUI server management
zday gui start [--auth none|cognito] [--host HOST] [--port PORT] [--cert FILE] [--key FILE] [--no-https]
zday gui stop
zday gui status
zday gui logs [--tail N] [--follow]
zday gui restart
# Printer management (ZPL-first discovery, port 9100)
zday printer scan [--ip-stub IP] # Scan via ZPL port 9100 (default)
zday printer scan --ip-stub 192.168.1 --scan-http-port 80 # Also probe HTTP
zday printer list [--lab LAB] # List configured printers (static config)
zday printer list --live # Query live status (Status + State)
zday printer test PRINTER_NAME # Send test print
# Template management
zday template list # List ZPL templates
zday template preview TEMPLATE # Generate PNG preview
zday template edit TEMPLATE # Open in editor
zday template show TEMPLATE # Display template contents
# Configuration management
zday config init # Initialize config from template
zday config show # Display current config
zday config path # Print path to config file
zday config validate # Validate config schema
zday config edit # Open config in $EDITOR
zday config reset [--force] # Reset config to template defaults
# Environment management (development)
zday env status # Show if environment is active
zday env activate # Show command to activate environment
zday env deactivate # Show command to deactivate environment
zday env reset # Show command to reset (deactivate + reactivate)
# Cognito authentication (requires pip install -e ".[auth]")
zday cognito status # Show auth configuration
zday cognito info # Setup instructions
# DynamoDB shared configuration (opt-in)
zday dynamo init # Create DynamoDB table + S3 bucket
zday dynamo status # Show table/bucket status
zday dynamo bootstrap # Push local config & templates to DynamoDB
zday dynamo export # Pull DynamoDB config & templates to local files
zday dynamo backup # Trigger immediate S3 backup snapshot
zday dynamo restore # Restore DynamoDB from S3 backup
zday dynamo destroy # Delete DynamoDB table (preserves S3 backups)
# Printer simulator (for testing without physical printers)
zday simulator start [--foreground] [--model MODEL] [--serial SN]
zday simulator stop [--all]
zday simulator list
# Interactive documentation browser
zday man # Interactive topic menu
zday man quickstart # View specific topic
zday man --list # List all topics
zday man --search "dynamo" # Search docs for a term
DynamoDB CLI Reference (zday dynamo)
Opt-in shared configuration via AWS DynamoDB with S3 backup snapshots. Local-file mode remains the default.
| Command | Description | Key Options |
|---|---|---|
zday dynamo init |
Create DynamoDB table and S3 bucket | --table-name, --region, --s3-bucket, --profile, --cost-center, --project, --skip-checks |
zday dynamo status |
Show table and S3 backup status | --create-s3-if-missing (use global --json flag) |
zday dynamo bootstrap |
Push local config + templates to DynamoDB | --config-file, --templates-dir, --include-package/--no-include-package, --create-s3-if-missing |
zday dynamo export |
Pull DynamoDB config + templates to local files | --output-dir, `--format json |
zday dynamo backup |
Trigger immediate S3 backup snapshot | --create-s3-if-missing |
zday dynamo restore |
Restore DynamoDB from an S3 backup | --s3-key, --list, --yes |
zday dynamo destroy |
Delete DynamoDB table (preserves S3) | --yes (required safety gate) |
DynamoDB Environment Variables
| Variable | Description | Default |
|---|---|---|
ZEBRA_DAY_CONFIG_BACKEND |
Set to dynamodb to enable DynamoDB mode |
local |
ZEBRA_DAY_DYNAMO_TABLE |
DynamoDB table name | zebra-day-config |
ZEBRA_DAY_DYNAMO_REGION |
AWS region | us-west-2 |
ZEBRA_DAY_S3_BACKUP_BUCKET |
S3 bucket for config backups | (none — prompted interactively) |
ZEBRA_DAY_S3_BACKUP_PREFIX |
S3 key prefix | zebra-day/ |
AWS_PROFILE |
AWS profile name (must not be default) |
(none) |
Quick DynamoDB Workflow
# 1. Create table + bucket
export AWS_PROFILE=my-profile
zday dynamo init --region us-west-2 --s3-bucket zebra-day-cfg-us-west-2
# 2. Push local config & templates
zday dynamo bootstrap --create-s3-if-missing
# 3. Confirm status
zday dynamo status
# 4. Switch a running GUI to DynamoDB (session-only, via web UI Config page)
# or set env vars and restart:
export ZEBRA_DAY_CONFIG_BACKEND=dynamodb
zday gui restart
Documentation Browser (zday man)
Browse built-in documentation topics from the terminal using Rich-rendered Markdown.
zday man # Interactive topic picker
zday man quickstart # Jump to a topic by slug
zday man --list # List all topics with descriptions
zday man --search "template" # Full-text search across all docs
Printer Simulator (zday simulator)
A mock Zebra printer simulator for testing and development without physical printers.
The simulator responds to standard ZPL queries (~HI, ~HS, ~HQSN, ~HQOD, ~HQES)
on a configurable TCP port (default 9100) and serves a Zebra-like HTTP page.
# Start a simulator in the foreground (Ctrl+C to stop)
zday simulator start --foreground --model "ZT411-300dpi ZPL" --serial DEMO001
# Start in the background (default host 0.0.0.0)
zday simulator start --model "ZD620-203dpi ZPL" --serial LAB01
# Simulate error conditions
zday simulator start --foreground --paper-out # Paper-out condition
zday simulator start --foreground --paused # Paused state
# List running simulators
zday simulator list
# Stop all running simulators
zday simulator stop --all
# Stop a specific simulator
zday simulator stop --host 0.0.0.0 --zpl-port 9100
| Option | Default | Description |
|---|---|---|
--host / -b |
0.0.0.0 |
Bind address |
--zpl-port / -z |
9100 |
ZPL TCP port |
--http-port / -p |
18080 |
HTTP status port |
--model / -m |
ZD620-203dpi ZPL |
Reported model string |
--serial / -s |
SIM1001 |
Reported serial number |
--foreground / -f |
false |
Block until Ctrl+C |
--paper-out |
false |
Simulate paper-out error |
--ribbon-out |
false |
Simulate ribbon-out error |
--head-up |
false |
Simulate head-up error |
--paused |
false |
Simulate paused state |
Network Scanner (ZPL-First)
The printer scanner probes port 9100 (ZPL) by default, sending the ~HI host identification
query. This is more reliable than HTTP-based discovery since port 9100 is the standard
Zebra printer protocol port.
# Default: ZPL-only scan (port 9100)
zday printer scan --ip-stub 192.168.1
# With optional HTTP fallback (also probe port 80)
zday printer scan --ip-stub 192.168.1 --scan-http-port 80
# JSON output
zday --json printer scan --ip-stub 192.168.1
The notes field in discovered printers records the discovery method:
"zpl"— found via ZPL port 9100 only"http(80)"— found via HTTP only"zpl+http(80)"— found via both ZPL and HTTP
Migration from 0.5.x
The old commands zday_start and zday_quickstart still work but are deprecated:
| Old Command | New Command |
|---|---|
zday_quickstart |
zday bootstrap && zday gui start |
zday_start |
zday gui start |
zday_start --auth cognito |
zday gui start --auth cognito |
Environment Activation
For development, use the provided activation scripts to set up your environment:
# Activate the development environment
source zday_activate
# Your prompt changes to show the active environment:
# (zday) $
# Check environment status
zday env status
The activation script:
- Activates the Python virtual environment (
.venv) - Installs zebra_day in development mode
- Enables tab completion for the
zdayCLI - Sets the
(zday)prefix in your shell prompt (cyan color)
Deactivation:
# Deactivate and restore your original environment
source zday_deactivate
Reset (for troubleshooting):
# If environment seems broken, reset it cleanly:
source zday_deactivate && source zday_activate
Activation Script Flags:
| Flag | Description |
|---|---|
--install-completion |
Force reinstall persistent tab completion |
--no-completion |
Skip tab completion setup |
Local HTTPS Setup
The zebra_day web UI supports HTTPS for secure local development. By default, HTTPS is enabled if certificates are found.
Note: The
zday bootstrapcommand automatically generates certificates if mkcert is installed and the CA is configured. Manual certificate generation (below) is only needed if you skip bootstrap or want custom hostnames.
One-Time Setup (mkcert)
# Install mkcert
# macOS
brew install mkcert
# Ubuntu/Debian
sudo apt install mkcert
# Create and install local CA (one-time, requires password)
mkcert -install
Automatic Certificate Generation (Recommended)
After installing mkcert and running mkcert -install, the bootstrap command will automatically generate certificates:
zday bootstrap
# Output includes:
# ✓ Certificates generated: ~/.config/zebra_day/certs/server.crt
Manual Certificate Generation (Alternative)
If you need to manually generate certificates (e.g., for custom hostnames):
# Create certificate directory
mkdir -p ~/.config/zebra_day/certs
# Generate locally-trusted certificates
mkcert -cert-file ~/.config/zebra_day/certs/server.crt \
-key-file ~/.config/zebra_day/certs/server.key \
localhost 127.0.0.1 ::1
HTTPS Options
| Method | Description |
|---|---|
| Auto-detect | If certs exist in ~/.config/zebra_day/certs/, HTTPS is enabled automatically |
| Explicit paths | zday gui start --cert /path/to/cert.crt --key /path/to/key.key |
| Environment vars | Set SSL_CERT_PATH and SSL_KEY_PATH |
| Force HTTP | zday gui start --no-https |
Verify HTTPS
# Start server (will use HTTPS if certs are found)
zday gui start
# Test connection
curl https://localhost:8118/healthz
Troubleshooting
| Issue | Solution |
|---|---|
| Browser shows "Not Secure" | Run mkcert -install to trust the local CA |
| Certificate not found | Check paths: ls ~/.config/zebra_day/certs/ |
| Permission denied | Ensure cert files are readable: chmod 644 ~/.config/zebra_day/certs/* |
| Want HTTP only | Use --no-https flag: zday gui start --no-https |
It Is 3+ Things
(1) Zebra Printer Management & Configuration
(2) ZPL Label Template Tools
(3) A Python Library To Manage Formulating & Sending Label Print Requests
(bonuses) * a web gui to make some of the above more approachable && expose (3) as a http API. * Documentation sufficent for organization to successfuly assemble & deploy a reasonalbly sized barcoding system in your operational environment in potentially weeks. * ... and cheaply! a 10 printer install could cost ~$5,000.00 in purchases. With ongoing operational expenses of ~$150/mo (depends on label stock consumption mostly).
And It Is Not
- An Identify Generating Authority
- you will need to produce your own UID/GUID/etc. This can be manual, spreadsheets, custom code, various RDBMS, LIMS systems, Salesforce... but should not be tangled in this package.
- also, METADATA regaring your UID is important as these metadata can be presented on the labels in addition to the human readable and scannable representation of the provided UID. Unique Identifier Maxims.
- you will need to produce your own UID/GUID/etc. This can be manual, spreadsheets, custom code, various RDBMS, LIMS systems, Salesforce... but should not be tangled in this package.
Getting Started
- Daylily is available to lead or contribute to the building and deployment of universal barcoding systems to your organizations operations. Daylily offers expertise with the entire process from evaluating existing operations systems, proposing integration options, securing all hardware, deploying hardware and software, and importantly: connecting newly deployed barcoding services to other LIS systems.
- Tested and runs on MAC and Ubuntu (but other flavors of Linux should be no problem). Windows would be a rather large hassle, though presumably possible.
- conda and mamba installed. This is not, in fact, a blocking requirement, but other env setups have not been tested yet. for MAC users, it may be advisable to install conda with homebrew.
- create conda environment
ZDAY, which will be used to run the UImamba create -n ZDAY -c conda-forge python==3.10 pip ipython
- create conda environment
- For MAC address discovery,
arpshould be installed (both for MAC and Linux).
Facilitated :: Daylily Orchestrated Build and Deploy ( deliverable in ~1month )
Universal Barcoding Capability Project Timing Estimates
Requirements
Nice To Have
Ubuntu
sudo apt-get install net-tools
MAC
- Should be pre-installed by default.
Install From PIP
you can pip install zebra_day to any python environment running 3.10.*. If you plan to run the web UI or use the HTTP API functionality, run this in the above described ZDAY conda env. To install with pip:
pip install zebra_day
- reload your environment/shell.
Install From Source
Clone Repository & Local PIP
git clone git@github.com:Daylily-Informatics/zebra_day.git
cd zebra_day
conda activate ZDAY # ZDAY was built with mamba earlier
# Modern install (recommended)
pip install -e . # Install in editable mode
pip install -e ".[dev]" # Include dev dependencies
pip install -e ".[lint]" # Include linting tools
pip install -e ".[all]" # Include all extras
# Build wheel/sdist
pip install build
python -m build # Creates dist/*.whl and dist/*.tar.gz
zebra_dayis now installed in your current python environment.- reload your environment/shell.
File Locations (XDG-compliant)
zebra_day uses XDG Base Directory specification for file storage:
| Type | macOS | Linux |
|---|---|---|
| Config | ~/.config/zebra_day/ |
~/.config/zebra_day/ |
| Data | ~/Library/Application Support/zebra_day/ |
~/.local/share/zebra_day/ |
| Logs | ~/Library/Logs/zebra_day/ |
~/.local/state/zebra_day/ |
| Cache | ~/Library/Caches/zebra_day/ |
~/.cache/zebra_day/ |
Key files:
zebra-day-config.yaml- Printer fleet configuration (in config dir)label_styles/- ZPL template files (in data dir)label_styles/tmps/- Draft templates (in data dir)
Note (macOS): older installs may still have config at
~/Library/Preferences/zebra_day/; zebra_day will copy that forward into the
XDG config directory the first time it loads config.
Use zday info to see the exact paths on your system.
Hardware Config
AWS DynamoDB Shared Config (opt-in)
- Multiple clients can share a single DynamoDB table as the config backend (printer fleet + ZPL templates).
- Enable via
ZEBRA_DAY_CONFIG_BACKEND=dynamodbenv var or the GUI's Switch Backend form. - Every DynamoDB write triggers an automatic S3 backup snapshot (JSON).
- See
zday dynamo --helpand the DynamoDB Plan for details.
Quick
- Connect all zebra printers to the same network as the machine you'll be running
zebra_dayis connected to. Load labels, power on printers , confirm status lights are green, etc.
Hardware Guide
- Info on hardware and consumables known to work with
zebra_day. User guides, notes, part#s and costs for:- Printers
- Label Stock
- Barcode Scanners
USAGE
- zebra printers -> power on and connect via cable or wifi to the same network the machine you installed
zebra_dayis on. - activate the environment you have
zebra_dayinstalled into. - If you have just pip installed
zebra_dayin the shell you are in, start a new shell.
QUICKSTART
# First-time setup: scan network for printers and initialize configuration
zday bootstrap
# Start the web UI (runs in background by default)
zday gui start
# Or start in foreground for debugging
zday gui start --foreground
Example Output From zday bootstrap
$ zday bootstrap Detecting local IP address... IP detected: 192.168.1.12 ... using IP root: 192.168.1 Scanning for Zebra printers on network (this may take a few minutes)... Zebra Printer Scan Complete. Found 2 printers: - 192.168.1.7 (ZTC GX420d) - 192.168.1.20 (ZD620) Configuration saved to: ~/.config/zebra_day/zebra-day-config.yaml Run 'zday gui start' to launch the web interface.
Example Output From zday gui start
$ zday gui start Starting zebra_day web server... Server running at: https://192.168.1.12:8118 Dashboard: https://192.168.1.12:8118/ API Docs: https://192.168.1.12:8118/docs Server started in background (PID: 12345) Use 'zday gui status' to check status Use 'zday gui stop' to stop the server
The web service runs in the background. Use
zday gui statusto check if it's running, andzday gui stopto stop it.
zebra_day Web GUI
Dashboard
Printer Fleet
Print Request
ZPL Template Editor
Configuration
API Documentation
Programatic
Quick
Open an ipython shell.
import zebra_day.print_mgr as zdpm
zlab = zdpm.zpl()
# Scan via ZPL port 9100 (default). REPLACE the IP stub with your network.
zlab.probe_zebra_printers_add_to_printers_json('192.168.1')
# Optional: also probe HTTP port 80 for web-based discovery
# zlab.probe_zebra_printers_add_to_printers_json('192.168.1', scan_http_port=80)
print(zlab.printers) # This should print out the config dict of all detected zebra printers. An empty dict, {}, is a failure of autodetection, and manual creation of the config file may be needed. If successful, the lab name assigned is 'default', this may be edited later.
# The config will look something like this (v2.1.0 schema with lab metadata + nested printers)
## {'schema_version': '2.1.0', 'labs': {'default': {'lab_name': 'Default', 'lab_display_name': 'Default', 'lab_description': '', 'network_stub': '192.168.1', 'printers': {'192.168.1.7': {'ip_address': '192.168.1.7', ...}}}}}
# Assuming a printer was detected, send a test print request. Using the 'lab', 'printer' and 'label_zpl_style' above (you'd have your own IP/Name, other values should remain the same for now. There are multiple label ZPL formats available, the test_2inX1in is for quick testing & only formats in the two UID values specified.
zlab.print_zpl(lab='default', printer_name='192.168.1.7', label_zpl_style='test_2inX1in', uid_barcode="123aUID")
# ZPL code sent successfully to the printer!
# Out[13]: '^XA\n^FO235,20\n^BY1\n^B3N,N,40,N,N\n^FD123aUID^FS\n^FO235,70\n^ADN,30,20\n^FD123aUID^FS\n^FO235,115\n^ADN,25,12\n^FDalt_a^FS\n^FO235,145\n^ADN,25,12\n^FDalt_b^FS\n^FO70,180\n^FO235,170\n^ADN,30,20\n^FDalt_c^FS\n^FO490,180\n^ADN,25,12\n^FDalt_d^FS\n^XZ'
- This will produce a label which looks like this (modulo printer config items needing attention).
Programatic Guide
Simplified API (v2.1.5+)
Starting with v2.1.5, zebra_day provides a simplified top-level API for common operations. These functions are thin wrappers around the print_mgr.zpl() class, making the library more intuitive to use.
Quick Example
import zebra_day as zd
# Query available labs
labs = zd.query_labs()
print(labs) # ['default', 'production']
# Query printers for a specific lab
printers = zd.query_printers('default')
print(printers) # {'192.168.1.100': {'ip_address': '...', 'model': 'ZD620', ...}}
# Scan network for Zebra printers
zd.scan(ip_stub='192.168.1', lab='default')
# Print a label
zpl_str = zd.print_zpl(
lab='default',
printer_name='192.168.1.100',
label_zpl_style='tube_2inX1in',
uid_barcode='SAMPLE-001',
alt_a='Patient Name',
alt_b='2024-01-15'
)
# Start the web GUI
zd.start_gui(host='0.0.0.0', port=8118, https=True)
Before/After Comparison
| Operation | Old API (print_mgr.zpl) | New Simplified API |
|---|---|---|
| Query labs | zp = zdpm.zpl(); list(zp.printers['labs'].keys()) |
zd.query_labs() |
| Query printers | zp.printers['labs']['default']['printers'] |
zd.query_printers('default') |
| Scan network | zp.probe_zebra_printers_add_to_printers_json(...) |
zd.scan(ip_stub='...', lab='...') |
| Print label | zp.print_zpl(lab=..., printer_name=..., ...) |
zd.print_zpl(lab=..., printer_name=..., ...) |
| Start GUI | (via CLI only) | zd.start_gui(port=8118) |
Function Reference
zd.query_labs() -> List[str]
Returns a list of all configured lab identifiers.
zd.query_printers(lab: str) -> Dict[str, Dict]
Returns a dictionary of printers for the specified lab. Raises KeyError if lab doesn't exist.
zd.scan(ip_stub: str = "192.168.1", lab: str = "default", scan_http_port: int | None = None) -> None
Scans the network range ({ip_stub}.0 to {ip_stub}.255) for Zebra printers via ZPL port 9100 and adds them to the specified lab. Optionally pass scan_http_port=80 for HTTP fallback discovery.
zd.print_zpl(lab, printer_name, label_zpl_style, uid_barcode='', alt_a='', ..., alt_f='') -> str
Sends a print job to the specified printer. Returns the ZPL string sent.
zd.start_gui(host: str = "0.0.0.0", port: int = 8118, https: bool = True) -> None
Starts the FastAPI web GUI server. HTTPS is enabled by default if certificates are available.
Print Request HTTP API
Quick Start
The HTTP API is available via the web UI, and can be used programatically as well. The following is a quick example of how to send a print request via the HTTP API.
curl "http://localhost:8118/_print_label?lab=MA&printer=192.168.1.31&printer_ip=&label_zpl_style=tube_2inX1in&uid_barcode=BARCODE&alt_a=FIELDAAAA&alt_b=FIELDBBBB&alt_c=FIELDCCCC&alt_d=FIELDDDD&alt_e=FIELDEEEE&alt_f=FIELDFFFF"
# RETURNS 200 OK -or- 500 Internal Server Error (usually b/c the target printer is not reachable)
The above would send a print request to the specified printer, identified by it's network IP. Label style can be set, and some styles use more
alt_*fields than others. This reuest will return200or500.
Web UI
Quick Start
# Start the web server (runs in background)
zday gui start
# Check status
zday gui status
# View logs
zday gui logs --tail 50
# Stop the server
zday gui stop
# Or run in foreground for debugging
zday gui start --foreground
# Or run via uvicorn directly (more control over options)
uvicorn zebra_day.web.app:create_app --host 0.0.0.0 --port 8118 --factory
Web UI
zebra_day 2.0.0+ features a modern, responsive web interface:
| Interface | URL | Description |
|---|---|---|
| Dashboard | https://localhost:8118/ |
Printer fleet stats, quick actions, navigation |
| Printers | https://localhost:8118/printers |
Printer status and management |
https://localhost:8118/print |
Send print requests | |
| Templates | https://localhost:8118/templates |
ZPL template editor with live preview |
| Config | https://localhost:8118/config |
Printer configuration management |
| API Docs | https://localhost:8118/docs |
Interactive OpenAPI/Swagger documentation |
| ReDoc | https://localhost:8118/redoc |
Alternative API documentation |
Note: Use
http://instead ofhttps://if running without certificates (--no-https).
The modern UI offers:
- Dashboard with printer fleet statistics
- Streamlined navigation
- Improved template editor with live PNG preview
- Better mobile responsiveness
- Configuration editing with backup management
- Live printer status monitoring with Status and State fields
Printer Status vs State (v2.2.0+)
The printer list (both CLI and Web UI) displays two distinct fields for printer health:
| Field | Purpose | Values |
|---|---|---|
| Status | Network reachability ("Can I reach it?") | Online, Offline, N/A, Unknown |
| State | Operational status ("Can it print right now?") | Ready, Paused, Error, Offline, Unknown |
Status indicates whether the printer responds to network queries (ZPL ~HI command).
State indicates the printer's operational condition based on the ~HS (Host Status) response:
Ready— Online and ready to printPaused— Printer is paused (front panel or software pause)Error— Has errors (paper out, ribbon out, print head up)Offline— Cannot reach the printerUnknown— State cannot be determined
Print via HTTP API
http://YOUR.HOST.IP.ADDR:8118/_print_label?lab=default&printer=192.168.1.7&label_zpl_style=test_2inX1in&uid_barcode=123aUID
- See the Web UI Guide for full details.
Web UI Guide
DynamoDB GUI Features (v2.2.0+)
The Config page includes controls for the DynamoDB shared backend:
- Backend Status Card — shows current backend type (
localordynamodb), table name, region, S3 bucket, AWS profile - Switch Backend Form — swap between
localanddynamodbbackends for the running session (does not persist to env vars)- Auto-detects existing DynamoDB tables in the selected region
- Auto-suggests S3 bucket name based on region
- Validates AWS profile (rejects
default) - "Create Bucket" button if the target S3 bucket doesn't exist
- Refresh Config — reload config from the active backend
The Templates page includes:
- Import to DynamoDB — when a DynamoDB backend is active, local templates (user + package) appear with checkboxes for selective import into DynamoDB
DynamoDB API Endpoints
All prefixed with /api/v1/. Available when the web server is running.
| Method | Endpoint | Description |
|---|---|---|
GET |
/config/backend-status |
Current backend type and AWS details |
POST |
/config/refresh |
Reload config from active backend |
GET |
/config/detect-tables |
List DynamoDB tables in a region (?region=&profile=) |
POST |
/config/check-s3-bucket |
Check if an S3 bucket exists |
POST |
/config/create-s3-bucket |
Create S3 bucket with lsmc cost tags |
POST |
/config/switch-backend |
Switch live backend (session-only) |
POST |
/templates/import-to-dynamo |
Import local templates into DynamoDB |
POST |
/templates |
Save/create a ZPL template |
DELETE |
/templates/{name} |
Delete a template |
# Example: check backend status
curl https://localhost:8118/api/v1/config/backend-status
# Example: switch to DynamoDB (session-only)
curl -X POST https://localhost:8118/api/v1/config/switch-backend \
-H "Content-Type: application/json" \
-d '{"backend_type":"dynamodb","table_name":"zebra-day-config","region":"us-west-2","s3_bucket":"zebra-day-cfg-us-west-2","profile":"my-profile"}'
# Example: import local templates to DynamoDB
curl -X POST https://localhost:8118/api/v1/templates/import-to-dynamo \
-H "Content-Type: application/json" \
-d '{"template_names":["tube_2inX1in","plate_1inX0.25in"]}'
Other Topics
Security
Authentication
zebra_day supports optional AWS Cognito authentication for production deployments.
Enabling Cognito Authentication
-
Install auth dependencies:
pip install -e ".[auth]"
-
Set required environment variables:
export COGNITO_USER_POOL_ID="us-west-2_XXXXXXXXX" export COGNITO_APP_CLIENT_ID="your-app-client-id" export COGNITO_REGION="us-west-2" # Optional, defaults to us-west-2 export AWS_PROFILE="your-profile" # Optional, for local development
-
Start server with authentication:
zday gui start --auth cognito # Check auth status zday cognito status
Authentication Modes
| Mode | CLI Flag | Description |
|---|---|---|
| None (default) | --auth none |
No authentication required. All endpoints are publicly accessible. |
| Cognito | --auth cognito |
AWS Cognito JWT authentication. All endpoints except health checks require a valid Bearer token. |
Protected Endpoints
When --auth cognito is enabled:
- All UI and API endpoints require a valid JWT Bearer token
- Health endpoints (
/healthz,/readyz) remain publicly accessible - Static files (
/static/*,/files/*,/etc/*) remain publicly accessible - API documentation (
/docs,/redoc,/openapi.json) remains accessible
Making Authenticated Requests
Include the JWT token in the Authorization header:
curl -H "Authorization: Bearer YOUR_JWT_TOKEN" https://localhost:8118/api/v1/printers
Secrets
No credentials of any kind are stored or used by zebra_day. It solely offers zebra printer management and label print request formatting and brokering services. It does not need to know how to connect to other systems, other systems will use the library code provided here, or the http api.
Host and Network Security
In it's present state, zebra_day is safe to run on a machine located in a properly configured & secure local network or cloud hosted instance residing in a secure VPN/VPC.
zebra_dayshould not be deployed in such a way the host is fully visible to the public internet.- a potential exception would be exposing the service via an encryped and secure open port. POC demonstrating this concept can be found below.
Programatic Use Of zebra_day Package/Library
Using the python library in other python code poses no particularly unique new risk. zebra_day may be treated similarly to how other third party tools are handled in each users organization.
Regulatory & Compliance
HIPAA / CAP / CLIA
No PHI is needed by zebra_day to function. PHI may be sent in print rquests, each organization will have their own use cases. zebra_day does not store any of the print request metadata sent to it, the info is redirected to the appropriate zebra printer, and that is that. It is straightforward when setting up the host machine/environment this package will be running in to check off the various HIPAA and CAP/CLIA expectations where they apply.
A Few Integration Demonstrations
Send Label Print Requests From Public Internet To Host PC w/In Your Private Network
Ditch The Private Local Network & Expose Server Publicly ( not advised )
really
Using NGROK To Present A Tunneled Port Connected To The zebra_day Host Port (up and running in <5 min!)
-
Create a tunnel to connect to the zebra_day service running on a machine within your network on port 8118. This could be a cloud instance w/in a VPC you control, or a machine physically present w/in your network.
Install ngrok
brew install ngrok/ngrok/ngrok
ngrok config add-authtoken MYTOKEN # you get this once registered (its free!)
Running ngrok
ngrok http 8118
Which starts a tunnel and presents a monitoring dashboard. And it looks like this:
ngrok (Ctrl+C to quit)
Introducing Always-On Global Server Load Balancer: https://ngrok.com/r/gslb
Session Status online
Account USERNAME (Plan: Free)
Version 3.3.5
Region United States (California) (us-cal-1)
Latency 12ms
Web Interface http://127.0.0.1:4040
Forwarding https://dfbf-23-93-175-197.ngrok-free.app -> http://localhost:8118
Connections ttl opn rt1 rt5 p50 p90
8 0 0.00 0.01 10.03 28.05
HTTP Requests
-------------
GET /_print_label 200 OK
GET /_print_label 200 OK
GET /build_print_request 200 OK
GET /send_print_request 200 OK
GET / 200 OK
GET /_print_label 200 OK
GET /_print_label 200 OK
GET /build_print_request 200 OK
GET /send_print_request 200 OK
GET /favicon.ico 200 OK ~
And looks like:
- If you leave the ngrok tunnel running, go to a different network, you can use the link named in the
Forwardingrow above to access the zebra_day UI, in the above example, this url would behttps://dfbf-23-93-175-197.ngrok-free.app.
Sending Label Print Requests
from a web browser on a different network
https://dfbf-23-93-175-197.ngrok-free.app/_print_label?uid_barcode=UID33344455&alt_a=altTEXTAA&alt_b=altTEXTBB&alt_c=altTEXTCC&alt_d=&alt_e=&alt_f=&lab=default&printer=192.168.1.20&printer_ip=192.168.1.20&label_zpl_style=tube_2inX1in
Using wget from a shell on a machine outside your local network
wget "https://dfbf-23-93-175-197.ngrok-free.app/_print_label?uid_barcode=UID33344455&alt_a=altTEXTAA&alt_b=altTEXTBB&alt_c=altTEXTCC&alt_d=&alt_e=&alt_f=&lab=default&printer=192.168.1.20&printer_ip=192.168.1.20&label_zpl_style=tube_2inX1in"
From SalesForce
- There are several ways to do this, but they all boil down to somehow formulating a URL for each print request, ie:
https://dfbf-23-93-175-197.ngrok-free.app/_print_label?uid_barcode=UID33344455&lab=default&printer=192.168.1.20&label_zpl_style=tube_2inX1in, and hitting the URL via Apex, Flow, etc.- To send a print request, you will need to know the API url, and the
lab,printer_name, andlabel_zpl_styleyou wish to print the salesforceNameakaUIDas a label. This example explains how to pass just one variable to print from salesforce, adding additional metadata to print involves adding additional params to the url being constructed.
- To send a print request, you will need to know the API url, and the
Print Upon Object Creation (Apex Class + Flow)
The following is a very quick prof of concept to see it work(success!). I fully expect there are more robust ways to reach this goal.
Create an Apex class to handle sending HTTP requests.
- Setup->Apex Classes, create new Apex Class, save the following as the Apex Class:
public class HttpRequestFlowAction {
public class RequestInput {
@InvocableVariable(label='Endpoint URL' required=true)
public String url;
// Add other variables as needed, e.g. headers, body, method, etc.
}
@InvocableMethod(label='Make HTTP Request' description='Makes an HTTP request from a Flow.')
public static List<String> makeHttpRequest(List<RequestInput> requests) {
List<String> responses = new List<String>();
for(RequestInput req : requests) {
Http http = new Http();
HttpRequest request = new HttpRequest();
request.setEndpoint(req.url);
request.setMethod('GET'); // Change method as needed: POST, PUT, etc.
// Add headers, body, etc. if needed.
HttpResponse response = http.send(request);
responses.add(response.getBody());
}
return responses;
}
}
- click save, the apex class is now ready. Check the security settings and verify the profile associated with your user has access to see/use this class.
Next, create a flow which uses this Apex Class.
- setup->Flow & click
New Flow. I remained in theAuto Layoutview. - Choose
Record-Triggered Flow - Select the object type the flow will be triggered when an instance of this object type is created.
- Select 'A record is created` as the trigger.
- Set Entry Conditions (this might be unecessary),
Any Condition Is Met, FieldName, OperatorStarts with, ValueX(X being the first letter of the Name field UID salesforce creates for this object. Again, this is probably not needed, but I have not gone back to try w/out this step). - Choose
Actions and Related Records, and check the box at the bottom of the page toInclude a Run Asynchronously path...- upon clicking this box, the graphic representation of the flow to the left of the page will now have 2 branches at the bottom of the flow rule, one
Run Immediatelyand oneRun Asynchronously. TheRun Immediatelybranch was throwing errors, so I removed it to debug at a latter date. - Click the node just below the
Run Asynchoval. Add anAction. Select theMake HTTP Requestwe created via the Apex Class above. Give it aLabel, let the API Name auto generate. - In the
Endpoint URLfield, enter the urlhttps://dfbf-23-93-175-197.ngrok-free.app/_print_label?uid_barcode={!$Record.Name}&lab=default&printer=!!YOURPRINTERIP!!&label_zpl_style=tube_2inX1in, where Record.Name will be replaced with the Object.Name from the object triggering the flow. Replace !!YOURPRINTERIP!! with one of the printer IPs zebra_day detected above. If you are using the auto-generated zebra printers config file, you may leavedefaultas the value forlab=as this will be the default name given when zebra_day autodetects printers. - add the same HTTPrequest action to the node just below
Run Immediately. - click
Savein the upper right corner of the page. Give it a name - Click
Debug Again, run theRun Immediatelybranch first. This will fail. - You need to whitelist the URL used by Apex in this flow with Salesforce. To do this: Setup->Remote Site Settings, click
New Remote Site. Give it a name, and enter your ngrok URL up to the.app, so:https://dfbf-23-93-175-197.ngrok-free.app. Click theactivecheckbox and then save. - CLick
Debug Again, run theAsyncronousbranch, this should succeed. - Click
Save As, new version. - Click
Activate - Go create one of the objects you made this flow for. This will fail!
- Go back to your flow, click
edit flow, switch fromAuto LayouttoFreeformview. - Click the connector labeled
Run Immediately, delete it (leave the Async branch intact) - Click
Save As, new version. - Click
Activate - Go create a new object of the type this trigger is built to respond to... it should print, and should do so each time a new object is created.
- upon clicking this box, the graphic representation of the flow to the left of the page will now have 2 branches at the bottom of the flow rule, one
- This toy example is intended to demonstrate this can work. Next, you should determine how you'd like to send print requests that best suits your needs.
This was all rather a PITA honestly.
Create a Formula Text Field For Objects
You can construct the print URL in the formula, and this formula field can be presented on the object salesforce page. If the user clicks the URL on the page, a print request is sent containing the data inserted by the formula for the current object.
Host Machine Options
Machine physically connected to your local network.
This is covered in the config and setup/install instructions above.
AWS
ec2 instance
more info coming soon. The instructions for getting the package s/w up and running is largely the same as above. However, care must be taken when configuring the network and instances which will be used at amazon.
From AMI?
Other Providers
If it will work on AWS, it will work anyplace really (with some provider specific tweaks).
Docker
Find an example in the docker folder at the top level of this project. Copy the Dockerfile and the docker-compose.yml to a local folder on your computer.
In that folder, mkdir etc && chmod 777 etc && mkdir logs && chmod 777 logs to setup the example folders.
Then run sudo docker compose up --build -d to run it then reach it at http://:8118. This doesn't auto-detect printers so you'll have to run the printer discovery and probably manually edit your YAML config file.
Add'l Future Development
- Set varios printer config via ZPL commands (presently this package only fetches config).
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file zebra_day-2.4.1.tar.gz.
File metadata
- Download URL: zebra_day-2.4.1.tar.gz
- Upload date:
- Size: 20.5 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
430aaa26917ff10e98746099db59237b0145bd88465974dc799eec398c7530c7
|
|
| MD5 |
b3d19bbaa5ce814868da6083363ff50b
|
|
| BLAKE2b-256 |
62297f95a90352adf85858e34b80ef419ba6cf7377f3f905754277f26b2ff1dc
|
File details
Details for the file zebra_day-2.4.1-py3-none-any.whl.
File metadata
- Download URL: zebra_day-2.4.1-py3-none-any.whl
- Upload date:
- Size: 20.7 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
14a244bdc7560c42bb137df3e8a822d428ff7dbff66fd4667c7362d8fa125c99
|
|
| MD5 |
2133c8b9280ccb9fd1e419a6049c4e9c
|
|
| BLAKE2b-256 |
841e4e802e9047e3a0a154bcd548d6cdfc4fbbe927ec5040e030b3d5ae4b91e2
|