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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) hide show
  1. zebra_day/__init__.py +7 -2
  2. zebra_day/_version.py +1 -0
  3. zebra_day/cli/__init__.py +80 -30
  4. zebra_day/cli/cognito.py +15 -9
  5. zebra_day/cli/gui.py +101 -13
  6. zebra_day/cli/printer.py +34 -27
  7. zebra_day/cli/template.py +19 -15
  8. zebra_day/cmd_mgr.py +3 -6
  9. zebra_day/docs/gx420d-gx430d-ug-en.pdf +0 -0
  10. zebra_day/docs/hardware_config_guide.md +149 -0
  11. zebra_day/docs/programatic_guide.md +181 -0
  12. zebra_day/docs/qln420_zebra_manual.pdf +0 -0
  13. zebra_day/docs/uid_screed_light.md +38 -0
  14. zebra_day/docs/zd620-zd420-ug-en.pdf +0 -0
  15. zebra_day/docs/zebra_day_ui_guide.md +194 -0
  16. zebra_day/etc/printer_config.json +5 -11
  17. zebra_day/etc/printer_config.template.json +5 -11
  18. zebra_day/etc/tmp_printers120.json +10 -0
  19. zebra_day/etc/tmp_printers139.json +10 -0
  20. zebra_day/etc/tmp_printers145.json +10 -0
  21. zebra_day/etc/tmp_printers147.json +10 -0
  22. zebra_day/etc/tmp_printers207.json +10 -0
  23. zebra_day/etc/tmp_printers34.json +10 -0
  24. zebra_day/etc/tmp_printers389.json +10 -0
  25. zebra_day/etc/tmp_printers398.json +10 -0
  26. zebra_day/etc/tmp_printers437.json +10 -0
  27. zebra_day/etc/tmp_printers439.json +10 -0
  28. zebra_day/etc/tmp_printers440.json +10 -0
  29. zebra_day/etc/tmp_printers469.json +10 -0
  30. zebra_day/etc/tmp_printers485.json +10 -0
  31. zebra_day/etc/tmp_printers508.json +10 -0
  32. zebra_day/etc/tmp_printers531.json +10 -0
  33. zebra_day/etc/tmp_printers540.json +10 -0
  34. zebra_day/etc/tmp_printers542.json +10 -0
  35. zebra_day/etc/tmp_printers543.json +10 -0
  36. zebra_day/etc/tmp_printers552.json +10 -0
  37. zebra_day/etc/tmp_printers715.json +10 -0
  38. zebra_day/etc/tmp_printers835.json +10 -0
  39. zebra_day/etc/tmp_printers842.json +10 -0
  40. zebra_day/etc/tmp_printers931.json +10 -0
  41. zebra_day/etc/tmp_printers969.json +10 -0
  42. zebra_day/etc/tmp_printers972.json +10 -0
  43. zebra_day/exceptions.py +1 -1
  44. zebra_day/files/blank_preview.png +0 -0
  45. zebra_day/files/corners_20cmX30cm_preview.png +0 -0
  46. zebra_day/files/corners_smallTube_preview.png +0 -0
  47. zebra_day/files/generic_2inX1in_preview.png +0 -0
  48. zebra_day/files/test_png_12020.png +0 -0
  49. zebra_day/files/test_png_12352.png +0 -0
  50. zebra_day/files/test_png_15472.png +0 -0
  51. zebra_day/files/test_png_24493.png +0 -0
  52. zebra_day/files/test_png_2897.png +0 -0
  53. zebra_day/files/test_png_30069.png +0 -0
  54. zebra_day/files/test_png_31690.png +0 -0
  55. zebra_day/files/test_png_33804.png +0 -0
  56. zebra_day/files/test_png_34737.png +0 -0
  57. zebra_day/files/test_png_4161.png +0 -0
  58. zebra_day/files/test_png_44748.png +0 -0
  59. zebra_day/files/test_png_4635.png +0 -0
  60. zebra_day/files/test_png_47791.png +0 -0
  61. zebra_day/files/test_png_47799.png +0 -0
  62. zebra_day/files/test_png_55588.png +0 -0
  63. zebra_day/files/test_png_56349.png +0 -0
  64. zebra_day/files/test_png_58809.png +0 -0
  65. zebra_day/files/test_png_5936.png +0 -0
  66. zebra_day/files/test_png_64110.png +0 -0
  67. zebra_day/files/test_png_64891.png +0 -0
  68. zebra_day/files/test_png_67242.png +0 -0
  69. zebra_day/files/test_png_69002.png +0 -0
  70. zebra_day/files/test_png_70065.png +0 -0
  71. zebra_day/files/test_png_72366.png +0 -0
  72. zebra_day/files/test_png_77793.png +0 -0
  73. zebra_day/files/test_png_89893.png +0 -0
  74. zebra_day/files/test_png_9572.png +0 -0
  75. zebra_day/files/tube_20mmX30mmA_preview.png +0 -0
  76. zebra_day/imgs/.hold +0 -0
  77. zebra_day/imgs/bar_ltpurp.png +0 -0
  78. zebra_day/imgs/bar_purp.png +0 -0
  79. zebra_day/imgs/bar_purp3.png +0 -0
  80. zebra_day/imgs/bar_red.png +0 -0
  81. zebra_day/imgs/legacy/UBC_gantt_chart.png +0 -0
  82. zebra_day/imgs/legacy/gx420d_network_config.png +0 -0
  83. zebra_day/imgs/legacy/gx420d_printer_config.png +0 -0
  84. zebra_day/imgs/legacy/ngrok.png +0 -0
  85. zebra_day/imgs/legacy/printer_details.png +0 -0
  86. zebra_day/imgs/legacy/quick_start_test_label.png +0 -0
  87. zebra_day/imgs/legacy/quick_start_test_label2.png +0 -0
  88. zebra_day/imgs/legacy/zd620_network_config.png +0 -0
  89. zebra_day/imgs/legacy/zd620_printer_config.png +0 -0
  90. zebra_day/imgs/legacy/zday_quick_gui.png +0 -0
  91. zebra_day/imgs/legacy/zebra_day_alt_css_dog.png +0 -0
  92. zebra_day/imgs/legacy/zebra_day_alt_css_flower.png +0 -0
  93. zebra_day/imgs/legacy/zebra_day_alt_css_main.png +0 -0
  94. zebra_day/imgs/legacy/zebra_day_available_zpl_templates.png +0 -0
  95. zebra_day/imgs/legacy/zebra_day_bkup_pconfig.png +0 -0
  96. zebra_day/imgs/legacy/zebra_day_home.png +0 -0
  97. zebra_day/imgs/legacy/zebra_day_manual_print.png +0 -0
  98. zebra_day/imgs/legacy/zebra_day_printer_fleet_json.png +0 -0
  99. zebra_day/imgs/legacy/zebra_day_quick_ex.png +0 -0
  100. zebra_day/imgs/legacy/zebra_day_zpl_template_IRLa.png +0 -0
  101. zebra_day/imgs/legacy/zebra_day_zpl_template_IRLb.png +0 -0
  102. zebra_day/imgs/ui_api_docs.png +0 -0
  103. zebra_day/imgs/ui_config.png +0 -0
  104. zebra_day/imgs/ui_dashboard.png +0 -0
  105. zebra_day/imgs/ui_print_request.png +0 -0
  106. zebra_day/imgs/ui_printers.png +0 -0
  107. zebra_day/imgs/ui_templates.png +0 -0
  108. zebra_day/logging_config.py +4 -9
  109. zebra_day/mkcert.py +157 -0
  110. zebra_day/paths.py +1 -2
  111. zebra_day/print_mgr.py +261 -185
  112. zebra_day/templates/modern/config.html +7 -0
  113. zebra_day/templates/modern/config_backups.html +59 -0
  114. zebra_day/templates/modern/config_editor.html +95 -0
  115. zebra_day/templates/modern/config_new.html +93 -0
  116. zebra_day/templates/modern/print_request.html +70 -8
  117. zebra_day/templates/modern/printer_detail.html +161 -34
  118. zebra_day/templates/modern/printers.html +17 -6
  119. zebra_day/templates/modern/template_editor.html +7 -4
  120. zebra_day/web/__init__.py +1 -1
  121. zebra_day/web/app.py +99 -17
  122. zebra_day/web/auth.py +17 -15
  123. zebra_day/web/middleware.py +8 -5
  124. zebra_day/web/routers/__init__.py +0 -1
  125. zebra_day/web/routers/api.py +330 -31
  126. zebra_day/web/routers/ui.py +174 -591
  127. zebra_day/zpl_renderer.py +45 -34
  128. {zebra_day-1.0.2.dist-info → zebra_day-2.1.4.dist-info}/METADATA +144 -74
  129. zebra_day-2.1.4.dist-info/RECORD +240 -0
  130. zebra_day/bin/fetch_zebra_config.py +0 -15
  131. zebra_day/bin/generate_coord_grid_zpl.py +0 -50
  132. zebra_day/bin/print_zpl_from_file.py +0 -21
  133. zebra_day/bin/probe_new_label_dimensions.py +0 -75
  134. zebra_day/bin/scan_for_networed_zebra_printers.py +0 -23
  135. zebra_day/bin/scan_for_networed_zebra_printers_arp_scan.sh +0 -1
  136. zebra_day/bin/scan_for_networed_zebra_printers_curl.sh +0 -30
  137. zebra_day/bin/zserve.py +0 -1062
  138. zebra_day/templates/base.html +0 -36
  139. zebra_day/templates/bpr.html +0 -72
  140. zebra_day/templates/build_new_config.html +0 -36
  141. zebra_day/templates/build_print_request.html +0 -32
  142. zebra_day/templates/chg_ui_style.html +0 -19
  143. zebra_day/templates/edit_template.html +0 -128
  144. zebra_day/templates/edit_zpl.html +0 -37
  145. zebra_day/templates/index.html +0 -82
  146. zebra_day/templates/legacy/base.html +0 -37
  147. zebra_day/templates/legacy/bpr.html +0 -72
  148. zebra_day/templates/legacy/build_new_config.html +0 -36
  149. zebra_day/templates/legacy/build_print_request.html +0 -32
  150. zebra_day/templates/legacy/chg_ui_style.html +0 -19
  151. zebra_day/templates/legacy/edit_template.html +0 -128
  152. zebra_day/templates/legacy/edit_zpl.html +0 -37
  153. zebra_day/templates/legacy/index.html +0 -82
  154. zebra_day/templates/legacy/list_prior_configs.html +0 -24
  155. zebra_day/templates/legacy/print_result.html +0 -30
  156. zebra_day/templates/legacy/printer_details.html +0 -25
  157. zebra_day/templates/legacy/printer_status.html +0 -70
  158. zebra_day/templates/legacy/save_result.html +0 -17
  159. zebra_day/templates/legacy/send_print_request.html +0 -34
  160. zebra_day/templates/legacy/simple_print.html +0 -94
  161. zebra_day/templates/legacy/view_pstation_json.html +0 -29
  162. zebra_day/templates/list_prior_configs.html +0 -24
  163. zebra_day/templates/print_result.html +0 -30
  164. zebra_day/templates/printer_details.html +0 -25
  165. zebra_day/templates/printer_status.html +0 -70
  166. zebra_day/templates/save_result.html +0 -17
  167. zebra_day/templates/send_print_request.html +0 -34
  168. zebra_day/templates/simple_print.html +0 -94
  169. zebra_day/templates/view_pstation_json.html +0 -29
  170. zebra_day-1.0.2.dist-info/RECORD +0 -179
  171. {zebra_day-1.0.2.dist-info → zebra_day-2.1.4.dist-info}/WHEEL +0 -0
  172. {zebra_day-1.0.2.dist-info → zebra_day-2.1.4.dist-info}/entry_points.txt +0 -0
  173. {zebra_day-1.0.2.dist-info → zebra_day-2.1.4.dist-info}/licenses/LICENSE +0 -0
  174. {zebra_day-1.0.2.dist-info → zebra_day-2.1.4.dist-info}/top_level.txt +0 -0
zebra_day/print_mgr.py CHANGED
@@ -1,14 +1,15 @@
1
1
  """
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.
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,19 +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
26
+ import zebra_day.cmd_mgr as zdcm
27
27
  from zebra_day import paths as xdg
28
+ from zebra_day.logging_config import get_logger
28
29
 
29
30
  _log = get_logger(__name__)
30
31
 
31
32
 
32
-
33
33
  def get_current_date():
34
34
  """
35
35
  get the current datetime
@@ -45,11 +45,11 @@ def send_zpl_code(zpl_code, printer_ip, printer_port=9100, is_test=False):
45
45
  The bit which passes the zpl to the specified printer.
46
46
  Port is more or less hard coded upstream from here fwiw
47
47
  """
48
-
48
+
49
49
  # In the case we are testing only, return None
50
50
  if is_test:
51
51
  return None
52
-
52
+
53
53
  # Create a socket object
54
54
  sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
55
55
  timeout = 5
@@ -63,18 +63,23 @@ def send_zpl_code(zpl_code, printer_ip, printer_port=9100, is_test=False):
63
63
  # ... the zebra printer will not throw an error if the request
64
64
  # content is incorrect, or for any reason except to reject request to the wrong port.
65
65
  return_code = sock.sendall(zpl_code.encode())
66
- if return_code in [None]:
66
+ if return_code in [None]:
67
67
  _log.info("ZPL code sent successfully to printer %s:%d", printer_ip, printer_port)
68
68
  else:
69
- raise Exception(f"\n\nPrint request to {printer_ip}:{printer_port} did not return None, but instead: {return_code} ... zpl: {zpl_code}\n")
70
-
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
+
71
73
  except ConnectionError as e:
72
- raise Exception(f"Error connecting to the printer: {printer_ip} on port {printer_port} \n\n\t"+str(e))
74
+ raise Exception(
75
+ f"Error connecting to the printer: {printer_ip} on port {printer_port} \n\n\t" + str(e)
76
+ ) from e
73
77
 
74
78
  finally:
75
79
  # Close the socket connection
76
80
  sock.close()
77
81
 
82
+
78
83
  """
79
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
80
85
  used when creating a new zpl() class. If absent, a minimal viable json
@@ -92,7 +97,7 @@ class zpl:
92
97
  from zebra_day import print_mgr as zd
93
98
  zd_pm = zd.zpl()
94
99
  """
95
-
100
+
96
101
  def __init__(self, json_config: str | None = None):
97
102
  """
98
103
  Initialize the class.
@@ -106,10 +111,10 @@ class zpl:
106
111
 
107
112
  # Determine config file location (XDG first, then package fallback)
108
113
  xdg_config = xdg.get_printer_config_path()
109
- pkg_config = Path(str(files('zebra_day'))) / "etc" / "printer_config.json"
114
+ pkg_config = Path(str(files("zebra_day"))) / "etc" / "printer_config.json"
110
115
 
111
116
  if json_config:
112
- jcfg = Path(json_config) if not json_config.startswith('/') else Path(json_config)
117
+ jcfg = Path(json_config) if not json_config.startswith("/") else Path(json_config)
113
118
  elif xdg_config.exists():
114
119
  jcfg = xdg_config
115
120
  elif pkg_config.exists():
@@ -122,10 +127,12 @@ class zpl:
122
127
  else:
123
128
  self.create_new_printers_json_with_single_test_printer(str(jcfg))
124
129
 
125
-
126
- def probe_zebra_printers_add_to_printers_json(self, ip_stub="192.168.1", scan_wait="0.25",lab="scan-results", relative=False):
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
+ ):
127
133
  """
128
- Scan the network for zebra printers
134
+ Scan the network for zebra printers.
135
+
129
136
  NOTE! this should work with no dependencies on a MAC
130
137
  UBUNTU requires system wide net-tools (for arp)
131
138
  Others... well, this may not work
@@ -133,66 +140,102 @@ class zpl:
133
140
  ---
134
141
  Requires:
135
142
  curl is pretty standard, arp seems less so
136
- arp
143
+ arp
137
144
  ---
138
-
139
- ip_stub = all 255 possibilities will be probed beneath this
140
- stub provided
141
145
 
142
- can_wait = seconds to re-try probing until moving on. 0.25
143
- default may be too squick
144
-
145
- lab = code for the lab key to add/update to given finding
146
- new printers. Existing printers will be over written.
146
+ ip_stub = all 255 possibilities will be probed beneath this stub provided
147
+ scan_wait = seconds to re-try probing until moving on. 0.25 default may be too quick
148
+ lab = code for the lab key to add/update to given finding new printers
147
149
  """
148
-
149
- if lab not in self.printers['labs']:
150
- self.printers['labs'][lab] = {}
151
-
152
- self.printers['labs'][lab]["Download-Label-png"] = {
153
- "ip_address": "dl_png",
154
- "label_zpl_styles": ["tube_2inX1in"],
155
- "print_method": "generate png",
156
- "model": "na",
157
- "serial": "na",
158
- "arp_data": ""
159
- }
160
-
161
- # Run scanner script using subprocess instead of os.popen
162
- script_path = Path(str(files('zebra_day'))) / "bin" / "scan_for_networed_zebra_printers_curl.sh"
163
- result = subprocess.run(
164
- [str(script_path), ip_stub, scan_wait],
165
- capture_output=True,
166
- text=True,
167
- check=False
168
- )
169
-
170
- for line in result.stdout.splitlines():
171
- line = line.rstrip()
172
- sl = line.split('|')
173
- if len(sl) > 1:
174
- zp = sl[0]
175
- ip = sl[1]
176
- model = sl[2]
177
- serial = sl[3]
178
- status = sl[4]
179
- arp_response = sl[5]
180
-
181
- if ip not in self.printers['labs'][lab]:
182
- # The label formats set here are the installed defaults
183
- self.printers['labs'][lab][ip] = {
184
- "ip_address": ip,
185
- "label_zpl_styles": ["tube_2inX1in", "plate_1inX0.25in", "tube_2inX0.3in"],
186
- "print_method": "unk",
187
- "model": model,
188
- "serial": serial,
189
- "arp_data": arp_response
190
- }
150
+ # Ensure schema version is set
151
+ if "schema_version" not in self.printers:
152
+ self.printers["schema_version"] = "2.0.0"
153
+
154
+ # Initialize lab with v2 structure if not exists
155
+ if lab not in self.printers["labs"]:
156
+ self.printers["labs"][lab] = {
157
+ "lab_name": lab.replace("-", " ").title(),
158
+ "available_locations": [],
159
+ "printers": {},
160
+ }
161
+
162
+ # Ensure lab has printers sub-object (migration from v1)
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", [])
167
+
168
+ # Scan network for Zebra printers using pure Python
169
+ wait_time = float(scan_wait) if scan_wait else 0.25
170
+
171
+ for i in range(1, 255):
172
+ ip = f"{ip_stub}.{i}"
173
+ try:
174
+ # Try to connect to ZPL port (9100)
175
+ import socket
176
+
177
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
178
+ sock.settimeout(wait_time)
179
+ result = sock.connect_ex((ip, 9100))
180
+ sock.close()
181
+
182
+ if result == 0:
183
+ # Port is open, try to get printer info
184
+ model = "Unknown"
185
+ serial = "Unknown"
186
+
187
+ try:
188
+ # Query printer for model and serial
189
+ printer = zdcm.ZebraPrinter(ip)
190
+ config = printer.get_configuration()
191
+
192
+ # Parse model from config
193
+ if "MODEL" in config:
194
+ for line in config.split("\n"):
195
+ if "MODEL" in line.upper():
196
+ parts = line.split(":")
197
+ if len(parts) > 1:
198
+ model = parts[1].strip()
199
+ break
200
+
201
+ # Parse serial from config
202
+ if "SERIAL" in config.upper():
203
+ for line in config.split("\n"):
204
+ if "SERIAL" in line.upper():
205
+ parts = line.split(":")
206
+ if len(parts) > 1:
207
+ serial = parts[1].strip()
208
+ break
209
+ except Exception:
210
+ pass # Use defaults if we can't query printer
211
+
212
+ if ip not in self.printers["labs"][lab]["printers"]:
213
+ # The label formats set here are the installed defaults
214
+ self.printers["labs"][lab]["printers"][ip] = {
215
+ "ip_address": ip,
216
+ "printer_name": None, # User can set friendly name later
217
+ "lab_location": None, # User can set location later
218
+ "manufacturer": "zebra",
219
+ "model": model,
220
+ "serial": serial,
221
+ "label_zpl_styles": [
222
+ "tube_2inX1in",
223
+ "plate_1inX0.25in",
224
+ "tube_2inX0.3in",
225
+ ],
226
+ "default_label_style": "tube_2inX1in", # Default to first style
227
+ "print_method": "socket",
228
+ "arp_data": "",
229
+ "notes": "",
230
+ }
231
+ except Exception:
232
+ pass # Skip unreachable IPs
191
233
 
192
234
  self.save_printer_json(self.printers_filename, relative=False)
193
235
 
194
-
195
- def save_printer_json(self, json_filename: str = "/etc/printer_config.json", relative: bool = True) -> None:
236
+ def save_printer_json(
237
+ self, json_filename: str = "/etc/printer_config.json", relative: bool = True
238
+ ) -> None:
196
239
  """
197
240
  Save the current self.printers to the specified JSON file.
198
241
 
@@ -204,12 +247,12 @@ class zpl:
204
247
  """
205
248
  # Resolve the target path
206
249
  if relative:
207
- json_path = Path(str(files('zebra_day'))) / json_filename.lstrip('/')
250
+ json_path = Path(str(files("zebra_day"))) / json_filename.lstrip("/")
208
251
  else:
209
252
  json_path = Path(json_filename)
210
253
 
211
254
  # Create backup if file exists
212
- if hasattr(self, 'printers_filename') and Path(self.printers_filename).exists():
255
+ if hasattr(self, "printers_filename") and Path(self.printers_filename).exists():
213
256
  backup_dir = xdg.get_config_backups_dir()
214
257
  rec_date = datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
215
258
  backup_path = backup_dir / f"{rec_date}_printer_config.json"
@@ -221,70 +264,68 @@ class zpl:
221
264
 
222
265
  # Save the config
223
266
  json_path.parent.mkdir(parents=True, exist_ok=True)
224
- with open(json_path, 'w') as json_file:
267
+ with open(json_path, "w") as json_file:
225
268
  json.dump(self.printers, json_file, indent=4)
226
269
 
227
270
  self.load_printer_json(str(json_path), relative=False)
228
271
 
229
-
230
- 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):
231
273
  """
232
274
  Loads printer json from a specified file, saves it to the active json.
233
275
  If specified file does not exist, it is created with the base
234
276
  printers json
235
-
277
+
236
278
  json_file = path to file
237
279
  """
238
280
  if relative:
239
281
  json_file = f"{str(files('zebra_day'))}/{json_file}"
240
282
  else:
241
283
  pass
242
-
284
+
243
285
  _log.debug("Loading printer config from: %s", json_file)
244
286
 
245
287
  if not os.path.exists(json_file):
246
- raise Exception(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}""")
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
+ )
247
291
  fh = open(json_file)
248
292
  self.printers_filename = json_file
249
293
  self.printers = json.load(fh)
250
294
  # self.save_printer_json() <--- use the save_printer_json call after calling this. Else, recursion.
251
-
252
295
 
253
296
  def create_new_printers_json_with_single_test_printer(self, fn=None):
254
297
  """
255
298
  Create a new printers json with just the png printer defined
256
299
  """
257
300
 
258
-
259
301
  if fn in [None]:
260
- fn = str(files('zebra_day'))+"/etc/printer_config.json"
261
-
262
- if not hasattr(self, 'printers'):
302
+ fn = str(files("zebra_day")) + "/etc/printer_config.json"
303
+
304
+ if not hasattr(self, "printers"):
263
305
  self.printers = {}
264
306
  self.printers_filename = fn
265
307
 
266
308
  jdat = None
267
- with open(f"{str(files('zebra_day'))}/etc/printer_config.template.json", 'r') as file:
309
+ with open(f"{str(files('zebra_day'))}/etc/printer_config.template.json") as file:
268
310
  jdat = json.load(file)
269
-
311
+
270
312
  self.printers = jdat
271
-
272
- self.save_printer_json(fn, relative=False)
273
313
 
314
+ self.save_printer_json(fn, relative=False)
274
315
 
275
316
  def clear_printers_json(self, json_file: str = "/etc/printer_config.json") -> None:
276
317
  """
