zebra-day 2.0.0__py3-none-any.whl → 2.1.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. zebra_day/__init__.py +7 -2
  2. zebra_day/_version.py +1 -0
  3. zebra_day/cli/__init__.py +80 -30
  4. zebra_day/cli/cognito.py +15 -9
  5. zebra_day/cli/gui.py +21 -16
  6. zebra_day/cli/printer.py +34 -27
  7. zebra_day/cli/template.py +19 -15
  8. zebra_day/cmd_mgr.py +3 -6
  9. zebra_day/docs/gx420d-gx430d-ug-en.pdf +0 -0
  10. zebra_day/docs/hardware_config_guide.md +149 -0
  11. zebra_day/docs/programatic_guide.md +181 -0
  12. zebra_day/docs/qln420_zebra_manual.pdf +0 -0
  13. zebra_day/docs/uid_screed_light.md +38 -0
  14. zebra_day/docs/zd620-zd420-ug-en.pdf +0 -0
  15. zebra_day/docs/zebra_day_ui_guide.md +194 -0
  16. zebra_day/etc/printer_config.json +7 -1
  17. zebra_day/etc/printer_config.template.json +3 -17
  18. zebra_day/etc/tmp_printers139.json +10 -0
  19. zebra_day/etc/tmp_printers147.json +10 -0
  20. zebra_day/etc/tmp_printers34.json +10 -0
  21. zebra_day/etc/tmp_printers389.json +10 -0
  22. zebra_day/etc/tmp_printers398.json +10 -0
  23. zebra_day/etc/tmp_printers437.json +10 -0
  24. zebra_day/etc/tmp_printers439.json +10 -0
  25. zebra_day/etc/tmp_printers440.json +10 -0
  26. zebra_day/etc/tmp_printers508.json +10 -0
  27. zebra_day/etc/tmp_printers543.json +10 -0
  28. zebra_day/etc/tmp_printers835.json +10 -0
  29. zebra_day/etc/tmp_printers842.json +10 -0
  30. zebra_day/etc/tmp_printers931.json +10 -0
  31. zebra_day/etc/tmp_printers969.json +10 -0
  32. zebra_day/exceptions.py +1 -1
  33. zebra_day/files/corners_smallTube_preview.png +0 -0
  34. zebra_day/files/test_png_2897.png +0 -0
  35. zebra_day/files/test_png_31690.png +0 -0
  36. zebra_day/files/test_png_33804.png +0 -0
  37. zebra_day/files/test_png_34737.png +0 -0
  38. zebra_day/files/test_png_4161.png +0 -0
  39. zebra_day/files/test_png_44748.png +0 -0
  40. zebra_day/files/test_png_4635.png +0 -0
  41. zebra_day/files/test_png_56349.png +0 -0
  42. zebra_day/files/test_png_5936.png +0 -0
  43. zebra_day/files/test_png_64110.png +0 -0
  44. zebra_day/files/test_png_64891.png +0 -0
  45. zebra_day/files/test_png_69002.png +0 -0
  46. zebra_day/files/test_png_70065.png +0 -0
  47. zebra_day/files/test_png_72366.png +0 -0
  48. zebra_day/files/test_png_77793.png +0 -0
  49. zebra_day/files/test_png_9572.png +0 -0
  50. zebra_day/imgs/.hold +0 -0
  51. zebra_day/imgs/bar_ltpurp.png +0 -0
  52. zebra_day/imgs/bar_purp.png +0 -0
  53. zebra_day/imgs/bar_purp3.png +0 -0
  54. zebra_day/imgs/bar_red.png +0 -0
  55. zebra_day/imgs/legacy/UBC_gantt_chart.png +0 -0
  56. zebra_day/imgs/legacy/gx420d_network_config.png +0 -0
  57. zebra_day/imgs/legacy/gx420d_printer_config.png +0 -0
  58. zebra_day/imgs/legacy/ngrok.png +0 -0
  59. zebra_day/imgs/legacy/printer_details.png +0 -0
  60. zebra_day/imgs/legacy/quick_start_test_label.png +0 -0
  61. zebra_day/imgs/legacy/quick_start_test_label2.png +0 -0
  62. zebra_day/imgs/legacy/zd620_network_config.png +0 -0
  63. zebra_day/imgs/legacy/zd620_printer_config.png +0 -0
  64. zebra_day/imgs/legacy/zday_quick_gui.png +0 -0
  65. zebra_day/imgs/legacy/zebra_day_alt_css_dog.png +0 -0
  66. zebra_day/imgs/legacy/zebra_day_alt_css_flower.png +0 -0
  67. zebra_day/imgs/legacy/zebra_day_alt_css_main.png +0 -0
  68. zebra_day/imgs/legacy/zebra_day_available_zpl_templates.png +0 -0
  69. zebra_day/imgs/legacy/zebra_day_bkup_pconfig.png +0 -0
  70. zebra_day/imgs/legacy/zebra_day_home.png +0 -0
  71. zebra_day/imgs/legacy/zebra_day_manual_print.png +0 -0
  72. zebra_day/imgs/legacy/zebra_day_printer_fleet_json.png +0 -0
  73. zebra_day/imgs/legacy/zebra_day_quick_ex.png +0 -0
  74. zebra_day/imgs/legacy/zebra_day_zpl_template_IRLa.png +0 -0
  75. zebra_day/imgs/legacy/zebra_day_zpl_template_IRLb.png +0 -0
  76. zebra_day/imgs/ui_api_docs.png +0 -0
  77. zebra_day/imgs/ui_config.png +0 -0
  78. zebra_day/imgs/ui_dashboard.png +0 -0
  79. zebra_day/imgs/ui_print_request.png +0 -0
  80. zebra_day/imgs/ui_printers.png +0 -0
  81. zebra_day/imgs/ui_templates.png +0 -0
  82. zebra_day/logging_config.py +4 -9
  83. zebra_day/mkcert.py +157 -0
  84. zebra_day/paths.py +1 -2
  85. zebra_day/print_mgr.py +165 -145
  86. zebra_day/templates/modern/config.html +7 -0
  87. zebra_day/templates/modern/print_request.html +61 -3
  88. zebra_day/web/__init__.py +1 -1
  89. zebra_day/web/app.py +21 -16
  90. zebra_day/web/auth.py +17 -15
  91. zebra_day/web/middleware.py +8 -5
  92. zebra_day/web/routers/__init__.py +0 -1
  93. zebra_day/web/routers/api.py +192 -43
  94. zebra_day/web/routers/ui.py +31 -33
  95. zebra_day/zpl_renderer.py +45 -34
  96. {zebra_day-2.0.0.dist-info → zebra_day-2.1.4.dist-info}/METADATA +76 -67
  97. {zebra_day-2.0.0.dist-info → zebra_day-2.1.4.dist-info}/RECORD +101 -29
  98. {zebra_day-2.0.0.dist-info → zebra_day-2.1.4.dist-info}/WHEEL +0 -0
  99. {zebra_day-2.0.0.dist-info → zebra_day-2.1.4.dist-info}/entry_points.txt +0 -0
  100. {zebra_day-2.0.0.dist-info → zebra_day-2.1.4.dist-info}/licenses/LICENSE +0 -0
  101. {zebra_day-2.0.0.dist-info → zebra_day-2.1.4.dist-info}/top_level.txt +0 -0
