44from __future__ import annotations
55
66import argparse
7+ import hashlib
78import io
89import json
910import pathlib
2021 from .tool_paths import DEFAULT_DEVICES_CSV_PATH , DEFAULT_LABEL_HTML_PATH
2122
2223LABEL_BORDER_WIDTH_MM = 0.35
24+ DEFAULT_LABEL_WIDTH_MM = 10.0
25+ DEFAULT_LABEL_HEIGHT_MM = 15.0
26+ LABEL_BUILD_FINGERPRINT_PREFIX = "label-build-fingerprint:"
2327
2428
2529HTML_TEMPLATE = """<!doctype html>
2832 <meta charset="utf-8">
2933 <meta name="viewport" content="width=device-width, initial-scale=1">
3034 <title>Matter Labels</title>
35+ <!-- {build_fingerprint_comment} -->
3136 <style>
3237 :root {{
3338 --page-pad: {page_pad_mm}mm;
@@ -553,13 +558,13 @@ def parse_args() -> argparse.Namespace:
553558 parser .add_argument (
554559 "--label-width-mm" ,
555560 type = float ,
556- default = 10.0 ,
561+ default = DEFAULT_LABEL_WIDTH_MM ,
557562 help = "Printed label width in millimeters. Max 10 mm." ,
558563 )
559564 parser .add_argument (
560565 "--label-height-mm" ,
561566 type = float ,
562- default = 15.0 ,
567+ default = DEFAULT_LABEL_HEIGHT_MM ,
563568 help = "Printed label height in millimeters. Max 15 mm." ,
564569 )
565570 return parser .parse_args ()
@@ -579,9 +584,27 @@ class LayoutMetrics:
579584def build_qr_svg_markup (payload : str ) -> str :
580585 segno_module = verify_segno_dependency (sys .executable )
581586 qr = segno_module .make (payload )
582- output = io .StringIO ()
587+ output = io .BytesIO ()
583588 qr .save (output , kind = "svg" , scale = 1 , border = 0 , xmldecl = False )
584- return output .getvalue ()
589+ return output .getvalue ().decode ("utf-8" )
590+
591+
592+ def build_label_html_fingerprint (
593+ label_rows : list [dict [str , str ]],
594+ * ,
595+ label_width_mm : float ,
596+ label_height_mm : float ,
597+ ) -> str :
598+ payload = json .dumps (
599+ {
600+ "label_rows" : label_rows ,
601+ "label_width_mm" : label_width_mm ,
602+ "label_height_mm" : label_height_mm ,
603+ },
604+ sort_keys = True ,
605+ separators = ("," , ":" ),
606+ )
607+ return hashlib .sha256 (payload .encode ("utf-8" )).hexdigest ()
585608
586609
587610def compute_layout_metrics (label_width_mm : float , label_height_mm : float ) -> LayoutMetrics :
@@ -622,12 +645,18 @@ def main() -> int:
622645 rows = load_device_rows (devices_csv )
623646 selected_rows = filter_rows_by_serial (rows , args .serial )
624647 label_rows = build_label_rows (selected_rows , include_passcode = False )
648+ build_fingerprint = build_label_html_fingerprint (
649+ label_rows ,
650+ label_width_mm = args .label_width_mm ,
651+ label_height_mm = args .label_height_mm ,
652+ )
625653 for label_row in label_rows :
626654 label_row ["qr_svg" ] = build_qr_svg_markup (label_row ["qrcode" ])
627655 layout = compute_layout_metrics (args .label_width_mm , args .label_height_mm )
628656 json_payload = json .dumps (label_rows ).replace ("</" , "<\\ /" )
629657
630658 html_output = HTML_TEMPLATE .format (
659+ build_fingerprint_comment = f"{ LABEL_BUILD_FINGERPRINT_PREFIX } { build_fingerprint } " ,
631660 json_payload = json_payload ,
632661 label_width_mm = args .label_width_mm ,
633662 label_height_mm = args .label_height_mm ,
0 commit comments