277
- Reset printers JSON to empty minimal structure.
318
+ Reset printers JSON to empty minimal v2.0.0 structure.
278
319
 
279
320
  Args:
280
321
  json_file: Path to the config file (relative to package)
281
322
  """
282
- json_path = Path(str(files('zebra_day'))) / json_file.lstrip('/')
323
+ json_path = Path(str(files("zebra_day"))) / json_file.lstrip("/")
283
324
 
284
- # Write empty config using pathlib
285
- empty_config = {"labs": {}}
325
+ # Write empty config with v2 schema
326
+ empty_config = {"schema_version": "2.0.0", "labs": {}}
286
327
  json_path.parent.mkdir(parents=True, exist_ok=True)
287
- with open(json_path, 'w') as f:
328
+ with open(json_path, "w") as f:
288
329
  json.dump(empty_config, f, indent=4)
289
330
 
290
331
  self.printers_filename = str(json_path)
@@ -298,7 +339,7 @@ class zpl:
298
339
 
299
340
  Copies the template JSON to the active config location.
300
341
  """
301
- pkg_path = Path(str(files('zebra_day')))
342
+ pkg_path = Path(str(files("zebra_day")))
302
343
  template_path = pkg_path / "etc" / "printer_config.template.json"
303
344
  target_path = pkg_path / "etc" / "printer_config.json"
304
345
 
@@ -311,33 +352,42 @@ class zpl:
311
352
 
312
353
  self.save_printer_json(self.printers_filename, relative=False)
313
354
 
314
-
315
-
316
- def get_valid_label_styles_for_lab(self,lab=None):
355
+ def get_valid_label_styles_for_lab(self, lab=None):
317
356
  """
318
- The intention for this method was to confirm a template
319
- being requested for use in printing to some printer
320
- was 'allowed' by checking with that printers printer json
321
- for the array of valid templates.
357
+ Get all unique label styles available for printers in a lab.
322
358
 
323
- This was a huge PITA in testing, could be re-enabled at some point
359
+ The intention for this method was to confirm a template
360
+ being requested for use in printing to some printer
361
+ was 'allowed' by checking with that printers printer json
362
+ for the array of valid templates.
324
363
 
364
+ This was a huge PITA in testing, could be re-enabled at some point.
325
365
  It is used once, but prints a warning only.
326
366
  """
327
-
328
367
  unique_labels = set()
329
368
 
330
- for printer in self.printers['labs'][lab]:
331
- for style in self.printers['labs'][lab][printer]['label_zpl_styles']:
369
+ # Access printers via nested 'printers' key (v2 schema)
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", []):
332
373
  unique_labels.add(style)
333
374
 
334
375
  result = list(unique_labels)
335
376
  return result
336
377
 
337
-
338
378
  # Given these inputs, format them in to the specified zpl template and
339
379
  # prepare a string to send to a printer
340
- def formulate_zpl(self,uid_barcode=None, alt_a=None, alt_b=None, alt_c=None, alt_d=None, alt_e=None, alt_f=None, label_zpl_style=None):
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
+ ):
341
391
  """
342
392
  Produce a ZPL string using the specified zpl template file, and
343
393
  formatting in the values, where appropriate.
@@ -350,21 +400,30 @@ class zpl:
350
400
  the zpl templates. They may be used in any way. uid_barcode
351
401
  just differntiates one.
352
402
  """
353
-
354
- zpl_file = str(files('zebra_day'))+f"/etc/label_styles/{label_zpl_style}.zpl"
403
+
404
+ zpl_file = str(files("zebra_day")) + f"/etc/label_styles/{label_zpl_style}.zpl"
355
405
  if not os.path.exists(zpl_file):
356
- zpl_file = str(files('zebra_day'))+f"/etc/label_styles/tmps/{label_zpl_style}.zpl"
406
+ zpl_file = str(files("zebra_day")) + f"/etc/label_styles/tmps/{label_zpl_style}.zpl"
357
407
  if not os.path.exists(zpl_file):
358
- raise Exception(f"ZPL File : {zpl_file} does not exist in the TOPLEVEL or TMPS zebra_day/etc/label_styles dir.")
408
+ raise Exception(
409
+ f"ZPL File : {zpl_file} does not exist in the TOPLEVEL or TMPS zebra_day/etc/label_styles dir."
410
+ )
359
411
 
360
- with open(zpl_file, 'r') as file:
412
+ with open(zpl_file) as file:
361
413
  content = file.read()
