epson_print_conf/epson_print_conf.py

2779 lines
112 KiB
Python
Raw Normal View History

2023-07-25 05:49:47 -04:00
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
2023-07-24 08:58:19 -04:00
"""
2023-08-01 13:14:09 -04:00
Epson Printer Configuration via SNMP (TCP/IP)
2023-07-24 08:58:19 -04:00
"""
import itertools
import re
2023-08-06 17:10:26 -04:00
from typing import Any, List
2023-07-24 08:58:19 -04:00
import datetime
2023-07-24 16:39:42 -04:00
import time
2023-07-25 21:18:33 -04:00
import textwrap
2023-07-31 07:41:37 -04:00
import ast
2023-08-06 00:51:18 -04:00
import logging
import os
import yaml
from pathlib import Path
# The pysnmp module uses functionality from importlib.util and
# importlib.machinery, which were seperated from the importlib module
# in python>=3.11
try:
import importlib.util
import importlib.machinery
except ImportError:
pass
2023-08-03 18:09:11 -04:00
from pysnmp.hlapi.v1arch import *
2023-08-04 03:47:53 -04:00
from pyasn1.type.univ import OctetString as OctetStringType
2023-08-16 13:18:41 -04:00
from itertools import chain
2023-07-24 08:58:19 -04:00
class EpsonPrinter:
2023-07-25 05:49:47 -04:00
"""SNMP Epson Printer Configuration."""
2023-07-25 21:18:33 -04:00
PRINTER_CONFIG = { # Known Epson models
"L386": {
"read_key": [16, 8],
"write_key": b"Sinabung",
"printer_head_id_h": range(122, 126),
"printer_head_id_f": [129],
"main_waste": {"oids": [24, 25, 30], "divider": 62.07},
"borderless_waste": {"oids": [26, 27, 34], "divider": 24.2},
"raw_waste_reset": {
24: 0, 25: 0, 30: 0, # Data of 1st counter
28: 0, 29: 0, # another store of 1st counter
46: 94, # Maintenance required level of 1st counter
26: 0, 27: 0, 34: 0, # Data of 2nd counter
47: 94, # Maintenance required level of 2nd counter
49: 0 # ?
},
"stats": {
"Manual cleaning counter": [147],
"Timer cleaning counter": [149],
"Power cleaning counter": [148],
"Total print pass counter": [171, 170, 169, 168],
"Total print page counter": [167, 166, 165, 164],
"Total scan counter": [471, 470, 469, 468],
"First TI received time": [173, 172],
"Maintenance required level of 1st waste ink counter": [46],
"Maintenance required level of 2nd waste ink counter": [47],
"Power off timer": [359, 358],
},
"serial_number": range(192, 202),
"last_printer_fatal_errors": [
453, 452, 455, 454, 457, 456, 459, 458, 461, 460, 467,
499, 498, 501, 500, 503, 502, 505, 504, 507, 506
],
},
2023-07-24 08:58:19 -04:00
"XP-205": {
2023-07-27 00:08:10 -04:00
"alias": ["XP-200", "XP-207"],
2023-07-24 08:58:19 -04:00
"read_key": [25, 7],
"printer_head_id_h": range(122, 127),
"printer_head_id_f": [136, 137, 138, 129],
2023-08-07 23:36:21 -04:00
"main_waste": {"oids": [24, 25, 30], "divider": 73.5},
"borderless_waste": {"oids": [26, 27, 34], "divider": 34.34},
"same-as": "XP-315"
2023-07-24 08:58:19 -04:00
},
2024-08-30 06:26:00 -04:00
"ET-4700": {
"read_key": [151, 7],
"write_key": b"Maribaya",
"main_waste": {"oids": [48, 49, 47], "divider": 63.46},
"borderless_waste": {"oids": [50, 51, 47], "divider": 34.16},
"raw_waste_reset": {
48: 0, 49: 0, 47: 0, 52: 0, 53: 0, 54: 94, 50: 0, 51: 0,
55: 94, 28: 0
},
"stats": {
"First TI received time": [9, 8],
"Total print pass counter": [133, 132, 131, 130],
"Total print page counter": [776, 775, 774, 773],
"Total scan counter": [1843, 1842, 1841, 1840],
"Total scan counter % (ADF)": [1855, 1854, 1853, 1852],
"Ink replacement counter - Black": [554],
"Ink replacement counter - Cyan": [555],
"Ink replacement counter - Magenta": [556],
"Ink replacement counter - Yellow": [557],
"Maintenance required level of 1st waste ink counter": [54],
"Maintenance required level of 2nd waste ink counter": [55],
},
"serial_number": range(1604, 1614),
},
"Stylus Photo PX720WD": {
"read_key": [54, 6],
"write_key": b"IhroroQU",
"main_waste": {"divider": 79.23, "oids": [14, 15]},
"borderless_waste": {"divider": 122.84, "oids": [16, 17]},
"raw_waste_reset": {
8: 0, 9: 0, 12: 0, 13: 0, 14: 0, 15: 0, 16: 0, 17: 0,
18: 0, 19: 0
},
"stats": {
"Manual cleaning counter": [126],
"Timer cleaning counter": [97],
"Total print pass counter": [47, 46, 45, 44],
"Total print page counter": [159, 158],
"Total print page counter - duplex": [161, 160],
"Total print CD-R counter": [75, 74],
"Total CD-R tray open/close counter": [163, 162],
"Total scan counter": [477, 476, 475, 474],
},
"ink_replacement_counters": {
"Black": {"1S": 102, "2S": 103, "3S": 98},
"Yellow": {"1S": 112, "2S": 113, "4S": 171},
"Magenta": {"1S": 104, "2S": 105, "4S": 99},
"Cyan": {"1S": 108, "2S": 109, "4S": 101},
"Light Magenta": {"1S": 106, "2S": 107, "3S": 100},
"Light Cyan": {"1S": 110, "2S": 111, "3S": 155},
},
"serial_number": range(231, 241),
"alias": ["TX720WD", "Artisan 720", "PX720WD"],
},
2023-07-25 21:18:33 -04:00
"Stylus Photo PX730WD": {
"alias": ["TX730WD", "PX730WD", "Stylus Photo PX730", "Artisan 730"],
"read_key": [119, 8], # "read_key": [0x8, 0x77], (I'm afraid 0x8, 0x77 is wrong)
2023-07-25 21:18:33 -04:00
"write_key": b'Cattleya',
"main_waste": {"oids": [0xe, 0xf, 60], "divider": 81.82},
"borderless_waste": {"oids": [0x10, 0x11, 60], "divider": 122.88},
"raw_waste_reset": {
8: 0, 9: 0, 12: 0, 13: 0, 14: 0, 15: 0, 16: 0, 17: 0,
18: 0, 19: 0, 60: 0, 61: 94, 62: 94
},
2023-07-25 21:18:33 -04:00
"stats": {
2023-07-26 06:55:29 -04:00
"Manual cleaning counter": [0x7e],
"Timer cleaning counter": [0x61],
"Total print pass counter": [0x2C, 0x2D, 0x2E, 0x2F],
"Total print page counter": [0x9E, 0x9F],
"Total print page counter (duplex)": [0xA0, 0xA1],
"Total print CD-R counter": [0x4A, 0x4B],
"Total print CD-R tray open/close counter": [0xA2, 0xA3],
"Total scan counter": [0x01DA, 0x01DB, 0x01DC, 0x01DD],
"Maintenance required level of 1st waste ink counter": [61],
"Maintenance required level of 2nd waste ink counter": [62],
2023-07-25 21:18:33 -04:00
},
"last_printer_fatal_errors": [0x3B, 0xC0, 0xC1, 0xC2, 0xC3, 0x5C],
"ink_replacement_counters": {
"Black": {"1S": 0x66, "2S": 0x67, "3S": 0x62},
"Yellow": {"1S": 0x70, "2S": 0x71, "4S": 0xAB},
"Magenta": {"1S": 0x68, "2S": 0x69, "4S": 0x63},
"Cyan": {"1S": 0x6C, "2S": 0x6D, "4S": 0x65},
"Light magenta": {"1S": 0x6A, "2S": 0x6B, "3S": 0x64},
"Light cyan": {"1S": 0x6E, "2S": 0x6F, "3S": 0x9B},
2023-07-25 21:18:33 -04:00
},
"serial_number": range(0xE7, 0xF1),
2023-07-25 21:18:33 -04:00
},
"WF-7525": {
"read_key": [101, 0],
"write_key": b'Sasanqua',
2023-08-07 23:36:21 -04:00
"alias": ["WF-7515"],
2024-07-07 18:14:54 -04:00
"main_waste": {"oids": [20, 21, 59], "divider": 196.5},
"borderless_waste": {"oids": [22, 23, 59], "divider": 52.05},
2023-07-25 21:18:33 -04:00
"serial_number": range(192, 202),
2023-07-31 07:41:37 -04:00
"stats": {
"Maintenance required level of 1st waste ink counter": [60],
"Maintenance required level of 2nd waste ink counter": [61],
2024-07-07 18:14:54 -04:00
"Manual cleaning counter": [131],
"Timer cleaning counter": [134],
"Ink replacement cleaning counter": [133],
"Total print pass counter": [159, 158, 157, 156],
"Total print page counter": [147, 146, 145, 144],
"Total scan counter": [471, 470, 469, 468],
"Total scan counter % (ADF)": [475, 474, 473, 472],
2023-07-31 07:41:37 -04:00
},
2024-07-07 18:14:54 -04:00
"ink_replacement_counters": {
"Black": {"1L": 242, "1S": 243, "2S": 244},
"Yellow": {"1S": 248, "2S": 249, "3S": 250},
"Magenta": {"1S": 251, "2S": 252, "3S": 253},
"Cyan": {"1S": 245, "2S": 246, "3S": 247},
},
"raw_waste_reset": {
2023-07-31 07:41:37 -04:00
20: 0, 21: 0, 22: 0, 23: 0, 24: 0, 25: 0, 59: 0, 60: 94, 61: 94
}
2023-07-25 21:18:33 -04:00
},
2023-07-24 19:48:19 -04:00
"L355": {
"read_key": [65, 9],
2024-07-07 18:14:54 -04:00
"write_key": b"Wakatobi",
"main_waste": {"oids": [24, 25, 30], "divider": 65.0},
"raw_waste_reset": {24: 0, 25: 0, 30: 0, 28: 0, 29: 0, 46: 94},
"stats": {
"Manual cleaning counter": [147],
"Timer cleaning counter": [149],
"Total print pass counter": [171, 170, 169, 168],
"Total print page counter - Color": [439, 438, 437, 436],
"Total print page counter - Black": [435, 434, 433, 432],
"Total print page counter - Blank": [443, 442, 441, 440],
"Total print page counter": [167, 166, 165, 164],
"Total scan counter": [471, 470, 469, 468],
"First TI received time": [173, 172],
"Ink replacement counter - Black": [242],
"Ink replacement counter - Yellow": [244],
"Ink replacement counter - Magenta": [245],
"Ink replacement counter - Cyan": [243],
},
"serial_number": range(192, 202),
2023-07-24 19:48:19 -04:00
},
2024-05-18 03:38:34 -04:00
"L366": {
"read_key": [130, 2],
"write_key": b'Gerbera*',
2024-05-18 03:38:34 -04:00
"main_waste": {"oids": [24, 25, 30], "divider": 62.0625},
"borderless_waste": {"oids": [26, 27, 34], "divider": 24.20},
"stats": {
"Maintenance required level of 1st waste ink counter": [46],
"Maintenance required level of 2nd waste ink counter": [47],
2024-07-07 18:14:54 -04:00
"Ink replacement counter - Black": [242],
"Ink replacement counter - Yellow": [243],
"Ink replacement counter - Cyan": [244],
"Ink replacement counter - Magenta": [245],
2024-05-18 03:38:34 -04:00
},
"raw_waste_reset": {
2024-07-07 18:14:54 -04:00
24: 0, 25: 0, 30: 0, 26: 0, 27: 0, 34: 0, 28: 0, 29: 0,
49: 0, 46: 94, 47: 94
},
"serial_number": range(192, 202),
2024-05-18 03:38:34 -04:00
},
2024-07-09 04:33:17 -04:00
"L3060": {
"read_key": [149, 3],
"write_key": b"Maninjau",
"main_waste": {"oids": [24, 25, 30], "divider": 62.07},
"borderless_waste": {"oids": [26, 27, 34], "divider": 24.2},
"raw_waste_reset": {
24: 0, 25: 0, 30: 0, 28: 0, 29: 0,
46: 94, 26: 0, 27: 0, 34: 0, 47: 94
},
"stats": {
"Maintenance required level of 1st waste ink counter": [46],
"Maintenance required level of 2nd waste ink counter": [47],
"Manual cleaning counter": [147],
"Timer cleaning counter": [149],
"Power cleaning counter": [148],
"Total print pass counter": [171, 170, 169, 168],
"Total print page counter - rear feed": [167, 166, 165, 164],
"Total scan counter": [471, 470, 469, 468],
"First TI received time": [173, 172],
"Ink replacement counter - Black": [242],
"Ink replacement counter - Yellow": [244],
"Ink replacement counter - Magenta": [245],
"Ink replacement counter - Cyan": [243],
},
"serial_number": range(192, 202),
},
2023-07-31 10:54:58 -04:00
"L3250": {
"alias": ["L3251", "L3253", "L3255"],
2023-07-31 10:54:58 -04:00
"read_key": [74, 54],
"write_key": b'Maribaya',
2023-08-05 06:05:42 -04:00
"serial_number": range(1604, 1614),
2024-07-07 18:14:54 -04:00
"main_waste": {"oids": [48, 49, 47], "divider": 63.45},
"second_waste": {"oids": [50, 51, 47], "divider": 34.15},
"third_waste": {"oids": [252, 253, 254], "divider": 13},
2023-07-31 10:54:58 -04:00
"raw_waste_reset": {
2024-07-07 18:14:54 -04:00
48: 0, 49: 0, 47: 0, 52: 0, 53: 0, 54: 94, 50: 0, 51: 0,
55: 94, 28: 0, 252: 0, 253: 0, 254: 0, 255: 94
2023-08-05 06:05:42 -04:00
},
"last_printer_fatal_errors": [
289, 288, 291, 290, 293, 292, 295, 294, 297, 296, 1831, 1832,
1833, 1834, 1835, 2037, 2036, 2039, 2038, 2041, 2040, 2043,
2042, 2045, 2044],
2024-07-07 18:14:54 -04:00
"stats": {
"Maintenance required level of 1st waste ink counter": [54],
"Maintenance required level of 2nd waste ink counter": [55],
"Maintenance required level of 3rd waste ink counter": [255],
2024-07-07 18:14:54 -04:00
"Manual cleaning counter": [90],
"Timer cleaning counter": [89],
"Power cleaning counter": [91],
"Total print pass counter": [133, 132, 131, 130],
"Total print page counter": [776, 775, 774, 773],
"Total scan counter": [1843, 1842, 1841, 1840],
},
},
"ET-2400": {
"alias": ["ET-2401", "ET-2403", "ET-2405"],
"read_key": [74, 54],
"write_key": b"Maribaya",
"main_waste": {"oids": [48, 49, 47], "divider": 63.46},
"borderless_waste": {"oids": [50, 51, 47], "divider": 34.16},
"third_waste": {"oids": [252, 253, 254], "divider": 13.0},
"raw_waste_reset": {
48: 0, 49: 0, 47: 0, 52: 0, 53: 0, 54: 94, 50: 0, 51: 0,
55: 94, 28: 0, 252: 0, 253: 0, 254: 0, 255: 94,
},
"stats": {
"Maintenance required level of 1st waste ink counter": [54],
"Maintenance required level of 2nd waste ink counter": [55],
"Maintenance required level of 3rd waste ink counter": [255],
"Manual cleaning counter": [90],
"Timer cleaning counter": [89],
"Power cleaning counter": [91],
"Total print pass counter": [133, 132, 131, 130],
"Total print page counter": [776, 775, 774, 773],
"Total scan counter": [1843, 1842, 1841, 1840],
},
"serial_number": range(1604, 1614),
2023-07-31 10:54:58 -04:00
},
2024-01-30 18:04:00 -05:00
"ET-2600": {
2024-07-07 18:14:54 -04:00
"alias": ["ET-2650", "L395"],
2024-01-30 18:04:00 -05:00
"read_key": [16, 8],
"write_key": b'Sinabung',
2024-07-07 18:14:54 -04:00
"main_waste": {"oids": [24, 25, 30], "divider": 62.06},
"raw_waste_reset": {24: 0, 25: 0, 30: 0, 28: 0, 29: 0, 46: 94},
"stats": {
"Manual cleaning counter": [147],
"Timer cleaning counter": [149],
"Power cleaning counter": [148],
"Total print pass counter": [171, 170, 169, 168],
"Total print page counter": [167, 166, 165, 164],
"Total scan counter": [471, 470, 469, 468],
"First TI received time": [173, 172],
},
"serial_number": range(192, 202),
2024-01-30 18:04:00 -05:00
},
2023-11-11 13:00:53 -05:00
"ET-2720": {
"alias": ["ET-2714", "ET-2721", "ET-2723", "ET-2725"],
2023-11-11 13:00:53 -05:00
"read_key": [151, 7],
2023-11-13 07:14:30 -05:00
"write_key": b'Maribaya',
2024-07-07 18:14:54 -04:00
"main_waste": {"oids": [48, 49, 47], "divider": 63.45},
"borderless_waste": {"oids": [50, 51, 47], "divider": 34.15},
2023-11-13 07:14:30 -05:00
"same-as": "ET-2700"
2023-11-11 13:00:53 -05:00
},
2024-07-06 20:45:00 -04:00
"ET-2750": {
2024-07-07 18:14:54 -04:00
"serial_number": range(1604, 1614),
2024-07-06 20:45:00 -04:00
"alias": ["ET-2751", "ET-2756"],
"read_key": [73, 8],
"write_key": b"Arantifo",
"main_waste": {"oids": [48, 49, 47], "divider": 109.13},
"borderless_waste": {"oids": [50, 51, 47], "divider": 16.31},
"raw_waste_reset": {
48: 0, 49: 0, 47: 0, 52: 0, 53: 0,
54: 94,
50: 0, 51: 0,
55: 94,
28: 0
},
"stats": {
"First TI received time": [9, 8],
"Total print pass counter": [133, 132, 131, 130],
"Total print page counter - rear feed": [755, 754, 753, 752],
"Total scan counter": [1843, 1842, 1841, 1840],
"Ink replacement counter - Black": [554],
"Ink replacement counter - Cyan": [555],
"Ink replacement counter - Magenta": [556],
"Ink replacement counter - Yellow": [557],
"Maintenance required level of 1st waste ink counter": [54],
"Maintenance required level of 2nd waste ink counter": [55],
},
},
2024-08-06 01:13:49 -04:00
"ET-2812": {
"read_key": [74, 54],
"write_key": b"Maribaya",
"main_waste": {"oids": [48, 49, 47], "divider": 63.46},
"borderless_waste": {"oids": [50, 51, 47], "divider": 34.16},
"third_waste": {"oids": [252, 253, 254], "divider": 13.0},
"raw_waste_reset": {
48: 0, 49: 0, 47: 0, 52: 0, 53: 0, 54: 94, 50: 0, 51: 0,
55: 94, 28: 0, 252: 0, 253: 0, 254: 0, 255: 94
},
"stats": {
"Maintenance required level of 1st waste ink counter": [54],
"Maintenance required level of 2nd waste ink counter": [55],
2024-08-30 06:26:00 -04:00
"Maintenance required level of 3rd waste ink counter": [255],
2024-08-06 01:13:49 -04:00
"Manual cleaning counter": [90],
"Timer cleaning counter": [89],
"Power cleaning counter": [91],
"Total print pass counter": [133, 132, 131, 130],
"Total print page counter": [776, 775, 774, 773],
"Total scan counter": [1843, 1842, 1841, 1840],
},
"serial_number": range(1604, 1614),
2024-08-30 06:26:00 -04:00
"alias": [
"ET-2814", "ET-2816", "ET-2818", "ET-2810", "ET-2811",
"ET-2813", "ET-2815"
],
2024-08-06 01:13:49 -04:00
},
"L3150": {
"alias": ["L3151", "L3160", "L3166", "L3168"],
2023-07-27 01:00:04 -04:00
"read_key": [151, 7],
"write_key": b'Maribaya',
2024-07-07 18:14:54 -04:00
"main_waste": {"oids": [48, 49, 47], "divider": 63.46},
"borderless_waste": {"oids": [50, 51, 47], "divider": 34.16},
2023-08-07 23:36:21 -04:00
"same-as": "L4160"
2023-07-27 01:00:04 -04:00
},
"L405": {
2024-07-07 18:14:54 -04:00
"read_key": [149, 3],
"write_key": b"Maninjau",
"main_waste": {"oids": [24, 25, 30], "divider": 62.07},
"raw_waste_reset": {24: 0, 25: 0, 30: 0, 28: 0, 29: 0, 46: 94},
"stats": {
2024-08-06 01:13:49 -04:00
"Maintenance required level of waste ink counter": [46],
2024-07-07 18:14:54 -04:00
"Manual cleaning counter": [147],
"Timer cleaning counter": [149],
"Power cleaning counter": [148],
"Total print pass counter": [171, 170, 169, 168],
"Total print page counter": [167, 166, 165, 164],
"Total scan counter": [471, 470, 469, 468],
"First TI received time": [173, 172],
"Ink replacement counter - Black": [242],
"Ink replacement counter - Yellow": [244],
"Ink replacement counter - Magenta": [245],
"Ink replacement counter - Cyan": [243],
},
2024-07-07 18:14:54 -04:00
"serial_number": range(192, 202),
},
2024-09-05 16:03:04 -04:00
"R2000": {
"read_key": [1, 0],
"write_key": b"Yutamori",
"main_waste": {"divider": 215.0, "oids": [87, 88]},
"borderless_waste": {"divider": 70.3, "oids": [89, 90]},
"raw_waste_reset": {
83: 0, 84: 0, 85: 0, 86: 0, 87: 0, 88: 0, 89: 0, 90: 0,
58: 0, 59: 0
},
"stats": {
"Manual cleaning counter": [362],
"Timer cleaning counter": [364],
"Ink replacement cleaning counter": [363],
"Total print pass counter": [183, 182, 181, 180],
"Total print page counter": [179, 178, 177, 176],
"Total print CD-R counter": [185, 184],
"Total print page counter - fine paper": [211, 210, 209, 208],
"Total print page counter - board paper": [193, 192],
"Total print page counter - roll paper": [397, 396, 395, 394],
"Ink replacement counter - Yellow": [400],
"Ink replacement counter - Magenta": [401],
"Ink replacement counter - Matte Black": [402],
"Ink replacement counter - Red": [403],
"Ink replacement counter - Orange": [404],
"Ink replacement counter - Photo Black": [405],
"Ink replacement counter - Gloss Optimizer": [406],
"Ink replacement counter - Cyan": [407],
},
"serial_number": range(466, 476),
},
2023-07-24 19:48:19 -04:00
"L4160": {
"read_key": [73, 8],
"write_key": b'Arantifo',
2024-07-07 18:14:54 -04:00
"main_waste": {"oids": [48, 49, 47], "divider": 109.13},
"borderless_waste": {"oids": [50, 51, 47], "divider": 16.31},
2023-07-31 07:41:37 -04:00
"stats": {
"Maintenance required level of 1st waste ink counter": [54],
"Maintenance required level of 2nd waste ink counter": [55],
2024-07-07 18:14:54 -04:00
"First TI received time": [9, 8],
"Total print pass counter": [133, 132, 131, 130],
"Total print page counter": [776, 775, 774, 773],
2024-07-07 18:14:54 -04:00
"Total scan counter": [1843, 1842, 1841, 1840],
"Ink replacement counter - Black": [554],
"Ink replacement counter - Cyan": [555],
"Ink replacement counter - Magenta": [556],
"Ink replacement counter - Yellow": [557],
2023-07-31 07:41:37 -04:00
},
2024-07-07 18:14:54 -04:00
"serial_number": range(1604, 1614),
2023-07-27 01:00:04 -04:00
"raw_waste_reset": {
48: 0, 49: 0, 47: 0, 52: 0, 53: 0,
54: 94,
50: 0, 51: 0,
55: 94,
28: 0
},
2023-07-24 19:48:19 -04:00
},
2023-07-24 08:58:19 -04:00
"XP-315": {
2023-08-06 20:09:41 -04:00
"alias": ["XP-312", "XP-313"],
2023-07-24 08:58:19 -04:00
"read_key": [129, 8],
2024-07-07 18:14:54 -04:00
"write_key": b'Wakatobi',
2023-08-07 23:36:21 -04:00
"printer_head_id_h": range(122, 126),
"printer_head_id_f": [129],
2023-08-06 20:09:41 -04:00
"main_waste": {"oids": [24, 25, 30], "divider": 69},
"borderless_waste": {"oids": [26, 27, 34], "divider": 32.53},
"serial_number": range(192, 202),
2023-07-31 07:41:37 -04:00
"stats": {
2023-08-06 20:09:41 -04:00
"Manual cleaning counter": [147],
"Timer cleaning counter": [149],
"Ink replacement cleaning counter": [148],
"Total print pass counter": [171, 170, 169, 168],
"Total print page counter": [167, 166, 165, 164],
"Total scan counter": [0x01d7, 0x01d6, 0x01d5, 0x01d4],
"First TI received time": [173, 172],
2023-07-31 07:41:37 -04:00
"Maintenance required level of 1st waste ink counter": [46],
"Maintenance required level of 2nd waste ink counter": [47],
2023-11-11 04:37:03 -05:00
"Power off timer": [359, 358],
2023-07-31 07:41:37 -04:00
},
"raw_waste_reset": {
2023-07-31 10:54:58 -04:00
24: 0, 25: 0, 30: 0, # Data of 1st counter
28: 0, 29: 0, # another store of 1st counter
46: 94, # Maintenance required level of 1st counter
26: 0, 27: 0, 34: 0, # Data of 2nd counter
47: 94, # Maintenance required level of 2st counter
49: 0 # ?
2023-08-06 20:09:41 -04:00
},
"ink_replacement_counters": {
"Black": {"1B": 242, "1S": 208, "1L": 209},
"Yellow": {"1B": 248, "1S": 246, "1L": 247},
"Magenta": {"1B": 251, "1S": 249, "1L": 250},
"Cyan": {"1B": 245, "1S": 243, "1L": 244},
},
"last_printer_fatal_errors": [60, 203, 204, 205, 206, 0x01d3],
2023-07-24 08:58:19 -04:00
},
"XP-422": {
"alias": ["XP-423", "XP-425"],
2023-07-24 08:58:19 -04:00
"read_key": [85, 5],
"write_key": b'Muscari.',
2023-07-31 07:41:37 -04:00
"main_waste": {"oids": [24, 25, 30], "divider": 196.5},
"borderless_waste": {"oids": [26, 27, 34], "divider": 52.05},
"stats": {
"Maintenance required level of 1st waste ink counter": [46],
"Maintenance required level of 2nd waste ink counter": [47],
},
"raw_waste_reset": {
24: 0, 25: 0, 26: 0, 27: 0, 28: 0,
29: 0, 30: 0, 34: 0, 46: 94, 47: 94, 49: 0
},
"serial_number": range(192, 202),
2023-07-24 08:58:19 -04:00
},
"XP-432": {
2023-07-24 08:58:19 -04:00
"read_key": [133, 5],
"write_key": b"Polyxena",
"main_waste": {"oids": [24, 25, 30], "divider": 39.9},
"borderless_waste": {"oids": [26, 27, 34], "divider": 32.55},
"raw_waste_reset": {
24: 0, 25: 0, 30: 0, 28: 0, 29: 0, 46: 94, 26: 0, 27: 0,
34: 0, 47: 94, 49: 0
},
"stats": {
"Maintenance required level of 1st waste ink counter": [46],
"Maintenance required level of 2nd waste ink counter": [47]
},
"alias": ["XP-235", "XP-433", "XP-435"],
2023-12-18 18:52:34 -05:00
},
2023-07-24 08:58:19 -04:00
"XP-540": {
"read_key": [20, 4],
"write_key": b"Firmiana",
"main_waste": {"oids": [16, 17, 6], "divider": 48.06},
"borderless_waste": {"oids": [18, 19, 6], "divider": 20.82},
"raw_waste_reset": {16: 0, 17: 0, 6: 0, 52: 94, 20: 0, 21: 0, 18: 0, 19: 0, 53: 94, 493: 0},
"stats": {
"Timer cleaning counter": [245],
"Total print pass counter": [99, 98, 97, 96],
"Total scan counter": [453, 452, 451, 450],
"Maintenance required level of 1st waste ink counter": [52],
"Maintenance required level of 2nd waste ink counter": [53],
},
"serial_number": range(216, 226),
2023-07-24 08:58:19 -04:00
},
"XP-610": {
"alias": ["XP-611", "XP-615", "XP-510", "XP-55"],
2023-07-24 08:58:19 -04:00
"read_key": [121, 4],
"write_key": b"Gossypiu",
"main_waste": {"oids": [16, 17, 6], "divider": 84.5},
"borderless_waste": {"oids": [18, 19, 6], "divider": 29.03},
"raw_waste_reset": {
16: 0, 17: 0, 6: 0, 52: 94, 20: 0, 21: 0, 18: 0, 19: 0,
53: 94, 493: 0
},
"stats": {
"Timer cleaning counter": [245],
"Total print pass counter": [99, 98, 97, 96],
"Total print CD-R counter": [255, 254],
"Maintenance required level of 1st waste ink counter": [52],
"Maintenance required level of 2nd waste ink counter": [53],
},
"serial_number": range(216, 226),
"alias": ["XP-611", "XP-615"],
2023-07-24 08:58:19 -04:00
},
"XP-620": {
2024-08-23 05:09:49 -04:00
"read_key": [87, 5],
"write_key": b"Althaea.",
"main_waste": {"oids": [16, 17, 6], "divider": 84.5},
"borderless_waste": {"oids": [18, 19, 6], "divider": 33.7},
"raw_waste_reset": {
16: 0, 17: 0, 6: 0, 52: 94, 20: 0, 21: 0, 18: 0, 19: 0,
53: 94, 493: 0
},
2024-08-23 05:09:49 -04:00
"stats": {
"Timer cleaning counter": [245],
"Total print pass counter": [99, 98, 97, 96],
"Total print CD-R counter": [255, 254],
"Maintenance required level of 1st waste ink counter": [52],
"Maintenance required level of 2nd waste ink counter": [53],
},
"serial_number": range(216, 226),
"alias": ["XP-621", "XP-625"],
2023-07-24 08:58:19 -04:00
},
"XP-700": {
"read_key": [40, 0],
2024-08-23 05:09:49 -04:00
"write_key": b"Hibiscus",
"main_waste": {"oids": [16, 17, 6], "divider": 84.5},
"borderless_waste": {"oids": [18, 19, 6], "divider": 29.03},
"raw_waste_reset": {16: 0, 17: 0, 6: 0, 52: 94, 20: 0, 21: 0, 18: 0, 19: 0, 53: 94, 493: 0},
"stats": {
"Timer cleaning counter": [245],
"Total print pass counter": [99, 98, 97, 96],
"Total print CD-R counter": [255, 254],
"Maintenance required level of 1st waste ink counter": [52],
"Maintenance required level of 2nd waste ink counter": [53],
},
"serial_number": range(216, 226),
"alias": ["XP-701", "XP-702"],
2023-07-24 08:58:19 -04:00
},
"XP-760": {
"read_key": [87, 5],
2024-08-23 05:09:49 -04:00
"write_key": b"Althaea.",
"main_waste": {"oids": [16, 17, 6], "divider": 84.5},
"borderless_waste": {"oids": [18, 19, 6], "divider": 33.7},
"raw_waste_reset": {16: 0, 17: 0, 6: 0, 52: 94, 20: 0, 21: 0, 18: 0, 19: 0, 53: 94, 493: 0},
"stats": {
"Timer cleaning counter": [245],
"Total print pass counter": [99, 98, 97, 96],
"Total print CD-R counter": [255, 254],
"Maintenance required level of 1st waste ink counter": [52],
"Maintenance required level of 2nd waste ink counter": [53],
},
"serial_number": range(216, 226),
2023-07-24 08:58:19 -04:00
},
"XP-830": {
"read_key": [40, 9],
2024-08-23 05:09:49 -04:00
"write_key": b"Irisgarm", # (Iris graminea with typo?)
"main_waste": {"oids": [16, 17, 6], "divider": 84.5},
"borderless_waste": {"oids": [18, 19, 6], "divider": 33.7},
"raw_waste_reset": {16: 0, 17: 0, 6: 0, 52: 94, 20: 0, 21: 0, 18: 0, 19: 0, 53: 94, 493: 0},
"stats": {
"Timer cleaning counter": [245],
"Total print pass counter": [99, 98, 97, 96],
"Total print CD-R counter": [255, 254],
"Total scan counter": [453, 452, 451, 450],
"Total scan counter % (ADF)": [457, 456, 455, 454],
"Maintenance required level of 1st waste ink counter": [52],
"Maintenance required level of 2nd waste ink counter": [53],
},
"serial_number": range(216, 226),
2023-07-24 08:58:19 -04:00
"idProduct": 0x110b,
2024-08-23 05:09:49 -04:00
},
"XP-960": {
"read_key": [40, 9],
"write_key": b"Irisgarm",
"main_waste": {"oids": [16, 17, 6], "divider": 93.85},
"borderless_waste": {"oids": [18, 19, 6], "divider": 29.77},
"raw_waste_reset": {
16: 0, 17: 0, 6: 0, 52: 94, 18: 0, 19: 0, 20: 0, 21: 0,
53: 94, 493: 0
},
"stats": {
"Timer cleaning counter": [245],
"Total print pass counter": [99, 98, 97, 96],
"Total print CD-R counter": [255, 254],
"Total scan counter": [453, 452, 451, 450],
"Maintenance required level of 1st waste ink counter": [52],
"Maintenance required level of 2nd waste ink counter": [53],
},
"serial_number": range(216, 226),
2023-07-24 08:58:19 -04:00
},
2024-09-05 16:03:04 -04:00
"XP-820": {
"read_key": [87, 5],
"write_key": b"Althaea.",
"borderless_waste": {"oids": [18, 19, 6], "divider": 33.7},
"alias": ["XP-821"],
"same-as": "XP-850"
},
2023-07-24 08:58:19 -04:00
"XP-850": {
"read_key": [40, 0],
2024-09-05 16:03:04 -04:00
"write_key": b"Hibiscus",
"main_waste": {"oids": [16, 17, 6], "divider": 84.5},
"borderless_waste": {"oids": [18, 19, 6], "divider": 29.03},
"raw_waste_reset": {
16: 0, 17: 0, 6: 0, 52: 94, 20: 0, 21: 0, 18: 0, 19: 0,
53: 94, 493: 0
},
"stats": {
"Timer cleaning counter": [245],
"Total print pass counter": [99, 98, 97, 96],
"Total print CD-R counter": [255, 254],
"Maintenance required level of 1st waste ink counter": [52],
"Maintenance required level of 2nd waste ink counter": [53],
},
"serial_number": range(216, 226),
2023-07-24 08:58:19 -04:00
},
"XP-7100": {
"read_key": [40, 5],
"write_key": b"Leucojum",
"main_waste": {"oids": [16, 17, 6], "divider": 84.5},
"borderless_waste": {"oids": [18, 19, 6], "divider": 33.7},
"raw_waste_reset": {
16: 0, 17: 0, 6: 0, 52: 94, 20: 0, 21: 0, 18: 0,
19: 0, 53: 94, 493: 0
},
"stats": {
"First TI received time": [9, 8],
"Total print pass counter": [99, 98, 97, 96],
"Total print page counter - front feed lower": [696, 695, 694, 693],
"Total print page counter - front feed upper": [744, 743, 742, 741],
"Total print page counter - rear": [748, 747, 746, 745],
"Total print page counter - duplex": [752, 751, 750, 749],
"Total print CD-R counter": [255, 254],
"Total scan counter": [453, 452, 451, 450],
"Total scan counter (ADF)": [457, 456, 455, 454],
"Ink replacement counter - Black": [701],
"Ink replacement counter % PB": [705],
"Ink replacement counter - Cyan": [702],
"Ink replacement counter - Magenta": [703],
"Ink replacement counter - Yellow": [704],
"Maintenance required level of 1st waste ink counter": [52],
"Maintenance required level of 2nd waste ink counter": [53],
},
"serial_number": range(216, 226),
2023-07-24 08:58:19 -04:00
},
2023-12-18 18:52:34 -05:00
"XP-2150": {
"read_key": [80, 9],
"write_key": b"Bidadari",
"main_waste": {"oids": [337, 338, 336], "divider": 69.0},
"borderless_waste": {"oids": [339, 340, 336], "divider": 30.49},
"raw_waste_reset": {
336: 0, 337: 0, 338: 0, 339: 0, 340: 0, 341: 0, 343: 94,
342: 0, 344: 94, 28: 0
},
"stats": {
"First TI received time": [9, 8],
"Manual cleaning counter": [203],
"Timer cleaning counter": [205],
"Total print pass counter": [133, 132, 131, 130],
"Total scan counter": [1843, 1842, 1841, 1840],
"Total print page counter": [792, 791, 790, 789],
"Ink replacement counter - Black": [554],
"Ink replacement counter - Cyan": [555],
"Ink replacement counter - Magenta": [556],
"Ink replacement counter - Yellow": [557],
"Maintenance required level of 1st waste ink counter": [343],
"Maintenance required level of 2nd waste ink counter": [344],
},
"serial_number": range(1604, 1614),
"alias": ["XP-2100", "XP-2151", "XP-2155"],
2023-12-18 18:52:34 -05:00
},
2023-07-31 07:41:37 -04:00
"ET-2500": {
"read_key": [68, 1],
"write_key": b"Gerbera*",
"main_waste": {"oids": [24, 25, 30], "divider": 62.07},
"raw_waste_reset": {24: 0, 25: 0, 30: 0, 28: 0, 29: 0, 46: 94},
"stats": {"Maintenance required level of 1st waste ink counter": [46]},
"serial_number": range(192, 202),
2023-07-31 07:41:37 -04:00
},
2023-08-03 18:09:11 -04:00
"XP-3150": {
2024-07-07 18:14:54 -04:00
"alias": ["XP-3151", "XP-3155"],
2023-08-03 18:09:11 -04:00
"read_key": [80, 9],
"write_key": b'Bidadari',
2023-10-14 00:54:37 -04:00
"serial_number": range(1604, 1614),
2023-08-16 13:18:41 -04:00
"printer_head_id_h": [171, 189, 190, 175],
"printer_head_id_f": [191, 188],
2023-08-03 18:09:11 -04:00
"stats": {
2023-08-16 13:18:41 -04:00
"MAC Address": range(0x780, 0x786),
"First TI received time": [9, 8],
"Total print pass counter": [133, 132, 131, 130],
"Total print page counter": [0x2fb, 0x2fa, 0x2f9, 0x2f8],
"Total scan counter": [0x0733, 0x0732, 0x0731, 0x0730],
"Paper count color": [0x314, 0x313, 0x312, 0x311],
"Paper count monochrome": [0x318, 0x317, 0x316, 0x315],
2024-07-06 20:45:00 -04:00
"Ink replacement counter - Black": [0x22a],
"Ink replacement counter - Cyan": [0x22b],
"Ink replacement counter - Magenta": [0x22c],
"Ink replacement counter - Yellow": [0x22d],
2023-08-16 13:18:41 -04:00
"Maintenance_box_replacement_counter": [0x22e],
2023-08-03 18:09:11 -04:00
},
2023-08-16 13:18:41 -04:00
"last_printer_fatal_errors": chain(
range(0x120, 0x12a), range(0x727, 0x72c), range(0x7f4, 0x7fe)
),
2023-08-03 18:09:11 -04:00
},
2023-10-06 03:15:44 -04:00
"ET-2550": { # Epson EcoTank ET-2550
"read_key": [0x44, 0x01],
"write_key": b'Gazania*',
2024-08-06 01:13:49 -04:00
"main_waste": {"oids": [24, 25, 30], "divider": 62.06},
"serial_number": range(192, 202),
"stats": {
"Maintenance required level of waste ink counter": [46]
},
"raw_waste_reset": {
24: 0, 25: 0, 30: 0, # Data of the waste ink counter
28: 0, 29: 0, # another store of the waste ink counter
46: 94, # Maintenance required level of the waste ink counter
}
},
2023-10-14 00:54:37 -04:00
"ET-2700": { # Epson EcoTank ET-2700 Series
2024-08-06 01:13:49 -04:00
"alias": ["ET-2701", "ET-2703", "ET-2705"],
2023-10-14 00:54:37 -04:00
"read_key": [73, 8],
"write_key": b'Arantifo',
"serial_number": range(1604, 1614),
2024-08-06 01:13:49 -04:00
"main_waste": {"oids": [48, 49, 47], "divider": 109.13},
"borderless_waste": {"oids": [50, 51, 47], "divider": 16.31},
2023-10-14 00:54:37 -04:00
"stats": {
"Maintenance required level of 1st waste ink counter": [54],
"Maintenance required level of 2nd waste ink counter": [55],
2024-07-07 18:14:54 -04:00
"First TI received time": [9, 8],
"Total print pass counter": [133, 132, 131, 130],
2024-08-06 01:13:49 -04:00
"Total print page counter - rear feed": [755, 754, 753, 752],
2024-07-07 18:14:54 -04:00
"Total scan counter": [1843, 1842, 1841, 1840],
"Ink replacement counter - Black": [554],
"Ink replacement counter - Cyan": [555],
"Ink replacement counter - Magenta": [556],
"Ink replacement counter - Yellow": [557],
2023-10-14 00:54:37 -04:00
},
"raw_waste_reset": {
48: 0, 49: 0, 47: 0, # Data of 1st counter
52: 0, 53: 0, # another store of 1st counter
54: 94, # Maintenance required level of 1st counter
50: 0, 51: 0, # Data of 2nd counter
55: 94, # Maintenance required level of 2st counter
28: 0 # ?
}
},
2023-07-24 08:58:19 -04:00
}
2023-08-16 13:18:41 -04:00
CARTRIDGE_TYPE = { # map cartridge number with color
1811: 'Black', 1812: 'Cyan', 1813: 'Magenta', 1814: 'Yellow', # T18xx / 18XL
711: 'Black', 712: 'Cyan', 713: 'Magenta', 714: 'Yellow', # T7xx
10332: 'Black', 10360: 'Cyan', 10361: 'Magenta', 10362: 'Yellow', # 603XL
}
2023-11-11 04:37:03 -05:00
MIB_MGMT = "1.3.6.1.2"
PRINT_MIB = MIB_MGMT + ".1.43"
MIB_OID_ENTERPRISE = "1.3.6.1.4.1"
MIB_EPSON = MIB_OID_ENTERPRISE + ".1248"
2023-07-24 08:58:19 -04:00
OID_PRV_CTRL = "1.2.2.44.1.1.2"
2023-11-11 04:37:03 -05:00
EEPROM_LINK = f'{MIB_EPSON}.{OID_PRV_CTRL}.1'
MIB_INFO = {
"Model": f"{MIB_MGMT}.1.25.3.2.1.3.1",
"Epson Printer Name": f"{MIB_EPSON}.1.2.2.1.1.1.2.1",
2023-11-11 04:37:03 -05:00
"Model short": f"{MIB_EPSON}.1.1.3.1.3.8.0",
"Epson Personal Name": f"{MIB_EPSON}.1.2.2.1.1.1.3.1",
2023-11-11 04:37:03 -05:00
"EEPS2 firmware version": f"{MIB_MGMT}.1.2.2.1.2.1",
"Epson Version number": f"{MIB_EPSON}.1.2.2.2.1.1.2.1.4",
2023-11-11 04:37:03 -05:00
"Descr": f"{MIB_MGMT}.1.1.1.0",
"UpTime": f"{MIB_MGMT}.1.1.3.0",
"Name": f"{MIB_MGMT}.1.1.5.0",
"MAC Address": f"{MIB_MGMT}.1.2.2.1.6.1",
"Print input": f"{PRINT_MIB}.8.2.1.13.1.1",
"Lang 1": f"{PRINT_MIB}.15.1.1.3.1.1",
"Lang 2": f"{PRINT_MIB}.15.1.1.3.1.2",
"Lang 3": f"{PRINT_MIB}.15.1.1.3.1.3",
"Lang 4": f"{PRINT_MIB}.15.1.1.3.1.4",
"Lang 5": f"{PRINT_MIB}.15.1.1.3.1.5",
"Emulation 1": f"{PRINT_MIB}.15.1.1.5.1.1",
"Emulation 2": f"{PRINT_MIB}.15.1.1.5.1.2",
"Emulation 3": f"{PRINT_MIB}.15.1.1.5.1.3",
"Emulation 4": f"{PRINT_MIB}.15.1.1.5.1.4",
"Emulation 5": f"{PRINT_MIB}.15.1.1.5.1.5",
"Total printed pages": f"{PRINT_MIB}.10.2.1.4.1.1",
#"Total copies": f"{PRINT_MIB}.11.1.1.9.1.1",
#"Serial number": f"{PRINT_MIB}.5.1.1.17.1",
"IP Address": f"{MIB_EPSON}.1.1.3.1.4.19.1.3.1",
"IPP_URL_path": f"{MIB_EPSON}.1.1.3.1.4.19.1.4.1",
"IPP_URL": f"{MIB_EPSON}.1.1.3.1.4.46.1.2.1",
"WiFi": f"{MIB_EPSON}.1.1.3.1.29.2.1.9.0",
"MAC Addr": f"{MIB_EPSON}.1.1.3.1.1.5.0",
"device_id": f"{MIB_OID_ENTERPRISE}.11.2.3.9.1.1.7.0",
"Epson device id": f"{MIB_EPSON}.1.2.2.1.1.1.1.1",
2023-11-11 04:37:03 -05:00
"Power Off Timer": f"{EEPROM_LINK}.111.116.2.0.1.1"
}
2023-07-24 08:58:19 -04:00
2024-08-09 17:22:32 -04:00
MIB_INFO_ADVANCED = {
"Printer Status": f"{MIB_MGMT}.1.25.3.5.1.1", # hrPrinterStatus
"Printer Alerts": f"{MIB_MGMT}.1.43.18.1.1.8", # prtAlertDescription
"Printer Marker Supplies Level": f"{MIB_MGMT}.1.43.11.1.1.9", # prtMarkerSuppliesLevel
"Printer Marker Life Count": f"{MIB_MGMT}.1.43.11.1.1.6", # prtMarkerLifeCount
"Input Tray Status": f"{MIB_MGMT}.1.43.8.2.1.10", # prtInputStatus
"Output Tray Status": f"{MIB_MGMT}.1.43.9.2.1.10", # prtOutputStatus
"Printer Description": f"{MIB_MGMT}.1.25.3.2.1.3", # hrDeviceDescr
"Device Identification": f"{MIB_MGMT}.1.43.5.1.1.17", # prtGeneralSerialNumber
"Job Count": f"{MIB_MGMT}.1.43.10.2.1.4", # prtJobEntryJobCount
"Toner Level": f"{MIB_MGMT}.1.43.11.1.1.9.1", # prtMarkerSuppliesLevel
"Error Status": f"{MIB_MGMT}.1.43.16.5.1.2", # prtConsoleDisplayBufferText
"Power On Time": f"{MIB_MGMT}.1.25.3.2.1.5", # hrDeviceUptime
"Device Name": f"{MIB_MGMT}.1.1.5", # sysName
"Device Location": f"{MIB_MGMT}.1.1.6", # sysLocation
}
2023-07-24 08:58:19 -04:00
session: object
2023-08-08 17:18:54 -04:00
model: str
2023-07-24 08:58:19 -04:00
hostname: str
parm: dict
2023-08-06 11:19:41 -04:00
mib_dict: dict = {}
2023-07-24 08:58:19 -04:00
def __init__(
self,
2024-07-28 21:29:30 -04:00
conf_dict: dict = {},
replace_conf = False,
2023-08-08 17:18:54 -04:00
model: str = None,
2023-08-06 00:51:18 -04:00
hostname: str = None,
2023-08-08 17:18:54 -04:00
port: int = 161,
2023-08-03 18:09:11 -04:00
timeout: (None, float) = None,
retries: (None, float) = None,
2023-08-08 17:18:54 -04:00
dry_run: bool = False
) -> None:
2023-07-24 08:58:19 -04:00
"""Initialise printer model."""
2024-07-28 21:29:30 -04:00
def merge(source, destination):
for key, value in source.items():
if isinstance(value, dict):
merge(value, destination.setdefault(key, {}))
else:
if key == "alias" and "alias" in destination:
destination[key] += value
else:
destination[key] = value
return destination
2024-08-13 12:01:12 -04:00
if conf_dict:
self.expand_printer_conf(conf_dict)
2024-07-28 21:29:30 -04:00
if conf_dict and replace_conf:
self.PRINTER_CONFIG = conf_dict
2024-08-13 12:01:12 -04:00
else:
self.expand_printer_conf(self.PRINTER_CONFIG)
2024-07-28 21:29:30 -04:00
if conf_dict and not replace_conf:
self.PRINTER_CONFIG = merge(self.PRINTER_CONFIG, conf_dict)
for key, values in self.PRINTER_CONFIG.items():
if 'alias' in values:
values['alias'] = [
i for i in values['alias']
if i not in self.PRINTER_CONFIG
]
if not values['alias']:
del values['alias']
2023-08-08 17:18:54 -04:00
self.model = model
2023-07-24 08:58:19 -04:00
self.hostname = hostname
2023-08-08 17:18:54 -04:00
self.port = port
2023-08-03 18:09:11 -04:00
self.timeout = timeout
self.retries = retries
2023-07-24 08:58:19 -04:00
self.dry_run = dry_run
2023-08-08 17:18:54 -04:00
if self.model in self.valid_printers:
self.parm = self.PRINTER_CONFIG[self.model]
2023-07-24 08:58:19 -04:00
else:
self.parm = None
2023-07-25 05:49:47 -04:00
@property
def valid_printers(self):
2023-08-01 13:14:09 -04:00
"""Return list of defined printers."""
2023-07-25 05:49:47 -04:00
return {
printer_name
for printer_name in self.PRINTER_CONFIG.keys()
if "read_key" in self.PRINTER_CONFIG[printer_name]
2023-07-25 05:49:47 -04:00
}
@property
def list_methods(self):
2023-08-01 13:14:09 -04:00
"""Return list of available information methods about a printer."""
2023-08-03 18:09:11 -04:00
return(filter(lambda x: x.startswith("get_"), dir(self)))
2023-07-25 05:49:47 -04:00
2024-08-13 12:01:12 -04:00
def expand_printer_conf(self, conf):
"""
Expand "alias" and "same-as" of a printer database for all printers
"""
# process "alias" definintion
for printer_name, printer_data in conf.copy().items():
if "alias" in printer_data:
aliases = printer_data["alias"]
del printer_data["alias"]
if not isinstance(aliases, list):
logging.error(
"Alias '%s' of printer '%s' in configuration "
"must be a list.",
aliases, printer_name
)
continue
for alias_name in aliases:
if alias_name in conf:
logging.error(
"Alias '%s' of printer '%s' is already defined "
"in configuration.",
alias_name, printer_name
)
else:
conf[alias_name] = printer_data
# process "same-as" definintion
for printer_name, printer_data in conf.copy().items():
if "same-as" in printer_data:
sameas = printer_data["same-as"]
#del printer_data["same-as"]
if sameas in conf:
conf[printer_name] = {
**conf[sameas],
**printer_data
}
else:
logging.error(
"Undefined 'same-as' printer '%s' "
"in '%s' configuration.",
sameas, printer_name
)
2024-09-18 22:49:14 -04:00
# process itertools classes
def expand_itertools_in_dict(d):
for key, value in d.items():
if isinstance(value, dict): # If the value is another dictionary, recurse into it
expand_itertools_in_dict(value)
elif isinstance(
value, (
itertools.chain, itertools.cycle, itertools.islice,
itertools.permutations, itertools.combinations,
itertools.product, itertools.zip_longest,
itertools.starmap, itertools.groupby
)
):
d[key] = list(value) # Convert itertools object to a list
elif isinstance(value, list): # Check inside lists for dictionaries
for i, item in enumerate(value):
if isinstance(item, dict):
expand_itertools_in_dict(item)
for printer_name, printer_data in conf.copy().items():
expand_itertools_in_dict(printer_data)
2024-08-13 12:01:12 -04:00
2023-07-24 08:58:19 -04:00
def stats(self):
2023-08-01 13:14:09 -04:00
"""Return all available information about a printer."""
2023-07-25 08:28:28 -04:00
stat_set = {}
for method in self.list_methods:
2023-08-03 18:09:11 -04:00
ret = self.__getattribute__(method)()
2023-07-25 08:28:28 -04:00
if ret:
stat_set[method[4:]] = ret
else:
2023-08-06 05:04:12 -04:00
logging.info(f"No value for method '{method}'.")
2023-07-25 08:28:28 -04:00
return stat_set
2023-07-24 08:58:19 -04:00
2023-10-14 00:54:37 -04:00
def caesar(self, key, hex=False):
2023-08-01 13:14:09 -04:00
"""Convert the string write key to a sequence of numbers"""
2023-10-14 00:54:37 -04:00
if hex:
2024-07-28 13:15:10 -04:00
return " ".join(
'00' if b == 0 else '{0:02x}'.format(b + 1) for b in key
)
return ".".join("0" if b == 0 else str(b + 1) for b in key)
2023-07-25 05:49:47 -04:00
2023-10-14 00:54:37 -04:00
def reverse_caesar(self, eight_bytes):
"""
Convert a bytes type sequence key (8 bytes length) to string.
Example:
import epson_print_conf
printer = epson_print_conf.EpsonPrinter()
printer.reverse_caesar(bytes.fromhex("48 62 7B 62 6F 6A 62 2B"))
"""
return "".join([chr(b - 1) for b in eight_bytes])
2023-07-26 06:55:29 -04:00
def eeprom_oid_read_address(
self,
oid: int,
msb: int = 0,
label: str = "unknown method") -> str:
2023-08-01 13:14:09 -04:00
"""
Return the OID string to read the value of the EEPROM address 'oid'.
oid can be a number between 0x0000 and 0xffff.
Return None in case of error.
"""
2023-07-26 06:55:29 -04:00
if oid > 255:
msb = oid // 256
oid = oid % 256
2023-08-01 13:14:09 -04:00
if msb > 255:
2023-08-06 05:04:12 -04:00
logging.error("EpsonPrinter - invalid API usage")
2023-08-01 13:14:09 -04:00
return None
2023-08-06 05:04:12 -04:00
if not self.parm:
logging.error("EpsonPrinter - invalid API usage")
return None
if 'read_key' not in self.parm:
2023-07-31 07:41:37 -04:00
return None
2023-07-24 08:58:19 -04:00
return (
2023-11-11 04:37:03 -05:00
f"{self.EEPROM_LINK}"
2023-10-14 00:54:37 -04:00
".124.124" # || (7C 7C)
".7.0" # read (07 00)
2023-08-03 18:09:11 -04:00
f".{self.parm['read_key'][0]}"
f".{self.parm['read_key'][1]}"
2023-07-24 08:58:19 -04:00
".65.190.160"
2023-07-26 06:55:29 -04:00
f".{oid}.{msb}"
2023-07-24 08:58:19 -04:00
)
2023-07-26 06:55:29 -04:00
def eeprom_oid_write_address(
self,
oid: int,
value: Any,
msb: int = 0,
label: str = "unknown method") -> str:
2023-08-01 13:14:09 -04:00
"""
Return the OID string to write a value to the EEPROM address 'oid'.
oid can be a number between 0x0000 and 0xffff.
Return None in case of error.
"""
2023-07-26 06:55:29 -04:00
if oid > 255:
msb = oid // 256
oid = oid % 256
2023-08-01 13:14:09 -04:00
if msb > 255:
2023-08-06 05:04:12 -04:00
logging.error("EpsonPrinter - invalid API usage")
return None
if not self.parm:
logging.error("EpsonPrinter - invalid API usage")
2023-08-01 13:14:09 -04:00
return None
2023-08-06 00:51:18 -04:00
if (
2023-08-06 05:04:12 -04:00
'write_key' not in self.parm
2023-08-03 18:09:11 -04:00
or 'read_key' not in self.parm):
2023-07-31 07:41:37 -04:00
return None
2023-07-24 08:58:19 -04:00
write_op = (
2023-11-11 04:37:03 -05:00
f"{self.EEPROM_LINK}"
2023-10-14 00:54:37 -04:00
".124.124" # || 7C 7C
".16.0" # write (10 00)
2023-08-03 18:09:11 -04:00
f".{self.parm['read_key'][0]}"
f".{self.parm['read_key'][1]}"
2023-10-14 00:54:37 -04:00
".66.189.33" # 42 BD 21
2023-07-26 06:55:29 -04:00
f".{oid}.{msb}.{value}"
2023-08-03 18:09:11 -04:00
f".{self.caesar(self.parm['write_key'])}"
2023-07-24 08:58:19 -04:00
)
if self.dry_run:
2023-08-06 05:04:12 -04:00
logging.warning("WRITE_DRY_RUN: %s", write_op)
2023-07-26 06:55:29 -04:00
return self.eeprom_oid_read_address(oid, label=label)
2023-07-24 08:58:19 -04:00
else:
return write_op
2023-08-08 17:18:54 -04:00
def snmp_mib(self, mib: str, label: str = "unknown") -> (str, Any):
2023-08-03 18:09:11 -04:00
"""Generic SNMP query, returning value of a MIB."""
2023-08-06 11:19:41 -04:00
if self.mib_dict:
if mib not in self.mib_dict:
logging.error(
"MIB '%s' not valued in the configuration file. "
"Operation: %s",
mib,
label
)
2023-08-08 11:35:34 -04:00
return None, False
2023-08-06 11:19:41 -04:00
return self.mib_dict[mib]
2023-08-06 00:51:18 -04:00
if not self.hostname:
2023-08-08 11:35:34 -04:00
return None, False
2024-01-09 17:54:27 -05:00
try:
utt = UdpTransportTarget(
(self.hostname, self.port),
)
except Exception as e:
logging.critical("snmp_mib invalid address: %s", e)
quit(3)
2023-08-03 18:09:11 -04:00
if self.timeout is not None:
utt.timeout = self.timeout
if self.retries is not None:
utt.retries = self.retries
iterator = getCmd(
SnmpDispatcher(),
CommunityData('public', mpModel=0),
utt,
(mib, None)
)
for response in iterator:
errorIndication, errorStatus, errorIndex, varBinds = response
if errorIndication:
2023-08-06 11:19:41 -04:00
logging.info(
"snmp_mib error: %s. MIB: %s. Operation: %s",
errorIndication, mib, label
)
2023-08-04 03:47:53 -04:00
if " timed out" in errorIndication:
raise TimeoutError(errorIndication)
2023-08-08 11:35:34 -04:00
return None, False
2023-08-03 18:09:11 -04:00
elif errorStatus:
2023-08-06 05:04:12 -04:00
logging.info(
2023-08-06 11:19:41 -04:00
'snmp_mib PDU error: %s at %s. MIB: %s. Operation: %s',
2023-08-06 00:51:18 -04:00
errorStatus.prettyPrint(),
2023-08-06 11:19:41 -04:00
errorIndex and varBinds[int(errorIndex) - 1][0] or '?',
mib,
label
2023-08-06 00:51:18 -04:00
)
2023-08-08 11:35:34 -04:00
return None, False
2023-08-03 18:09:11 -04:00
else:
for varBind in varBinds:
2023-08-04 03:47:53 -04:00
if isinstance(varBind[1], OctetStringType):
2023-08-08 11:35:34 -04:00
return(
varBind[1].__class__.__name__,
varBind[1].asOctets()
)
2023-08-04 03:47:53 -04:00
else:
2023-08-08 11:35:34 -04:00
return(
varBind[1].__class__.__name__,
varBind[1].prettyPrint()
)
2023-08-06 11:19:41 -04:00
logging.info(
"snmp_mib value error: invalid multiple data. "
"MIB: %s. Operation: %s",
mib,
label
)
2023-08-08 11:35:34 -04:00
return None, False
2023-08-06 11:19:41 -04:00
logging.info(
"snmp_mib value error: invalid data. MIB: %s. Operation: %s",
label
)
2023-08-08 11:35:34 -04:00
return None, False
2023-07-25 05:49:47 -04:00
2023-08-07 23:36:21 -04:00
def invalid_response(self, response):
if response is False:
return True
2023-08-07 23:36:21 -04:00
return len(response) < 2 or response[0] != 0 or response[-1] != 12
2023-07-26 06:55:29 -04:00
def read_eeprom(
self,
oid: int,
label: str = "unknown method") -> str:
2023-08-01 13:14:09 -04:00
"""Read a single byte from the Epson EEPROM address 'oid'."""
2023-08-06 00:51:18 -04:00
logging.debug(
f"EEPROM_DUMP {label}:\n"
f" ADDRESS: "
f"{self.eeprom_oid_read_address(oid, label=label)}\n"
f" OID: {oid}={hex(oid)}"
)
2023-08-08 11:35:34 -04:00
tag, response = self.snmp_mib(
2024-01-30 18:04:00 -05:00
self.eeprom_oid_read_address(oid, label=label), label=label
)
2023-08-04 03:47:53 -04:00
if not response:
return None
2023-08-07 23:36:21 -04:00
if self.invalid_response(response):
logging.error(
f"Invalid response: '%s' for oid %s (%s)",
repr(response), oid, label
)
return None
2023-08-08 11:35:34 -04:00
logging.debug(" TAG: %s\n RESPONSE: %s", tag, repr(response))
2023-07-31 07:41:37 -04:00
try:
2023-08-16 13:18:41 -04:00
response = re.findall(
r"EE:[0-9a-fA-F]{6}", response.decode())[0][3:]
2023-08-03 18:09:11 -04:00
except (TypeError, IndexError):
2023-08-06 05:04:12 -04:00
logging.info(f"Invalid read key.")
2023-07-31 07:41:37 -04:00
return None
2023-07-26 06:55:29 -04:00
chk_addr = response[0:4]
2023-07-24 08:58:19 -04:00
value = response[4:6]
if int(chk_addr, 16) != oid:
raise ValueError(
2023-07-25 05:49:47 -04:00
f"Address and response address are"
f" not equal: {oid} != {chk_addr}"
2023-07-24 08:58:19 -04:00
)
return value
2023-07-26 06:55:29 -04:00
def read_eeprom_many(
self,
oids: list,
2023-08-07 23:36:21 -04:00
label: str = "unknown method") -> list:
2023-08-01 13:14:09 -04:00
"""
Read a list of bytes from the list of Epson EEPROM addresses 'oids'.
"""
2023-08-07 23:36:21 -04:00
response = [self.read_eeprom(oid, label=label) for oid in oids]
for i in response:
if i is None:
return [None]
return response
2023-07-24 08:58:19 -04:00
2023-07-26 06:55:29 -04:00
def write_eeprom(
self,
oid: int,
value: int,
label: str = "unknown method") -> None:
2023-08-01 13:14:09 -04:00
"""Write a single byte 'value' to the Epson EEPROM address 'oid'."""
2023-08-06 05:04:12 -04:00
if not self.parm:
logging.error("EpsonPrinter - invalid API usage")
return False
if "write_key" not in self.parm:
2023-08-06 00:51:18 -04:00
logging.error(
f"Missing 'write_key' parameter in configuration.")
2023-07-31 07:41:37 -04:00
return False
2023-08-06 00:51:18 -04:00
if not self.dry_run:
2023-07-31 07:41:37 -04:00
response = self.read_eeprom(oid, label=label)
2023-08-06 00:51:18 -04:00
logging.debug(f"Previous value for {label}: {response}")
2023-07-26 06:55:29 -04:00
oid_string = self.eeprom_oid_write_address(oid, value, label=label)
2023-08-06 00:51:18 -04:00
logging.debug(
f"EEPROM_WRITE {label}:\n"
f" ADDRESS: {oid_string}\n"
2023-08-06 11:19:41 -04:00
f" OID: {oid}={hex(oid)}\n"
f" VALUE: {value} = {hex(int(value))}"
2023-08-06 00:51:18 -04:00
)
2023-08-08 11:35:34 -04:00
tag, response = self.snmp_mib(oid_string, label=label)
2023-08-06 00:51:18 -04:00
if response:
2023-08-08 11:35:34 -04:00
logging.debug(" TAG: %s\n RESPONSE: %s", tag, repr(response))
2023-08-04 03:47:53 -04:00
if not self.dry_run and response and not ":OK;" in repr(response):
2023-08-07 23:36:21 -04:00
logging.info(
"Write error. Oid=%s, value=%s, label=%s", oid, value, label)
2023-08-04 03:47:53 -04:00
return False # ":NA;" is an error
2023-08-07 23:36:21 -04:00
if self.invalid_response(response):
logging.error(
"Invalid write response. Oid=%s, value=%s, label=%s",
oid, value, label
)
return False
2023-07-31 07:41:37 -04:00
return True
2023-07-24 08:58:19 -04:00
def status_parser(self, data):
"""
Parse an ST2 status response and decode as much as possible.
Example:
import epson_print_conf
import pprint
printer = epson_print_conf.EpsonPrinter()
pprint.pprint(printer.status_parser(bytes.fromhex(
"40 42 44 43 20 53 54 32 0D 0A....."
)))
"""
2023-08-04 19:29:16 -04:00
colour_ids = { # Ink cartridge name
0x01: 'Black',
0x03: 'Cyan',
0x04: 'Magenta',
0x05: 'Yellow',
0x06: 'Light Cyan',
0x07: 'Light Magenta',
0x0a: 'Light Black',
0x0b: 'Matte Black',
0x0f: 'Light Light Black',
0x10: 'Orange',
0x11: 'Green',
2023-07-24 08:58:19 -04:00
}
2023-08-16 13:18:41 -04:00
ink_color_ids = { # Ink color
0x00: 'Black',
0x01: 'Cyan',
0x02: 'Magenta',
0x03: 'Yellow',
0x04: 'Light Cyan',
0x05: 'Light Magenta',
2024-07-07 09:21:39 -04:00
0x06: "Dark Yellow",
0x07: "Grey",
0x08: "Light Black",
0x09: "Red",
0x0A: "Blue",
0x0B: "Gloss Optimizer",
0x0C: "Light Grey",
0x0D: "Orange",
2023-08-16 13:18:41 -04:00
}
2023-07-24 08:58:19 -04:00
status_ids = {
2023-08-04 19:29:16 -04:00
0x00: 'Error',
0x01: 'Self Printing',
0x02: 'Busy',
0x03: 'Waiting',
2024-07-07 09:21:39 -04:00
0x04: 'Idle (ready to print)',
2023-08-04 19:29:16 -04:00
0x05: 'Paused',
0x07: 'Cleaning',
2024-07-07 09:21:39 -04:00
0x08: 'Factory shipment (not initialized)',
2023-08-04 19:29:16 -04:00
0x0a: 'Shutdown',
0x0f: 'Nozzle Check',
2024-07-07 09:21:39 -04:00
0x11: "Charging",
2023-08-04 19:29:16 -04:00
}
errcode_ids = {
0x00: "Fatal error",
0x01: "Other I/F is selected",
0x02: "Cover Open",
0x04: "Paper jam",
0x05: "Ink out",
0x06: "Paper out",
0x0c: "Paper size or paper type or paper path error",
2024-07-07 09:21:39 -04:00
0x10: "Ink overflow error (Waste ink pad counter overflow)",
2023-08-04 19:29:16 -04:00
0x11: "Wait return from the tear-off position",
0x12: "Double Feed",
2024-07-07 09:21:39 -04:00
0x1a: "Cartridge cover is opened error",
2023-08-04 19:29:16 -04:00
0x1c: "Cutter error (Fatal Error)",
0x1d: "Cutter jam error (recoverable)",
2024-07-07 09:21:39 -04:00
0x22: "Maintenance cartridge is missing error",
0x25: "Rear cover is opened error",
0x29: "CD-R tray is out error",
0x2a: "Memory Card loading Error",
0x2B: "Tray cover is opened",
0x2C: "Ink cartridge overflow error",
0x2F: "Battery abnormal voltage error",
0x30: "Battery abnormal temperature error",
0x31: "Battery is empty error",
0x33: "Initial filling is impossible error",
0x36: "Maintenance cartridge cover is opened error",
0x37: "Scanner or front cover is opened error",
0x41: "Maintenance request",
2023-08-04 19:29:16 -04:00
0x47: "Printing disable error",
0x4a: "Maintenance Box near End error",
2024-03-14 10:26:29 -04:00
0x4b: "Driver mismatch error ",
2023-08-04 19:29:16 -04:00
}
warning_ids = {
0x10: "Ink low (Black or Yellow)",
0x11: "Ink low (Magenta)",
0x12: "Ink low (Yellow or Cyan)",
0x13: "Ink low (Cyan or Matte Black)",
0x14: "Ink low (Photo Black)",
0x15: "Ink low (Red)",
0x16: "Ink low (Blue)",
0x17: "Ink low (Gloss optimizer)",
0x44: "Black print mode",
0x51: "Cleaning Disabled (Cyan)",
0x52: "Cleaning Disabled (Magenta)",
0x53: "Cleaning Disabled (Yellow)",
0x54: "Cleaning Disabled (Black)",
2023-07-24 08:58:19 -04:00
}
if len(data) < 16:
2023-08-06 05:04:12 -04:00
logging.info("status_parser: invalid packet")
2023-07-24 08:58:19 -04:00
return "invalid packet"
if data[:11] != b'\x00@BDC ST2\r\n':
2023-08-06 00:51:18 -04:00
logging.debug("Unaligned BDC ST2 header. Trying to fix...")
2023-08-04 12:45:54 -04:00
start = data.find(b'BDC ST2\r\n')
if start < 0:
2023-08-06 05:04:12 -04:00
logging.info(
"status_parser: "
"printer status error (must start with BDC ST2...)")
2023-08-04 12:45:54 -04:00
return "printer status error (must start with BDC ST2...)"
data = bytes(2) + data[start:]
2023-07-24 08:58:19 -04:00
len_p = int.from_bytes(data[11:13], byteorder='little')
if len(data) - 13 != len_p:
2023-08-06 05:04:12 -04:00
logging.info("status_parser: message error (invalid length)")
2023-08-04 19:29:16 -04:00
return "message error (invalid length)"
2023-07-24 08:58:19 -04:00
buf = data[13:]
data_set = {}
while len(buf):
if len(buf) < 3:
2023-08-06 05:04:12 -04:00
logging.info("status_parser: invalid element")
2023-07-24 08:58:19 -04:00
return "invalid element"
(ftype, length) = buf[:2]
buf = buf[2:]
item = buf[:length]
if len(item) != length:
2023-08-06 05:04:12 -04:00
logging.info("status_parser: invalid element length")
2023-07-24 08:58:19 -04:00
return "invalid element length"
buf = buf[length:]
2023-08-06 00:51:18 -04:00
logging.debug(
"Processing status - ftype %s, length: %s, item: %s",
hex(ftype), length, item.hex(' ')
)
2023-11-11 07:14:01 -05:00
if ftype == 0x01: # Status code
2023-07-24 08:58:19 -04:00
printer_status = item[0]
status_text = "unknown"
if printer_status in status_ids:
status_text = status_ids[printer_status]
else:
status_text = 'unknown: %d' % printer_status
if printer_status == 3 or printer_status == 4:
data_set["ready"] = True
else:
data_set["ready"] = False
data_set["status"] = (printer_status, status_text)
2023-11-11 07:14:01 -05:00
elif ftype == 0x02: # Error code
2023-08-04 19:29:16 -04:00
printer_status = item[0]
if printer_status in errcode_ids:
data_set["errcode"] = errcode_ids[printer_status]
else:
data_set["errcode"] = 'unknown: %d' % printer_status
2023-07-24 08:58:19 -04:00
2023-07-25 05:49:47 -04:00
elif ftype == 0x03: # Self print code
2023-07-24 08:58:19 -04:00
data_set["self_print_code"] = item
2023-08-04 19:29:16 -04:00
if item[0] == 0:
data_set["self_print_code"] = "Nozzle test printing"
2023-07-24 08:58:19 -04:00
2023-11-11 07:14:01 -05:00
elif ftype == 0x04: # Warning code
2023-08-04 19:29:16 -04:00
data_set["warning_code"] = []
for i in item:
if i in warning_ids:
data_set["warning_code"].append(warning_ids[i])
else:
data_set["warning_code"].append('unknown: %d' % i)
2023-07-24 08:58:19 -04:00
2023-07-25 05:49:47 -04:00
elif ftype == 0x06: # Paper path
2023-07-24 08:58:19 -04:00
data_set["paper_path"] = item
if item == b'\x01\xff':
data_set["paper_path"] = "Cut sheet (Rear)"
2023-08-04 19:29:16 -04:00
if item == b'\x03\x01':
data_set["paper_path"] = "Roll paper"
if item == b'\x03\x02':
data_set["paper_path"] = "Photo Album"
2024-03-14 10:26:29 -04:00
if item == b'\x02\x01\x00':
data_set["paper_path"] = "Cut Sheet (Auto Select)"
if item == b'\x02\x01':
data_set["paper_path"] = "CD-R, cardboard"
2023-08-04 19:29:16 -04:00
if item == b'\x02\x01':
data_set["paper_path"] = "CD-R, cardboard"
elif ftype == 0x07: # Paper mismatch error
data_set["paper_error"] = item
2023-07-24 08:58:19 -04:00
2023-08-04 18:01:15 -04:00
elif ftype == 0x0c: # Cleaning time information
data_set["cleaning_time"] = int.from_bytes(
item , "little", signed=True)
2023-08-04 06:25:02 -04:00
elif ftype == 0x0d: # maintenance tanks
data_set["tanks"] = str([i for i in item])
2023-08-04 12:45:54 -04:00
elif ftype == 0x0e: # Replace cartridge information
data_set["replace_cartridge"] = "{:08b}".format(item[0])
2023-11-11 07:14:01 -05:00
elif ftype == 0x0f: # Ink information
2023-08-04 06:25:02 -04:00
colourlen = item[0]
offset = 1
inks = []
while offset < length:
colour = item[offset]
2023-08-04 12:45:54 -04:00
ink_color = item[offset + 1]
level = item[offset + 2]
2023-08-04 06:25:02 -04:00
offset += colourlen
if colour in colour_ids:
name = colour_ids[colour]
else:
name = "0x%X" % colour
2023-08-16 13:18:41 -04:00
if ink_color in ink_color_ids:
ink_name = ink_color_ids[ink_color]
2023-08-04 12:45:54 -04:00
else:
ink_name = "0x%X" % ink_color
2023-08-04 06:25:02 -04:00
2023-08-04 12:45:54 -04:00
inks.append((colour, ink_color, name, ink_name, level))
2023-08-04 06:25:02 -04:00
2023-08-04 12:45:54 -04:00
data_set["ink_level"] = inks
2023-07-24 14:58:50 -04:00
2023-07-25 05:49:47 -04:00
elif ftype == 0x10: # Loading path information
2023-07-24 08:58:19 -04:00
data_set["loading_path"] = item.hex().upper()
2023-08-04 13:16:24 -04:00
if data_set["loading_path"] in [
"01094E", "01084E0E4E4E014E4E", "010C4E0E4E4E084E4E"]:
2023-07-24 14:58:50 -04:00
data_set["loading_path"] = "fixed"
2023-07-24 08:58:19 -04:00
2023-07-25 05:49:47 -04:00
elif ftype == 0x13: # Cancel code
2023-07-24 08:58:19 -04:00
data_set["cancel_code"] = item
if item == b'\x01':
data_set["cancel_code"] = "No request"
if item == b'\xA1':
2023-07-25 05:49:47 -04:00
data_set["cancel_code"] = (
"Received cancel command and printer initialization"
)
2023-07-24 08:58:19 -04:00
if item == b'\x81':
data_set["cancel_code"] = "Request"
2023-08-04 19:29:16 -04:00
elif ftype == 0x14: # Cutter information
try:
data_set["cutter"] = item.decode()
except Exception:
data_set["cutter"] = str(item)
if item == b'\x01':
data_set["cutter"] = "Set cutter"
2023-08-05 06:05:42 -04:00
elif ftype == 0x18: # Stacker(tray) open status
data_set["tray_open"] = item
if item == b'\x02':
data_set["tray_open"] = "Closed"
if item == b'\x03':
data_set["tray_open"] = "Open"
2023-11-11 07:14:01 -05:00
elif ftype == 0x19: # Current job name information
2023-08-04 06:25:02 -04:00
data_set["jobname"] = item
if item == b'\x00\x00\x00\x00\x00unknown':
data_set["jobname"] = "Not defined"
2023-08-05 06:05:42 -04:00
elif ftype == 0x1c: # Temperature information
data_set["temperature"] = item
if item == b'\x01':
data_set["temperature"] = (
"The printer temperature is higher than 40C"
)
if item == b'\x00':
data_set["temperature"] = (
"The printer temperature is lower than 40C"
)
2023-08-04 06:25:02 -04:00
elif ftype == 0x1f: # serial
2023-08-04 13:16:24 -04:00
try:
data_set["serial"] = item.decode()
except Exception:
data_set["serial"] = str(item)
2023-08-04 19:29:16 -04:00
elif ftype == 0x35: # Paper jam error information
data_set["paper_jam"] = item
if item == b'\x00':
data_set["paper_jam"] = "No jams"
if item == b'\x01':
data_set["paper_jam"] = "Paper jammed at ejecting"
if item == b'\x02':
data_set["paper_jam"] = "Paper jam in rear ASF or no feed"
if item == b'\x80':
data_set["paper_jam"] = "No papers at rear ASF"
2023-08-04 18:01:15 -04:00
elif ftype == 0x36: # Paper count information
if length != 20:
data_set["paper_count"] = "error"
2023-08-06 05:04:12 -04:00
logging.info(
"status_parser: paper_count error. Length: %s", length)
2023-08-04 18:01:15 -04:00
continue
data_set["paper_count_normal"] = int.from_bytes(
item[0:4] , "little", signed=True)
data_set["paper_count_page"] = int.from_bytes(
item[4:8] , "little", signed=True)
data_set["paper_count_color"] = int.from_bytes(
item[8:12] , "little", signed=True)
data_set["paper_count_monochrome"] = int.from_bytes(
item[12:16] , "little", signed=True)
data_set["paper_count_blank"] = int.from_bytes(
item[16:20] , "little", signed=True)
2023-08-04 06:25:02 -04:00
elif ftype == 0x37: # Maintenance box information
2023-08-04 12:45:54 -04:00
num_bytes = item[0]
if num_bytes < 1 or num_bytes > 2:
data_set["maintenance_box"] = "unknown"
continue
j = 1
for i in range(1, length, num_bytes):
2023-07-24 14:58:50 -04:00
if item[i] == 0:
2023-07-25 05:49:47 -04:00
data_set[f"maintenance_box_{j}"] = (
2023-08-04 06:25:02 -04:00
f"not full ({item[i]})"
2023-07-25 05:49:47 -04:00
)
2023-07-24 14:58:50 -04:00
elif item[i] == 1:
2023-07-25 05:49:47 -04:00
data_set[f"maintenance_box_{j}"] = (
2023-08-04 06:25:02 -04:00
f"near full ({item[i]})"
2023-07-25 05:49:47 -04:00
)
2023-07-24 14:58:50 -04:00
elif item[i] == 2:
2023-07-25 05:49:47 -04:00
data_set[f"maintenance_box_{j}"] = (
2023-08-04 06:25:02 -04:00
f"full ({item[i]})"
2023-07-25 05:49:47 -04:00
)
2023-07-24 14:58:50 -04:00
else:
2023-07-25 05:49:47 -04:00
data_set[f"maintenance_box_{j}"] = (
2023-08-04 06:25:02 -04:00
f"unknown ({item[i]})"
2023-07-25 05:49:47 -04:00
)
2023-08-04 12:45:54 -04:00
if num_bytes > 1:
data_set[f"maintenance_box_reset_count_{j}"] = item[
i + 1]
j += 1
2023-11-11 07:14:01 -05:00
elif ftype == 0x3d: # Printer I/F status
2023-08-04 19:29:16 -04:00
data_set["interface_status"] = item
if item == b'\x00':
data_set["interface_status"] = (
"Available to accept data and reply"
)
if item == b'\x01':
data_set["interface_status"] = (
"Not available to accept data"
)
2023-08-04 12:45:54 -04:00
elif ftype == 0x40: # Serial No. information
try:
data_set["serial_number_info"] = item.decode()
except Exception:
data_set["serial_number_info"] = str(item)
2023-07-24 14:58:50 -04:00
2023-08-16 13:18:41 -04:00
elif ftype == 0x45 and length == 4: # Ink replacement counter (TBV)
data_set["ink_replacement_counter"] = {
2024-07-06 20:45:00 -04:00
"Black": item[0],
"Cyan": item[1],
"Magenta": item[2],
"Yellow": item[3],
2023-08-16 13:18:41 -04:00
}
elif ftype == 0x46 and length == 1: # Maintenance_box_replacement_counter (TBV)
data_set["maintenance_box_replacement_counter"] = item[0]
2023-07-25 05:49:47 -04:00
else: # unknown stuff
2023-07-24 08:58:19 -04:00
if "unknown" not in data_set:
data_set["unknown"] = []
data_set["unknown"].append((hex(ftype), item))
return data_set
2024-08-09 17:22:32 -04:00
def get_snmp_info(
self,
mib_name: str = None,
advanced: bool = False
) -> str:
2023-07-25 05:49:47 -04:00
"""Return general SNMP information of printer."""
sys_info = {}
2024-08-09 17:22:32 -04:00
if advanced:
oids = {**self.MIB_INFO, **self.MIB_INFO_ADVANCED}
else:
oids = self.MIB_INFO
if mib_name and mib_name in oids.keys():
snmp_info = {mib_name: oids[mib_name]}
2023-07-25 05:49:47 -04:00
else:
2024-08-09 17:22:32 -04:00
snmp_info = oids
2023-07-25 05:49:47 -04:00
for name, oid in snmp_info.items():
2023-08-06 11:19:41 -04:00
logging.debug(
f"SNMP_DUMP {name}:\n"
f" ADDRESS: {oid}"
)
2023-08-08 11:35:34 -04:00
tag, result = self.snmp_mib(oid, label="get_snmp_info " + name)
logging.debug(" TAG: %s\n RESPONSE: %s", tag, repr(result))
2023-11-10 15:23:51 -05:00
if name == "Power Off Timer" and result and result.find(
b'@BDC PS\r\not:01') > 0:
try:
power_off_h = int.from_bytes(bytes.fromhex(
result[
result.find(b'@BDC PS\r\not:01') + 14
:
result.find(b';')
].decode()
), byteorder="little") / 60
sys_info[name] = f"{power_off_h} hours"
except Exception:
sys_info[name] = "(unknown)"
elif name == "hex_data" and result is not False:
2023-08-04 03:47:53 -04:00
sys_info[name] = result.hex(" ").upper()
elif name == "UpTime" and result is not False:
sys_info[name] = time.strftime(
'%H:%M:%S', time.gmtime(int(result)/100))
2023-08-29 15:44:22 -04:00
elif name.startswith("MAC ") and result is not False:
2023-08-04 03:47:53 -04:00
sys_info[name] = result.hex("-").upper()
elif isinstance(result, bytes):
sys_info[name] = result.decode()
elif isinstance(result, str):
sys_info[name] = result
else:
2023-08-06 05:04:12 -04:00
logging.info(
2023-08-06 00:51:18 -04:00
f"No value for SNMP OID '{name}'. MIB: {oid}.")
2023-07-25 05:49:47 -04:00
return sys_info
def get_serial_number(self) -> str:
2023-08-07 23:36:21 -04:00
"""Return the serial number of the printer (or "?" if error)."""
2023-08-06 05:04:12 -04:00
if not self.parm:
logging.error("EpsonPrinter - invalid API usage")
return None
if "serial_number" not in self.parm:
2023-07-25 05:49:47 -04:00
return None
return "".join(
2023-08-07 23:36:21 -04:00
chr(int(value or "0x3f", 16)) # "0x3f" --> "?"
2023-07-25 05:49:47 -04:00
for value in self.read_eeprom_many(
2023-08-03 18:09:11 -04:00
self.parm["serial_number"], label="serial_number")
2023-07-25 05:49:47 -04:00
)
def get_stats(self, stat_name: str = None) -> str:
2023-07-25 05:49:47 -04:00
"""Return printer statistics."""
2023-08-06 05:04:12 -04:00
if not self.parm:
logging.error("EpsonPrinter - invalid API usage")
return None
if "stats" not in self.parm:
2023-07-25 05:49:47 -04:00
return None
2023-08-03 18:09:11 -04:00
if stat_name and stat_name in self.parm["stats"].keys():
stat_info = {stat_name: self.parm["stats"][stat_name]}
2023-07-26 06:55:29 -04:00
else:
2023-08-03 18:09:11 -04:00
stat_info = self.parm["stats"]
2023-07-25 05:49:47 -04:00
stats_result = {}
2023-07-26 06:55:29 -04:00
for stat_name, oids in stat_info.items():
2023-07-25 05:49:47 -04:00
total = 0
2023-07-26 06:55:29 -04:00
for val in self.read_eeprom_many(oids, label=stat_name):
2023-08-03 18:09:11 -04:00
if val is None:
2023-08-16 13:18:41 -04:00
total = None
break
else:
total = (total << 8) + int(val, 16)
2023-07-25 05:49:47 -04:00
stats_result[stat_name] = total
2023-08-16 13:18:41 -04:00
if stat_name == "MAC Address" and total != None:
stats_result[stat_name] = total.to_bytes(
length=6, byteorder='big').hex("-").upper()
2023-08-04 06:25:02 -04:00
if "First TI received time" not in stats_result:
2023-08-04 12:45:54 -04:00
return stats_result
2023-07-25 05:49:47 -04:00
ftrt = stats_result["First TI received time"]
year = 2000 + ftrt // (16 * 32)
month = (ftrt - (year - 2000) * (16 * 32)) // 32
2023-07-26 06:55:29 -04:00
day = ftrt - (year - 2000) * 16 * 32 - 32 * month
2023-07-25 05:49:47 -04:00
stats_result["First TI received time"] = datetime.datetime(
year, month, day).strftime('%d %b %Y')
return stats_result
2023-08-07 23:36:21 -04:00
def get_printer_head_id(self) -> str: # only partially correct
2023-07-25 05:49:47 -04:00
"""Return printer head id."""
2023-08-06 05:04:12 -04:00
if not self.parm:
logging.error("EpsonPrinter - invalid API usage")
return None
if "printer_head_id_h" not in self.parm:
2023-07-25 05:49:47 -04:00
return None
2023-08-03 18:09:11 -04:00
if "printer_head_id_f" not in self.parm:
2023-07-25 05:49:47 -04:00
return None
2023-07-26 06:55:29 -04:00
a = self.read_eeprom_many(
2023-08-03 18:09:11 -04:00
self.parm["printer_head_id_h"], label="printer_head_id_h")
2023-07-26 06:55:29 -04:00
b = self.read_eeprom_many(
2023-08-03 18:09:11 -04:00
self.parm["printer_head_id_f"], label="printer_head_id_f")
2023-08-07 23:36:21 -04:00
if a == [None] or b == [None]:
2023-08-03 18:09:11 -04:00
return None
2023-07-25 05:49:47 -04:00
return(f'{"".join(a)} - {"".join(b)}')
def get_firmware_version(self) -> str:
"""
Return firmware version.
Query firmware version: 1.3.6.1.4.1.1248.1.2.2.44.1.1.2.1.118.105.1.0.0
"""
2023-11-11 04:37:03 -05:00
oid = f"{self.EEPROM_LINK}.118.105.1.0.0" # 76 69 01 00 00
2023-08-06 11:19:41 -04:00
label = "get_firmware_version"
logging.debug(
f"SNMP_DUMP {label}:\n"
f" ADDRESS: {oid}"
)
2023-08-08 11:35:34 -04:00
tag, firmware_string = self.snmp_mib(oid, label=label)
2023-07-25 05:49:47 -04:00
if not firmware_string:
return None
2023-08-07 23:36:21 -04:00
if self.invalid_response(firmware_string):
logging.error(
f"Invalid response for %s: '%s'",
label, repr(firmware_string)
)
2023-08-08 11:35:34 -04:00
logging.debug(" TAG: %s\n RESPONSE: %s", tag, repr(firmware_string))
2023-08-04 03:47:53 -04:00
firmware = re.sub(
r".*vi:00:(.{6}).*", r'\g<1>', firmware_string.decode())
2023-07-25 05:49:47 -04:00
year = ord(firmware[4:5]) + 1945
month = int(firmware[5:], 16)
day = int(firmware[2:4])
return firmware + " " + datetime.datetime(
year, month, day).strftime('%d %b %Y')
def get_cartridges(self) -> str:
"""Return list of cartridge types."""
2023-11-11 04:37:03 -05:00
oid = f"{self.EEPROM_LINK}.105.97.1.0.0" # 69 61 01 00 00
2023-08-06 11:19:41 -04:00
label = "get_cartridges"
logging.debug(
f"SNMP_DUMP {label}:\n"
f" ADDRESS: {oid}"
)
2023-08-08 11:35:34 -04:00
tag, cartridges_string = self.snmp_mib(oid, label=label)
2023-08-07 23:36:21 -04:00
if self.invalid_response(cartridges_string):
logging.error(
f"Invalid response for %s: '%s'",
label, repr(cartridges_string)
)
2023-07-25 05:49:47 -04:00
if not cartridges_string:
return None
2023-08-08 11:35:34 -04:00
logging.debug(
" TAG: %s\n RESPONSE: %s", tag, repr(cartridges_string))
2023-07-25 05:49:47 -04:00
cartridges = re.sub(
2023-08-04 03:47:53 -04:00
r".*IA:00;(.*);.*", r'\g<1>',
cartridges_string.decode(),
flags=re.S
)
2023-07-25 05:49:47 -04:00
return [i.strip() for i in cartridges.split(',')]
def get_ink_replacement_counters(self) -> str:
"""Return list of ink replacement counters."""
2023-08-06 05:04:12 -04:00
if not self.parm:
logging.error("EpsonPrinter - invalid API usage")
return None
if "ink_replacement_counters" not in self.parm:
2023-07-25 05:49:47 -04:00
return None
irc = {
(
color,
counter,
2023-08-03 18:09:11 -04:00
int(
self.read_eeprom(
value, label="ink_replacement_counters") or "-1", 16
),
)
2023-08-03 18:09:11 -04:00
for color, data in self.parm[
"ink_replacement_counters"].items()
for counter, value in data.items()
2023-07-25 05:49:47 -04:00
}
return irc
2023-07-24 08:58:19 -04:00
def get_printer_status(self):
"""
Return printer status and ink levels.
Query printer status: 1.3.6.1.4.1.1248.1.2.2.44.1.1.2.1.115.116.1.0.1
or 1.3.6.1.4.1.1248.1.2.2.1.1.1.4.1
"""
2023-11-11 04:37:03 -05:00
address = f"{self.EEPROM_LINK}.115.116.1.0.1" # 73 74 01 00 01
2023-08-06 00:51:18 -04:00
logging.debug(f"PRINTER_STATUS:\n ADDRESS: {address}")
2023-08-08 11:35:34 -04:00
tag, result = self.snmp_mib(address, label="get_printer_status")
2023-07-25 05:49:47 -04:00
if not result:
return None
2023-08-08 11:35:34 -04:00
logging.debug(" TAG: %s\n RESPONSE: %s...\n%s",
tag,
repr(result[:20]),
2023-08-06 00:51:18 -04:00
textwrap.fill(
result.hex(' '),
initial_indent=" ",
subsequent_indent=" ",
2023-08-05 07:51:38 -04:00
)
2023-08-06 00:51:18 -04:00
)
2023-08-04 03:47:53 -04:00
return self.status_parser(result)
2023-07-24 08:58:19 -04:00
def get_waste_ink_levels(self):
"""Return waste ink levels as a percentage."""
2023-08-06 05:04:12 -04:00
if not self.parm:
logging.error("EpsonPrinter - invalid API usage")
return None
if "main_waste" not in self.parm:
2023-07-25 05:49:47 -04:00
return None
2023-07-31 10:54:58 -04:00
results = {}
for waste_type in ["main_waste", "borderless_waste", "first_waste",
"second_waste", "third_waste"]:
2023-08-03 18:09:11 -04:00
if waste_type not in self.parm:
2023-07-31 10:54:58 -04:00
continue
2023-07-25 05:49:47 -04:00
level = self.read_eeprom_many(
2023-08-03 18:09:11 -04:00
self.parm[waste_type]["oids"], label=waste_type)
2023-08-07 23:36:21 -04:00
if level == [None]:
2023-08-03 18:09:11 -04:00
return None
2023-07-25 05:49:47 -04:00
level_b10 = int("".join(reversed(level)), 16)
2023-07-31 10:54:58 -04:00
results[waste_type] = round(
2023-08-03 18:09:11 -04:00
level_b10 / self.parm[waste_type]["divider"], 2)
2023-07-24 08:58:19 -04:00
return results
2023-08-07 23:36:21 -04:00
def get_last_printer_fatal_errors(self) -> list:
"""
Return the list of last printer fatal errors in hex format
(or [None] if error).
"""
2023-08-06 05:04:12 -04:00
if not self.parm:
logging.error("EpsonPrinter - invalid API usage")
return None
if "last_printer_fatal_errors" not in self.parm:
2023-07-25 05:49:47 -04:00
return None
2023-07-26 06:55:29 -04:00
return self.read_eeprom_many(
2023-08-03 18:09:11 -04:00
self.parm["last_printer_fatal_errors"],
2023-07-26 06:55:29 -04:00
label="last_printer_fatal_errors"
)
2023-08-05 11:05:09 -04:00
def ink_color(self, number):
2023-08-07 23:36:21 -04:00
"""
2023-08-16 13:18:41 -04:00
Return a list including the cartridge input number and the related
name of the ink color (or "unknown color" if not included
in self.CARTRIDGE_TYPE).
2023-08-07 23:36:21 -04:00
"""
2023-08-16 13:18:41 -04:00
return [
number,
self.CARTRIDGE_TYPE[
number] if number in self.CARTRIDGE_TYPE else "unknown color",
]
2023-08-05 11:05:09 -04:00
def get_cartridge_information(self) -> str:
"""Return list of cartridge properties."""
response = []
for i in range(1, 9):
2023-11-11 04:37:03 -05:00
mib = f"{self.EEPROM_LINK}.105.105.2.0.1." + str(i) # 69 69 02 00 01
2023-08-06 00:51:18 -04:00
logging.debug(
f"Cartridge {i}:\n"
f" ADDRESS: {mib}"
)
2023-08-08 11:35:34 -04:00
tag, cartridge = self.snmp_mib(
2024-01-30 18:04:00 -05:00
mib, label="get_cartridge_information"
)
2023-08-08 11:35:34 -04:00
logging.debug(" TAG: %s\n RESPONSE: %s", tag, repr(cartridge))
2023-08-05 11:13:29 -04:00
if not cartridge:
continue
2023-08-07 23:36:21 -04:00
if self.invalid_response(cartridge):
logging.error(
f"Invalid cartridge response: '%s'",
repr(cartridge)
)
return None
2023-08-05 11:13:29 -04:00
if cartridge.find(b'ii:NA;') > 0 or cartridge.find(
2023-08-06 17:10:26 -04:00
b'@BDC PS\r\n') < 0:
2023-08-05 11:13:29 -04:00
break
2023-08-06 17:10:26 -04:00
response.append(cartridge)
if not response:
return None
2023-08-07 04:22:10 -04:00
return self.cartridge_parser(response)
def cartridge_parser(self, cartridges: List[bytes]) -> str:
2023-08-16 13:18:41 -04:00
"""Parse the cartridge properties and decode as much as possible."""
2023-08-06 17:10:26 -04:00
response = [
cartridge[cartridge.find(b'@BDC PS\r\n') + 9
:
-2 if cartridge[-1] == 12 else -1]
.decode()
.split(';')
for cartridge in cartridges
]
2023-08-05 11:13:29 -04:00
if not response:
return None
2023-08-06 00:51:18 -04:00
try:
cartridges = [
{i[0]: i[1] for i in map(lambda x: x.split(':'), j)}
for j in response
]
except Exception as e:
2023-08-07 04:22:10 -04:00
logging.error("Cartridge map error: %s", e)
2023-08-06 00:51:18 -04:00
return None
2023-08-07 23:36:21 -04:00
if logging.getLogger().level <= logging.DEBUG:
for i in cartridges:
logging.debug("Raw cartridge information:")
for j in i:
value = ""
if len(i[j]) < 6:
try:
value = str(int(i[j], 16))
except Exception:
pass
if i[j] == "NAVL":
value = "(Not available)"
logging.debug(
" %s = %s %s",
j.rjust(4), i[j].rjust(4), value.rjust(4)
)
2023-08-06 00:51:18 -04:00
try:
2024-08-06 01:13:49 -04:00
missing = "Not available"
2023-08-06 00:51:18 -04:00
return [
{
2023-11-11 07:14:01 -05:00
k: v for k, v in
{
2024-08-06 01:13:49 -04:00
"ink_color": self.ink_color(int(i['IC1'], 16))
if 'IC1' in i else missing,
"ink_quantity": int(i['IQT'], 16)
if 'IQT' in i else missing,
2023-11-11 07:14:01 -05:00
"production_year": int(i['PDY'], 16) + (
2024-08-06 01:13:49 -04:00
1900 if int(i['PDY'], 16) > 80 else 2000)
if 'PDY' in i else missing,
"production_month": int(i['PDM'], 16)
if 'PDM' in i else missing,
"data": i.get('SID').strip()
if 'SID' in i else missing,
"manufacturer": i.get('LOG').strip()
if 'LOG' in i else missing,
2023-11-11 07:14:01 -05:00
}.items()
if v # exclude items without value
2024-08-06 01:13:49 -04:00
} if 'II' in i and i['II'] == '03' else {
"Ink Information": f"Unknown {i['II']}"
if 'II' in i and i['II'] != '00' else missing
2023-11-11 07:14:01 -05:00
}
for i in cartridges
2023-08-06 00:51:18 -04:00
]
except Exception as e:
2024-08-06 01:13:49 -04:00
logging.error("Cartridge value error: %s.\n%s", e, cartridges)
2023-08-06 00:51:18 -04:00
return None
2023-08-05 11:05:09 -04:00
2023-07-26 06:55:29 -04:00
def dump_eeprom(self, start: int = 0, end: int = 0xFF):
2023-07-25 05:49:47 -04:00
"""
Dump EEPROM data from start to end (less significant byte).
"""
d = {}
2023-07-26 06:55:29 -04:00
for oid in range(start, end + 1):
2023-08-08 05:33:12 -04:00
d[oid] = int(
self.read_eeprom(oid, label="dump_eeprom") or "-0x1",
16
)
2023-07-25 05:49:47 -04:00
return d
2024-09-18 22:49:14 -04:00
def reset_waste_ink_levels(self, dry_run=False) -> bool:
2023-07-24 08:58:19 -04:00
"""
Set waste ink levels to 0.
"""
2023-08-06 00:51:18 -04:00
if not self.parm:
2023-08-06 05:04:12 -04:00
logging.error("EpsonPrinter - invalid API usage")
2023-08-06 00:51:18 -04:00
return None
2023-08-03 18:09:11 -04:00
if "raw_waste_reset" in self.parm:
2024-09-18 22:49:14 -04:00
if dry_run:
return True
2023-08-03 18:09:11 -04:00
for oid, value in self.parm["raw_waste_reset"].items():
2023-07-31 07:41:37 -04:00
if not self.write_eeprom(oid, value, label="raw_waste_reset"):
return False
return True
2023-08-03 18:09:11 -04:00
if "main_waste" not in self.parm:
2023-07-31 07:41:37 -04:00
return None
2024-09-18 22:49:14 -04:00
if dry_run:
return True
2023-08-03 18:09:11 -04:00
for oid in self.parm["main_waste"]["oids"]:
2023-07-31 07:41:37 -04:00
if not self.write_eeprom(oid, 0, label="main_waste"):
return False
2023-08-03 18:09:11 -04:00
if "borderless_waste" not in self.parm:
2023-07-31 07:41:37 -04:00
return True
2023-08-03 18:09:11 -04:00
for oid in self.parm["borderless_waste"]["oids"]:
2023-07-31 07:41:37 -04:00
if not self.write_eeprom(oid, 0, label="borderless_waste"):
return False
return True
2023-07-24 08:58:19 -04:00
2023-07-25 05:49:47 -04:00
def write_first_ti_received_time(
2023-07-31 07:41:37 -04:00
self, year: int, month: int, day: int) -> bool:
2023-07-25 05:49:47 -04:00
"""Update first TI received time"""
2023-08-06 00:51:18 -04:00
if not self.parm:
2023-08-06 05:04:12 -04:00
logging.error("EpsonPrinter - invalid API usage")
2023-08-06 00:51:18 -04:00
return None
2023-07-31 07:41:37 -04:00
try:
2023-08-03 18:09:11 -04:00
msb = self.parm["stats"]["First TI received time"][0]
lsb = self.parm["stats"]["First TI received time"][1]
2023-07-31 07:41:37 -04:00
except KeyError:
2023-08-06 05:04:12 -04:00
logging.info("write_first_ti_received_time: missing parameter")
2023-07-31 07:41:37 -04:00
return False
2023-07-25 05:49:47 -04:00
n = (year - 2000) * 16 * 32 + 32 * month + day
2023-08-06 00:51:18 -04:00
logging.debug(
"FTRT: %s %s = %s %s",
hex(n // 256), hex(n % 256), n // 256, n % 256)
2023-07-31 07:41:37 -04:00
if not self.write_eeprom(msb, n // 256, label="First TI received time"):
return False
if not self.write_eeprom(lsb, n % 256, label="First TI received time"):
return False
return True
2023-11-10 18:39:31 -05:00
def write_poweroff_timer(self, mins: int) -> bool:
"""Update power-off timer"""
if not self.parm:
logging.error("EpsonPrinter - invalid API usage")
return None
try:
msb = self.parm["stats"]["Power off timer"][0]
lsb = self.parm["stats"]["Power off timer"][1]
2023-11-10 18:39:31 -05:00
except KeyError:
logging.info("write_poweroff_timer: missing parameter")
return False
logging.debug(
"poweroff: %s %s = %s %s",
hex(mins // 256), hex(mins % 256), mins // 256, mins % 256)
if not self.write_eeprom(
msb, mins // 256, label="Write power off timer"
):
return False
if not self.write_eeprom(
lsb, mins % 256, label="Write power off timer"
):
return False
return True
2023-08-04 03:47:53 -04:00
def list_known_keys(self):
2023-08-16 13:18:41 -04:00
""" List all known read and write keys for all defined printers. """
2023-08-06 00:51:18 -04:00
known_keys = []
2023-08-03 18:09:11 -04:00
for model, chars in self.PRINTER_CONFIG.items():
2023-07-31 07:41:37 -04:00
if 'write_key' in chars:
2023-08-06 00:51:18 -04:00
known_keys.append(
2023-08-04 03:47:53 -04:00
f"{repr(model).rjust(25)}: "
f"{repr(chars['read_key']).rjust(10)} - "
f"{repr(chars['write_key'])[1:]}"
)
2023-07-31 10:54:58 -04:00
else:
2023-08-06 00:51:18 -04:00
known_keys.append(
2023-08-04 03:47:53 -04:00
f"{repr(model).rjust(25)}: "
f"{repr(chars['read_key']).rjust(10)} "
f"(unknown write key)"
)
2023-08-06 00:51:18 -04:00
return known_keys
2023-07-25 05:49:47 -04:00
2023-08-06 00:51:18 -04:00
def brute_force_read_key(self, minimum: int = 0x00, maximum: int = 0xFF):
2023-07-24 08:58:19 -04:00
"""Brute force read_key for printer."""
2023-08-06 00:51:18 -04:00
if not self.parm:
2023-08-06 05:04:12 -04:00
logging.error("EpsonPrinter - invalid API usage")
2023-08-06 00:51:18 -04:00
return None
2023-07-31 10:54:58 -04:00
for x, y in itertools.permutations(range(minimum, maximum + 1), r=2):
2023-08-03 18:09:11 -04:00
self.parm['read_key'] = [x, y]
2023-08-06 05:04:12 -04:00
logging.warning(f"Trying {self.parm['read_key']}...")
2023-07-31 10:54:58 -04:00
val = self.read_eeprom(0x00, label="brute_force_read_key")
if val is None:
2023-07-24 08:58:19 -04:00
continue
2023-08-03 18:09:11 -04:00
return self.parm['read_key']
2023-07-24 08:58:19 -04:00
return None
def find_serial_number(self, eeprom_range):
2024-10-13 17:55:50 -04:00
"""
Detect serial number analyzing eeprom_range addresses
A valid value for eeprom_range is range(2048)
"""
# Read the EEPROM data
hex_bytes = self.read_eeprom_many(
eeprom_range, label="detect_serial_number"
)
# Convert the hex bytes to characters
sequence = ''.join(chr(int(byte, 16)) for byte in hex_bytes)
# Serial number pattern (10 consecutive uppercase letters or digits)
serial_number_pattern = r'[A-Z0-9]{10}'
# Find all matches
return hex_bytes, list(re.finditer(serial_number_pattern, sequence))
def write_key_list(self, read_key):
""" Produce a list of distinct write_key prioritizing ones with same read_key """
write_key_list = []
for p, v in self.PRINTER_CONFIG.items():
if (
'read_key' in v
and v['read_key'] == read_key
and 'write_key' in v
and v['write_key'] not in write_key_list
):
write_key_list.append(v['write_key'])
for p, v in self.PRINTER_CONFIG.items():
if (
'write_key' in v
and v['write_key'] not in write_key_list
):
write_key_list.append(v['write_key'])
return write_key_list
def validate_write_key(self, addr, value, label):
""" Validate write_key by writing values to the EEPROM """
if not self.write_eeprom(addr, value + 1, label=label): # test write
return None
ret_value = int(self.read_eeprom(addr), 16)
if not self.write_eeprom(addr, value, label=label): # restore previous value
return None
if int(self.read_eeprom(addr), 16) != value:
return None
return ret_value == value + 1
2023-07-31 07:41:37 -04:00
def write_sequence_to_string(self, write_sequence):
2023-08-16 13:18:41 -04:00
""" Convert write key sequence to string """
2023-07-31 10:54:58 -04:00
try:
int_sequence = [int(b) for b in write_sequence[0].split(".")]
return "".join([chr(b-1) for b in int_sequence])
except Exception:
return None
2023-07-31 07:41:37 -04:00
2023-08-06 11:19:41 -04:00
def read_config_file(self, file):
2023-08-16 13:18:41 -04:00
"""
Read a configuration file including the full log dump of a
previous operation with '-d' flag and create the internal mib_dict
dictionary, which is used in place of the SNMP query, simulating them
instead of accessing the printer via SNMP.
"""
2023-08-06 12:33:12 -04:00
class NextLine:
def __init__(self, file):
self.next_line = None
self.recursion = 0
self.file = file
def readline(self):
next_line = self.next_line
if self.next_line != None and self.recursion < 2:
self.next_line = None
return next_line
if next_line != None:
logginf.error("Recursion error: '%s'", next_line)
self.next_line = None
self.recursion = 0
return next(self.file)
def pushline(self, line):
if self.next_line != None:
logginf.error(
"Line already pushed: '%s', '%s'",
self.next_line, line
)
self.next_line = line
self.recursion += 1
2023-08-06 11:19:41 -04:00
mib_dict = {}
2023-08-06 12:33:12 -04:00
next_line = NextLine(file)
2023-08-06 17:10:26 -04:00
process = False
2023-08-06 11:19:41 -04:00
try:
while True:
2023-08-06 12:33:12 -04:00
line = next_line.readline()
2023-08-06 11:19:41 -04:00
oid = None
value = None
process = None
address_val = None
response_val = None
2023-08-08 11:35:34 -04:00
tag_val = None
2023-08-06 11:19:41 -04:00
response_val_bytes = None
if line.startswith("PRINTER_STATUS:"):
oid = False
value = False
process = True
response_next = True
if line.startswith("Cartridge "):
oid = False
value = False
process = True
response_next = False
if line.startswith("SNMP_DUMP "):
oid = False
value = False
process = True
response_next = False
if line.startswith("EEPROM_DUMP "):
oid = True
value = False
process = True
response_next = False
if line.startswith("EEPROM_WRITE "):
oid = True
value = True
process = True
response_next = False
if process:
# address
2023-08-06 12:33:12 -04:00
address_line = next_line.readline()
2023-08-06 11:19:41 -04:00
if not address_line.startswith(" ADDRESS: "):
logging.error(
"Missing ADDRESS: '%s'", address_line.rstrip())
2023-08-06 12:33:12 -04:00
next_line.pushline(address_line)
2023-08-06 11:19:41 -04:00
continue
address_val = address_line[11:].rstrip()
if not address_val:
logging.error(
"Invalid ADDRESS: '%s'", address_line.rstrip())
2023-08-06 12:33:12 -04:00
next_line.pushline(address_line)
2023-08-06 11:19:41 -04:00
continue
# oid
if oid:
2023-08-06 12:33:12 -04:00
oid_line = next_line.readline()
2023-08-06 11:19:41 -04:00
if not oid_line.startswith(" OID: "):
logging.error(
"Missing OID: '%s'", oid_line.rstrip())
2023-08-06 12:33:12 -04:00
next_line.pushline(oid_line)
2023-08-06 11:19:41 -04:00
continue
# value
if value:
2023-08-06 12:33:12 -04:00
value_line = next_line.readline()
2023-08-06 11:19:41 -04:00
if not value_line.startswith(" VALUE: "):
logging.error(
"Missing VALUE: '%s'", value_line.rstrip())
2023-08-06 12:33:12 -04:00
next_line.pushline(value_line)
2023-08-06 11:19:41 -04:00
continue
2023-08-08 11:35:34 -04:00
# tag
tag_line = next_line.readline()
if tag_line.startswith(" TAG: "):
tag_val = tag_line[7:].rstrip()
if not tag_val:
logging.error(
"Invalid TAG '%s'", tag_line.rstrip())
next_line.pushline(tag_line)
continue
2023-08-06 11:19:41 -04:00
# response
2023-08-06 12:33:12 -04:00
response_line = next_line.readline()
2023-08-06 11:19:41 -04:00
if response_line.startswith(" RESPONSE: "):
response_val = response_line[12:].rstrip()
if not response_val:
logging.error(
"Invalid RESPONSE '%s'", response_line.rstrip())
2023-08-06 12:33:12 -04:00
next_line.pushline(response_line)
2023-08-06 11:19:41 -04:00
continue
if response_next:
dump_hex_str = ""
while True:
2023-08-06 12:33:12 -04:00
dump_hex = next_line.readline()
2023-08-06 11:19:41 -04:00
if not dump_hex.startswith(" "):
2023-08-06 12:33:12 -04:00
next_line.pushline(dump_hex)
2023-08-06 11:19:41 -04:00
break
try:
val = bytes.fromhex(dump_hex)
except ValueError:
2023-08-06 12:33:12 -04:00
next_line.pushline(dump_hex)
2023-08-06 11:19:41 -04:00
continue
dump_hex_str += dump_hex
if not dump_hex_str:
logging.error(
"Invalid DUMP: '%s'", dump_hex.rstrip())
2023-08-06 12:33:12 -04:00
next_line.pushline(dump_hex)
2023-08-06 11:19:41 -04:00
continue
try:
val = bytes.fromhex(dump_hex_str)
except ValueError:
logging.error(
"Invalid DUMP %s", dump_hex_str.rstrip())
2023-08-06 12:33:12 -04:00
next_line.pushline(dump_hex)
2023-08-06 11:19:41 -04:00
continue
if val:
2023-08-08 11:35:34 -04:00
mib_dict[address_val] = tag_val, val
2023-08-06 11:19:41 -04:00
else:
try:
response_val_bytes = ast.literal_eval(
response_val)
except Exception as e:
logging.error(
"Invalid response %s: %s",
response_line.rstrip(),
e
)
2023-08-06 12:33:12 -04:00
next_line.pushline(response_line)
2023-08-06 11:19:41 -04:00
continue
if response_val_bytes:
2023-08-08 11:35:34 -04:00
mib_dict[address_val] = tag_val, response_val_bytes
2023-08-06 11:19:41 -04:00
else:
logging.error(
"Null value for response %s",
response_line.rstrip()
)
2023-08-06 12:33:12 -04:00
next_line.pushline(response_line)
2023-08-06 11:19:41 -04:00
except StopIteration:
pass
2023-08-06 17:10:26 -04:00
if process:
logging.error("EOF while processing record set")
2023-08-06 13:03:52 -04:00
self.mib_dict = mib_dict
2023-08-06 11:19:41 -04:00
return mib_dict
2023-08-08 11:35:34 -04:00
def write_simdata(self, file):
2023-08-16 13:18:41 -04:00
"""
Convert the internal mib_dict dictionary into a configuration file
(named simdata configuration file) compatible with
https://github.com/etingof/snmpsim/
"""
2023-08-08 11:35:34 -04:00
tagnum = {
"OctetString": "4x",
"TimeTicks": "2", # 64
"Integer": "2",
}
try:
for key, (tag, value) in self.mib_dict.items():
if tag == "OctetString":
if isinstance(value, bytes):
write_line = f"{key}|{tagnum[tag]}|{value.hex()}\n"
else:
logging.error(
"OctetString is not byte type: key=%s, tag=%s, "
"value=%s, type=%s",
key, tag, value, type(value)
)
continue
else:
write_line = f"{key}|{tagnum[tag]}|{value}\n"
file.write(write_line)
file.close()
except Exception as e:
logging.error("simdata write error: %s", e)
return False
return True
2023-07-24 08:58:19 -04:00
if __name__ == "__main__":
import argparse
from pprint import pprint
2023-11-11 04:28:09 -05:00
def auto_int(x):
return int(x, 0)
2023-07-24 08:58:19 -04:00
parser = argparse.ArgumentParser(
2024-01-30 18:04:00 -05:00
epilog='Epson Printer Configuration via SNMP (TCP/IP)'
)
2023-07-24 08:58:19 -04:00
parser.add_argument(
'-m',
'--model',
dest='model',
action="store",
help='Printer model. Example: -m XP-205'
' (use ? to print all supported models)',
2023-07-24 08:58:19 -04:00
required=True)
parser.add_argument(
'-a',
'--address',
dest='hostname',
action="store",
2024-07-28 21:29:30 -04:00
help='Printer host name or IP address. (Example: -a 192.168.1.87)',
2023-07-24 08:58:19 -04:00
required=True)
2023-08-08 17:18:54 -04:00
parser.add_argument(
'-p',
'--port',
dest='port',
2023-11-11 04:28:09 -05:00
type=auto_int,
2023-08-08 17:18:54 -04:00
default=161,
action="store",
help='Printer port (default is 161)')
2023-07-24 08:58:19 -04:00
parser.add_argument(
'-i',
'--info',
dest='info',
action='store_true',
2023-07-25 05:49:47 -04:00
help='Print all available information and statistics (default option)')
parser.add_argument(
'-q',
'--query',
dest='query',
action='store',
type=str,
nargs=1,
2023-08-08 05:33:12 -04:00
metavar='QUERY_NAME',
2023-07-25 05:49:47 -04:00
help='Print specific information.'
' (Use ? to list all available queries)')
2023-07-24 08:58:19 -04:00
parser.add_argument(
'--reset_waste_ink',
dest='reset_waste_ink',
action='store_true',
help='Reset all waste ink levels to 0')
parser.add_argument(
'-d',
'--debug',
dest='debug',
action='store_true',
help='Print debug information')
parser.add_argument(
'--write-first-ti-received-time',
dest='ftrt',
2023-07-24 19:48:19 -04:00
type=int,
2023-08-06 11:34:30 -04:00
help='Change the first TI received time',
2023-07-24 08:58:19 -04:00
nargs=3,
2023-08-08 05:33:12 -04:00
metavar=('YEAR', 'MONTH', 'DAY'),
2023-07-24 08:58:19 -04:00
)
2023-11-10 18:39:31 -05:00
parser.add_argument(
'--write-poweroff-timer',
dest='poweroff',
2023-11-11 04:28:09 -05:00
type=auto_int,
help='Update the poweroff timer. Use 0xffff or 65535 to disable it.',
2023-11-10 18:39:31 -05:00
nargs=1,
metavar=('MINUTES'),
)
2023-08-08 05:33:12 -04:00
parser.add_argument(
'--dry-run',
dest='dry_run',
action='store_true',
help='Dry-run change operations')
2023-07-31 07:41:37 -04:00
parser.add_argument(
'-R',
'--read-eeprom',
dest='read_eeprom',
action='store',
type=str,
nargs=1,
2023-08-08 05:33:12 -04:00
metavar='ADDRESS_SET',
2023-07-31 07:41:37 -04:00
help='Read the values of a list of printer EEPROM addreses.'
' Format is: address [, ...]')
parser.add_argument(
'-W',
'--write-eeprom',
dest='write_eeprom',
action='store',
type=str,
nargs=1,
2023-08-08 05:33:12 -04:00
metavar='ADDRESS_VALUE_SET',
2023-07-31 07:41:37 -04:00
help='Write related values to a list of printer EEPROM addresses.'
' Format is: address: value [, ...]')
2023-08-08 05:33:12 -04:00
parser.add_argument(
'-e',
'--eeprom-dump',
dest='dump_eeprom',
action='store',
type=str,
nargs=2,
metavar=('FIRST_ADDRESS', 'LAST_ADDRESS'),
help='Dump EEPROM')
parser.add_argument(
"--detect-key",
dest='detect_key',
action='store_true',
help="Detect the read_key via brute force")
2023-07-31 07:41:37 -04:00
parser.add_argument(
'-S',
'--write-sequence-to-string',
dest='ws_to_string',
action='store',
type=str,
nargs=1,
2023-08-08 05:33:12 -04:00
metavar='SEQUENCE_STRING',
2023-07-31 07:41:37 -04:00
help='Convert write sequence of numbers to string.'
)
2023-08-03 18:09:11 -04:00
parser.add_argument(
'-t',
'--timeout',
dest='timeout',
type=float,
default=None,
help='SNMP GET timeout (floating point argument)',
)
parser.add_argument(
'-r',
'--retries',
dest='retries',
type=float,
default=None,
help='SNMP GET retries (floating point argument)',
)
2023-08-06 11:19:41 -04:00
parser.add_argument(
'-c',
"--config",
dest='config_file',
type=argparse.FileType('r'),
help="read a configuration file including the full log dump of a "
2023-08-06 11:24:30 -04:00
"previous operation with '-d' flag (instead of accessing the "
"printer via SNMP)",
2023-08-06 11:19:41 -04:00
default=0,
nargs=1,
2024-01-30 18:04:00 -05:00
metavar='CONFIG_FILE'
)
2023-08-08 11:35:34 -04:00
parser.add_argument(
"--simdata",
dest='simdata_file',
type=argparse.FileType('a'),
help="write SNMP dictionary map to simdata file",
default=0,
nargs=1,
2024-01-30 18:04:00 -05:00
metavar='SIMDATA_FILE'
)
2024-07-28 21:29:30 -04:00
parser.add_argument(
'-P',
"--pickle",
dest='pickle',
type=argparse.FileType('rb'),
2024-07-29 19:18:02 -04:00
help="Load a pickle configuration archive saved by parse_devices.py",
2024-07-28 21:29:30 -04:00
default=None,
nargs=1,
metavar='PICKLE_FILE'
)
parser.add_argument(
'-O',
"--override",
dest='override',
action='store_true',
2024-07-29 19:18:02 -04:00
help="Replace the default configuration with the one in the pickle "
"file instead of merging (default is to merge)",
2024-07-28 21:29:30 -04:00
)
2023-07-24 08:58:19 -04:00
args = parser.parse_args()
2023-08-06 05:04:12 -04:00
logging_level = logging.WARNING
2023-08-06 00:51:18 -04:00
logging_fmt = "%(message)s"
env_key=os.path.basename(Path(__file__).stem).upper() + '_LOG_CFG'
path = Path(__file__).stem + '-log.yaml'
value = os.getenv(env_key, None)
#print("Configuration file:", path, "| Environment variable:", env_key)
if value:
path = value
if os.path.exists(path):
with open(path, 'rt') as f:
config = yaml.safe_load(f.read())
try:
logging.config.dictConfig(config)
except Exception as e:
logging.basicConfig(level=logging_level, format=logging_fmt)
logging.critical("Cannot configure logs: %s. %s", e, path)
else:
logging.basicConfig(level=logging_level, format=logging_fmt)
if args.debug:
logging.getLogger().setLevel(logging.DEBUG)
2024-07-28 21:29:30 -04:00
conf_dict = {}
if args.pickle:
conf_dict = pickle.load(args.pickle[0])
2023-07-24 08:58:19 -04:00
printer = EpsonPrinter(
2024-07-28 21:29:30 -04:00
conf_dict=conf_dict,
replace_conf=args.override,
2023-08-08 17:18:54 -04:00
model=args.model,
hostname=args.hostname,
port=args.port,
2023-08-03 18:09:11 -04:00
timeout=args.timeout,
retries=args.retries,
dry_run=args.dry_run)
2023-08-06 11:19:41 -04:00
if args.config_file:
2023-08-06 13:03:52 -04:00
if not printer.read_config_file(args.config_file[0]):
print("Error while reading configuration file")
quit(1)
args.config_file[0].close()
2023-08-08 11:35:34 -04:00
if args.simdata_file:
if not printer.write_simdata(args.simdata_file[0]):
print("Error while writing simdata file")
quit(1)
args.simdata_file[0].close()
2023-07-24 08:58:19 -04:00
if not printer.parm:
2023-07-25 21:18:33 -04:00
print(textwrap.fill("Unknown printer. Valid printers: " + ", ".join(
printer.valid_printers),
initial_indent='', subsequent_indent=' ')
)
2023-07-24 11:34:19 -04:00
quit(1)
2023-07-25 05:49:47 -04:00
print_opt = False
2023-07-24 11:34:19 -04:00
try:
2023-07-31 07:41:37 -04:00
if args.ws_to_string:
print_opt = True
2023-08-04 03:47:53 -04:00
print(printer.write_sequence_to_string(args.ws_to_string))
2023-07-24 11:34:19 -04:00
if args.reset_waste_ink:
2023-07-25 05:49:47 -04:00
print_opt = True
2023-08-04 03:47:53 -04:00
if printer.reset_waste_ink_levels():
2023-07-31 07:41:37 -04:00
print("Reset waste ink levels done.")
else:
print("Failed to reset waste ink levels. Check configuration.")
if args.detect_key:
2023-07-25 05:49:47 -04:00
print_opt = True
2023-08-06 00:51:18 -04:00
read_key = printer.brute_force_read_key()
2023-07-31 07:41:37 -04:00
if read_key:
print(f"read_key found: {read_key}")
2023-07-31 10:54:58 -04:00
print("List of known keys:")
2023-08-06 00:51:18 -04:00
print("\n".join(printer.list_known_keys()))
2023-07-31 07:41:37 -04:00
else:
print(f"Cannot found read_key")
2023-07-24 11:34:19 -04:00
if args.ftrt:
2023-07-25 05:49:47 -04:00
print_opt = True
2023-08-04 03:47:53 -04:00
if printer.write_first_ti_received_time(
2023-07-31 07:41:37 -04:00
int(args.ftrt[0]), int(args.ftrt[1]), int(args.ftrt[2])):
print("Write first TI received time done.")
else:
print(
"Failed to write first TI received time."
" Check configuration."
)
2023-11-10 18:39:31 -05:00
if args.poweroff:
print_opt = True
if printer.write_poweroff_timer(args.poweroff[0]):
print(
"Write power off timer done ("
+ str(args.poweroff[0])
+ " minutes)."
)
else:
print(
"Failed to write power off timer."
" Check configuration."
)
2023-07-24 19:48:19 -04:00
if args.dump_eeprom:
2023-07-25 05:49:47 -04:00
print_opt = True
2023-08-04 03:47:53 -04:00
for addr, val in printer.dump_eeprom(
2023-08-08 05:33:12 -04:00
int(ast.literal_eval(args.dump_eeprom[0])),
int(ast.literal_eval(args.dump_eeprom[1]))
2023-07-24 19:48:19 -04:00
).items():
2023-08-06 11:19:41 -04:00
print(
f"EEPROM_ADDR {hex(addr).rjust(4)} = "
f"{str(addr).rjust(3)}: "
f"{val:#04x} = {str(val).rjust(3)}"
)
2023-07-25 05:49:47 -04:00
if args.query:
print_opt = True
2023-07-26 06:55:29 -04:00
if ("stats" in printer.parm and
args.query[0] in printer.parm["stats"]):
2023-08-04 03:47:53 -04:00
ret = printer.get_stats(args.query[0])
2023-07-26 06:55:29 -04:00
if ret:
2024-08-06 01:13:49 -04:00
pprint(ret, width=100, compact=True)
2023-07-26 06:55:29 -04:00
else:
print("No information returned. Check printer definition.")
2023-11-11 04:37:03 -05:00
elif args.query[0] in printer.MIB_INFO.keys():
2023-08-04 03:47:53 -04:00
ret = printer.get_snmp_info(args.query[0])
2023-07-25 05:49:47 -04:00
if ret:
2024-08-06 01:13:49 -04:00
pprint(ret, width=100, compact=True)
2023-07-25 05:49:47 -04:00
else:
print("No information returned. Check printer definition.")
else:
if args.query[0].startswith("get_"):
method = args.query[0]
else:
method = "get_" + args.query[0]
if method in printer.list_methods:
2023-08-04 03:47:53 -04:00
ret = printer.__getattribute__(method)()
2023-07-25 05:49:47 -04:00
if ret:
2024-08-06 01:13:49 -04:00
pprint(ret, width=100, compact=True)
2023-07-25 05:49:47 -04:00
else:
print(
"No information returned."
" Check printer definition."
)
2023-07-25 05:49:47 -04:00
else:
print(
"Option error: unavailable query.\n" +
2023-07-25 21:18:33 -04:00
textwrap.fill(
"Available queries: " +
2023-07-26 06:55:29 -04:00
", ".join(printer.list_methods),
initial_indent='', subsequent_indent=' '
2023-07-26 06:55:29 -04:00
) + "\n" +
(
(
textwrap.fill(
"Available statistics: " +
", ".join(printer.parm["stats"].keys()),
initial_indent='', subsequent_indent=' '
2023-07-26 06:55:29 -04:00
) + "\n"
) if "stats" in printer.parm else ""
) +
textwrap.fill(
"Available SNMP elements: " +
2023-11-11 04:37:03 -05:00
", ".join(printer.MIB_INFO.keys()),
initial_indent='', subsequent_indent=' '
2023-07-26 06:55:29 -04:00
)
2023-07-25 05:49:47 -04:00
)
2023-07-31 07:41:37 -04:00
if args.read_eeprom:
print_opt = True
2024-09-20 02:35:28 -04:00
read_list = re.split(r',\s*', args.read_eeprom[0])
2023-07-31 07:41:37 -04:00
for value in read_list:
try:
2023-08-06 11:19:41 -04:00
addr = int(ast.literal_eval(value))
val = printer.read_eeprom(addr, label='read_eeprom')
2023-07-31 07:41:37 -04:00
if val is None:
print("EEPROM read error.")
else:
2023-08-06 11:19:41 -04:00
print(
f"EEPROM_ADDR {hex(addr).rjust(4)} = "
f"{str(addr).rjust(3)}: "
f"{int(val):#04x} = {val.rjust(3)}"
)
2023-07-31 07:41:37 -04:00
except (ValueError, SyntaxError):
print("invalid argument for read_eeprom")
quit(1)
if args.write_eeprom:
print_opt = True
2024-09-20 02:35:28 -04:00
read_list = re.split(r',\s*|;\s*|\|\s*', args.write_eeprom[0])
2023-07-31 07:41:37 -04:00
for key_val in read_list:
key, val = re.split(':|=', key_val)
try:
val_int = ast.literal_eval(val)
2023-08-04 03:47:53 -04:00
if not printer.write_eeprom(
2023-07-31 07:41:37 -04:00
ast.literal_eval(key),
str(val_int), label='write_eeprom'
):
print("invalid write operation")
quit(1)
except (ValueError, SyntaxError):
print("invalid argument for write_eeprom")
2024-01-09 17:54:27 -05:00
quit(1)
2023-07-25 05:49:47 -04:00
if args.info or not print_opt:
2023-07-25 08:28:28 -04:00
ret = printer.stats()
2023-07-25 05:49:47 -04:00
if ret:
2024-08-06 01:13:49 -04:00
pprint(ret, width=100, compact=True)
2023-07-25 05:49:47 -04:00
else:
print("No information returned. Check printer definition.")
2023-07-24 11:34:19 -04:00
except TimeoutError as e:
print(f"Timeout error: {str(e)}")
2023-08-06 05:09:47 -04:00
except ValueError as e:
raise(f"Generic error: {str(e)}")
2023-07-24 11:34:19 -04:00
except KeyboardInterrupt:
quit(2)