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/print_mgr.py
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
2
|
+
Primary zebra_day module. Primary functions: consistent and clear management
|
|
3
|
+
of 1+ networked zebra printers, automated discovery of printers on a
|
|
4
|
+
network. Clear formulation and delivery of ZPL strings to destination
|
|
5
|
+
printers. Management of zpl template files, which may have format value
|
|
6
|
+
components for inserting data on the fly. (elsewhere, a simple ui on
|
|
7
|
+
top of this).
|
|
8
|
+
|
|
9
|
+
This module is primarily focused on print request and package config mgmt.
|
|
10
|
+
See 'cmd_mgr' for interacting with zebras printer config capabilities.
|
|
11
11
|
"""
|
|
12
|
+
|
|
12
13
|
from __future__ import annotations
|
|
13
14
|
|
|
14
15
|
import datetime
|
|
@@ -17,20 +18,18 @@ import os
|
|
|
17
18
|
import shutil
|
|
18
19
|
import socket
|
|
19
20
|
import subprocess
|
|
20
|
-
import sys
|
|
21
21
|
import time
|
|
22
|
-
from pathlib import Path
|
|
23
|
-
|
|
24
22
|
from importlib.resources import files
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
from typing import Literal
|
|
25
25
|
|
|
26
|
-
from zebra_day.logging_config import get_logger
|
|
27
|
-
from zebra_day import paths as xdg
|
|
28
26
|
import zebra_day.cmd_mgr as zdcm
|
|
27
|
+
from zebra_day import paths as xdg
|
|
28
|
+
from zebra_day.logging_config import get_logger
|
|
29
29
|
|
|
30
30
|
_log = get_logger(__name__)
|
|
31
31
|
|
|
32
32
|
|
|
33
|
-
|
|
34
33
|
def get_current_date():
|
|
35
34
|
"""
|
|
36
35
|
get the current datetime
|
|
@@ -46,11 +45,11 @@ def send_zpl_code(zpl_code, printer_ip, printer_port=9100, is_test=False):
|
|
|
46
45
|
The bit which passes the zpl to the specified printer.
|
|
47
46
|
Port is more or less hard coded upstream from here fwiw
|
|
48
47
|
"""
|
|
49
|
-
|
|
48
|
+
|
|
50
49
|
# In the case we are testing only, return None
|
|
51
50
|
if is_test:
|
|
52
51
|
return None
|
|
53
|
-
|
|
52
|
+
|
|
54
53
|
# Create a socket object
|
|
55
54
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
56
55
|
timeout = 5
|
|
@@ -64,18 +63,23 @@ def send_zpl_code(zpl_code, printer_ip, printer_port=9100, is_test=False):
|
|
|
64
63
|
# ... the zebra printer will not throw an error if the request
|
|
65
64
|
# content is incorrect, or for any reason except to reject request to the wrong port.
|
|
66
65
|
return_code = sock.sendall(zpl_code.encode())
|
|
67
|
-
if return_code
|
|
66
|
+
if return_code in [None]:
|
|
68
67
|
_log.info("ZPL code sent successfully to printer %s:%d", printer_ip, printer_port)
|
|
69
68
|
else:
|
|
70
|
-
raise Exception(
|
|
71
|
-
|
|
69
|
+
raise Exception(
|
|
70
|
+
f"\n\nPrint request to {printer_ip}:{printer_port} did not return None, but instead: {return_code} ... zpl: {zpl_code}\n"
|
|
71
|
+
)
|
|
72
|
+
|
|
72
73
|
except ConnectionError as e:
|
|
73
|
-
raise Exception(
|
|
74
|
+
raise Exception(
|
|
75
|
+
f"Error connecting to the printer: {printer_ip} on port {printer_port} \n\n\t" + str(e)
|
|
76
|
+
) from e
|
|
74
77
|
|
|
75
78
|
finally:
|
|
76
79
|
# Close the socket connection
|
|
77
80
|
sock.close()
|
|
78
81
|
|
|
82
|
+
|
|
79
83
|
"""
|
|
80
84
|
The zpl.printers object is critical part of zebra_day. There is an in memory js on which can be stored to an active use json file. This active use file is
|
|
81
85
|
used when creating a new zpl() class. If absent, a minimal viable json
|
|
@@ -93,7 +97,7 @@ class zpl:
|
|
|
93
97
|
from zebra_day import print_mgr as zd
|
|
94
98
|
zd_pm = zd.zpl()
|
|
95
99
|
"""
|
|
96
|
-
|
|
100
|
+
|
|
97
101
|
def __init__(self, json_config: str | None = None):
|
|
98
102
|
"""
|
|
99
103
|
Initialize the class.
|
|
@@ -107,10 +111,10 @@ class zpl:
|
|
|
107
111
|
|
|
108
112
|
# Determine config file location (XDG first, then package fallback)
|
|
109
113
|
xdg_config = xdg.get_printer_config_path()
|
|
110
|
-
pkg_config = Path(str(files(
|
|
114
|
+
pkg_config = Path(str(files("zebra_day"))) / "etc" / "printer_config.json"
|
|
111
115
|
|
|
112
116
|
if json_config:
|
|
113
|
-
jcfg = Path(json_config) if not json_config.startswith(
|
|
117
|
+
jcfg = Path(json_config) if not json_config.startswith("/") else Path(json_config)
|
|
114
118
|
elif xdg_config.exists():
|
|
115
119
|
jcfg = xdg_config
|
|
116
120
|
elif pkg_config.exists():
|
|
@@ -123,8 +127,9 @@ class zpl:
|
|
|
123
127
|
else:
|
|
124
128
|
self.create_new_printers_json_with_single_test_printer(str(jcfg))
|
|
125
129
|
|
|
126
|
-
|
|
127
|
-
|
|
130
|
+
def probe_zebra_printers_add_to_printers_json(
|
|
131
|
+
self, ip_stub="192.168.1", scan_wait="0.25", lab="default", relative=False
|
|
132
|
+
):
|
|
128
133
|
"""
|
|
129
134
|
Scan the network for zebra printers.
|
|
130
135
|
|
|
@@ -147,33 +152,18 @@ class zpl:
|
|
|
147
152
|
self.printers["schema_version"] = "2.0.0"
|
|
148
153
|
|
|
149
154
|
# Initialize lab with v2 structure if not exists
|
|
150
|
-
if lab not in self.printers[
|
|
151
|
-
self.printers[
|
|
152
|
-
"lab_name": lab,
|
|
155
|
+
if lab not in self.printers["labs"]:
|
|
156
|
+
self.printers["labs"][lab] = {
|
|
157
|
+
"lab_name": lab.replace("-", " ").title(),
|
|
153
158
|
"available_locations": [],
|
|
154
|
-
"printers": {}
|
|
159
|
+
"printers": {},
|
|
155
160
|
}
|
|
156
161
|
|
|
157
162
|
# Ensure lab has printers sub-object (migration from v1)
|
|
158
|
-
if "printers" not in self.printers[
|
|
159
|
-
self.printers[
|
|
160
|
-
self.printers[
|
|
161
|
-
self.printers[
|
|
162
|
-
|
|
163
|
-
# Add the virtual PNG printer
|
|
164
|
-
self.printers['labs'][lab]["printers"]["Download-Label-png"] = {
|
|
165
|
-
"ip_address": "dl_png",
|
|
166
|
-
"printer_name": "Download Label as PNG",
|
|
167
|
-
"lab_location": None,
|
|
168
|
-
"manufacturer": "virtual",
|
|
169
|
-
"model": "na",
|
|
170
|
-
"serial": "na",
|
|
171
|
-
"label_zpl_styles": ["tube_2inX1in"],
|
|
172
|
-
"default_label_style": "tube_2inX1in",
|
|
173
|
-
"print_method": "generate png",
|
|
174
|
-
"arp_data": "",
|
|
175
|
-
"notes": ""
|
|
176
|
-
}
|
|
163
|
+
if "printers" not in self.printers["labs"][lab]:
|
|
164
|
+
self.printers["labs"][lab]["printers"] = {}
|
|
165
|
+
self.printers["labs"][lab].setdefault("lab_name", lab.replace("-", " ").title())
|
|
166
|
+
self.printers["labs"][lab].setdefault("available_locations", [])
|
|
177
167
|
|
|
178
168
|
# Scan network for Zebra printers using pure Python
|
|
179
169
|
wait_time = float(scan_wait) if scan_wait else 0.25
|
|
@@ -183,6 +173,7 @@ class zpl:
|
|
|
183
173
|
try:
|
|
184
174
|
# Try to connect to ZPL port (9100)
|
|
185
175
|
import socket
|
|
176
|
+
|
|
186
177
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
187
178
|
sock.settimeout(wait_time)
|
|
188
179
|
result = sock.connect_ex((ip, 9100))
|
|
@@ -200,46 +191,51 @@ class zpl:
|
|
|
200
191
|
|
|
201
192
|
# Parse model from config
|
|
202
193
|
if "MODEL" in config:
|
|
203
|
-
for line in config.split(
|
|
194
|
+
for line in config.split("\n"):
|
|
204
195
|
if "MODEL" in line.upper():
|
|
205
|
-
parts = line.split(
|
|
196
|
+
parts = line.split(":")
|
|
206
197
|
if len(parts) > 1:
|
|
207
198
|
model = parts[1].strip()
|
|
208
199
|
break
|
|
209
200
|
|
|
210
201
|
# Parse serial from config
|
|
211
202
|
if "SERIAL" in config.upper():
|
|
212
|
-
for line in config.split(
|
|
203
|
+
for line in config.split("\n"):
|
|
213
204
|
if "SERIAL" in line.upper():
|
|
214
|
-
parts = line.split(
|
|
205
|
+
parts = line.split(":")
|
|
215
206
|
if len(parts) > 1:
|
|
216
207
|
serial = parts[1].strip()
|
|
217
208
|
break
|
|
218
209
|
except Exception:
|
|
219
210
|
pass # Use defaults if we can't query printer
|
|
220
211
|
|
|
221
|
-
if ip not in self.printers[
|
|
212
|
+
if ip not in self.printers["labs"][lab]["printers"]:
|
|
222
213
|
# The label formats set here are the installed defaults
|
|
223
|
-
self.printers[
|
|
214
|
+
self.printers["labs"][lab]["printers"][ip] = {
|
|
224
215
|
"ip_address": ip,
|
|
225
216
|
"printer_name": None, # User can set friendly name later
|
|
226
217
|
"lab_location": None, # User can set location later
|
|
227
218
|
"manufacturer": "zebra",
|
|
228
219
|
"model": model,
|
|
229
220
|
"serial": serial,
|
|
230
|
-
"label_zpl_styles": [
|
|
221
|
+
"label_zpl_styles": [
|
|
222
|
+
"tube_2inX1in",
|
|
223
|
+
"plate_1inX0.25in",
|
|
224
|
+
"tube_2inX0.3in",
|
|
225
|
+
],
|
|
231
226
|
"default_label_style": "tube_2inX1in", # Default to first style
|
|
232
227
|
"print_method": "socket",
|
|
233
228
|
"arp_data": "",
|
|
234
|
-
"notes": ""
|
|
229
|
+
"notes": "",
|
|
235
230
|
}
|
|
236
231
|
except Exception:
|
|
237
232
|
pass # Skip unreachable IPs
|
|
238
233
|
|
|
239
234
|
self.save_printer_json(self.printers_filename, relative=False)
|
|
240
235
|
|
|
241
|
-
|
|
242
|
-
|
|
236
|
+
def save_printer_json(
|
|
237
|
+
self, json_filename: str = "/etc/printer_config.json", relative: bool = True
|
|
238
|
+
) -> None:
|
|
243
239
|
"""
|
|
244
240
|
Save the current self.printers to the specified JSON file.
|
|
245
241
|
|
|
@@ -251,12 +247,12 @@ class zpl:
|
|
|
251
247
|
"""
|
|
252
248
|
# Resolve the target path
|
|
253
249
|
if relative:
|
|
254
|
-
json_path = Path(str(files(
|
|
250
|
+
json_path = Path(str(files("zebra_day"))) / json_filename.lstrip("/")
|
|
255
251
|
else:
|
|
256
252
|
json_path = Path(json_filename)
|
|
257
253
|
|
|
258
254
|
# Create backup if file exists
|
|
259
|
-
if hasattr(self,
|
|
255
|
+
if hasattr(self, "printers_filename") and Path(self.printers_filename).exists():
|
|
260
256
|
backup_dir = xdg.get_config_backups_dir()
|
|
261
257
|
rec_date = datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
|
|
262
258
|
backup_path = backup_dir / f"{rec_date}_printer_config.json"
|
|
@@ -268,56 +264,54 @@ class zpl:
|
|
|
268
264
|
|
|
269
265
|
# Save the config
|
|
270
266
|
json_path.parent.mkdir(parents=True, exist_ok=True)
|
|
271
|
-
with open(json_path,
|
|
267
|
+
with open(json_path, "w") as json_file:
|
|
272
268
|
json.dump(self.printers, json_file, indent=4)
|
|
273
269
|
|
|
274
270
|
self.load_printer_json(str(json_path), relative=False)
|
|
275
271
|
|
|
276
|
-
|
|
277
|
-
def load_printer_json(self, json_file=f"etc/printer_config.json", relative=True):
|
|
272
|
+
def load_printer_json(self, json_file="etc/printer_config.json", relative=True):
|
|
278
273
|
"""
|
|
279
274
|
Loads printer json from a specified file, saves it to the active json.
|
|
280
275
|
If specified file does not exist, it is created with the base
|
|
281
276
|
printers json
|
|
282
|
-
|
|
277
|
+
|
|
283
278
|
json_file = path to file
|
|
284
279
|
"""
|
|
285
280
|
if relative:
|
|
286
281
|
json_file = f"{str(files('zebra_day'))}/{json_file}"
|
|
287
282
|
else:
|
|
288
283
|
pass
|
|
289
|
-
|
|
284
|
+
|
|
290
285
|
_log.debug("Loading printer config from: %s", json_file)
|
|
291
286
|
|
|
292
287
|
if not os.path.exists(json_file):
|
|
293
|
-
raise Exception(
|
|
288
|
+
raise Exception(
|
|
289
|
+
f"""The file specified does not exist. Consider specifying the default 'etc/printer_config.json , provided: {json_file}, which had {str(files("zebra_day"))} prefixed to it', for {json_file}"""
|
|
290
|
+
)
|
|
294
291
|
fh = open(json_file)
|
|
295
292
|
self.printers_filename = json_file
|
|
296
293
|
self.printers = json.load(fh)
|
|
297
294
|
# self.save_printer_json() <--- use the save_printer_json call after calling this. Else, recursion.
|
|
298
|
-
|
|
299
295
|
|
|
300
296
|
def create_new_printers_json_with_single_test_printer(self, fn=None):
|
|
301
297
|
"""
|
|
302
298
|
Create a new printers json with just the png printer defined
|
|
303
299
|
"""
|
|
304
300
|
|
|
305
|
-
|
|
306
301
|
if fn in [None]:
|
|
307
|
-
fn = str(files(
|
|
308
|
-
|
|
309
|
-
if not hasattr(self,
|
|
302
|
+
fn = str(files("zebra_day")) + "/etc/printer_config.json"
|
|
303
|
+
|
|
304
|
+
if not hasattr(self, "printers"):
|
|
310
305
|
self.printers = {}
|
|
311
306
|
self.printers_filename = fn
|
|
312
307
|
|
|
313
308
|
jdat = None
|
|
314
|
-
with open(f"{str(files('zebra_day'))}/etc/printer_config.template.json"
|
|
309
|
+
with open(f"{str(files('zebra_day'))}/etc/printer_config.template.json") as file:
|
|
315
310
|
jdat = json.load(file)
|
|
316
|
-
|
|
311
|
+
|
|
317
312
|
self.printers = jdat
|
|
318
|
-
|
|
319
|
-
self.save_printer_json(fn, relative=False)
|
|
320
313
|
|
|
314
|
+
self.save_printer_json(fn, relative=False)
|
|
321
315
|
|
|
322
316
|
def clear_printers_json(self, json_file: str = "/etc/printer_config.json") -> None:
|
|
323
317
|
"""
|
|
@@ -326,15 +320,12 @@ class zpl:
|
|
|
326
320
|
Args:
|
|
327
321
|
json_file: Path to the config file (relative to package)
|
|
328
322
|
"""
|
|
329
|
-
json_path = Path(str(files(
|
|
323
|
+
json_path = Path(str(files("zebra_day"))) / json_file.lstrip("/")
|
|
330
324
|
|
|
331
325
|
# Write empty config with v2 schema
|
|
332
|
-
empty_config = {
|
|
333
|
-
"schema_version": "2.0.0",
|
|
334
|
-
"labs": {}
|
|
335
|
-
}
|
|
326
|
+
empty_config = {"schema_version": "2.0.0", "labs": {}}
|
|
336
327
|
json_path.parent.mkdir(parents=True, exist_ok=True)
|
|
337
|
-
with open(json_path,
|
|
328
|
+
with open(json_path, "w") as f:
|
|
338
329
|
json.dump(empty_config, f, indent=4)
|
|
339
330
|
|
|
340
331
|
self.printers_filename = str(json_path)
|
|
@@ -348,7 +339,7 @@ class zpl:
|
|
|
348
339
|
|
|
349
340
|
Copies the template JSON to the active config location.
|
|
350
341
|
"""
|
|
351
|
-
pkg_path = Path(str(files(
|
|
342
|
+
pkg_path = Path(str(files("zebra_day")))
|
|
352
343
|
template_path = pkg_path / "etc" / "printer_config.template.json"
|
|
353
344
|
target_path = pkg_path / "etc" / "printer_config.json"
|
|
354
345
|
|
|
@@ -361,8 +352,6 @@ class zpl:
|
|
|
361
352
|
|
|
362
353
|
self.save_printer_json(self.printers_filename, relative=False)
|
|
363
354
|
|
|
364
|
-
|
|
365
|
-
|
|
366
355
|
def get_valid_label_styles_for_lab(self, lab=None):
|
|
367
356
|
"""
|
|
368
357
|
Get all unique label styles available for printers in a lab.
|
|
@@ -378,18 +367,27 @@ class zpl:
|
|
|
378
367
|
unique_labels = set()
|
|
379
368
|
|
|
380
369
|
# Access printers via nested 'printers' key (v2 schema)
|
|
381
|
-
lab_printers = self.printers[
|
|
382
|
-
for
|
|
383
|
-
for style in printer_data.get(
|
|
370
|
+
lab_printers = self.printers["labs"][lab].get("printers", {})
|
|
371
|
+
for _printer_id, printer_data in lab_printers.items():
|
|
372
|
+
for style in printer_data.get("label_zpl_styles", []):
|
|
384
373
|
unique_labels.add(style)
|
|
385
374
|
|
|
386
375
|
result = list(unique_labels)
|
|
387
376
|
return result
|
|
388
377
|
|
|
389
|
-
|
|
390
378
|
# Given these inputs, format them in to the specified zpl template and
|
|
391
379
|
# prepare a string to send to a printer
|
|
392
|
-
def formulate_zpl(
|
|
380
|
+
def formulate_zpl(
|
|
381
|
+
self,
|
|
382
|
+
uid_barcode=None,
|
|
383
|
+
alt_a=None,
|
|
384
|
+
alt_b=None,
|
|
385
|
+
alt_c=None,
|
|
386
|
+
alt_d=None,
|
|
387
|
+
alt_e=None,
|
|
388
|
+
alt_f=None,
|
|
389
|
+
label_zpl_style=None,
|
|
390
|
+
):
|
|
393
391
|
"""
|
|
394
392
|
Produce a ZPL string using the specified zpl template file, and
|
|
395
393
|
formatting in the values, where appropriate.
|
|
@@ -402,21 +400,30 @@ class zpl:
|
|
|
402
400
|
the zpl templates. They may be used in any way. uid_barcode
|
|
403
401
|
just differntiates one.
|
|
404
402
|
"""
|
|
405
|
-
|
|
406
|
-
zpl_file = str(files(
|
|
403
|
+
|
|
404
|
+
zpl_file = str(files("zebra_day")) + f"/etc/label_styles/{label_zpl_style}.zpl"
|
|
407
405
|
if not os.path.exists(zpl_file):
|
|
408
|
-
zpl_file = str(files(
|
|
406
|
+
zpl_file = str(files("zebra_day")) + f"/etc/label_styles/tmps/{label_zpl_style}.zpl"
|
|
409
407
|
if not os.path.exists(zpl_file):
|
|
410
|
-
raise Exception(
|
|
408
|
+
raise Exception(
|
|
409
|
+
f"ZPL File : {zpl_file} does not exist in the TOPLEVEL or TMPS zebra_day/etc/label_styles dir."
|
|
410
|
+
)
|
|
411
411
|
|
|
412
|
-
with open(zpl_file
|
|
412
|
+
with open(zpl_file) as file:
|
|
413
413
|
content = file.read()
|
|
414
|
-
zpl_string = content.format(
|
|
414
|
+
zpl_string = content.format(
|
|
415
|
+
uid_barcode=uid_barcode,
|
|
416
|
+
alt_a=alt_a,
|
|
417
|
+
alt_b=alt_b,
|
|
418
|
+
alt_c=alt_c,
|
|
419
|
+
alt_d=alt_d,
|
|
420
|
+
alt_e=alt_e,
|
|
421
|
+
alt_f=alt_f,
|
|
422
|
+
label_zpl_style=label_zpl_style,
|
|
423
|
+
)
|
|
415
424
|
|
|
416
425
|
return zpl_string
|
|
417
426
|
|
|
418
|
-
|
|
419
|
-
|
|
420
427
|
def generate_label_png(self, zpl_string=None, png_fn=None, relative=False):
|
|
421
428
|
"""
|
|
422
429
|
Generate a PNG image from ZPL string using local renderer.
|
|
@@ -436,10 +443,10 @@ class zpl:
|
|
|
436
443
|
from zebra_day.zpl_renderer import render_zpl_to_png
|
|
437
444
|
|
|
438
445
|
if relative:
|
|
439
|
-
png_fn = str(files(
|
|
446
|
+
png_fn = str(files("zebra_day")) + "/" + png_fn
|
|
440
447
|
|
|
441
448
|
if zpl_string is None or png_fn is None:
|
|
442
|
-
raise ValueError(
|
|
449
|
+
raise ValueError("ERROR: zpl_string and png_fn may not be None.")
|
|
443
450
|
|
|
444
451
|
try:
|
|
445
452
|
result = render_zpl_to_png(zpl_string, png_fn)
|
|
@@ -448,18 +455,29 @@ class zpl:
|
|
|
448
455
|
except Exception as e:
|
|
449
456
|
_log.error("Failed to convert ZPL to image: %s", e)
|
|
450
457
|
raise
|
|
451
|
-
|
|
452
458
|
|
|
453
|
-
def print_raw_zpl(self,zpl_content,printer_ip, port=9100):
|
|
459
|
+
def print_raw_zpl(self, zpl_content, printer_ip, port=9100):
|
|
454
460
|
"""
|
|
455
461
|
For use when no use of the printer mapping config json is needed. This assumes you know which IP is your desired printer. The spcified zpl_content will be sent to that IP+port.
|
|
456
462
|
"""
|
|
457
463
|
send_zpl_code(zpl_content, printer_ip, printer_port=port)
|
|
458
464
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
465
|
+
def print_zpl(
|
|
466
|
+
self,
|
|
467
|
+
lab=None,
|
|
468
|
+
printer_name=None,
|
|
469
|
+
uid_barcode="",
|
|
470
|
+
alt_a="",
|
|
471
|
+
alt_b="",
|
|
472
|
+
alt_c="",
|
|
473
|
+
alt_d="",
|
|
474
|
+
alt_e="",
|
|
475
|
+
alt_f="",
|
|
476
|
+
label_zpl_style=None,
|
|
477
|
+
client_ip="pkg",
|
|
478
|
+
print_n=1,
|
|
479
|
+
zpl_content=None,
|
|
480
|
+
):
|
|
463
481
|
"""
|
|
464
482
|
The main print method. Accepts info to determine the desired
|
|
465
483
|
printer IP and to request the desired ZPL string to be sent
|
|
@@ -479,63 +497,68 @@ class zpl:
|
|
|
479
497
|
if print_n < 1:
|
|
480
498
|
raise Exception(f"\n\nprint_n < 1 , specified {print_n}")
|
|
481
499
|
|
|
482
|
-
rec_date = str(datetime.datetime.now()).replace(' ', '_')
|
|
483
500
|
print_n = int(print_n)
|
|
484
501
|
|
|
485
|
-
if printer_name in [
|
|
486
|
-
raise Exception(
|
|
502
|
+
if printer_name in ["", "None", None] and lab in [None, "", "None"]:
|
|
503
|
+
raise Exception(
|
|
504
|
+
f"lab and printer_name are both required to route a zebra print request, the following was what was received: lab:{lab} & printer_name:{printer_name}"
|
|
505
|
+
)
|
|
487
506
|
|
|
488
507
|
# Access printer via nested 'printers' key (v2 schema)
|
|
489
|
-
printer_data = self.printers[
|
|
508
|
+
printer_data = self.printers["labs"][lab]["printers"][printer_name]
|
|
490
509
|
|
|
491
|
-
if label_zpl_style in [None,
|
|
510
|
+
if label_zpl_style in [None, "", "None"]:
|
|
492
511
|
# Use default_label_style if set, otherwise fall back to first in list
|
|
493
|
-
label_zpl_style =
|
|
494
|
-
|
|
512
|
+
label_zpl_style = (
|
|
513
|
+
printer_data.get("default_label_style") or printer_data["label_zpl_styles"][0]
|
|
514
|
+
)
|
|
515
|
+
elif label_zpl_style not in printer_data["label_zpl_styles"]:
|
|
495
516
|
_log.warning(
|
|
496
517
|
"ZPL style '%s' is not valid for %s/%s. Valid styles: %s",
|
|
497
|
-
label_zpl_style,
|
|
498
|
-
|
|
518
|
+
label_zpl_style,
|
|
519
|
+
lab,
|
|
520
|
+
printer_name,
|
|
521
|
+
printer_data["label_zpl_styles"],
|
|
499
522
|
)
|
|
500
523
|
|
|
501
524
|
printer_ip = printer_data["ip_address"]
|
|
502
525
|
|
|
503
|
-
zpl_string =
|
|
526
|
+
zpl_string = ""
|
|
504
527
|
if zpl_content in [None]:
|
|
505
|
-
zpl_string = self.formulate_zpl(
|
|
528
|
+
zpl_string = self.formulate_zpl(
|
|
529
|
+
uid_barcode=uid_barcode,
|
|
530
|
+
alt_a=alt_a,
|
|
531
|
+
alt_b=alt_b,
|
|
532
|
+
alt_c=alt_c,
|
|
533
|
+
alt_d=alt_d,
|
|
534
|
+
alt_e=alt_e,
|
|
535
|
+
alt_f=alt_f,
|
|
536
|
+
label_zpl_style=label_zpl_style,
|
|
537
|
+
)
|
|
506
538
|
else:
|
|
507
539
|
zpl_string = zpl_content
|
|
508
540
|
|
|
509
541
|
# Log print request to file (using pathlib, not shell)
|
|
510
542
|
log_file = xdg.get_logs_dir() / "print_requests.log"
|
|
511
543
|
log_entry = f"{lab}\t{printer_name}\t{uid_barcode}\t{label_zpl_style}\t{printer_ip}\t{print_n}\t{client_ip}\t{zpl_content}\n"
|
|
512
|
-
with open(log_file,
|
|
544
|
+
with open(log_file, "a") as f:
|
|
513
545
|
f.write(log_entry)
|
|
514
546
|
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
ret_s = self.generate_label_png(zpl_string, png_fn, False)
|
|
547
|
+
# Send to printer
|
|
548
|
+
for _ in range(print_n):
|
|
549
|
+
send_zpl_code(zpl_string, printer_ip)
|
|
519
550
|
|
|
520
|
-
|
|
521
|
-
pn = 1
|
|
522
|
-
while pn <= print_n:
|
|
523
|
-
send_zpl_code(zpl_string, printer_ip)
|
|
524
|
-
pn += 1
|
|
525
|
-
|
|
526
|
-
ret_s = zpl_string
|
|
527
|
-
|
|
528
|
-
return ret_s
|
|
551
|
+
return zpl_string
|
|
529
552
|
|
|
530
553
|
|
|
531
554
|
def _get_local_ip() -> str:
|
|
532
555
|
"""Get the local IP address of this machine."""
|
|
533
556
|
ipcmd = r"""(ip addr show | grep -Eo 'inet (addr:)?([0-9]*\.){3}[0-9]*' | grep -Eo '([0-9]*\.){3}[0-9]*' | grep -v '127.0.0.1' || ifconfig | grep -Eo 'inet (addr:)?([0-9]*\.){3}[0-9]*' | grep -Eo '([0-9]*\.){3}[0-9]*' | grep -v '127.0.0.1') 2>/dev/null"""
|
|
534
557
|
result = subprocess.run(ipcmd, shell=True, capture_output=True, text=True)
|
|
535
|
-
return result.stdout.strip().split(
|
|
558
|
+
return result.stdout.strip().split("\n")[0] if result.stdout.strip() else "127.0.0.1"
|
|
536
559
|
|
|
537
560
|
|
|
538
|
-
def _parse_auth_args() ->
|
|
561
|
+
def _parse_auth_args() -> Literal["none", "cognito"]:
|
|
539
562
|
"""Parse --auth CLI argument.
|
|
540
563
|
|
|
541
564
|
Returns:
|
|
@@ -552,7 +575,8 @@ def _parse_auth_args() -> str:
|
|
|
552
575
|
help="Authentication mode: 'none' (public, default) or 'cognito' (AWS Cognito)",
|
|
553
576
|
)
|
|
554
577
|
args, _ = parser.parse_known_args()
|
|
555
|
-
|
|
578
|
+
auth_mode: Literal["none", "cognito"] = args.auth
|
|
579
|
+
return auth_mode
|
|
556
580
|
|
|
557
581
|
|
|
558
582
|
def zday_start() -> None:
|
|
@@ -571,14 +595,13 @@ def zday_start() -> None:
|
|
|
571
595
|
zday_start --auth cognito # Enable Cognito authentication
|
|
572
596
|
"""
|
|
573
597
|
import warnings
|
|
598
|
+
|
|
574
599
|
warnings.warn(
|
|
575
600
|
"zday_start is deprecated. Use 'zday gui start' instead.",
|
|
576
601
|
DeprecationWarning,
|
|
577
602
|
stacklevel=2,
|
|
578
603
|
)
|
|
579
|
-
_log.warning(
|
|
580
|
-
"DEPRECATED: zday_start is deprecated. Use 'zday gui start' instead."
|
|
581
|
-
)
|
|
604
|
+
_log.warning("DEPRECATED: zday_start is deprecated. Use 'zday gui start' instead.")
|
|
582
605
|
|
|
583
606
|
from zebra_day.web.app import run_server
|
|
584
607
|
|
|
@@ -606,6 +629,7 @@ def main() -> None:
|
|
|
606
629
|
zday_quickstart --auth cognito # Enable Cognito authentication
|
|
607
630
|
"""
|
|
608
631
|
import warnings
|
|
632
|
+
|
|
609
633
|
warnings.warn(
|
|
610
634
|
"zday_quickstart is deprecated. Use 'zday bootstrap' then 'zday gui start' instead.",
|
|
611
635
|
DeprecationWarning,
|
|
@@ -621,7 +645,7 @@ def main() -> None:
|
|
|
621
645
|
auth_mode = _parse_auth_args()
|
|
622
646
|
|
|
623
647
|
ip = _get_local_ip()
|
|
624
|
-
ip_root = ".".join(ip.split(
|
|
648
|
+
ip_root = ".".join(ip.split(".")[:-1])
|
|
625
649
|
|
|
626
650
|
_log.info("IP detected: %s ... using IP root: %s", ip, ip_root)
|
|
627
651
|
_log.info("Scanning for zebra printers on this network (may take a few minutes)...")
|
|
@@ -632,8 +656,7 @@ def main() -> None:
|
|
|
632
656
|
|
|
633
657
|
_log.info("Zebra Printer Scan Complete. Results: %s", zp.printers)
|
|
634
658
|
_log.info(
|
|
635
|
-
"Starting zebra_day web GUI at %s:8118 (auth=%s). "
|
|
636
|
-
"Press Ctrl+C to shut down.",
|
|
659
|
+
"Starting zebra_day web GUI at %s:8118 (auth=%s). Press Ctrl+C to shut down.",
|
|
637
660
|
ip,
|
|
638
661
|
auth_mode,
|
|
639
662
|
)
|
|
@@ -642,10 +665,7 @@ def main() -> None:
|
|
|
642
665
|
run_server(host="0.0.0.0", port=8118, reload=False, auth=auth_mode)
|
|
643
666
|
|
|
644
667
|
_log.info("EXITING ZDAY QUICKSTART")
|
|
645
|
-
_log.info(
|
|
646
|
-
"If the web GUI did not run, check if a service is already running at %s:8118",
|
|
647
|
-
ip
|
|
648
|
-
)
|
|
668
|
+
_log.info("If the web GUI did not run, check if a service is already running at %s:8118", ip)
|
|
649
669
|
|
|
650
670
|
|
|
651
671
|
if __name__ == "__main__":
|
|
@@ -660,5 +680,5 @@ if __name__ == "__zday_start__":
|
|
|
660
680
|
"""
|
|
661
681
|
entry point for zday_start
|
|
662
682
|
"""
|
|
663
|
-
|
|
683
|
+
|
|
664
684
|
zday_start()
|
|
@@ -81,6 +81,13 @@
|
|
|
81
81
|
<i class="fas fa-external-link-alt"></i> View Full
|
|
82
82
|
</a>
|
|
83
83
|
</div>
|
|
84
|
+
|
|
85
|
+
<!-- Config File Path -->
|
|
86
|
+
<div class="mb-md" style="padding: 0.75rem 1rem; background: var(--color-gray-700); border-radius: var(--radius-sm); font-family: 'JetBrains Mono', monospace; font-size: 0.875rem;">
|
|
87
|
+
<span class="text-muted"><i class="fas fa-file-alt"></i> Loaded from:</span>
|
|
88
|
+
<code style="color: var(--color-highlight); margin-left: 0.5rem;">{{ config_file_path | default('Unknown') }}</code>
|
|
89
|
+
</div>
|
|
90
|
+
|
|
84
91
|
{% if config_summary %}
|
|
85
92
|
<div class="grid grid-4 mb-lg">
|
|
86
93
|
<div class="stat-card">
|