zebra-day 1.0.2__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 (174) 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 +101 -13
  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 +5 -11
  17. zebra_day/etc/printer_config.template.json +5 -11
  18. zebra_day/etc/tmp_printers120.json +10 -0
  19. zebra_day/etc/tmp_printers139.json +10 -0
  20. zebra_day/etc/tmp_printers145.json +10 -0
  21. zebra_day/etc/tmp_printers147.json +10 -0
  22. zebra_day/etc/tmp_printers207.json +10 -0
  23. zebra_day/etc/tmp_printers34.json +10 -0
  24. zebra_day/etc/tmp_printers389.json +10 -0
  25. zebra_day/etc/tmp_printers398.json +10 -0
  26. zebra_day/etc/tmp_printers437.json +10 -0
  27. zebra_day/etc/tmp_printers439.json +10 -0
  28. zebra_day/etc/tmp_printers440.json +10 -0
  29. zebra_day/etc/tmp_printers469.json +10 -0
  30. zebra_day/etc/tmp_printers485.json +10 -0
  31. zebra_day/etc/tmp_printers508.json +10 -0
  32. zebra_day/etc/tmp_printers531.json +10 -0
  33. zebra_day/etc/tmp_printers540.json +10 -0
  34. zebra_day/etc/tmp_printers542.json +10 -0
  35. zebra_day/etc/tmp_printers543.json +10 -0
  36. zebra_day/etc/tmp_printers552.json +10 -0
  37. zebra_day/etc/tmp_printers715.json +10 -0
  38. zebra_day/etc/tmp_printers835.json +10 -0
  39. zebra_day/etc/tmp_printers842.json +10 -0
  40. zebra_day/etc/tmp_printers931.json +10 -0
  41. zebra_day/etc/tmp_printers969.json +10 -0
  42. zebra_day/etc/tmp_printers972.json +10 -0
  43. zebra_day/exceptions.py +1 -1
  44. zebra_day/files/blank_preview.png +0 -0
  45. zebra_day/files/corners_20cmX30cm_preview.png +0 -0
  46. zebra_day/files/corners_smallTube_preview.png +0 -0
  47. zebra_day/files/generic_2inX1in_preview.png +0 -0
  48. zebra_day/files/test_png_12020.png +0 -0
  49. zebra_day/files/test_png_12352.png +0 -0
  50. zebra_day/files/test_png_15472.png +0 -0
  51. zebra_day/files/test_png_24493.png +0 -0
  52. zebra_day/files/test_png_2897.png +0 -0
  53. zebra_day/files/test_png_30069.png +0 -0
  54. zebra_day/files/test_png_31690.png +0 -0
  55. zebra_day/files/test_png_33804.png +0 -0
  56. zebra_day/files/test_png_34737.png +0 -0
  57. zebra_day/files/test_png_4161.png +0 -0
  58. zebra_day/files/test_png_44748.png +0 -0
  59. zebra_day/files/test_png_4635.png +0 -0
  60. zebra_day/files/test_png_47791.png +0 -0
  61. zebra_day/files/test_png_47799.png +0 -0
  62. zebra_day/files/test_png_55588.png +0 -0
  63. zebra_day/files/test_png_56349.png +0 -0
  64. zebra_day/files/test_png_58809.png +0 -0
  65. zebra_day/files/test_png_5936.png +0 -0
  66. zebra_day/files/test_png_64110.png +0 -0
  67. zebra_day/files/test_png_64891.png +0 -0
  68. zebra_day/files/test_png_67242.png +0 -0
  69. zebra_day/files/test_png_69002.png +0 -0
  70. zebra_day/files/test_png_70065.png +0 -0
  71. zebra_day/files/test_png_72366.png +0 -0
  72. zebra_day/files/test_png_77793.png +0 -0
  73. zebra_day/files/test_png_89893.png +0 -0
  74. zebra_day/files/test_png_9572.png +0 -0
  75. zebra_day/files/tube_20mmX30mmA_preview.png +0 -0
  76. zebra_day/imgs/.hold +0 -0
  77. zebra_day/imgs/bar_ltpurp.png +0 -0
  78. zebra_day/imgs/bar_purp.png +0 -0
  79. zebra_day/imgs/bar_purp3.png +0 -0
  80. zebra_day/imgs/bar_red.png +0 -0
  81. zebra_day/imgs/legacy/UBC_gantt_chart.png +0 -0
  82. zebra_day/imgs/legacy/gx420d_network_config.png +0 -0
  83. zebra_day/imgs/legacy/gx420d_printer_config.png +0 -0
  84. zebra_day/imgs/legacy/ngrok.png +0 -0
  85. zebra_day/imgs/legacy/printer_details.png +0 -0
  86. zebra_day/imgs/legacy/quick_start_test_label.png +0 -0
  87. zebra_day/imgs/legacy/quick_start_test_label2.png +0 -0
  88. zebra_day/imgs/legacy/zd620_network_config.png +0 -0
  89. zebra_day/imgs/legacy/zd620_printer_config.png +0 -0
  90. zebra_day/imgs/legacy/zday_quick_gui.png +0 -0
  91. zebra_day/imgs/legacy/zebra_day_alt_css_dog.png +0 -0
  92. zebra_day/imgs/legacy/zebra_day_alt_css_flower.png +0 -0
  93. zebra_day/imgs/legacy/zebra_day_alt_css_main.png +0 -0
  94. zebra_day/imgs/legacy/zebra_day_available_zpl_templates.png +0 -0
  95. zebra_day/imgs/legacy/zebra_day_bkup_pconfig.png +0 -0
  96. zebra_day/imgs/legacy/zebra_day_home.png +0 -0
  97. zebra_day/imgs/legacy/zebra_day_manual_print.png +0 -0
  98. zebra_day/imgs/legacy/zebra_day_printer_fleet_json.png +0 -0
  99. zebra_day/imgs/legacy/zebra_day_quick_ex.png +0 -0
  100. zebra_day/imgs/legacy/zebra_day_zpl_template_IRLa.png +0 -0
  101. zebra_day/imgs/legacy/zebra_day_zpl_template_IRLb.png +0 -0
  102. zebra_day/imgs/ui_api_docs.png +0 -0
  103. zebra_day/imgs/ui_config.png +0 -0
  104. zebra_day/imgs/ui_dashboard.png +0 -0
  105. zebra_day/imgs/ui_print_request.png +0 -0
  106. zebra_day/imgs/ui_printers.png +0 -0
  107. zebra_day/imgs/ui_templates.png +0 -0
  108. zebra_day/logging_config.py +4 -9
  109. zebra_day/mkcert.py +157 -0
  110. zebra_day/paths.py +1 -2
  111. zebra_day/print_mgr.py +261 -185
  112. zebra_day/templates/modern/config.html +7 -0
  113. zebra_day/templates/modern/config_backups.html +59 -0
  114. zebra_day/templates/modern/config_editor.html +95 -0
  115. zebra_day/templates/modern/config_new.html +93 -0
  116. zebra_day/templates/modern/print_request.html +70 -8
  117. zebra_day/templates/modern/printer_detail.html +161 -34
  118. zebra_day/templates/modern/printers.html +17 -6
  119. zebra_day/templates/modern/template_editor.html +7 -4
  120. zebra_day/web/__init__.py +1 -1
  121. zebra_day/web/app.py +99 -17
  122. zebra_day/web/auth.py +17 -15
  123. zebra_day/web/middleware.py +8 -5
  124. zebra_day/web/routers/__init__.py +0 -1
  125. zebra_day/web/routers/api.py +330 -31
  126. zebra_day/web/routers/ui.py +174 -591
  127. zebra_day/zpl_renderer.py +45 -34
  128. {zebra_day-1.0.2.dist-info → zebra_day-2.1.4.dist-info}/METADATA +144 -74
  129. zebra_day-2.1.4.dist-info/RECORD +240 -0
  130. zebra_day/bin/fetch_zebra_config.py +0 -15
  131. zebra_day/bin/generate_coord_grid_zpl.py +0 -50
  132. zebra_day/bin/print_zpl_from_file.py +0 -21
  133. zebra_day/bin/probe_new_label_dimensions.py +0 -75
  134. zebra_day/bin/scan_for_networed_zebra_printers.py +0 -23
  135. zebra_day/bin/scan_for_networed_zebra_printers_arp_scan.sh +0 -1
  136. zebra_day/bin/scan_for_networed_zebra_printers_curl.sh +0 -30
  137. zebra_day/bin/zserve.py +0 -1062
  138. zebra_day/templates/base.html +0 -36
  139. zebra_day/templates/bpr.html +0 -72
  140. zebra_day/templates/build_new_config.html +0 -36
  141. zebra_day/templates/build_print_request.html +0 -32
  142. zebra_day/templates/chg_ui_style.html +0 -19
  143. zebra_day/templates/edit_template.html +0 -128
  144. zebra_day/templates/edit_zpl.html +0 -37
  145. zebra_day/templates/index.html +0 -82
  146. zebra_day/templates/legacy/base.html +0 -37
  147. zebra_day/templates/legacy/bpr.html +0 -72
  148. zebra_day/templates/legacy/build_new_config.html +0 -36
  149. zebra_day/templates/legacy/build_print_request.html +0 -32
  150. zebra_day/templates/legacy/chg_ui_style.html +0 -19
  151. zebra_day/templates/legacy/edit_template.html +0 -128
  152. zebra_day/templates/legacy/edit_zpl.html +0 -37
  153. zebra_day/templates/legacy/index.html +0 -82
  154. zebra_day/templates/legacy/list_prior_configs.html +0 -24
  155. zebra_day/templates/legacy/print_result.html +0 -30
  156. zebra_day/templates/legacy/printer_details.html +0 -25
  157. zebra_day/templates/legacy/printer_status.html +0 -70
  158. zebra_day/templates/legacy/save_result.html +0 -17
  159. zebra_day/templates/legacy/send_print_request.html +0 -34
  160. zebra_day/templates/legacy/simple_print.html +0 -94
  161. zebra_day/templates/legacy/view_pstation_json.html +0 -29
  162. zebra_day/templates/list_prior_configs.html +0 -24
  163. zebra_day/templates/print_result.html +0 -30
  164. zebra_day/templates/printer_details.html +0 -25
  165. zebra_day/templates/printer_status.html +0 -70
  166. zebra_day/templates/save_result.html +0 -17
  167. zebra_day/templates/send_print_request.html +0 -34
  168. zebra_day/templates/simple_print.html +0 -94
  169. zebra_day/templates/view_pstation_json.html +0 -29
  170. zebra_day-1.0.2.dist-info/RECORD +0 -179
  171. {zebra_day-1.0.2.dist-info → zebra_day-2.1.4.dist-info}/WHEEL +0 -0
  172. {zebra_day-1.0.2.dist-info → zebra_day-2.1.4.dist-info}/entry_points.txt +0 -0
  173. {zebra_day-1.0.2.dist-info → zebra_day-2.1.4.dist-info}/licenses/LICENSE +0 -0
  174. {zebra_day-1.0.2.dist-info → zebra_day-2.1.4.dist-info}/top_level.txt +0 -0
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
zebra_day/cli/template.py CHANGED
@@ -1,10 +1,12 @@
1
1
  """ZPL template management commands for zebra_day CLI."""
