Skip to content

Instantly share code, notes, and snippets.

@jameskraus
Created March 10, 2026 05:14
Show Gist options
  • Select an option

  • Save jameskraus/26759734c92d7afdc4a78927ed3d2641 to your computer and use it in GitHub Desktop.

Select an option

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
#!/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