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/zpl_renderer.py CHANGED
@@ -13,9 +13,9 @@ Supports the ZPL commands used in zebra_day templates:
13
13
  - ^BQN: QR code
14
14
  - ^BXN: Data Matrix
15
15
  """
16
+
16
17
  from __future__ import annotations
17
18
 
18
- import io
19
19
  import logging
20
20
  import re
21
21
  from dataclasses import dataclass, field
@@ -25,6 +25,7 @@ from PIL import Image, ImageDraw, ImageFont
25
25
 
26
26
  try:
27
27
  import zint
28
+
28
29
  ZINT_AVAILABLE = True
29
30
  except ImportError:
30
31
  ZINT_AVAILABLE = False
@@ -32,21 +33,22 @@ except ImportError:
32
33
  _log = logging.getLogger(__name__)
33
34
 
34
35
  # Default label dimensions for 4x6 inch label at 8 dpmm (203 dpi)
35
- DEFAULT_LABEL_WIDTH_DOTS = 812 # 4 inches * 203 dpi
36
+ DEFAULT_LABEL_WIDTH_DOTS = 812 # 4 inches * 203 dpi
36
37
  DEFAULT_LABEL_HEIGHT_DOTS = 1218 # 6 inches * 203 dpi
37
38
 
38
39
  # Barcode type mappings
39
40
  BARCODE_TYPES = {
40
- 'B3N': 'CODE39', # Code 39
41
- 'BCN': 'CODE128', # Code 128
42
- 'BQN': 'QRCODE', # QR Code
43
- 'BXN': 'DATAMATRIX', # Data Matrix
41
+ "B3N": "CODE39", # Code 39
42
+ "BCN": "CODE128", # Code 128
43
+ "BQN": "QRCODE", # QR Code
44
+ "BXN": "DATAMATRIX", # Data Matrix
44
45
  }
45
46
 
46
47
 
47
48
  @dataclass
48
49
  class FontSpec:
49
50
  """Font specification from ZPL ^A command."""
51
+
50
52
  height: int = 30
51
53
  width: int = 20
52
54
 
@@ -54,6 +56,7 @@ class FontSpec:
54
56
  @dataclass
55
57
  class BarcodeSpec:
56
58
  """Barcode specification from ZPL ^BY command."""
59
+
57
60
  module_width: int = 2
58
61
  ratio: float = 3.0
59
62
  height: int = 10
@@ -62,6 +65,7 @@ class BarcodeSpec:
62
65
  @dataclass
63
66
  class RenderState:
64
67
  """Current rendering state while parsing ZPL."""
68
+
65
69
  x: int = 0
66
70
  y: int = 0
67
71
  font: FontSpec = field(default_factory=FontSpec)
@@ -74,7 +78,7 @@ def _get_font(height: int) -> ImageFont.FreeTypeFont | ImageFont.ImageFont:
74
78
  """Get a font at the specified height. Falls back to default if unavailable."""
75
79
  try:
76
80
  # Try common monospace fonts
77
- for font_name in ['DejaVuSansMono.ttf', 'Menlo.ttc', 'Courier New.ttf', 'monospace']:
81
+ for font_name in ["DejaVuSansMono.ttf", "Menlo.ttc", "Courier New.ttf", "monospace"]:
78
82
  try:
79
83
  return ImageFont.truetype(font_name, height)
80
84
  except OSError:
@@ -85,26 +89,28 @@ def _get_font(height: int) -> ImageFont.FreeTypeFont | ImageFont.ImageFont:
85
89
  return ImageFont.load_default()
86
90
 
87
91
 
88
- def _render_barcode(barcode_type: str, data: str, height: int = 40, module_width: int = 2) -> Image.Image | None:
92
+ def _render_barcode(
93
+ barcode_type: str, data: str, height: int = 40, module_width: int = 2
94
+ ) -> Image.Image | None:
89
95
  """Render a barcode using zint-bindings."""
90
96
  if not ZINT_AVAILABLE:
91
97
  _log.warning("zint-bindings not available, cannot render barcode")
92
98
  return None
93
99
 
94
- import tempfile
95
100
  import os
101
+ import tempfile
96
102
 
97
103
  try:
98
104
  symbol = zint.Symbol()
99
105
 
100
106
  # Map barcode type to zint symbology
101
- if barcode_type == 'CODE39':
107
+ if barcode_type == "CODE39":
102
108
  symbol.symbology = zint.Symbology.CODE39
103
- elif barcode_type == 'CODE128':
109
+ elif barcode_type == "CODE128":
104
110
  symbol.symbology = zint.Symbology.CODE128
105
- elif barcode_type == 'QRCODE':
111
+ elif barcode_type == "QRCODE":
106
112
  symbol.symbology = zint.Symbology.QRCODE
107
- elif barcode_type == 'DATAMATRIX':
113
+ elif barcode_type == "DATAMATRIX":
108
114
  symbol.symbology = zint.Symbology.DATAMATRIX
109
115
  else:
110
116
  _log.warning("Unknown barcode type: %s", barcode_type)
@@ -115,7 +121,7 @@ def _render_barcode(barcode_type: str, data: str, height: int = 40, module_width
115
121
  symbol.show_text = False # ZPL typically handles text separately
116
122
 
117
123
  # Create temp file for output
118
- with tempfile.NamedTemporaryFile(suffix='.png', delete=False) as f:
124
+ with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as f:
119
125
  temp_path = f.name
120
126
 
121
127
  symbol.outfile = temp_path
@@ -124,7 +130,7 @@ def _render_barcode(barcode_type: str, data: str, height: int = 40, module_width
124
130
 
125
131
  # Load the image
126
132
  if os.path.exists(temp_path):
127
- img = Image.open(temp_path).convert('RGBA')
133
+ img = Image.open(temp_path).convert("RGBA")
128
134
  os.unlink(temp_path) # Clean up temp file
129
135
  return img
130
136
  return None
@@ -136,7 +142,7 @@ def _render_barcode(barcode_type: str, data: str, height: int = 40, module_width
136
142
  def _parse_font_command(cmd: str) -> FontSpec:
137
143
  """Parse ^A0N or ^ADN font command."""
138
144
  # ^A0N,height,width or ^ADN,height,width
139
- parts = cmd.split(',')
145
+ parts = cmd.split(",")
140
146
  height = int(parts[1]) if len(parts) > 1 and parts[1].strip() else 30
141
147
  width = int(parts[2]) if len(parts) > 2 and parts[2].strip() else height // 2
142
148
  return FontSpec(height=height, width=width)
@@ -145,7 +151,7 @@ def _parse_font_command(cmd: str) -> FontSpec:
145
151
  def _parse_barcode_default(cmd: str) -> BarcodeSpec:
146
152
  """Parse ^BY command for barcode defaults."""
147
153
  # ^BY[module_width],[ratio],[height]
148
- parts = cmd.split(',')
154
+ parts = cmd.split(",")
149
155
  module_width = int(parts[0]) if parts[0].strip() else 2
150
156
  ratio = float(parts[1]) if len(parts) > 1 and parts[1].strip() else 3.0
151
157
  height = int(parts[2]) if len(parts) > 2 and parts[2].strip() else 10
@@ -155,7 +161,7 @@ def _parse_barcode_default(cmd: str) -> BarcodeSpec:
155
161
  def _parse_position(cmd: str) -> tuple[int, int]:
156
162
  """Parse ^FO command for field origin."""
157
163
  # ^FOx,y
158
- parts = cmd.split(',')
164
+ parts = cmd.split(",")
159
165
  x = int(parts[0]) if parts[0].strip() else 0
160
166
  y = int(parts[1]) if len(parts) > 1 and parts[1].strip() else 0
161
167
  return x, y
@@ -164,7 +170,7 @@ def _parse_position(cmd: str) -> tuple[int, int]:
164
170
  def _parse_barcode_command(cmd: str) -> int:
165
171
  """Parse barcode command (^B3N, ^BCN, etc.) and return height."""
166
172
  # Format: ^B3N,orientation,height,... or ^BCN,orientation,height,...
167
- parts = cmd.split(',')
173
+ parts = cmd.split(",")
168
174
  # Height is usually the 3rd parameter (index 2) for most barcode commands
169
175
  if len(parts) > 2 and parts[2].strip():
170
176
  try:
@@ -196,37 +202,41 @@ def render_zpl_to_png(
196
202
  output_path.parent.mkdir(parents=True, exist_ok=True)
197
203
 
198
204
  # Create white background image
199
- img = Image.new('RGB', (width, height), 'white')
205
+ img = Image.new("RGB", (width, height), "white")
200
206
  draw = ImageDraw.Draw(img)
201
207
 
202
208
  state = RenderState()
203
209
 
204
210
  # Parse ZPL commands - split on ^ character
205
- commands = re.split(r'\^', zpl_string)
206
-
207
- pending_text: str | None = None
211
+ commands = re.split(r"\^", zpl_string)
208
212
 
209
213
  for cmd in commands:
210
214
  cmd = cmd.strip()
211
215
  if not cmd:
212
216
  continue
213
217
 
218
+ # Strip inline comments (ZPL uses ; for comments)
219
+ if ";" in cmd:
220
+ cmd = cmd.split(";")[0].strip()
221
+ if not cmd:
222
+ continue
223
+
214
224
  # Label start/end - ignore
215
- if cmd.startswith('XA') or cmd.startswith('XZ'):
225
+ if cmd.startswith("XA") or cmd.startswith("XZ"):
216
226
  continue
217
227
 
218
228
  # Field origin - set position
219
- if cmd.startswith('FO'):
229
+ if cmd.startswith("FO"):
220
230
  state.x, state.y = _parse_position(cmd[2:])
221
231
  continue
222
232
 
223
233
  # Font commands
224
- if cmd.startswith('A0N') or cmd.startswith('ADN'):
234
+ if cmd.startswith("A0N") or cmd.startswith("ADN"):
225
235
  state.font = _parse_font_command(cmd)
226
236
  continue
227
237
 
228
238
  # Barcode default
229
- if cmd.startswith('BY'):
239
+ if cmd.startswith("BY"):
230
240
  state.barcode = _parse_barcode_default(cmd[2:])
231
241
  continue
232
242
 
@@ -238,10 +248,10 @@ def render_zpl_to_png(
238
248
  break
239
249
  else:
240
250
  # Not a barcode command, check for field data
241
- if cmd.startswith('FD'):
251
+ if cmd.startswith("FD"):
242
252
  # Extract data between FD and FS
243
253
  data = cmd[2:]
244
- if data.endswith('FS'):
254
+ if data.endswith("FS"):
245
255
  data = data[:-2]
246
256
 
247
257
  # If there's a pending barcode type, render barcode
@@ -253,21 +263,22 @@ def render_zpl_to_png(
253
263
  module_width=state.barcode.module_width,
254
264
  )
255
265
  if bc_img:
256
- img.paste(bc_img, (state.x, state.y), bc_img if bc_img.mode == 'RGBA' else None)
266
+ img.paste(
267
+ bc_img, (state.x, state.y), bc_img if bc_img.mode == "RGBA" else None
268
+ )
257
269
  state.current_barcode_type = None
258
270
  else:
259
271
  # Render text
260
272
  font = _get_font(state.font.height)
261
- draw.text((state.x, state.y), data, fill='black', font=font)
273
+ draw.text((state.x, state.y), data, fill="black", font=font)
262
274
  continue
263
275
 
264
276
  # Field separator - just a marker, usually handled with FD
265
- if cmd.startswith('FS'):
277
+ if cmd.startswith("FS"):
266
278
  continue
267
279
 
268
280
  # Save the image
269
- img.save(str(output_path), 'PNG')
281
+ img.save(str(output_path), "PNG")
270
282
  _log.info("Label image saved as %s", output_path)
271
283
 
272
284
  return str(output_path)
273
-
@@ -1,9 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: zebra_day
3
- Version: 2.0.0
3
+ Version: 2.1.4
4
4
  Summary: A Python library to manage a Zebra printer fleet and an API for ZPL print requests.
5
- Home-page: https://github.com/Daylily-Informatics/zebra_day
6
- Author: John Major
7
5
  Author-email: John Major <john@daylilyinformatics.com>
8
6
  License: MIT
9
7
  Project-URL: Homepage, https://github.com/Daylily-Informatics/zebra_day
@@ -37,12 +35,13 @@ Requires-Dist: typer>=0.9.0
37
35
  Requires-Dist: rich>=13.0.0
38
36
  Requires-Dist: pillow>=10.0.0
39
37
  Requires-Dist: zint-bindings>=1.2.0
38
+ Requires-Dist: httpx
40
39
  Provides-Extra: dev
41
40
  Requires-Dist: pytest>=7.4.0; extra == "dev"
42
41
  Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
43
42
  Requires-Dist: ipython>=8.16.0; extra == "dev"
43
+ Requires-Dist: playwright>=1.40.0; extra == "dev"
44
44
  Provides-Extra: lint
45
- Requires-Dist: black>=23.0.0; extra == "lint"
46
45
  Requires-Dist: ruff>=0.1.0; extra == "lint"
47
46
  Requires-Dist: mypy>=1.0.0; extra == "lint"
48
47
  Provides-Extra: docs
@@ -54,13 +53,17 @@ Requires-Dist: python-jose[cryptography]>=3.3.0; extra == "auth"
54
53
  Requires-Dist: boto3>=1.26.0; extra == "auth"
55
54
  Provides-Extra: all
56
55
  Requires-Dist: zebra_day[auth,dev,docs,lint]; extra == "all"
57
- Dynamic: author
58
- Dynamic: home-page
59
56
  Dynamic: license-file
60
57
 
61
58
  <img src=zebra_day/imgs/bar_red.png>
62
59
 
63
- ## zebra_day Overview [2.0.0](https://github.com/Daylily-Informatics/zebra_day/releases/tag/2.0.0)
60
+ ## zebra_day
61
+
62
+ [![GitHub Release](https://img.shields.io/github/v/release/Daylily-Informatics/zebra_day?style=flat-square&label=release)](https://github.com/Daylily-Informatics/zebra_day/releases/latest)
63
+ [![GitHub Tag](https://img.shields.io/github/v/tag/Daylily-Informatics/zebra_day?style=flat-square&label=tag)](https://github.com/Daylily-Informatics/zebra_day/tags)
64
+ [![PyPI](https://img.shields.io/pypi/v/zebra_day?style=flat-square)](https://pypi.org/project/zebra_day/)
65
+ [![Python CI](https://github.com/Daylily-Informatics/zebra_day/actions/workflows/main.yaml/badge.svg)](https://github.com/Daylily-Informatics/zebra_day/actions/workflows/main.yaml)
66
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](https://opensource.org/licenses/MIT)
64
67
 
65
68
  ### Build, Deploy, Run, Monitor, Teardown
66
69
 
@@ -76,10 +79,10 @@ pytest -v
76
79
 
77
80
  # Run Linting (requires pip install -e ".[lint]")
78
81
  ruff check zebra_day tests
79
- black --check zebra_day tests
82
+ ruff format --check zebra_day tests
80
83
  mypy zebra_day --ignore-missing-imports
81
84
 
82
- # CLI Commands (new in 0.6.0)
85
+ # CLI Commands
83
86
  zday --help # Show all commands
84
87
  zday bootstrap # First-time setup: scan for printers
85
88
  zday gui start # Start web UI in background
@@ -153,19 +156,20 @@ zday gui stop
153
156
 
154
157
  > <a href=zebra_day/docs/zebra_day_ui_guide.md >ui capabilities full details</a>
155
158
 
156
- #### Some UI Niceties
157
- ##### Zebra Printer Fleet Dashboard
158
- <img width="400" alt="fleetreport" src="https://github.com/Daylily-Informatics/zebra_day/assets/4713659/8a66bc11-f8f5-4c40-9970-36d554a4593a">
159
+ #### Modern Web UI
160
+
161
+ ##### Dashboard
162
+ <img width="800" alt="dashboard" src="zebra_day/imgs/ui_dashboard.png">
159
163
 
160
- ##### Zebra Printer, Single Printer Detail View
161
- <img width="690" alt="Screenshot 2023-11-01 at 1 35 36 AM" src="https://github.com/Daylily-Informatics/zebra_day/assets/4713659/7438df35-9e92-474e-a2ef-57d3c3ee23d7">
164
+ ##### Printer Fleet
165
+ <img width="800" alt="printers" src="zebra_day/imgs/ui_printers.png">
162
166
 
163
- ##### ZPL Label Editing IRT
164
- <img width="345" alt="zpl_editing" src="https://github.com/Daylily-Informatics/zebra_day/assets/4713659/15aac332-c5f8-4ce6-be6c-9c403fd8d35d">
167
+ ##### ZPL Template Editor
168
+ <img width="800" alt="templates" src="zebra_day/imgs/ui_templates.png">
165
169
 
166
170
  </ul>
167
171
 
168
- ### CLI Reference (0.6.0+)
172
+ ### CLI Reference
169
173
 
170
174
  The `zday` CLI provides a comprehensive interface for managing your Zebra printer fleet.
171
175
 
@@ -217,6 +221,9 @@ The old commands `zday_start` and `zday_quickstart` still work but are deprecate
217
221
 
218
222
  The zebra_day web UI supports HTTPS for secure local development. By default, HTTPS is enabled if certificates are found.
219
223
 
224
+ > **Note**: The `zday bootstrap` command automatically generates certificates if mkcert is installed and the CA is configured.
225
+ > Manual certificate generation (below) is only needed if you skip bootstrap or want custom hostnames.
226
+
220
227
  #### One-Time Setup (mkcert)
221
228
 
222
229
  ```bash
@@ -231,7 +238,19 @@ sudo apt install mkcert
231
238
  mkcert -install
232
239
  ```
233
240
 
234
- #### Generate Certificates for zebra_day
241
+ #### Automatic Certificate Generation (Recommended)
242
+
243
+ After installing mkcert and running `mkcert -install`, the bootstrap command will automatically generate certificates:
244
+
245
+ ```bash
246
+ zday bootstrap
247
+ # Output includes:
248
+ # ✓ Certificates generated: ~/.config/zebra_day/certs/server.crt
249
+ ```
250
+
251
+ #### Manual Certificate Generation (Alternative)
252
+
253
+ If you need to manually generate certificates (e.g., for custom hostnames):
235
254
 
236
255
  ```bash
237
256
  # Create certificate directory
@@ -304,7 +323,7 @@ curl https://localhost:8118/healthz
304
323
  <ul>
305
324
  <ul>
306
325
 
307
- > <img src=zebra_day/imgs/UBC_gantt_chart.png height=200 width=450>
326
+ > <img src=zebra_day/imgs/legacy/UBC_gantt_chart.png height=200 width=450>
308
327
 
309
328
  </ul>
310
329
  </ul>
@@ -362,7 +381,7 @@ python -m build # Creates dist/*.whl and dist/*.tar.gz
362
381
 
363
382
  ### File Locations (XDG-compliant)
364
383
 
365
- zebra_day 0.6.0+ uses XDG Base Directory specification for file storage:
384
+ zebra_day uses XDG Base Directory specification for file storage:
366
385
 
367
386
  | Type | macOS | Linux |
368
387
  |------|-------|-------|
@@ -444,11 +463,10 @@ Run 'zday gui start' to launch the web interface.
444
463
  $ zday gui start
445
464
 
446
465
  Starting zebra_day web server...
447
- Server running at: http://192.168.1.12:8118
466
+ Server running at: https://192.168.1.12:8118
448
467
 
449
- Modern UI: http://192.168.1.12:8118/
450
- Legacy UI: http://192.168.1.12:8118/legacy
451
- API Docs: http://192.168.1.12:8118/docs
468
+ Dashboard: https://192.168.1.12:8118/
469
+ API Docs: https://192.168.1.12:8118/docs
452
470
 
453
471
  Server started in background (PID: 12345)
454
472
  Use 'zday gui status' to check status
@@ -459,26 +477,23 @@ Use 'zday gui stop' to stop the server
459
477
 
460
478
  #### zebra_day Web GUI
461
479
 
462
- ##### Home Page
463
- <img src=zebra_day/imgs/zday_quick_gui.png>
480
+ ##### Dashboard
481
+ <img width="800" alt="dashboard" src="zebra_day/imgs/ui_dashboard.png">
464
482
 
465
- ##### Zebra Fleet Auto Discovery & Status Report
483
+ ##### Printer Fleet
484
+ <img width="800" alt="printers" src="zebra_day/imgs/ui_printers.png">
466
485
 
467
- <img width="1024" alt="fleetreport" src="https://github.com/Daylily-Informatics/zebra_day/assets/4713659/8a66bc11-f8f5-4c40-9970-36d554a4593a">
486
+ ##### Print Request
487
+ <img width="800" alt="print_request" src="zebra_day/imgs/ui_print_request.png">
468
488
 
489
+ ##### ZPL Template Editor
490
+ <img width="800" alt="templates" src="zebra_day/imgs/ui_templates.png">
469
491
 
470
- ##### Zebra Printer Fleet Config Json Editing
471
- <img width="472" alt="pconfjson" src="https://github.com/Daylily-Informatics/zebra_day/assets/4713659/0813cb07-4c5a-4cc9-9b33-d00e8424385e">
472
- One printer configured.
473
-
474
-
475
- ##### ZPL Template Drafting / Preview PNG / Test Print / Save
476
- <img width="953" alt="zpl_editing" src="https://github.com/Daylily-Informatics/zebra_day/assets/4713659/15aac332-c5f8-4ce6-be6c-9c403fd8d35d">
477
-
478
-
479
- ##### Manual Print Requests
480
- <img width="895" alt="printmanual" src="https://github.com/Daylily-Informatics/zebra_day/assets/4713659/72442f68-984f-4264-93ec-9878372d26f2">
492
+ ##### Configuration
493
+ <img width="800" alt="config" src="zebra_day/imgs/ui_config.png">
481
494
 
495
+ ##### API Documentation
496
+ <img width="800" alt="api_docs" src="zebra_day/imgs/ui_api_docs.png">
482
497
 
483
498
  <br><br>
484
499
 
@@ -494,21 +509,20 @@ zlab = zdpm.zpl()
494
509
 
495
510
  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.
496
511
 
497
- 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 'scan-results', this may be edited latter.
512
+ 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.
498
513
 
499
- # The json will loook something like this
500
- ## {'labs': {'scan-results': {'192.168.1.7': {'ip_address': '192.168.1.7', 'label_zpl_styles': ['test_2inX1in'], 'print_method': 'unk'}}}
501
- ## 'lab' name 'printer' name(can be edited latter) label_zpl_style
514
+ # The json will look something like this (v2.0.0 schema with nested printers)
515
+ ## {'schema_version': '2.0.0', 'labs': {'default': {'lab_name': 'Default', 'printers': {'192.168.1.7': {'ip_address': '192.168.1.7', ...}}}}}
502
516
 
503
- # 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.
517
+ # 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.
504
518
 
505
- zlab.print_zpl(lab='scan-results', printer_name='192.168.1.7', label_zpl_style='test_2inX1in', uid_barcode="123aUID")
519
+ zlab.print_zpl(lab='default', printer_name='192.168.1.7', label_zpl_style='test_2inX1in', uid_barcode="123aUID")
506
520
  # ZPL code sent successfully to the printer!
507
521
  # 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'
508
522
  ```
509
523
 
510
524
  * This will produce a label which looks like this (modulo printer config items needing attention).
511
- ![test_lab](zebra_day/imgs/quick_start_test_label2.png)
525
+ ![test_lab](zebra_day/imgs/legacy/quick_start_test_label2.png)
512
526
 
513
527
 
514
528
  ### [Programatic Guide](zebra_day/docs/programatic_guide.md)
@@ -554,29 +568,33 @@ zday gui start --foreground
554
568
  uvicorn zebra_day.web.app:create_app --host 0.0.0.0 --port 8118 --factory
555
569
  ```
556
570
 
557
- ### Modern vs Legacy UI
571
+ ### Web UI
558
572
 
559
- zebra_day 0.6.0+ includes a redesigned modern UI alongside the preserved legacy interface:
573
+ zebra_day 2.0.0+ features a modern, responsive web interface:
560
574
 
561
575
  | Interface | URL | Description |
562
576
  |-----------|-----|-------------|
563
- | **Modern UI** | `https://localhost:8118/` | New dashboard with stats, quick actions, improved navigation |
564
- | **Legacy UI** | `https://localhost:8118/legacy` | Original interface, fully functional |
577
+ | **Dashboard** | `https://localhost:8118/` | Printer fleet stats, quick actions, navigation |
578
+ | **Printers** | `https://localhost:8118/printers` | Printer status and management |
579
+ | **Print** | `https://localhost:8118/print` | Send print requests |
580
+ | **Templates** | `https://localhost:8118/templates` | ZPL template editor with live preview |
581
+ | **Config** | `https://localhost:8118/config` | Printer configuration management |
565
582
  | **API Docs** | `https://localhost:8118/docs` | Interactive OpenAPI/Swagger documentation |
566
583
  | **ReDoc** | `https://localhost:8118/redoc` | Alternative API documentation |
567
584
 
568
585
  > **Note:** Use `http://` instead of `https://` if running without certificates (`--no-https`).
569
586
 
570
- Both interfaces provide full functionality. The modern UI offers:
587
+ The modern UI offers:
571
588
  - Dashboard with printer fleet statistics
572
589
  - Streamlined navigation
573
- - Improved template editor
590
+ - Improved template editor with live PNG preview
574
591
  - Better mobile responsiveness
592
+ - Configuration editing with backup management
575
593
 
576
594
  ### Print via HTTP API
577
595
 
578
596
  ```http
579
- http://YOUR.HOST.IP.ADDR:8118/_print_label?lab=scan-results&printer=192.168.1.7&label_zpl_style=test_2inX1in&uid_barcode=123aUID
597
+ http://YOUR.HOST.IP.ADDR:8118/_print_label?lab=default&printer=192.168.1.7&label_zpl_style=test_2inX1in&uid_barcode=123aUID
580
598
  ```
581
599
 
582
600
  * See the [Web UI Guide](zebra_day/docs/zebra_day_ui_guide.md) for full details.
@@ -719,23 +737,23 @@ GET /favicon.ico 200 OK ~
719
737
  </pre>
720
738
 
721
739
  And looks like:
722
- <img src=zebra_day/imgs/ngrok.png>
740
+ <img src=zebra_day/imgs/legacy/ngrok.png>
723
741
 
724
742
  * If you leave the ngrok tunnel running, go to a different network, you can use the link named in the `Forwarding` row above to access the zebra_day UI, in the above example, this url would be `https://dfbf-23-93-175-197.ngrok-free.app`.
725
743
 
726
744
  #### Sending Label Print Requests
727
745
  ##### from a web browser on a different network
728
746
 
729
- `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=scan-results&printer=192.168.1.20&printer_ip=192.168.1.20&label_zpl_style=tube_2inX1in`
747
+ `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`
730
748
 
731
749
  ##### Using wget from a shell on a machine outside your local network
732
750
  ```bash
733
- 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=scan-results&printer=192.168.1.20&printer_ip=192.168.1.20&label_zpl_style=tube_2inX1in"
751
+ 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"
734
752
  ```
735
753
 
736
754
  ### From SalesForce
737
755
 
738
- * 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=scan-results&printer=192.168.1.20&label_zpl_style=tube_2inX1in`, and hitting the URL via Apex, Flow, etc.
756
+ * 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.
739
757
  * To send a print request, you will need to know the API url, and the `lab`, `printer_name`, and `label_zpl_style` you wish to print the salesforce `Name` aka `UID` as 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.
740
758
 
741
759
 
@@ -790,7 +808,7 @@ Next, create a flow which uses this Apex Class.
790
808
  * Choose `Actions and Related Records`, and check the box at the bottom of the page to `Include a Run Asynchronously path...`
791
809
  * 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 Immediately` and one `Run Asynchronously`. The `Run Immediately` branch was throwing errors, so I removed it to debug at a latter date.
792
810
  * Click the node just below the `Run Asynch` oval. Add an `Action`. Select the `Make HTTP Request` we created via the Apex Class above. Give it a `Label`, let the API Name auto generate.
793
- * In the `Endpoint URL` field, enter the url `https://dfbf-23-93-175-197.ngrok-free.app/_print_label?uid_barcode={!$Record.Name}&lab=scan-results&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 json file, you may leave `scan-results` as the value for `lab=` as this will be the default name given when zebra_day autodetects printers.
811
+ * In the `Endpoint URL` field, enter the url `https://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 json file, you may leave `default` as the value for `lab=` as this will be the default name given when zebra_day autodetects printers.
794
812
  * add the same HTTPrequest action to the node just below `Run Immediately`.
795
813
  * click `Save` in the upper right corner of the page. Give it a name
796
814
  * Click `Debug Again`, run the `Run Immediately` branch first. This will fail.
@@ -835,13 +853,4 @@ Tthen run `sudo docker compose up --build -d` to run it then reach it at http://
835
853
 
836
854
  * Set varios printer config via ZPL commands (presently this package only fetches config).
837
855
 
838
-
839
- # BadgeLand
840
-
841
- [![Python CI](https://github.com/Daylily-Informatics/zebra_day/actions/workflows/main.yaml/badge.svg)](https://github.com/Daylily-Informatics/zebra_day/actions/workflows/main.yaml)
842
-
843
- <br>
844
-
845
-
846
-
847
856