2
2
 
3
+ from __future__ import annotations
4
+
3
5
  import json
4
6
  import os
5
7
  import subprocess
6
8
  from pathlib import Path
7
- from typing import Optional
9
+ from typing import Any
8
10
 
9
11
  import typer
10
12
  from rich.console import Console
@@ -37,7 +39,7 @@ def _get_template_dirs() -> list[Path]:
37
39
  return dirs
38
40
 
39
41
 
40
- def _find_template(name: str) -> Optional[Path]:
42
+ def _find_template(name: str) -> Path | None:
41
43
  """Find a template file by name."""
42
44
  for template_dir in _get_template_dirs():
43
45
  # Try exact match
@@ -59,18 +61,20 @@ def list_templates(
59
61
  verbose: bool = typer.Option(False, "--verbose", "-v", help="Show full paths"),
60
62
  ):
61
63
  """List available ZPL templates."""
62
- templates = []
64
+ templates: list[dict[str, Any]] = []
63
65
 
64
66
  for template_dir in _get_template_dirs():
65
67
  for f in template_dir.iterdir():
66
68
  if f.is_file() and not f.name.startswith("."):
67
69
  if f.suffix in [".zpl", ".txt", ""] or f.name.startswith("zpl_"):
68
- templates.append({
69
- "name": f.stem,
70
- "path": str(f),
71
- "size": f.stat().st_size,
72
- "source": "user" if "zebra_day" not in str(template_dir) else "package",
73
- })
70
+ templates.append(
71
+ {
72
+ "name": f.stem,
73
+ "path": str(f),
74
+ "size": f.stat().st_size,
75
+ "source": "user" if "zebra_day" not in str(template_dir) else "package",
76
+ }
77
+ )
74
78
 