362
- zpl_string = content.format(uid_barcode=uid_barcode, alt_a=alt_a, alt_b=alt_b, alt_c=alt_c, alt_d=alt_d, alt_e=alt_e, alt_f=alt_f, label_zpl_style=label_zpl_style)
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
+ )
363
424
 
364
425
  return zpl_string
365
426
 
366
-
367
-
368
427
  def generate_label_png(self, zpl_string=None, png_fn=None, relative=False):
369
428
  """
370
429
  Generate a PNG image from ZPL string using local renderer.
@@ -384,10 +443,10 @@ class zpl:
384
443
  from zebra_day.zpl_renderer import render_zpl_to_png
385
444
 
386
445
  if relative:
387
- png_fn = str(files('zebra_day')) + '/' + png_fn
446
+ png_fn = str(files("zebra_day")) + "/" + png_fn
388
447
 
389
448
  if zpl_string is None or png_fn is None:
390
- raise ValueError('ERROR: zpl_string and png_fn may not be None.')
449
+ raise ValueError("ERROR: zpl_string and png_fn may not be None.")
391
450
 
392
451
  try:
393
452
  result = render_zpl_to_png(zpl_string, png_fn)
@@ -396,90 +455,110 @@ class zpl:
396
455
  except Exception as e:
397
456
  _log.error("Failed to convert ZPL to image: %s", e)
398
457
  raise
399
-
400
458
 
401
- def print_raw_zpl(self,zpl_content,printer_ip, port=9100):
459
+ def print_raw_zpl(self, zpl_content, printer_ip, port=9100):
402
460
  """
403
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.
404
462
  """
405
463
  send_zpl_code(zpl_content, printer_ip, printer_port=port)
406
464
 
407
-
408
-
409
-
410
- def print_zpl(self, lab=None, printer_name=None, uid_barcode='', alt_a='', alt_b='', alt_c='', alt_d='', alt_e='', alt_f='', label_zpl_style=None, client_ip='pkg', print_n=1, zpl_content=None):
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
+ ):
411
481
  """
412
482
  The main print method. Accepts info to determine the desired
413
- printer IP and to request the desired ZPL string to be sent
414
- to the printer.
415
-
416
- lab = top level key in self.printers['labs']
417
- printer_name = key for printer info (ie: ip_address) needed
418
- to satisfy print requests.
419
- label_zpl_style = template code, see above for addl deets
420
- client_ip = optional, this is logged with print request info
421
- print_n = integer, > 0
422
- zpl_content = DO NOT USE -- hacky way to directly pass a zpl
423
- string to a printer. to do: write a cleaner
424
- string+ip method of printing.
425
- """
483
+ printer IP and to request the desired ZPL string to be sent
484
+ to the printer.
426
485
 
486
+ Args:
487
+ lab: top level key in self.printers['labs']
488
+ printer_name: key for printer info (ie: ip_address) needed
489
+ to satisfy print requests.
490
+ label_zpl_style: template code, see above for addl deets
491
+ client_ip: optional, this is logged with print request info
492
+ print_n: integer, > 0
493
+ zpl_content: DO NOT USE -- hacky way to directly pass a zpl
494
+ string to a printer. to do: write a cleaner
495
+ string+ip method of printing.
496
+ """
427
497
  if print_n < 1:
428
498
  raise Exception(f"\n\nprint_n < 1 , specified {print_n}")
429
499
 
430
- rec_date = str(datetime.datetime.now()).replace(' ','_')
431
500
  print_n = int(print_n)
432
501
 
433
- if printer_name in ['','None',None] and lab in [None,'','None']:
434
- raise Exception(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}")
435
-
436
- if label_zpl_style in [None,'','None']:
437
- label_zpl_style = self.printers['labs'][lab][printer_name]['label_zpl_styles'][0] # If a style is not specified, assume the first
438
- elif label_zpl_style not in self.printers['labs'][lab][printer_name]['label_zpl_styles']:
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
+ )
506
+
507
+ # Access printer via nested 'printers' key (v2 schema)
508
+ printer_data = self.printers["labs"][lab]["printers"][printer_name]
509
+
510
+ if label_zpl_style in [None, "", "None"]:
511
+ # Use default_label_style if set, otherwise fall back to first in list
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"]:
439
516
  _log.warning(
440
517
  "ZPL style '%s' is not valid for %s/%s. Valid styles: %s",
441
- label_zpl_style, lab, printer_name,
442
- self.printers['labs'][lab][printer_name]['label_zpl_styles']
518
+ label_zpl_style,
519
+ lab,
520
+ printer_name,
521
+ printer_data["label_zpl_styles"],
443
522
  )
444
523
 
445
- printer_ip = self.printers['labs'][lab][printer_name]["ip_address"]
524
+ printer_ip = printer_data["ip_address"]
446
525
 
447
- zpl_string = ''
526
+ zpl_string = ""
448
527
  if zpl_content in [None]:
449
- zpl_string = self.formulate_zpl(uid_barcode=uid_barcode, alt_a=alt_a, alt_b=alt_b, alt_c=alt_c, alt_d=alt_d, alt_e=alt_e, alt_f=alt_f, label_zpl_style=label_zpl_style)
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
+ )
450
538
  else:
