mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-11-11 00:27:01 +01:00
`requires-python` and the CI pin already said 3.14, but ruff, mypy and pylint were still grading `meta/` against 3.10 -- three tools judging an interpreter nothing runs any more, and ruff withholding the pyupgrade fixes that only become safe at the newer target. `pin_ci_python.py` rewrites these three knobs. What the stale target was hiding: `datetime.timezone.utc` where `datetime.UTC` is now correct (UP017), and ruff's stable formatter applying PEP 758 -- `except (A, B):` becomes `except A, B:`. That makes the source 3.14-only at the syntax level, which is what `requires-python = ">=3.14"` already promised; kuhy chose it deliberately over holding ruff a release back (2026-08-29). Also drops a comment in `wsg_grabber/models.py` that the bump makes false: it explained why the file used ``str, Enum`` instead of ``StrEnum`` under mypy at 3.10, sitting directly above a ``class Verdict(StrEnum)`` that had already stopped obeying it. ruff, ruff-format, mypy --strict and pylint all clean at the new target. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SfnJmxxsZ534AKk16rC2Gr
232 lines
8.0 KiB
Python
232 lines
8.0 KiB
Python
"""Constants, status codes, and lookup tables for Brother printer checking."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
|
|
# ── Colors ───────────────────────────────────────────────────────────
|
|
|
|
RED = "\033[0;31m"
|
|
YELLOW = "\033[1;33m"
|
|
GREEN = "\033[0;32m"
|
|
CYAN = "\033[0;36m"
|
|
BOLD = "\033[1m"
|
|
DIM = "\033[2m"
|
|
RESET = "\033[0m"
|
|
|
|
# ── SNMP supply level sentinel values ────────────────────────────────────────
|
|
|
|
SNMP_LEVEL_OK = -3
|
|
SNMP_LEVEL_LOW = -2
|
|
SUPPLY_LOW_PCT = 10
|
|
SUPPLY_WARN_PCT = 25
|
|
PROGRESS_BAR_WIDTH = 25
|
|
|
|
# Brother HL-1110 consumable page ratings
|
|
TONER_RATED_PAGES = 1000
|
|
DRUM_RATED_PAGES = 10000
|
|
CUPS_PAGE_LOG_PATH = "/var/log/cups/page_log"
|
|
CONSUMABLE_STATE_DIR = ".config/brother_printer"
|
|
MIN_LPSTAT_JOB_PARTS = 4
|
|
|
|
# How many pages CUPS may claim over the printer's own counter before we call
|
|
# it dropping pages. A small gap is just timing (a job finishing as we look);
|
|
# a large one means the printer is discarding work and saying nothing.
|
|
PAGE_DROP_WARN_THRESHOLD = 5
|
|
|
|
BROTHER_USB_VENDOR_ID = 0x04F9
|
|
|
|
|
|
def _out(text: str = "") -> None:
|
|
"""Write a line to stdout."""
|
|
sys.stdout.write(text + "\n")
|
|
|
|
|
|
def _prompt(text: str) -> str:
|
|
"""Read user input with a prompt."""
|
|
sys.stdout.write(text)
|
|
sys.stdout.flush()
|
|
return sys.stdin.readline().strip()
|
|
|
|
|
|
# ── PJL status codes ─────────────────────────────────────────────────
|
|
# Transcribed from the HP PJL Technical Reference Manual, Appendix D
|
|
# ("PJL Status Codes"). Brother publishes no PJL status table of its own,
|
|
# but an HL-1110 was probed on real hardware (2026-07-17) and matched the HP
|
|
# codes for every state that could be induced:
|
|
#
|
|
# 10001 READY 10003 WARMING UP 10023 PRINTING
|
|
# 40000 SLEEP 40021 TOP COVER OPEN 41213 NO PAPER
|
|
#
|
|
# Only codes documented in that reference appear below. Anything else is
|
|
# reported as unknown by get_status_info() rather than guessed at: a previous
|
|
# revision of this table invented meanings (40000 "Paper Jam", 30010 "Toner
|
|
# Low", 41000 "Cover Open" ...) and consequently reported a sleeping printer
|
|
# as a critical paper jam, while a genuine toner-low warning (10006) was
|
|
# rendered as a benign "Processing".
|
|
#
|
|
# Format: code -> (severity, short_text, action)
|
|
# Severities: ok, info, warn, critical
|
|
|
|
_ORDER_TONER = "Order replacement toner cartridge (TN-1050/TN-1030 compatible)."
|
|
|
|
BROTHER_STATUS_CODES: dict[int, tuple[str, str, str]] = {
|
|
# Informational (10xxx)
|
|
10001: ("ok", "Ready", ""),
|
|
10002: ("info", "Ready (offline)", "Press the power button to bring it online."),
|
|
10003: ("ok", "Warming up", ""),
|
|
10004: ("info", "Self test", ""),
|
|
10005: ("info", "Resetting / clearing memory", ""),
|
|
10006: ("warn", "Toner Low", _ORDER_TONER),
|
|
10007: ("info", "Canceling job", ""),
|
|
10014: ("info", "Printing", ""),
|
|
10023: ("info", "Printing / processing job", ""),
|
|
# Auto-continuable conditions (30xxx)
|
|
30010: ("info", "Status buffer overflow", ""),
|
|
# Potential operator intervention (35xxx)
|
|
35078: ("ok", "Power save", ""),
|
|
# Operator intervention required (40xxx). 40000 is flagged in the
|
|
# reference as "not an error - the printer is waiting for data".
|
|
40000: ("ok", "Sleep (standby)", ""),
|
|
40010: (
|
|
"critical",
|
|
"No Toner Cartridge",
|
|
"Install a toner cartridge (TN-1050/TN-1030 compatible).",
|
|
),
|
|
40019: (
|
|
"warn",
|
|
"Remove Paper From Output Bin",
|
|
"Take the printed pages out of the output bin.",
|
|
),
|
|
40021: (
|
|
"critical",
|
|
"Cover Open",
|
|
"Close the printer cover and check the toner cartridge is seated.",
|
|
),
|
|
40022: ("critical", "Paper Jam", "Clear the paper jam and close all covers."),
|
|
40026: ("critical", "Install Paper Tray", "Insert the paper tray."),
|
|
40038: ("warn", "Toner Low", _ORDER_TONER),
|
|
40050: (
|
|
"critical",
|
|
"Fuser Error (50 SERVICE)",
|
|
"Power-cycle the printer. If the error persists, contact service.",
|
|
),
|
|
40079: (
|
|
"warn",
|
|
"Offline",
|
|
"The printer is offline. Press the power button to bring it online.",
|
|
),
|
|
}
|
|
|
|
# Code families that encode a sub-status in their trailing digits. The
|
|
# HL-1110 reports 41213 for an empty tray, which places it in the reference's
|
|
# "Foreground Paper Loading (41xyy)" family - but its x/yy digits do not
|
|
# decode per the HP tray/media tables (x=2 "upper cassette", yy=13 "Japan B5"
|
|
# on a single-tray A4 printer). So match the family and ignore the digits.
|
|
# Each entry is an inclusive low/high bound plus the usual status triple.
|
|
|
|
_STATUS_CODE_RANGES: tuple[tuple[int, int, tuple[str, str, str]], ...] = (
|
|
(
|
|
41000,
|
|
41999,
|
|
("critical", "Out of Paper", "Load paper into the paper tray."),
|
|
),
|
|
(
|
|
50000,
|
|
50999,
|
|
(
|
|
"critical",
|
|
"Hardware Error",
|
|
"Power-cycle the printer. If the error persists, contact service.",
|
|
),
|
|
),
|
|
)
|
|
|
|
# ── CUPS status code mappings ────────────────────────────────────────
|
|
|
|
# These translate what CUPS reports into the PJL codes above, for the
|
|
# fallback path used when the usblp device is unavailable and the printer
|
|
# cannot be asked directly. Every value must name a real code from
|
|
# BROTHER_STATUS_CODES (or a range family), otherwise the fallback silently
|
|
# degrades to "unknown status".
|
|
|
|
_CUPS_REASONS_TO_STATUS: dict[str, int] = {
|
|
"paused": 10002,
|
|
"moving-to-paused": 10002,
|
|
"toner-low": 40038,
|
|
"toner-empty": 40010,
|
|
"marker-supply-low": 40038,
|
|
"marker-supply-empty": 40010,
|
|
"media-empty": 41000,
|
|
"media-needed": 41000,
|
|
"media-jam": 40022,
|
|
"cover-open": 40021,
|
|
"door-open": 40021,
|
|
"input-tray-missing": 40026,
|
|
}
|
|
|
|
_CUPS_STATE_TO_STATUS: dict[str, int] = {
|
|
"idle": 10001,
|
|
"processing": 10023,
|
|
"stopped": 40079,
|
|
}
|
|
|
|
_ERROR_REASON_MAP: tuple[tuple[tuple[str, ...], str, str], ...] = (
|
|
(("media-jam",), "40022", "Paper Jam"),
|
|
(("cover-open", "door-open"), "40021", "Cover Open"),
|
|
(("toner-empty",), "40010", "No Toner Cartridge"),
|
|
(("toner-low",), "40038", "Toner Low"),
|
|
)
|
|
|
|
|
|
# States this tool infers itself, rather than reads off the printer: from the
|
|
# CUPS page-count estimate, or from an error bit whose cause we cannot name.
|
|
# They deliberately are not PJL numbers - borrowing a PJL code for a state the
|
|
# printer never reported is how the old table came to claim a sleeping printer
|
|
# had jammed.
|
|
|
|
DERIVED_TONER_END = "estimate:toner-end"
|
|
DERIVED_TONER_LOW = "estimate:toner-low"
|
|
DERIVED_CUPS_ERROR = "cups:error"
|
|
|
|
_DERIVED_STATUS_CODES: dict[str, tuple[str, str, str]] = {
|
|
DERIVED_TONER_END: (
|
|
"critical",
|
|
"Toner End (estimated from page count)",
|
|
_ORDER_TONER,
|
|
),
|
|
DERIVED_TONER_LOW: ("warn", "Toner Low (estimated from page count)", _ORDER_TONER),
|
|
DERIVED_CUPS_ERROR: (
|
|
"warn",
|
|
"Printer reports an error",
|
|
"Check the printer display for details.",
|
|
),
|
|
}
|
|
|
|
|
|
def get_status_info(code: str) -> tuple[str, str, str]:
|
|
"""Look up a PJL status code. Returns (severity, text, action)."""
|
|
derived = _DERIVED_STATUS_CODES.get(code)
|
|
if derived is not None:
|
|
return derived
|
|
try:
|
|
numeric = int(code)
|
|
except TypeError, ValueError:
|
|
return _unknown_status(code)
|
|
exact = BROTHER_STATUS_CODES.get(numeric)
|
|
if exact is not None:
|
|
return exact
|
|
for low, high, info in _STATUS_CODE_RANGES:
|
|
if low <= numeric <= high:
|
|
return info
|
|
return _unknown_status(code)
|
|
|
|
|
|
def _unknown_status(code: str) -> tuple[str, str, str]:
|
|
"""Describe a code that is not in the PJL reference. Never guesses."""
|
|
return (
|
|
"info",
|
|
f"Unknown status (code {code})",
|
|
"Check printer display for details.",
|
|
)
|