75
79
  # Dedupe by name, prefer user templates
76
80
  seen = {}
@@ -107,7 +111,7 @@ def list_templates(
107
111
  @template_app.command("preview")
108
112
  def preview(
109
113
  template_name: str = typer.Argument(..., help="Template name to preview"),
110
- output: Optional[str] = typer.Option(None, "--output", "-o", help="Output PNG file path"),
114
+ output: str | None = typer.Option(None, "--output", "-o", help="Output PNG file path"),
111
115
  ):
112
116
  """Generate a PNG preview of a ZPL template."""
113
117
  template_path = _find_template(template_name)
@@ -119,6 +123,7 @@ def preview(
119
123
 
120
124
  try:
121
125
  import zebra_day.print_mgr as zdpm
126
+
122
127
  zp = zdpm.zpl()
123
128
 
124
129
  # Read template
@@ -130,18 +135,18 @@ def preview(
130
135
  else:
131
136
  output_path = Path(output)
132
137
 
133
- result = zp.generate_label_png(zpl_content, str(output_path), False)
138
+ zp.generate_label_png(zpl_content, str(output_path), False)
134
139
  console.print(f"[green]✓[/green] Preview generated: {output_path}")
135
140
 
136
141
  except Exception as e:
137
142
  console.print(f"[red]✗[/red] Preview error: {e}")
138
- raise typer.Exit(1)
143
+ raise typer.Exit(1) from None
139
144
 
140
145
 
141
146
  @template_app.command("edit")
142
147
  def edit(
143
148
  template_name: str = typer.Argument(..., help="Template name to edit"),
144
- editor: Optional[str] = typer.Option(None, "--editor", "-e", help="Editor command"),
149
+ editor: str | None = typer.Option(None, "--editor", "-e", help="Editor command"),
145
150
  ):
146
151
  """Open a ZPL template in an editor."""
147
152
  template_path = _find_template(template_name)
@@ -158,7 +163,7 @@ def edit(
158
163
  subprocess.run([editor, str(template_path)])
159
164
  except Exception as e:
160
165
  console.print(f"[red]✗[/red] Error opening editor: {e}")
161
- raise typer.Exit(1)
166
+ raise typer.Exit(1) from None
162
167
 
163
168
 
164
169
  @template_app.command("show")
@@ -173,4 +178,3 @@ def show(
173
178
 
174
179
  console.print(f"[dim]# {template_path}[/dim]\n")
175
180
  console.print(template_path.read_text())
176
-
zebra_day/cmd_mgr.py CHANGED
@@ -1,17 +1,16 @@
1
1
  """
2
- Tools to manage a zebra printer fleet and expose an API for routing print requests.
2
+ Tools to manage a zebra printer fleet and expose an API for routing print requests.
3
3
  """
4
4
 
5
5
  import socket
6
6
 
7
- class ZebraPrinter:
8
7
 
8
+ class ZebraPrinter:
9
9
  def __init__(self, ip_address, port=9100, buffer_size=1024):
10
10
  self.ip_address = ip_address
11
11
  self.port = port
12
12
  self.buffer_size = buffer_size
13
13
 
14
-
15
14
  def send_command(self, command):
16
15
  with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
17
16
  s.connect((self.ip_address, self.port))
@@ -19,15 +18,13 @@ class ZebraPrinter:
19
18
  response = s.recv(self.buffer_size)
20
19
  return response.decode()
21
20
 
22
-
23
21
  def get_configuration(self):
24
22
  """Retrieve printer configuration using ^HH command"""
25
23
  return self.send_command("^XA^HH^XZ")
26
24
 
27
-
28
25
  def set_configuration(self, config):
29
26
  """
30
- Set printer configuration.
27
+ Set printer configuration.
31
28
  The `config` parameter should contain the necessary ZPL commands to adjust the configuration.
32
29
  After sending the configuration commands, the ^JUS command saves the configuration.
33
30
  """
Binary file
@@ -0,0 +1,149 @@
1
+ ## Hardware
2
+
3
+ ### Zebra Printers
4
+ * Printers will need to be able to secure an IP address either via a wired connection or via wireless. The printers will need to be visible to the machine running this package in order to accept print requests. Further, there are tools to scan the local network and identify Zebra Printers. Identified printers are automatically configured for use by `zebra_day`.
5
+
6
+ * Network attached printers should be able to run w/out any connection to a PC/laptop. This affors much greater flexibility in placing print stations. In fact, `zebra_day` does not communicate with USB only printers.
7
+ * The below zebra printer models have been tested, but any zebra printer able to obtain an IP address and accept `ZPL` should also work.
8
+ * _IMPORTANT_, The initial configuration of zebra printers can be a little gnarly. I intend to add some notes on this topic soon. In the mean time, the GUI printer status report includes links to access the web server admin utility each zebra printer exposes.
9
+ * the printer admin UIs all use the default un/pw: `admin/1234`.
10
+ * The first thing I suggest when setting up a new printer is resetting the factory defaults - doable via the zebra printer admin UIs.
11
+ * Wired ethernet connections are advised for greater robustness. Configure the wired network settings to obtain an IP automatically. ask your network admins to set DNS rules which will assign the same IP to each printer. You'll need to supply the printer MAC address to do this, which you can find on via each printers admin UI.
12
+ * __NOTE__ Sometimes these printers can not seem to obtain an IP with DHCP, this could be a router/hub issue, but sometimes you'll need to set the printer to a static IP. This can be done via the printer admin UI. If the network config is saved with an error, you'll now need to connect to the printer via USB to reset (there might be a button seuquece cycle which can be hit to factory reset...).
13
+ * Wireless setup is _SUPER_ fussy. You'll need to know precisely what bands your router is running on, and the precise auth used. This can be done via the zebra printer admin UI, or when connected via a USB cable to a computer running a driver config program(not advised really).
14
+ * Next, you may have to mess around with calibration settings for label width and length.
15
+ * I have used this driver/config tool on a MAC when I have needed to connect to a printer via `USB`, [Peninsula Zebra Printer Driver](https://www.peninsula-group.com/install-zebra-printer-mac-osx/install-zebra-printer-mac-osx.html). I'm fairly sure there are several windows options.
16
+
17
+
18
+ #### GX420d - wired, no LCD screen
19
+
20
+ These printers are NOT reccomended b/c the lack of LCD screen makes them a pain to configure. You will probably need to connect via USB to set good initial settings. If you have the `zebra_day` UI running, try connecting the printer to the network and powering on, then run the zebra printer network scan. If the printer is discovered, you're in luck and can admin it via the UI on the printer.
21
+
22
+ * [Available From Amazon](https://www.amazon.com/gp/product/B07KCQ67Y1/ref=ppx_yo_dt_b_search_asin_title?ie=UTF8&psc=1)
23
+ * Cost per printer : `$264.00`
24
+ * [manual]()
25
+
26
+ #### GX420d - wired, with LCD screen
27
+ These are solid, but aging out and not as easy to find for sale.
28
+
29
+ * [Available From Amazon](https://www.amazon.com/gp/product/B011Q95XX2/ref=ppx_yo_dt_b_search_asin_title?ie=UTF8&psc=1)
30
+ * Cost per printer : `$425.00`
31
+ * [manual]()
32
+
33
+ #### ZD620d - wired and wireless, with color LCD screen
34
+ These are solid, but aging out and not as easy to find for sale.
35
+
36
+ * [Available From Amazon](https://www.amazon.com/gp/product/B07VHDR33Z/ref=ppx_yo_dt_b_search_asin_title?ie=UTF8&psc=1)
37
+ * Cost per printer : `$331.00`
38
+ * [manual]()
39
+
40
+ #### Zebra QLn420 Direct Thermal Printer - Monochrome - Portable
41
+
42
+ * [Amazon](https://www.amazon.com/dp/B084P4KBWS?psc=1&ref=ppx_yo2ov_dt_b_product_details)
43
+ * Cost per printer : `$136`
44
+ * [manual]()
45
+
46
+ #### Zebra - ZD620d Direct Thermal Desktop Printer with LCD Screen - Print Width 4 in - 203 dpi - Interface: WiFi, Bluetooth, USB, Serial, Ethernet - ZD62142-D01L01EZ
47
+
48
+ * [Amazon](https://www.amazon.com/dp/B07VHDR33Z?psc=1&ref=ppx_yo2ov_dt_b_product_details)
49
+ * Cost per printer : `$331`
50
+ * [manual]()
51
+
52
+
53
+ #### Zebra ZD620t Thermal Transfer Desktop Printer 203 dpi Print Width 4 in Ethernet Serial USB ZD62042
54
+ <font color=magenta>note, this is a thermal transfer printer & requires ribbons to print</font>
55
+ * [Amazon](https://www.amazon.com/dp/B08PW6ZRL6?psc=1&ref=ppx_yo2ov_dt_b_product_details)
56
+ * Cost per printer : `$500`
57
+ * [manual]()
58
+
59
+ ### Initial Printer Configuration
60
+ Ideally, you will be able to use `zebra_day` to auto-detect printers on your network, and connect to each ones web based admin tool (links are presented on the [printer fleet summary page](), un/pw is `admin/1234` ). Failing this, you will need to connect via usb, [there are several drivers to choose from if this is necessary, I liked this one for a MAC](https://www.peninsula-group.com/mac-thermal-printer-driver/). The main changes to make (and do so after consulting each printers manual):
61
+ * reset to factory defaults.
62
+ * set the network settings (wired and wifi) & hard code IPs if needed. It is helpful if they retain the same IP over time.
63
+ * Set the media type to `web`, set behavior on close and restart to be calibrate.
64
+ * Once you begin printing, you might find you need to adjust the print width and length for some printers if they do not set properly during calibration.
65
+ * Increase the darkness.
66
+ * Decrease print speed, `IPS` to 2 or 3.
67
+
68
+ ##### Reference Printer Config Reports
69
+ * [GX420d - printer](zebra_day/imgs/legacy/gx420d_printer_config.png) .... [GX420d - network](zebra_day/imgs/legacy/gx420d_network_config.png)
70
+ * [ZD620 - printer](zebra_day/imgs/legacy/zd620_printer_config.png) .... [ZD620 - network](zebra_day/imgs/legacy/zd620_network_config.png)
71
+
72
+
73
+
74
+
75
+ ### Thermal Transfer Ribbons
76
+ Also require distinct label stock from the direct thermal transfer printer labels.
77
+
78
+ * [Amazon](https://www.amazon.com/dp/B07FX3PJ2M?psc=1&ref=ppx_yo2ov_dt_b_product_details)
79
+ * Cost per roll: `$7`
80
+
81
+
82
+ # Label Stock
83
+ The `zebra_day` code can easily manage a printer fleet comprised of different printing mechanism.
84
+
85
+ > Thermal Transfer vs. Direct Thermal Transfer Labels
86
+ * Printers are only capable of printing using one or the other method only.
87
+
88
+ > Direct thermal transfer labels require no ribbon, cheaper, can be less robust in some situations, speciality use case label stock mfgs are numerous.
89
+
90
+ > Thermal transfer labels require a ribbon, can be more durable, prone to smudging, more costly
91
+
92
+ ### Aegis Labels
93
+ For general purpose use. Very inexpensive and easy to source.
94
+
95
+ #### 2in x 1in - Direct TT
96
+ Good for paperwork, some larger tubes.
97
+
98
+ * [Amazon](https://www.amazon.com/gp/product/B098Z8JYZC/ref=ppx_yo_dt_b_search_asin_title?ie=UTF8&th=1)
99
+ * Cost per roll : `$3.21`
100
+
101
+ #### 2in x 0.5in - Direct TT
102
+ Good for smaller tubes, or tubes that already have space taken up by labels.
103
+
104
+ * [Available On Amazon](https://www.amazon.com/gp/product/B098Z8JYZC/ref=ppx_yo_dt_b_search_asin_title?ie=UTF8&th=1)
105
+ * Cost per roll : `$3.25`
106
+
107
+
108
+ ### [Labtag](http://www.labtag.com)
109
+
110
+ #### 2in x 1in / a few colors / cryo - Direct TT
111
+
112
+ * [LabTag](https://www.labtag.com/shop/product/cryogenic-direct-thermal-labels-2-x-1-dfp-dfpc-28-2/?attribute_pa_core-size=1&attribute_pa_qty-uom=1000&attribute_pa_color=white)
113
+ * Cost per roll : `$81`
114
+
115
+ #### 2in x 0.25in / plate style / cryo - Direct TT
116
+
117
+ * [LabTag](https://www.labtag.com/shop/product/cryogenic-direct-thermal-labels-2-x-0-25-dfp-227/?attribute_pa_core-size=1&attribute_pa_qty-uom=2000)
118
+ * Cost per roll : `$67`
119
+
120
+ #### small tube w/dot / cryo - Direct TT
121
+
122
+ * [LabTag](https://www.labtag.com/shop/product/cryogenic-direct-thermal-labels-1-25-x-0-625-0-375-dfp-103/?attribute_pa_core-size=1&attribute_pa_qty-uom=2000)
123
+ * Cost per roll : `$105`
124
+
125
+ #### 2in x 1in / cryo / many colors - Thermal Transfer
126
+
127
+ * [LabTag](https://www.labtag.com/shop/product/cryogenic-barcode-labels-2-x-1-jtta-28/?attribute_pa_core-size=1&attribute_pa_qty-uom=2000&attribute_pa_color=white)
128
+ * Cost per roll : `$117`
129
+
130
+
131
+ #### 2in x 0.25in / plate / cryo - Thermal Transfer
132
+
133
+ * [LabTag](https://www.labtag.com/shop/product/thermal-transfer-cryogenic-labels-for-frozen-vials-surfaces-2-x-0-25-uc-227/?attribute_pa_core-size=1&attribute_pa_qty-uom=2000)
134
+ * Cost per roll : `$80`
135
+
136
+
137
+ # Barcode Scanners
138
+
139
+ ## Tera 1d, 2d, QR scanner. Corded and bluetooth and wireless
140
+ Programable and well supported/adopted.
141
+
142
+ * [Available From Amazon](https://www.amazon.com/dp/B0953FJZDG?psc=1&ref=ppx_yo2ov_dt_b_product_details)
143
+ * Cost per scanner : `$63.00`
144
+
145
+ ## Tera Mini 1d 2d QR. Corded and wireless and bluetooth.
146
+ _experimenting_... tiny handheld pretty well behaving non-corded scanner.
147
+
148
+ * [Available From Amazon](https://www.amazon.com/dp/B08NDFWFKJ?psc=1&ref=ppx_yo2ov_dt_b_product_details)
149
+ * Cost per Scanner : `$38.00`
@@ -0,0 +1,181 @@
1
+ # Library Usage
2
+ <a href=../../README.md ><img src="http://flux.glass/format_gh_text?txt=back+home&bg_color=%23443636&txt_color=%232eecef&font=Monoid-Regular-HalfTight-Dollar-0-1-l&font_size=12&width=95&ret_type=img" /></a>
3
+ ## Quickstart
4
+
5
+ ```python
6
+ import zebra_day.print_mgr as zdpm
7
+
8
+ zlab = zdpm.zpl()
9
+
10
+ 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.
11
+
12
+ print(zlab.printers) # This should print out the json dict of all detected zebra printers. An empty dict, {}, is a failure of autodetection, and manual creation of the json file may be needed. If successful, the lab name assigned is 'default', this may be edited later.
13
+ # The json will look something like this (v2.0.0 schema with nested printers)
14
+ ## {'schema_version': '2.0.0', 'labs': {'default': {'lab_name': 'Default', 'printers': {'192.168.1.7': {'ip_address': '192.168.1.7', ...}}}}}
15
+
16
+ # 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.
17
+
18
+ zlab.print_zpl(lab='default', printer_name='192.168.1.7', label_zpl_style='test_2inX1in', uid_barcode="123aUID")
19
+ # ZPL code sent successfully to the printer!
20
+ # 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'
21
+ ```
22
+
23
+ ## Primary Operations
24
+
25
+ ### Init Object
26
+ > def zpl():
27
+
28
+
29
+ ```python
30
+ import zebra_day.print_mgr as zdpm
31
+
32
+ zlab = zdpm.zpl()
33
+ ```
34
+
35
+ The IP of the machine creating the obj is determined, and the default printer config.json is read.
36
+
37
+
38
+ ### Load/Save/Clear Printer Config json
39
+
40
+ > As of 0.6.0, printer configuration is stored in XDG-compliant locations:
41
+ > - **Linux**: `~/.config/zebra_day/printer_config.json`
42
+ > - **macOS**: `~/Library/Preferences/zebra_day/printer_config.json`
43
+ >
44
+ > Use `zday info` to see the exact path on your system.
45
+
46
+ ```python
47
+ # These methods now use the XDG paths automatically
48
+ zlab.save_printer_json()
49
+ zlab.load_printer_json()
50
+ zlab.clear_printers_json()
51
+ zlab.replace_printer_json_from_template()
52
+ ```
53
+
54
+ When clearing or writing a new config.json, the existing one is saved to a backup location. Users can open these and effectively rollback if errors are made. Replace from template means overwriting the active one with the json example file which accompanies the repo.
55
+
56
+
57
+ ### Scan Local Network For Zebra Printers
58
+ > def probe_zebra_printers_add_to_printers_json(self, ip_stub="192.168.1", scan_wait="0.25",lab="scan-results"):
59
+
60
+ ### Check label styles allowed for a lab
61
+ > def get_valid_label_styles_for_lab(self,lab=None):
62
+
63
+ A never quite implemented idea.
64
+
65
+ ### Produce a ZPL string which will be sent to a printer
66
+
67
+ > def formulate_zpl(self,uid_barcode=None, alt_a=None, alt_b=None, alt_c=None, alt_d=None, alt_e=None, alt_f=None, label_zpl_style=None):
68
+
69
+ * The `lab` & `printer_name` are used to resolve the IP address for the printer this tuple identify.
70
+ * `label_zpl_style` is used to find the `zpl` template file the remaining values passed in are templated in to.
71
+ * `uid_barcode` is the value which is encoded as a barcode and presented in human readable form. The way this value is displayed will vary by template.
72
+ * `alt_[a-f]` these are used diferently, or not at all, depending on the zpl template.
73
+
74
+ ### Send ZPL To Zebra To Print A Label
75
+ > def print_zpl(self, lab=None, printer_name=None, uid_barcode='', alt_a='', alt_b='', alt_c='', alt_d='', alt_e='', alt_f='', label_zpl_style=None, client_ip='pkg', print_n=1):
76
+
77
+ With the ZPL string produced, determine the printer IP and send the string to it.
78
+
79
+ ### Send ZPL To PDF Generation Service
80
+ > def generate_label_png(self,zpl_string=None, png_fn=None):
81
+
82
+ Rather than print a physical label, produce a `png`... this is most helpful when we get to the UI.
83
+
84
+ ## Data Structures
85
+
86
+ ### Printer json (v2.0.0 Schema)
87
+ This is the file which describes the printer fleet. It may be manually edited or edited via the GUI.
88
+
89
+ ```json
90
+ {
91
+ "schema_version": "2.0.0",
92
+ "labs": {
93
+ "default": {
94
+ "lab_name": "Default",
95
+ "available_locations": ["Bench A", "Bench B"],
96
+ "printers": {
97
+ "192.168.1.7": {
98
+ "ip_address": "192.168.1.7",
99
+ "printer_name": "Main Lab Printer",
100
+ "lab_location": "Bench A",
101
+ "manufacturer": "zebra",
102
+ "model": "ZD620",
103
+ "serial": "12345",
104
+ "label_zpl_styles": ["tube_2inX1in", "plate_1inX0.25in"],
105
+ "default_label_style": "tube_2inX1in",
106
+ "print_method": "socket",
107
+ "arp_data": "",
108
+ "notes": "Primary sample printer"
109
+ }
110
+ }
111
+ }
112
+ }
113
+ }
114
+ ```
115
+
116
+ **Schema v2.0.0 Changes:**
117
+ - `labs` now contains nested `printers` object (not flat printer entries)
118
+ - Added `lab_name` and `available_locations` at lab level
119
+ - Added `printer_name`, `lab_location`, `manufacturer`, `default_label_style`, `notes` at printer level
120
+ - Added `schema_version` at root level
121
+
122
+ The `lab` key in this example is `default`, which is the lab name assigned when autodetect runs. These names are editable via the GUI. Printers are nested under the `printers` key within each lab.
123
+
124
+ ### Rendering ZPL to PNG (without printing)
125
+
126
+ To generate a PNG preview of a label without sending to a printer, use the `/api/v1/render` endpoint:
127
+
128
+ ```bash
129
+ # Render using a template
130
+ curl -X POST "https://localhost:8118/api/v1/render" \
131
+ -H "Content-Type: application/json" \
132
+ -d '{
133
+ "template_name": "tube_2inX1in",
134
+ "uid_barcode": "SAMPLE123",
135
+ "alt_a": "Field A",
136
+ "alt_b": "Field B"
137
+ }' \
138
+ --output label.png
139
+
140
+ # Render raw ZPL content
141
+ curl -X POST "https://localhost:8118/api/v1/render" \
142
+ -H "Content-Type: application/json" \
143
+ -d '{
144
+ "zpl_content": "^XA^FO50,50^ADN,36,20^FDHello World^FS^XZ"
145
+ }' \
146
+ --output label.png
147
+ ```
148
+
149
+ The render endpoint returns a PNG image directly. This is useful for:
150
+ - Previewing labels before printing
151
+ - Generating label images for documentation
152
+ - Testing ZPL templates without physical printers
153
+
154
+ ### ZPL Template Files
155
+ These are template files for various different label styles. These may be manually edited (but its a nicer expereience using the UI)
156
+
157
+ * Template files are easiest to design via the UI. The ZPL format is very old school word processor vibes.
158
+
159
+ ```text
160
+ ^XA
161
+ ^FO200,20
162
+ ^BY1
163
+ ^B3N,N,40,N,N
164
+ ^FD{uid_barcode}^FS
165
+ ^FO200,70
166
+ ^ADN,30,20
167
+ ^FD{uid_barcode}^FS
168
+ ^FO460,18
169
+ ^ADN,24,14
170
+ ^FD{alt_a}^FS
171
+ ^FO515,62
172
+ ^ADN,40,26
173
+ ^FD{alt_b}^FS
174
+ ^XZ
175
+ ```
176
+
177
+ * The `{}` format keys match those from above in the zpl string formulation call.
178
+ * [ZPL docs](https://labelary.com/zpl.html)
179
+ * This ZPL creates this label:<ul>
180
+
181
+ <img width="312" alt="zpl_exa" src="https://github.com/Daylily-Informatics/zebra_day/assets/4713659/9d2b53b3-03d0-4095-9622-64273734ff76">
Binary file
@@ -0,0 +1,38 @@
1
+ # Unique Identifiers **** D R A F T NOTES ****
2
+
3
+ ## Maxims
4
+
5
+ ### Be unique in the namespace... if possible, even outside the namespace.
6
+ * Be extremely cautious when working with test and sandbox environments which produce potentially overlapping UIDs to your production systems. Better yet, do not produce UIDs which overlap with other systems you control. `barcode labels IRL`.
7
+ * Issued by a single UID authority.
8
+
9
+ ### Absolutely should not change once issued.
10
+
11
+ ### Each distinct thing deserves its own UID.
12
+ * corrolaries:
13
+ * copies of things are now distinct things, and should have their own UID (metadata and relationships should be stored indicating relationships to other UIDs).
14
+ * In other words __at all costs, avoid re-using UIDs for children of things. This may momentarily be convenient, but I can promise you in the end, you'll have more than made up for the immediate convenience with an unecessarily fragile and opaque process/system.
15
+
16
+ ### Encode only the unique ID
17
+ #### Vigourously avoid encoding any meta data regarding the thing identied by a UID in the UID. Similarly, avoid encoding process/state information.
18
+
19
+ * There is one exception, 'enterprise UIDs', with a brief prefix indicating object have not been a problem in my experience ([see stripe thinking on this](stripe.com)). These prefixes even offer a benefit when talking about object classes, as well as speeding up certain database queries.
20
+ * A few tests to apply when this temptation presents itself (and it will, more than you'd like):
21
+ * Is the information being added to the UID mutable ever? And if the answer is 'almost never' or 'it should not happen(but not actively blocked by a system)', this is BAD. What if there was a clerical error post UID assignment and now the UID does not reflect the corrected info? Chage the UID? (bad idea, see above)
22
+ * Is there a better way to accomplish whatever use case having the additional data in the UID is attempting to meet (in effectively every situation I've encountered, the answer is yes). Explore how to better serve this use case with other solutions.
23
+
24
+
25
+ ### Use only alpha numeric characters, ideally all uppercase alpha, no special characters.
26
+ * This means no `-`
27
+
28
+ ### Under no circumstances should you add 0's as a prefix to a number being used as a UID
29
+ * If a UID is randomly assigning alphanumeric and leading 0s happen to be assigned, is the only exception I can imagine. Otherwise, this is a huge driver of errors, and mostly of the silent variety.
30
+ * Consider this: are `EX001` and `EX1` the same? What about `EX0001`? The answer is that to software at least, these are each unique UIDs. To people, these more often than not are all `EX1`, and are spoken about this way very frequently. Also - the leading zeros are often omitted when writing the UID down.
31
+ * The arguments for using padded zeros boil down to `they look good in spreadsheets`. Which is not a very compelling reason to adopt a very error prone and fragile design element.
32
+ * What happens when you outgrow the pre-establised number of characters?
33
+ * many s/w systems will auto trim the leading zeros.
34
+ * some barcode scanners will trim leading zeros.
35
+ * What if leading zeros are adjacent to the character 'O'?
36
+
37
+ ### Reading and writing UIDs should not be done by hand
38
+ * label printers and scanners.
Binary file