zebra_day/__init__.py CHANGED
@@ -1,8 +1,14 @@
1
1
  """
2
2
  zebra_day - A Python library to manage Zebra printer fleets and ZPL print requests.
3
3
  """
4
+
4
5
  from __future__ import annotations
5
6
 
7
+ try:
8
+ from zebra_day._version import __version__
9
+ except ImportError:
10
+ __version__ = "0.0.0.dev0"
11
+
6
12
  from zebra_day.exceptions import (
7
13
  ConfigError,
8
14
  ConfigFileNotFoundError,
@@ -18,10 +24,9 @@ from zebra_day.exceptions import (
18
24
  from zebra_day.logging_config import configure_logging, get_logger
19
25
 
20
26
  __all__ = [
21
- # Logging
27
+ "__version__",
22
28
  "configure_logging",
23
29
  "get_logger",
24
- # Exceptions
25
30
  "ZebraDayError",
26
31
  "PrinterConnectionError",
27
32
  "PrinterNotFoundError",
zebra_day/_version.py ADDED
@@ -0,0 +1 @@
1
+ __version__ = "2.1.4"
zebra_day/cli/__init__.py CHANGED
@@ -1,19 +1,21 @@
1
1
  """zebra_day CLI - Zebra Printer Fleet Management CLI using Typer."""
2
2
 
3
+ from __future__ import annotations
4
+
3
5
  import os
6
+ import socket
4
7
  import sys
5
- from pathlib import Path
6
- from typing import Optional
8
+ from typing import Any
7
9
 
8
10
  import typer
9
11
  from rich.console import Console
10
12
  from rich.table import Table
11
13
 
12
14
  from zebra_day import paths as xdg
15
+ from zebra_day.cli.cognito import cognito_app
13
16
  from zebra_day.cli.gui import gui_app
14
17
  from zebra_day.cli.printer import printer_app
15
18
  from zebra_day.cli.template import template_app
16
- from zebra_day.cli.cognito import cognito_app
17
19
 
18
20
  console = Console()
19
21
 
@@ -35,6 +37,7 @@ def _get_version() -> str:
35
37
  """Get zebra_day version."""
36
38
  try:
37
39
  from importlib.metadata import version
40
+
38
41
  return version("zebra_day")
39
42
  except Exception:
40
43
  return "dev"
@@ -92,7 +95,7 @@ def status(
92
95
  """Show printer fleet status, network connectivity, and service health."""
93
96
  import json as json_mod
94
97
 
95
- status_data = {
98
+ status_data: dict[str, dict[str, Any]] = {
96
99
  "gui_server": {"running": False, "pid": None, "url": None},
97
100
  "printers": {"configured": 0, "labs": []},
98
101
  "config": {"exists": False, "path": None},
@@ -117,14 +120,12 @@ def status(
117
120
  status_data["config"]["exists"] = True
118
121
  try:
119
122
  import zebra_day.print_mgr as zdpm
123
+
120
124
  zp = zdpm.zpl()
121
125
  if hasattr(zp, "printers") and "labs" in zp.printers:
122
126
  labs = list(zp.printers["labs"].keys())
123
127
  status_data["printers"]["labs"] = labs
124
- total_printers = sum(
125
- len([k for k in zp.printers["labs"][lab].keys()])
126
- for lab in labs
127
- )
128
+ total_printers = sum(len(list(zp.printers["labs"][lab].keys())) for lab in labs)
128
129
  status_data["printers"]["configured"] = total_printers
129
130
  except Exception:
130
131
  pass
@@ -136,25 +137,29 @@ def status(
136
137
  # Human-readable output
137
138
  console.print("\n[bold]Service Status[/bold]")
138
139
  if status_data["gui_server"]["running"]:
139
- console.print(f" [green]●[/green] GUI Server: [green]Running[/green] (PID {status_data['gui_server']['pid']})")
140
+ console.print(
141
+ f" [green]●[/green] GUI Server: [green]Running[/green] (PID {status_data['gui_server']['pid']})"
142
+ )
140
143
  console.print(f" URL: [cyan]{status_data['gui_server']['url']}[/cyan]")
141
144
  else:
142
145
  console.print(" [dim]○[/dim] GUI Server: [dim]Not running[/dim]")
143
146
 
144
147
  console.print("\n[bold]Printer Fleet[/bold]")
145
148
  if status_data["config"]["exists"]:
146
- console.print(f" [green]●[/green] Config: [green]Loaded[/green]")
149
+ console.print(" [green]●[/green] Config: [green]Loaded[/green]")
147
150
  console.print(f" Printers: {status_data['printers']['configured']}")
148
151
  console.print(f" Labs: {', '.join(status_data['printers']['labs']) or 'none'}")
149
152
  else:
150
153
  console.print(" [yellow]○[/yellow] Config: [yellow]Not found[/yellow]")
151
- console.print(f" Run [cyan]zday bootstrap[/cyan] to initialize")
154
+ console.print(" Run [cyan]zday bootstrap[/cyan] to initialize")
152
155
  console.print()
153
156
 
154
157
 
155
158
  @app.command("bootstrap")
156
159
  def bootstrap(
157
- ip_stub: Optional[str] = typer.Option(None, "--ip-stub", "-i", help="IP stub for printer scan (e.g., 192.168.1)"),
160
+ ip_stub: str | None = typer.Option(
161
+ None, "--ip-stub", "-i", help="IP stub for printer scan (e.g., 192.168.1)"
162
+ ),
158
163
  skip_scan: bool = typer.Option(False, "--skip-scan", "-s", help="Skip printer network scan"),
159
164
  json_output: bool = typer.Option(False, "--json", "-j", help="Output as JSON"),
160
165
  ):
@@ -166,20 +171,16 @@ def bootstrap(
166
171
  3. Scan the network for Zebra printers (unless --skip-scan)
167
172
  """
168
173
  import json as json_mod
169
- import time
170
- import socket
171
-
172
- result = {
173
- "config_dir": str(xdg.get_config_dir()),
174
- "data_dir": str(xdg.get_data_dir()),
175
- "printers_found": 0,
176
- "labs": [],
177
- }
174
+
175
+ config_dir = str(xdg.get_config_dir())
176
+ data_dir = str(xdg.get_data_dir())
177
+ printers_found = 0
178
+ labs: list[str] = []
178
179
 
179
180
  if not json_output:
180
181
  console.print("\n[bold cyan]zebra_day Bootstrap[/bold cyan]\n")
181
- console.print("[green]✓[/green] Config directory: " + result["config_dir"])
182
- console.print("[green]✓[/green] Data directory: " + result["data_dir"])
182
+ console.print("[green]✓[/green] Config directory: " + config_dir)
183
+ console.print("[green]✓[/green] Data directory: " + data_dir)
183
184
 
184
185
  if skip_scan:
185
186
  if not json_output:
@@ -202,24 +203,74 @@ def bootstrap(
202
203
 
203
204
  try:
204
205
  import zebra_day.print_mgr as zdpm
206
+
205
207
  zp = zdpm.zpl()
206
208
  zp.probe_zebra_printers_add_to_printers_json(ip_stub=ip_stub)
207
209
 
208
210
  if hasattr(zp, "printers") and "labs" in zp.printers:
209
211
  for lab in zp.printers["labs"]:
210
- printers_in_lab = len([k for k in zp.printers["labs"][lab].keys()])
211
- result["printers_found"] += printers_in_lab
212
- result["labs"].append(lab)
212
+ printers_in_lab = len(list(zp.printers["labs"][lab].keys()))
213
+ printers_found += printers_in_lab
214
+ labs.append(lab)
213
215
 
214
216
  if not json_output:
215
- console.print(f"[green]✓[/green] Scan complete: {result['printers_found']} printer(s) found")
216
- if result["labs"]:
217
- console.print(f" Labs: {', '.join(result['labs'])}")
217
+ console.print(f"[green]✓[/green] Scan complete: {printers_found} printer(s) found")
218
+ if labs:
219
+ console.print(f" Labs: {', '.join(labs)}")
218
220
  except Exception as e:
219
221
  if not json_output:
220
222
  console.print(f"[yellow]⚠[/yellow] Scan error: {e}")
221
223
 
224
+ # Generate HTTPS certificates if mkcert is available
225
+ certs_generated = False
226
+ cert_path_str = None
227
+
228
+ if not json_output:
229
+ console.print("\n[cyan]→[/cyan] Checking HTTPS certificates...")
230
+
231
+ try:
232
+ from zebra_day import mkcert
233
+
234
+ if not mkcert.is_mkcert_installed():
235
+ if not json_output:
236
+ console.print("[yellow]⚠[/yellow] mkcert not installed")
237
+ console.print(
238
+ " Install with: [dim]brew install mkcert[/dim] (macOS) or "
239
+ "[dim]sudo apt install mkcert[/dim] (Ubuntu)"
240
+ )
241
+ elif not mkcert.is_ca_installed():
242
+ if not json_output:
243
+ console.print("[yellow]⚠[/yellow] mkcert CA not installed")
244
+ console.print(" Run: [dim]mkcert -install[/dim] (one-time, requires password)")
245
+ elif mkcert.certificates_exist():
246
+ if not json_output:
247
+ console.print(f"[green]✓[/green] Certificates exist: {mkcert.CERT_FILE}")
248
+ certs_generated = True
249
+ cert_path_str = str(mkcert.CERT_FILE)
250
+ else:
251
+ if not json_output:
252
+ console.print(" Generating certificates...")
253
+ if mkcert.generate_certificates():
254
+ if not json_output:
255
+ console.print(f"[green]✓[/green] Certificates generated: {mkcert.CERT_FILE}")
256
+ certs_generated = True
257
+ cert_path_str = str(mkcert.CERT_FILE)
258
+ else:
259
+ if not json_output:
260
+ console.print("[yellow]⚠[/yellow] Failed to generate certificates")
261
+ except Exception as e:
262
+ if not json_output:
263
+ console.print(f"[yellow]⚠[/yellow] Certificate check error: {e}")
264
+
222
265
  if json_output:
266
+ result = {
267
+ "config_dir": config_dir,
268
+ "data_dir": data_dir,
269
+ "printers_found": printers_found,
270
+ "labs": labs,
271
+ "https_certs_generated": certs_generated,
272
+ "cert_path": cert_path_str,
273
+ }
223
274
  console.print(json_mod.dumps(result, indent=2))
224
275
  else:
225
276
  console.print("\n[bold green]✓ Bootstrap complete![/bold green]")
@@ -237,4 +288,3 @@ def main():
237
288
 
238
289
  if __name__ == "__main__":
239
290
  raise SystemExit(main())
240
-
zebra_day/cli/cognito.py CHANGED
@@ -5,7 +5,6 @@ basic status and info commands.
5
5
  """
6
6
 
7
7
  import os
8
- from typing import Optional
9
8
 
10
9
  import typer
11
10
  from rich.console import Console
@@ -18,6 +17,7 @@ def _is_cognito_available() -> bool:
18
17
  """Check if daylily-cognito is installed."""
19
18
  try:
20
19
  from daylily_cognito.cli import cognito_app as _ # noqa: F401
20
+
21
21
  return True
22
22
  except ImportError:
23
23
  return False
@@ -28,7 +28,8 @@ def _get_cognito_app() -> typer.Typer:
28
28
  if _is_cognito_available():
29
29
  # Import and return the full cognito CLI from daylily-cognito
30
30
  from daylily_cognito.cli import cognito_app
31
- return cognito_app
31
+
32
+ return cognito_app # type: ignore[no-any-return]
32
33
  else:
33
34
  # Return a minimal fallback app
34
35
  return _create_fallback_app()
@@ -36,7 +37,9 @@ def _get_cognito_app() -> typer.Typer:
36
37
 
37
38
  def _create_fallback_app() -> typer.Typer:
38
39
  """Create a fallback cognito app with basic commands."""
39
- app = typer.Typer(help="Cognito authentication management (limited - daylily-cognito not installed)")
40
+ app = typer.Typer(
41
+ help="Cognito authentication management (limited - daylily-cognito not installed)"
42
+ )
40
43
 
41
44
  @app.command("status")
42
45
  def status():
@@ -76,7 +79,9 @@ def _create_fallback_app() -> typer.Typer:
76
79
  console.print(" Start server with: [cyan]zday gui start --auth cognito[/cyan]")
77
80
  else:
78
81
  console.print("\n[yellow]⚠[/yellow] Cognito is not fully configured")
79
- console.print(" Set environment variables or install daylily-cognito for full management")
82
+ console.print(
83
+ " Set environment variables or install daylily-cognito for full management"
84
+ )
80
85
 
81
86
  @app.command("info")
82
87
  def info():
@@ -85,7 +90,7 @@ def _create_fallback_app() -> typer.Typer:
85
90
  console.print("To enable Cognito authentication for zebra_day:\n")
86
91
 
87
92
  console.print("[bold]1. Install auth dependencies:[/bold]")
88
- console.print(" [cyan]pip install -e \".[auth]\"[/cyan]\n")
93
+ console.print(' [cyan]pip install -e ".[auth]"[/cyan]\n')
89
94
 
90
95
  console.print("[bold]2. Set environment variables:[/bold]")
91
96
  console.print(" [cyan]export COGNITO_USER_POOL_ID=your-pool-id[/cyan]")
@@ -96,21 +101,23 @@ def _create_fallback_app() -> typer.Typer:
96
101
  console.print(" [cyan]zday gui start --auth cognito[/cyan]\n")
97
102
 
98
103
  if not _is_cognito_available():
99
- console.print("[dim]For full Cognito management (create, teardown), install daylily-cognito:[/dim]")
104
+ console.print(
105
+ "[dim]For full Cognito management (create, teardown), install daylily-cognito:[/dim]"
106
+ )
100
107
  console.print("[dim] pip install daylily-cognito[/dim]")
101
108
 
102
109
  @app.command("create")
103
110
  def create():
104
111
  """Create/configure a Cognito user pool (requires daylily-cognito)."""
105
112
  console.print("[yellow]⚠[/yellow] This command requires daylily-cognito")
106
- console.print(" Install with: [cyan]pip install -e \".[auth]\"[/cyan]")
113
+ console.print(' Install with: [cyan]pip install -e ".[auth]"[/cyan]')
107
114
  raise typer.Exit(1)
108
115
 
109
116
  @app.command("teardown")
110
117
  def teardown():
111
118
  """Remove Cognito configuration (requires daylily-cognito)."""
112
119
  console.print("[yellow]⚠[/yellow] This command requires daylily-cognito")
113
- console.print(" Install with: [cyan]pip install -e \".[auth]\"[/cyan]")
120
+ console.print(' Install with: [cyan]pip install -e ".[auth]"[/cyan]')
114
121
  raise typer.Exit(1)
115
122
 
116
123
  return app
@@ -118,4 +125,3 @@ def _create_fallback_app() -> typer.Typer:
118
125
 
119
126
  # Export the cognito app - either the full version from daylily-cognito or the fallback
120
127
  cognito_app = _get_cognito_app()
121
-
zebra_day/cli/gui.py CHANGED
@@ -7,7 +7,6 @@ import sys
7
7
  import time
8
8
  from datetime import datetime
9
9
  from pathlib import Path
10
- from typing import Optional, Literal
11
10
 
12
11
  import typer
13
12
  from rich.console import Console
@@ -39,13 +38,13 @@ def _get_log_file() -> Path:
39
38
  return LOG_DIR / f"gui_{ts}.log"
40
39
 
41
40
 
42
- def _get_latest_log() -> Optional[Path]:
41
+ def _get_latest_log() -> Path | None:
43
42
  """Get the most recent log file."""
44
43
  logs = sorted(LOG_DIR.glob("gui_*.log"), reverse=True)
45
44
  return logs[0] if logs else None
46
45
 
47
46
 
48
- def _get_pid() -> Optional[int]:
47
+ def _get_pid() -> int | None:
49
48
  """Get the running server PID if exists."""
50
49
  if PID_FILE.exists():
51
50
  try:
@@ -61,14 +60,13 @@ def _check_auth_dependencies() -> bool:
61
60
  """Check if auth dependencies are available."""
62
61
  try:
63
62
  import jose # noqa: F401
63
+
64
64
  return True
65
65
  except ImportError:
66
66
  return False
67
67
 
68
68
 
69
- def _resolve_ssl_paths(
70
- cert: Optional[str], key: Optional[str]
71
- ) -> tuple[Optional[str], Optional[str], bool]:
69
+ def _resolve_ssl_paths(cert: str | None, key: str | None) -> tuple[str | None, str | None, bool]:
72
70
  """
73
71
  Resolve SSL certificate and key paths.
74
72
 
@@ -109,10 +107,14 @@ def start(
109
107
  host: str = typer.Option("0.0.0.0", "--host", "-h", help="Host to bind to"),
110
108
  auth: str = typer.Option("none", "--auth", "-a", help="Authentication mode: none or cognito"),
111
109
  reload: bool = typer.Option(False, "--reload", "-r", help="Enable auto-reload (foreground)"),
112
- background: bool = typer.Option(True, "--background/--foreground", "-b/-f", help="Run in background"),
113
- cert: Optional[str] = typer.Option(None, "--cert", help="Path to SSL certificate file"),
114
- key: Optional[str] = typer.Option(None, "--key", help="Path to SSL private key file"),
115
- no_https: bool = typer.Option(False, "--no-https", help="Disable HTTPS even if certificates are available"),
110
+ background: bool = typer.Option(
111
+ True, "--background/--foreground", "-b/-f", help="Run in background"
112
+ ),
113
+ cert: str | None = typer.Option(None, "--cert", help="Path to SSL certificate file"),
114
+ key: str | None = typer.Option(None, "--key", help="Path to SSL private key file"),
115
+ no_https: bool = typer.Option(
116
+ False, "--no-https", help="Disable HTTPS even if certificates are available"
117
+ ),
116
118
  ):
117
119
  """Start the zebra_day web UI server.
118
120
 
@@ -141,7 +143,7 @@ def start(
141
143
  if auth == "cognito":
142
144
  if not _check_auth_dependencies():
143
145
  console.print("[red]✗[/red] Authentication requested but python-jose is not installed")
144
- console.print(" Install with: [cyan]pip install -e \".[auth]\"[/cyan]")
146
+ console.print(' Install with: [cyan]pip install -e ".[auth]"[/cyan]')
145
147
  raise typer.Exit(1)
146
148
 
147
149
  # Check required env vars
@@ -168,14 +170,16 @@ def start(
168
170
  protocol = "https" if use_https else "http"
169
171
 
170
172
  if use_https:
171
- console.print(f"[green]✓[/green] HTTPS enabled")
173
+ console.print("[green]✓[/green] HTTPS enabled")
172
174
  console.print(f" Certificate: [dim]{cert_path}[/dim]")
173
175
  console.print(f" Private key: [dim]{key_path}[/dim]")
174
176
  else:
175
177
  console.print("[yellow]⚠[/yellow] Running in HTTP mode (insecure)")
176
178
  console.print(" For HTTPS, generate certificates with mkcert:")
177
179
  console.print(f" [dim]mkdir -p {DEFAULT_CERT_DIR}[/dim]")
178
- console.print(f" [dim]mkcert -cert-file {DEFAULT_CERT_FILE} -key-file {DEFAULT_KEY_FILE} localhost 127.0.0.1 ::1[/dim]")
180
+ console.print(
181
+ f" [dim]mkcert -cert-file {DEFAULT_CERT_FILE} -key-file {DEFAULT_KEY_FILE} localhost 127.0.0.1 ::1[/dim]"
182
+ )
179
183
 
180
184
  # Build command with SSL parameters
181
185
  ssl_args = ""
@@ -232,7 +236,9 @@ def start(
232
236
  console.print(f" URL: [cyan]{protocol}://{host}:{port}[/cyan]")
233
237
  console.print(f" Logs: [dim]{log_file}[/dim]")
234
238
  else:
235
- console.print(f"[green]✓[/green] Starting server on [cyan]{protocol}://{host}:{port}[/cyan]")
239
+ console.print(
240
+ f"[green]✓[/green] Starting server on [cyan]{protocol}://{host}:{port}[/cyan]"
241
+ )
236
242
  console.print(" Press Ctrl+C to stop\n")
237
243
  try:
238
244
  result = subprocess.run(cmd, cwd=Path.cwd(), env=env)
@@ -268,7 +274,7 @@ def stop():
268
274
  console.print("[yellow]⚠[/yellow] Server was not running")
269
275
  except PermissionError:
270
276
  console.print(f"[red]✗[/red] Permission denied stopping PID {pid}")
271
- raise typer.Exit(1)
277
+ raise typer.Exit(1) from None
272
278
 
273
279
 
274
280
  @gui_app.command("status")
@@ -335,4 +341,3 @@ def restart(
335
341
  stop()
336
342
  time.sleep(1)
337
343
  start(port=port, host=host, auth=auth, reload=False, background=True)
338
-
zebra_day/cli/printer.py CHANGED
@@ -2,14 +2,11 @@
2
2
 
3
3
  import json
4
4
  import socket
5
- from typing import Optional
6
5
 
7
6
  import typer
8
7
  from rich.console import Console
9
8
  from rich.table import Table
10
9
 
11
- from zebra_day import paths as xdg
12
-
13
10
  printer_app = typer.Typer(help="Printer fleet management commands")
14
11
  console = Console()
15
12
 
@@ -19,7 +16,7 @@ def _get_local_ip() -> str:
19
16
  try:
20
17
  s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
21
18
  s.connect(("8.8.8.8", 80))
22
- ip = s.getsockname()[0]
19
+ ip: str = s.getsockname()[0]
23
20
  s.close()
24
21
  return ip
25
22
  except Exception:
@@ -28,9 +25,13 @@ def _get_local_ip() -> str:
28
25
 
29
26
  @printer_app.command("scan")
30
27
  def scan(
31
- ip_stub: Optional[str] = typer.Option(None, "--ip-stub", "-i", help="IP stub to scan (e.g., 192.168.1)"),
28
+ ip_stub: str | None = typer.Option(
29
+ None, "--ip-stub", "-i", help="IP stub to scan (e.g., 192.168.1)"
30
+ ),
32
31
  wait: float = typer.Option(0.25, "--wait", "-w", help="Seconds to wait per IP probe"),
33
- lab: str = typer.Option("scan-results", "--lab", "-l", help="Lab name to assign found printers"),
32
+ lab: str = typer.Option(
33
+ "scan-results", "--lab", "-l", help="Lab name to assign found printers"
34
+ ),
34
35
  json_output: bool = typer.Option(False, "--json", "-j", help="Output as JSON"),
35
36
  ):
36
37
  """Scan network for Zebra printers."""
@@ -45,6 +46,7 @@ def scan(
45
46
 
46
47
  try:
47
48
  import zebra_day.print_mgr as zdpm
49
+
48
50
  zp = zdpm.zpl()
49
51
  zp.probe_zebra_printers_add_to_printers_json(
50
52
  ip_stub=ip_stub,
@@ -56,12 +58,14 @@ def scan(
56
58
  if hasattr(zp, "printers") and "labs" in zp.printers and lab in zp.printers["labs"]:
57
59
  for name, info in zp.printers["labs"][lab].items():
58
60
  if isinstance(info, dict) and info.get("ip_address") not in ["dl_png"]:
59
- found.append({
60
- "name": name,
61
- "ip": info.get("ip_address"),
62
- "model": info.get("model", "unknown"),
63
- "serial": info.get("serial", "unknown"),
64
- })
61
+ found.append(
62
+ {
63
+ "name": name,
64
+ "ip": info.get("ip_address"),
65
+ "model": info.get("model", "unknown"),
66
+ "serial": info.get("serial", "unknown"),
67
+ }
68
+ )
65
69
 
66
70
  if json_output:
67
71
  console.print(json.dumps(found, indent=2))
@@ -82,17 +86,18 @@ def scan(
82
86
  console.print(json.dumps({"error": str(e)}))
83
87
  else:
84
88
  console.print(f"[red]✗[/red] Scan error: {e}")
85
- raise typer.Exit(1)
89
+ raise typer.Exit(1) from None
86
90
 
87
91
 
88
92
  @printer_app.command("list")
89
93
  def list_printers(
90
- lab: Optional[str] = typer.Option(None, "--lab", "-l", help="Filter by lab name"),
94
+ lab: str | None = typer.Option(None, "--lab", "-l", help="Filter by lab name"),
91
95
  json_output: bool = typer.Option(False, "--json", "-j", help="Output as JSON"),
92
96
  ):
93
97
  """List configured printers."""
94
98
  try:
95
99
  import zebra_day.print_mgr as zdpm
100
+
96
101
  zp = zdpm.zpl()
97
102
 
98
103
  printers = []
@@ -102,13 +107,15 @@ def list_printers(
102
107
  continue
103
108
  for name, info in lab_printers.items():
104
109
  if isinstance(info, dict):
105
- printers.append({
106
- "lab": lab_name,
107
- "name": name,
108
- "ip": info.get("ip_address", "unknown"),
109
- "model": info.get("model", "unknown"),
110
- "styles": info.get("label_zpl_styles", []),
111
- })
110
+ printers.append(
111
+ {
112
+ "lab": lab_name,
113
+ "name": name,
114
+ "ip": info.get("ip_address", "unknown"),
115
+ "model": info.get("model", "unknown"),
116
+ "styles": info.get("label_zpl_styles", []),
117
+ }
118
+ )
112
119
 
113
120
  if json_output:
114
121
  console.print(json.dumps(printers, indent=2))
@@ -128,7 +135,7 @@ def list_printers(
128
135
  for p in printers:
129
136
  styles = ", ".join(p["styles"][:2])
130
137
  if len(p["styles"]) > 2:
131
- styles += f" (+{len(p['styles'])-2})"
138
+ styles += f" (+{len(p['styles']) - 2})"
132
139
  table.add_row(p["lab"], p["name"], p["ip"], p["model"], styles)
133
140
  console.print(table)
134
141
 
@@ -137,7 +144,7 @@ def list_printers(
137
144
  console.print(json.dumps({"error": str(e)}))
138
145
  else:
139
146
  console.print(f"[red]✗[/red] Error: {e}")
140
- raise typer.Exit(1)
147
+ raise typer.Exit(1) from None
141
148
 
142
149
 
143
150
  @printer_app.command("test")
@@ -149,10 +156,11 @@ def test_print(
149
156
  """Send a test print to a specific printer."""
150
157
  try:
151
158
  import zebra_day.print_mgr as zdpm
159
+
152
160
  zp = zdpm.zpl()
153
161
 
154
162
  console.print(f"[cyan]→[/cyan] Sending test print to {printer_name}...")
155
- result = zp.print_zpl(
163
+ zp.print_zpl(
156
164
  lab=lab,
157
165
  printer_name=printer_name,
158
166
  uid_barcode="TEST-PRINT",
@@ -160,9 +168,8 @@ def test_print(
160
168
  alt_b="zebra_day CLI",
161
169
  label_zpl_style=label_style,
162
170
  )
163
- console.print(f"[green]✓[/green] Test print sent successfully")
171
+ console.print("[green]✓[/green] Test print sent successfully")
164
172
 
165
173
  except Exception as e:
166
174
  console.print(f"[red]✗[/red] Print error: {e}")
167
- raise typer.Exit(1)
168
-
175
+ raise typer.Exit(1) from None