Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 87 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# AGENTS.md — zebra_day Project Directives

## What This Repo Is

`zebra_day` is a Python library + CLI + web GUI for managing fleets of Zebra label printers.
It speaks ZPL over TCP (port 9100), provides a FastAPI web UI, and optionally stores
config in AWS DynamoDB with S3 backups.

## Architecture Quick Reference

| Layer | Key Files | Notes |
|-------|-----------|-------|
| CLI | `zebra_day/cli/__init__.py` | Built on `cli-core-yo` (`create_app(spec)` + plugin `register()` pattern) |
| Core | `zebra_day/print_mgr.py`, `zebra_day/cmd_mgr.py` | `zpl()` class, ZPL TCP communication |
| Backends | `zebra_day/backends/` | `ConfigBackend` protocol → `LocalBackend` (files) or `DynamoBackend` (AWS) |
| Web | `zebra_day/web/` | FastAPI app, Jinja2 templates in `zebra_day/templates/modern/` |
| Simulator | `zebra_day/simulator.py` | Mock ZPL printer for testing (TCP 9100 + HTTP) |
| Paths | `zebra_day/paths.py` | XDG Base Directory helpers |

## CLI Rules

- The CLI uses **cli-core-yo** as its foundation. All command modules expose a `register(registry, spec)` function.
- **Global `--json/-j` flag** lives on the root callback. Do NOT add per-command `--json` flags.
- Use `output.*` primitives (`heading`, `success`, `warning`, `error`, `action`, `detail`, `bullet`, `emit_json`, `print_text`) — never raw `print()` or `console.print()` for user-facing output.
- In JSON mode, `output.error()` and other display primitives are **auto-suppressed**. Use `output.emit_json()` to emit machine-readable payloads.
- Pin cli-core-yo, typer, and rich versions per `pyproject.toml` ranges.

## Testing

- **Framework**: `pytest` + `pytest-cov`
- **Test files**: `tests/test_*.py` (13 files, 334+ tests)
- **AWS mocks**: Use `moto[dynamodb,s3]` — never real AWS credentials in tests
- **Run all**: `pytest tests/ -v --tb=short`
- **Run one file**: `pytest tests/test_cli.py -v`
- **Linting**: `ruff check zebra_day tests && ruff format --check zebra_day tests`
- **Type checking**: `mypy zebra_day --ignore-missing-imports`

## Quality Gates (must pass before merge)

```bash
ruff check zebra_day tests
ruff format --check zebra_day tests
mypy zebra_day --ignore-missing-imports
pytest tests/ -v --tb=short
```

## Config Format

- Printer fleet config is **YAML** (`zebra-day-config.yaml`) for historical reasons.
- All new config/data interchange should prefer **JSON**.
- DynamoDB stores config as JSON-encoded strings.

## Network Scanner

- Default discovery: **ZPL port 9100** (`~HI` query).
- Optional HTTP fallback via `scan_http_port` parameter.
- The `notes` field records discovery method: `"zpl"`, `"http(port)"`, or `"zpl+http(port)"`.

## Web UI

- FastAPI + Jinja2 templates in `zebra_day/templates/modern/`.
- API routes: `zebra_day/web/routers/api.py` (JSON), `zebra_day/web/routers/ui.py` (HTML).
- Default port: **8118**. HTTPS by default if mkcert certs exist.
- Follow the API documentation exposure rules from global `~/.augment/rules/01_http_https_api_rules.md`.

## Versioning

- `setuptools_scm` — no hardcoded version. Tags are `X.Y.Z` (no `v` prefix).
- `_version.py` is generated — do not edit or commit it.

## Environment Variables

| Variable | Default | Purpose |
|----------|---------|---------|
| `ZEBRA_DAY_CONFIG_BACKEND` | `local` | `local` or `dynamodb` |
| `ZEBRA_DAY_DYNAMO_TABLE` | `zebra-day-config` | DynamoDB table name |
| `ZEBRA_DAY_DYNAMO_REGION` | `us-west-2` | AWS region |
| `ZEBRA_DAY_S3_BACKUP_BUCKET` | _(none)_ | Required for dynamodb backend |
| `AWS_PROFILE` | _(none)_ | Never pass `"default"` explicitly |

## Do NOT

- Add per-command `--json` flags (use the global one).
- Use `datetime.UTC` (use `datetime.timezone.utc` for mypy compat).
- Use `console.print()` for user-facing output (use `output.*` primitives).
- Store real patient data or PHI in tests or examples.
- Edit `_version.py` manually.
19 changes: 18 additions & 1 deletion MODERNIZE.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,21 @@ This document tracks the modernization of zebra_day from 0.5.0 to 2.2.0.
- [x] Added 60-second caching for printer status queries
- [x] 152+ tests passing

### Phase 8 - cli-core-yo Migration + Simulator + ZPL-First Scanner
- [x] Migrated CLI from raw Typer to cli-core-yo foundation (`create_app(spec)` + plugin system)
- [x] Converted all command modules to `register()` plugin pattern (8 modules)
- [x] Standardized output: `console.print()` → `output.*` primitives (heading, success, warning, error, etc.)
- [x] Global `--json/-j` flag via RuntimeContext (replaced per-command `--json` flags)
- [x] Added mock Zebra printer simulator (`zday simulator start/stop/list`)
- ZPL TCP server (port 9100) + HTTP status server (port 18080)
- Configurable model, serial, firmware, and error conditions
- [x] Refactored network scanner to ZPL-first discovery (port 9100 default)
- Optional HTTP fallback via `--scan-http-port`
- Discovery method tracked in `notes` field: "zpl", "http(port)", "zpl+http(port)"
- [x] Fixed 27 pre-existing mypy errors (0 remaining)
- [x] ruff check + ruff format clean on all modified files
- [x] 334 tests passing across 13 test files

---

## Commands Reference
Expand All @@ -84,13 +99,15 @@ ruff check zebra_day tests # Lint
ruff format --check zebra_day tests # Format check
mypy zebra_day # Type check

# CLI
# CLI (global --json/-j flag available on all commands)
zday --help # Show all commands
zday info # Show config paths and status
zday bootstrap # First-time setup
zday gui start # Start web server (HTTPS by default)
zday gui start --no-https # Start without HTTPS
zday gui stop # Stop web server
zday simulator start --foreground # Mock printer for testing
zday printer scan --ip-stub 192.168.1 # ZPL-first scanner

# Build
python -m build # Build wheel and sdist
Expand Down
103 changes: 93 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,12 +120,24 @@ zday gui stop

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 |

```bash
# 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
Expand All @@ -138,11 +150,12 @@ zday gui status
zday gui logs [--tail N] [--follow]
zday gui restart

# Printer management
zday printer scan [--ip-stub IP] # Scan network for printers
zday printer list [--lab LAB] # List configured printers (static config)
zday printer list --live # Query live status from printers (Status + State)
zday printer test PRINTER_NAME # Send test print
# 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
Expand All @@ -152,7 +165,7 @@ zday template show TEMPLATE # Display template contents

# Configuration management
zday config init # Initialize config from template
zday config show # Display current config (YAML)
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
Expand All @@ -177,6 +190,11 @@ 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
Expand All @@ -191,7 +209,7 @@ Opt-in shared configuration via AWS DynamoDB with S3 backup snapshots. Local-fil
| 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 | `--json`, `--create-s3-if-missing` |
| `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|yaml` |
| `zday dynamo backup` | Trigger immediate S3 backup snapshot | `--create-s3-if-missing` |
Expand Down Expand Up @@ -239,6 +257,68 @@ 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.

```bash
# 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.

```bash
# 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:
Expand Down Expand Up @@ -591,7 +671,10 @@ import zebra_day.print_mgr as zdpm

zlab = zdpm.zpl()

zlab.probe_zebra_printers_add_to_printers_json('192.168.1') # REPLACE the IP stub with the correct value for your network. This may take a few min to run. !! This command is not required if you've sucessuflly run the quickstart already, also, won't hurt.
# 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.

Expand Down Expand Up @@ -666,8 +749,8 @@ 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") -> None`
Scans the network range (`{ip_stub}.0` to `{ip_stub}.255`) for Zebra printers and adds them to the specified lab.
#### `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.
Expand Down
5 changes: 3 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,9 @@ dependencies = [
"jinja2>=3.1.0",
"pydantic>=2.0.0",
"python-multipart>=0.0.6",
"typer>=0.9.0",
"rich>=13.0.0",
"cli-core-yo>=0.2.1",
"typer>=0.21.0,<0.22.0",
"rich>=14.0.0,<15.0.0",
"pillow>=10.0.0",
"zint-bindings>=1.2.0",
"httpx",
Expand Down
Loading
Loading