451
539
  zpl_string = zpl_content
452
540
 
453
541
  # Log print request to file (using pathlib, not shell)
454
542
  log_file = xdg.get_logs_dir() / "print_requests.log"
455
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"
456
- with open(log_file, 'a') as f:
544
+ with open(log_file, "a") as f:
457
545
  f.write(log_entry)
458
546
 
459
- ret_s = None
460
- if printer_ip in ['dl_png']:
461
- png_fn = str(xdg.get_generated_files_dir() / f"zpl_label_{label_zpl_style}_{rec_date}.png")
462
- 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)
463
550
 
464
- else:
465
- pn = 1
466
- while pn <= print_n:
467
- send_zpl_code(zpl_string, printer_ip)
468
- pn += 1
469
-
470
- ret_s = zpl_string
471
-
472
- return ret_s
551
+ return zpl_string
473
552
 
474
553
 
475
554
  def _get_local_ip() -> str:
476
555
  """Get the local IP address of this machine."""
477
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"""
478
557
  result = subprocess.run(ipcmd, shell=True, capture_output=True, text=True)
479
- return result.stdout.strip().split('\n')[0] if result.stdout.strip() else "127.0.0.1"
558
+ return result.stdout.strip().split("\n")[0] if result.stdout.strip() else "127.0.0.1"
480
559
 
481
560
 
482
- def _parse_auth_args() -> str:
561
+ def _parse_auth_args() -> Literal["none", "cognito"]:
483
562
  """Parse --auth CLI argument.
484
563
 
485
564
  Returns:
@@ -496,7 +575,8 @@ def _parse_auth_args() -> str:
496
575
  help="Authentication mode: 'none' (public, default) or 'cognito' (AWS Cognito)",
497
576
  )
498
577
  args, _ = parser.parse_known_args()
499
- return args.auth
578
+ auth_mode: Literal["none", "cognito"] = args.auth
579
+ return auth_mode
500
580
 
501
581
 
502
582
  def zday_start() -> None:
@@ -515,14 +595,13 @@ def zday_start() -> None:
515
595
  zday_start --auth cognito # Enable Cognito authentication
516
596
  """
517
597
  import warnings
598
+
518
599
  warnings.warn(
519
600
  "zday_start is deprecated. Use 'zday gui start' instead.",
520
601
  DeprecationWarning,
521
602
  stacklevel=2,
522
603
  )
523
- _log.warning(
524
- "DEPRECATED: zday_start is deprecated. Use 'zday gui start' instead."
525
- )
604
+ _log.warning("DEPRECATED: zday_start is deprecated. Use 'zday gui start' instead.")
526
605
 
527
606
  from zebra_day.web.app import run_server
528
607
 
@@ -550,6 +629,7 @@ def main() -> None:
550
629
  zday_quickstart --auth cognito # Enable Cognito authentication
551
630
  """
552
631
  import warnings
632
+
553
633
  warnings.warn(
554
634
  "zday_quickstart is deprecated. Use 'zday bootstrap' then 'zday gui start' instead.",
555
635
  DeprecationWarning,
@@ -565,7 +645,7 @@ def main() -> None:
565
645
  auth_mode = _parse_auth_args()
566
646
 
567
647
  ip = _get_local_ip()
568
- ip_root = ".".join(ip.split('.')[:-1])
648
+ ip_root = ".".join(ip.split(".")[:-1])
569
649
 
570
650
  _log.info("IP detected: %s ... using IP root: %s", ip, ip_root)
571
651
  _log.info("Scanning for zebra printers on this network (may take a few minutes)...")
@@ -576,8 +656,7 @@ def main() -> None:
576
656
 
577
657
  _log.info("Zebra Printer Scan Complete. Results: %s", zp.printers)
578
658
  _log.info(
579
- "Starting zebra_day web GUI at %s:8118 (auth=%s). "
580
- "Press Ctrl+C to shut down.",
659
+ "Starting zebra_day web GUI at %s:8118 (auth=%s). Press Ctrl+C to shut down.",
581
660
  ip,
582
661
  auth_mode,
583
662
  )
@@ -586,10 +665,7 @@ def main() -> None:
586
665
  run_server(host="0.0.0.0", port=8118, reload=False, auth=auth_mode)
587
666
 
588
667
  _log.info("EXITING ZDAY QUICKSTART")
589
- _log.info(
590
- "If the web GUI did not run, check if a service is already running at %s:8118",
591
- ip
592
- )
668
+ _log.info("If the web GUI did not run, check if a service is already running at %s:8118", ip)
593
669
 
594
670
 
595
671
  if __name__ == "__main__":
@@ -604,5 +680,5 @@ if __name__ == "__zday_start__":
604
680
  """
605
681
  entry point for zday_start
606
682
  """
607
-
683
+
608
684
  zday_start()