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.
- zebra_day/__init__.py +7 -2
- zebra_day/_version.py +1 -0
- zebra_day/cli/__init__.py +80 -30
- zebra_day/cli/cognito.py +15 -9
- zebra_day/cli/gui.py +21 -16
- zebra_day/cli/printer.py +34 -27
- zebra_day/cli/template.py +19 -15
- zebra_day/cmd_mgr.py +3 -6
- zebra_day/docs/gx420d-gx430d-ug-en.pdf +0 -0
- zebra_day/docs/hardware_config_guide.md +149 -0
- zebra_day/docs/programatic_guide.md +181 -0
- zebra_day/docs/qln420_zebra_manual.pdf +0 -0
- zebra_day/docs/uid_screed_light.md +38 -0
- zebra_day/docs/zd620-zd420-ug-en.pdf +0 -0
- zebra_day/docs/zebra_day_ui_guide.md +194 -0
- zebra_day/etc/printer_config.json +7 -1
- zebra_day/etc/printer_config.template.json +3 -17
- zebra_day/etc/tmp_printers139.json +10 -0
- zebra_day/etc/tmp_printers147.json +10 -0
- zebra_day/etc/tmp_printers34.json +10 -0
- zebra_day/etc/tmp_printers389.json +10 -0
- zebra_day/etc/tmp_printers398.json +10 -0
- zebra_day/etc/tmp_printers437.json +10 -0
- zebra_day/etc/tmp_printers439.json +10 -0
- zebra_day/etc/tmp_printers440.json +10 -0
- zebra_day/etc/tmp_printers508.json +10 -0
- zebra_day/etc/tmp_printers543.json +10 -0
- zebra_day/etc/tmp_printers835.json +10 -0
- zebra_day/etc/tmp_printers842.json +10 -0
- zebra_day/etc/tmp_printers931.json +10 -0
- zebra_day/etc/tmp_printers969.json +10 -0
- zebra_day/exceptions.py +1 -1
- zebra_day/files/corners_smallTube_preview.png +0 -0
- zebra_day/files/test_png_2897.png +0 -0
- zebra_day/files/test_png_31690.png +0 -0
- zebra_day/files/test_png_33804.png +0 -0
- zebra_day/files/test_png_34737.png +0 -0
- zebra_day/files/test_png_4161.png +0 -0
- zebra_day/files/test_png_44748.png +0 -0
- zebra_day/files/test_png_4635.png +0 -0
- zebra_day/files/test_png_56349.png +0 -0
- zebra_day/files/test_png_5936.png +0 -0
- zebra_day/files/test_png_64110.png +0 -0
- zebra_day/files/test_png_64891.png +0 -0
- zebra_day/files/test_png_69002.png +0 -0
- zebra_day/files/test_png_70065.png +0 -0
- zebra_day/files/test_png_72366.png +0 -0
- zebra_day/files/test_png_77793.png +0 -0
- zebra_day/files/test_png_9572.png +0 -0
- zebra_day/imgs/.hold +0 -0
- zebra_day/imgs/bar_ltpurp.png +0 -0
- zebra_day/imgs/bar_purp.png +0 -0
- zebra_day/imgs/bar_purp3.png +0 -0
- zebra_day/imgs/bar_red.png +0 -0
- zebra_day/imgs/legacy/UBC_gantt_chart.png +0 -0
- zebra_day/imgs/legacy/gx420d_network_config.png +0 -0
- zebra_day/imgs/legacy/gx420d_printer_config.png +0 -0
- zebra_day/imgs/legacy/ngrok.png +0 -0
- zebra_day/imgs/legacy/printer_details.png +0 -0
- zebra_day/imgs/legacy/quick_start_test_label.png +0 -0
- zebra_day/imgs/legacy/quick_start_test_label2.png +0 -0
- zebra_day/imgs/legacy/zd620_network_config.png +0 -0
- zebra_day/imgs/legacy/zd620_printer_config.png +0 -0
- zebra_day/imgs/legacy/zday_quick_gui.png +0 -0
- zebra_day/imgs/legacy/zebra_day_alt_css_dog.png +0 -0
- zebra_day/imgs/legacy/zebra_day_alt_css_flower.png +0 -0
- zebra_day/imgs/legacy/zebra_day_alt_css_main.png +0 -0
- zebra_day/imgs/legacy/zebra_day_available_zpl_templates.png +0 -0
- zebra_day/imgs/legacy/zebra_day_bkup_pconfig.png +0 -0
- zebra_day/imgs/legacy/zebra_day_home.png +0 -0
- zebra_day/imgs/legacy/zebra_day_manual_print.png +0 -0
- zebra_day/imgs/legacy/zebra_day_printer_fleet_json.png +0 -0
- zebra_day/imgs/legacy/zebra_day_quick_ex.png +0 -0
- zebra_day/imgs/legacy/zebra_day_zpl_template_IRLa.png +0 -0
- zebra_day/imgs/legacy/zebra_day_zpl_template_IRLb.png +0 -0
- zebra_day/imgs/ui_api_docs.png +0 -0
- zebra_day/imgs/ui_config.png +0 -0
- zebra_day/imgs/ui_dashboard.png +0 -0
- zebra_day/imgs/ui_print_request.png +0 -0
- zebra_day/imgs/ui_printers.png +0 -0
- zebra_day/imgs/ui_templates.png +0 -0
- zebra_day/logging_config.py +4 -9
- zebra_day/mkcert.py +157 -0
- zebra_day/paths.py +1 -2
- zebra_day/print_mgr.py +165 -145
- zebra_day/templates/modern/config.html +7 -0
- zebra_day/templates/modern/print_request.html +61 -3
- zebra_day/web/__init__.py +1 -1
- zebra_day/web/app.py +21 -16
- zebra_day/web/auth.py +17 -15
- zebra_day/web/middleware.py +8 -5
- zebra_day/web/routers/__init__.py +0 -1
- zebra_day/web/routers/api.py +192 -43
- zebra_day/web/routers/ui.py +31 -33
- zebra_day/zpl_renderer.py +45 -34
- {zebra_day-2.0.0.dist-info → zebra_day-2.1.4.dist-info}/METADATA +76 -67
- {zebra_day-2.0.0.dist-info → zebra_day-2.1.4.dist-info}/RECORD +101 -29
- {zebra_day-2.0.0.dist-info → zebra_day-2.1.4.dist-info}/WHEEL +0 -0
- {zebra_day-2.0.0.dist-info → zebra_day-2.1.4.dist-info}/entry_points.txt +0 -0
- {zebra_day-2.0.0.dist-info → zebra_day-2.1.4.dist-info}/licenses/LICENSE +0 -0
- {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
|
|
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
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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 [
|
|
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(
|
|
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 ==
|
|
107
|
+
if barcode_type == "CODE39":
|
|
102
108
|
symbol.symbology = zint.Symbology.CODE39
|
|
103
|
-
elif barcode_type ==
|
|
109
|
+
elif barcode_type == "CODE128":
|
|
104
110
|
symbol.symbology = zint.Symbology.CODE128
|
|
105
|
-
elif barcode_type ==
|
|
111
|
+
elif barcode_type == "QRCODE":
|
|
106
112
|
symbol.symbology = zint.Symbology.QRCODE
|
|
107
|
-
elif barcode_type ==
|
|
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=
|
|
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(
|
|
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(
|
|
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
|
|
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(
|
|
225
|
+
if cmd.startswith("XA") or cmd.startswith("XZ"):
|
|
216
226
|
continue
|
|
217
227
|
|
|
218
228
|
# Field origin - set position
|
|
219
|
-
if cmd.startswith(
|
|
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(
|
|
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(
|
|
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(
|
|
251
|
+
if cmd.startswith("FD"):
|
|
242
252
|
# Extract data between FD and FS
|
|
243
253
|
data = cmd[2:]
|
|
244
|
-
if data.endswith(
|
|
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(
|
|
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=
|
|
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(
|
|
277
|
+
if cmd.startswith("FS"):
|
|
266
278
|
continue
|
|
267
279
|
|
|
268
280
|
# Save the image
|
|
269
|
-
img.save(str(output_path),
|
|
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.
|
|
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
|
|
60
|
+
## zebra_day
|
|
61
|
+
|
|
62
|
+
[](https://github.com/Daylily-Informatics/zebra_day/releases/latest)
|
|
63
|
+
[](https://github.com/Daylily-Informatics/zebra_day/tags)
|
|
64
|
+
[](https://pypi.org/project/zebra_day/)
|
|
65
|
+
[](https://github.com/Daylily-Informatics/zebra_day/actions/workflows/main.yaml)
|
|
66
|
+
[](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
|
-
|
|
82
|
+
ruff format --check zebra_day tests
|
|
80
83
|
mypy zebra_day --ignore-missing-imports
|
|
81
84
|
|
|
82
|
-
# CLI Commands
|
|
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
|
-
####
|
|
157
|
-
|
|
158
|
-
|
|
159
|
+
#### Modern Web UI
|
|
160
|
+
|
|
161
|
+
##### Dashboard
|
|
162
|
+
<img width="800" alt="dashboard" src="zebra_day/imgs/ui_dashboard.png">
|
|
159
163
|
|
|
160
|
-
#####
|
|
161
|
-
<img width="
|
|
164
|
+
##### Printer Fleet
|
|
165
|
+
<img width="800" alt="printers" src="zebra_day/imgs/ui_printers.png">
|
|
162
166
|
|
|
163
|
-
|
|
164
|
-
<img width="
|
|
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
|
|
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
|
-
####
|
|
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
|
|
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:
|
|
466
|
+
Server running at: https://192.168.1.12:8118
|
|
448
467
|
|
|
449
|
-
|
|
450
|
-
|
|
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
|
-
#####
|
|
463
|
-
|
|
480
|
+
##### Dashboard
|
|
481
|
+
<img width="800" alt="dashboard" src="zebra_day/imgs/ui_dashboard.png">
|
|
464
482
|
|
|
465
|
-
#####
|
|
483
|
+
##### Printer Fleet
|
|
484
|
+
<img width="800" alt="printers" src="zebra_day/imgs/ui_printers.png">
|
|
466
485
|
|
|
467
|
-
|
|
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
|
-
#####
|
|
471
|
-
<img width="
|
|
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 '
|
|
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
|
|
500
|
-
## {'labs': {'
|
|
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.
|
|
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='
|
|
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
|
-

|
|
525
|
+

|
|
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
|
-
###
|
|
571
|
+
### Web UI
|
|
558
572
|
|
|
559
|
-
zebra_day 0.
|
|
573
|
+
zebra_day 2.0.0+ features a modern, responsive web interface:
|
|
560
574
|
|
|
561
575
|
| Interface | URL | Description |
|
|
562
576
|
|-----------|-----|-------------|
|
|
563
|
-
| **
|
|
564
|
-
| **
|
|
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
|
-
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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
|
-
[](https://github.com/Daylily-Informatics/zebra_day/actions/workflows/main.yaml)
|
|
842
|
-
|
|
843
|
-
<br>
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
856
|
|