Created
March 10, 2026 05:14
-
-
Save jameskraus/26759734c92d7afdc4a78927ed3d2641 to your computer and use it in GitHub Desktop.
Minimal script for detecting the status of Brother QL-820NWB, including paper text color
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| """qlpy requires snmpget from net-snmp. | |
| Usage: python3 qlpy.py [-v v1|v2c] [-c public] [-t 2] [-r 1] host | |
| """ | |
| import argparse | |
| import json | |
| import subprocess | |
| OID = "1.3.6.1.4.1.2435.3.3.9.1.6.1.0" | |
| CONTINUOUS = {12: "L12mm", 29: "L29mm", 38: "L38mm", 50: "L50mm", 54: "L54mm", 62: "L62mm"} | |
| DIECUT = { | |
| (17, 54): "L17mmx54mm", (17, 87): "L17mmx87mm", (23, 23): "L23mmx23mm", (29, 42): "L29mmx42mm", | |
| (29, 90): "L29mmx90mm", (38, 90): "L38mmx90mm", (39, 48): "L39mmx48mm", (52, 29): "L52mmx29mm", | |
| (54, 29): "L54mmx29mm", (60, 86): "L60mmx86mm", (62, 29): "L62mmx29mm", (62, 100): "L62mmx100mm", | |
| (12, 12): "L12mmDia", (24, 24): "L24mmDia", (58, 58): "L58mmDia", | |
| } | |
| MEDIA_TYPES = {0x4A: "Continuous", 0x0A: "Continuous", 0x4B: "DieCut", 0x0B: "DieCut"} | |
| NOTIFICATIONS = {0x00: "NotAvailable", 0x03: "CoolingStarted", 0x04: "CoolingFinished"} | |
| TEXT_COLORS = {0x01: "Black", 0x81: "RedAndBlack"} | |
| def emit(obj, code=0): | |
| print(json.dumps(obj, separators=(",", ":"))) | |
| raise SystemExit(code) | |
| def main(): | |
| p = argparse.ArgumentParser(prog="qlpy") | |
| p.add_argument("host") | |
| p.add_argument("-v", "--version", choices=("v1", "v2c"), default="v1") | |
| p.add_argument("-c", "--community", default="public") | |
| p.add_argument("-t", "--timeout", default="2") | |
| p.add_argument("-r", "--retries", default="1") | |
| a = p.parse_args() | |
| try: | |
| cp = subprocess.run( | |
| ["snmpget", "-Oqv", f"-{a.version}", "-c", a.community, "-t", a.timeout, "-r", a.retries, a.host, OID], | |
| text=True, | |
| capture_output=True, | |
| ) | |
| except OSError as e: | |
| emit({"query_status": "protocol_error", "detail": str(e)}, 1) | |
| text = (cp.stdout or cp.stderr).strip() | |
| if cp.returncode: | |
| emit({"query_status": "timeout" if "Timeout:" in text else "protocol_error", "detail": text}, 1) | |
| try: | |
| payload = bytes.fromhex(text.replace('"', " ")) | |
| except ValueError as e: | |
| emit({"query_status": "protocol_error", "detail": str(e)}, 1) | |
| if len(payload) != 32: | |
| emit({"query_status": "protocol_error", "detail": f"expected 32-byte payload, got {len(payload)}"}, 1) | |
| if payload[:3] != b"\x80\x20\x42": | |
| emit({"query_status": "invalid_status_payload", "detail": "payload prefix must equal 802042"}, 1) | |
| present = payload[11] != 0 and not (payload[8] & 0x01) | |
| media_type = "NoMedia" if not present else MEDIA_TYPES.get(payload[11]) | |
| media_size = None if not present else ( | |
| CONTINUOUS.get(payload[10]) if media_type == "Continuous" else | |
| DIECUT.get((payload[10], payload[17])) if media_type == "DieCut" else | |
| None | |
| ) | |
| emit({ | |
| "query_status": "ok", | |
| "media_size": media_size, | |
| "media_type": media_type, | |
| "notification": NOTIFICATIONS.get(payload[22]), | |
| "paper_background_color": None if not present else ("Standard" if payload[24] == 0x00 else None), | |
| "paper_text_color": None if not present else TEXT_COLORS.get(payload[25]), | |
| }) | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment