mirror of
https://github.com/Ircama/epson_print_conf.git
synced 2025-05-13 06:34:37 -04:00
Add user interface (#6)
- Add user interface - Add L386 - Fix pysnmp for newer python versions - Add requirements.txt - Add printer discovery script - Add UI script - Fix ping opening window in pyinstaller exe - remove scapy from requirements.txt - improve comment formatting - scan all local ips - Fix power off timer write function - Update L386 config
This commit is contained in:
parent
1237c71ab8
commit
befa212a4f
4 changed files with 256 additions and 2 deletions
|
@ -16,7 +16,17 @@ 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
|
||||
from pysnmp.hlapi.v1arch import *
|
||||
|
||||
from pyasn1.type.univ import OctetString as OctetStringType
|
||||
from itertools import chain
|
||||
|
||||
|
@ -25,6 +35,39 @@ class EpsonPrinter:
|
|||
"""SNMP Epson Printer Configuration."""
|
||||
|
||||
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
|
||||
],
|
||||
},
|
||||
"XP-205": {
|
||||
"alias": ["XP-200", "XP-207"],
|
||||
"read_key": [25, 7],
|
||||
|
@ -1702,8 +1745,8 @@ class EpsonPrinter:
|
|||
logging.error("EpsonPrinter - invalid API usage")
|
||||
return None
|
||||
try:
|
||||
msb = self.parm["stats"]["poweroff_timer"][0]
|
||||
lsb = self.parm["stats"]["poweroff_timer"][1]
|
||||
msb = self.parm["stats"]["Power off timer"][0]
|
||||
lsb = self.parm["stats"]["Power off timer"][1]
|
||||
except KeyError:
|
||||
logging.info("write_poweroff_timer: missing parameter")
|
||||
return False
|
||||
|
|
81
find_printers.py
Normal file
81
find_printers.py
Normal file
|
@ -0,0 +1,81 @@
|
|||
import os
|
||||
import socket
|
||||
import subprocess
|
||||
import threading
|
||||
import warnings
|
||||
|
||||
from epson_print_conf import EpsonPrinter
|
||||
|
||||
|
||||
# suppress pysnmp warnings
|
||||
warnings.filterwarnings("ignore", category=SyntaxWarning)
|
||||
|
||||
# common printer ports
|
||||
PRINTER_PORTS = [9100, 515, 631]
|
||||
|
||||
class PrinterScanner:
|
||||
|
||||
def ping(self, host):
|
||||
result = subprocess.run(['ping', '-n', '1', host], stdout=subprocess.PIPE, creationflags=subprocess.CREATE_NO_WINDOW)
|
||||
return 'Reply from' in result.stdout.decode('utf-8')
|
||||
|
||||
def check_printer(self, ip, port):
|
||||
try:
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock.settimeout(1)
|
||||
sock.connect((ip, port))
|
||||
sock.close()
|
||||
return True
|
||||
except socket.error:
|
||||
return False
|
||||
|
||||
def get_printer_name(self, ip):
|
||||
printer = EpsonPrinter(hostname=ip)
|
||||
try:
|
||||
printer_info = printer.get_snmp_info("Model")
|
||||
return printer_info["Model"]
|
||||
except:
|
||||
return None
|
||||
|
||||
def scan_ip(self, ip):
|
||||
if self.ping(ip):
|
||||
for port in PRINTER_PORTS:
|
||||
if self.check_printer(ip, port):
|
||||
try:
|
||||
hostname = socket.gethostbyaddr(ip)[0]
|
||||
except socket.herror:
|
||||
hostname = "Unknown"
|
||||
|
||||
printer_name = self.get_printer_name(ip)
|
||||
if printer_name:
|
||||
return {"ip": ip, "hostname": hostname, "name": printer_name}
|
||||
else:
|
||||
return {"ip": ip, "hostname": hostname, "name": "Unknown"}
|
||||
return None
|
||||
def get_all_printers(self):
|
||||
local_device_ip_list = socket.gethostbyname_ex(socket.gethostname())[2]
|
||||
for local_device_ip in local_device_ip_list:
|
||||
base_ip = local_device_ip[:local_device_ip.rfind('.') + 1]
|
||||
ips=[f"{base_ip}{i}" for i in range(1, 255)]
|
||||
printers = []
|
||||
threads = []
|
||||
|
||||
def worker(ip):
|
||||
result = self.scan_ip(ip)
|
||||
if result:
|
||||
printers.append(result)
|
||||
|
||||
for ip in ips:
|
||||
thread = threading.Thread(target=worker, args=(ip,))
|
||||
threads.append(thread)
|
||||
thread.start()
|
||||
|
||||
for thread in threads:
|
||||
thread.join()
|
||||
|
||||
return printers
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
scanner = PrinterScanner()
|
||||
print(scanner.get_all_printers())
|
4
requirements.txt
Normal file
4
requirements.txt
Normal file
|
@ -0,0 +1,4 @@
|
|||
PyYAML
|
||||
git+https://github.com/etingof/pysnmp.git@master#egg=pysnmp
|
||||
pyasyncore;python_version>="3.12"
|
||||
|
126
ui.py
Normal file
126
ui.py
Normal file
|
@ -0,0 +1,126 @@
|
|||
import tkinter as tk
|
||||
from tkinter import ttk
|
||||
from tkinter.scrolledtext import ScrolledText
|
||||
import threading
|
||||
import ipaddress
|
||||
from epson_print_conf import EpsonPrinter
|
||||
from find_printers import PrinterScanner
|
||||
from pprint import pformat
|
||||
|
||||
class EpsonPrinterUI(tk.Tk):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.title("Epson Printer Configuration")
|
||||
self.geometry("450x400")
|
||||
|
||||
# configure the main window to be resizable
|
||||
self.columnconfigure(0, weight=1)
|
||||
self.rowconfigure(0, weight=1)
|
||||
|
||||
# main Frame
|
||||
main_frame = ttk.Frame(self, padding="10")
|
||||
main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
|
||||
main_frame.columnconfigure(0, weight=1)
|
||||
main_frame.rowconfigure(3, weight=1)
|
||||
|
||||
# printer model selection
|
||||
model_frame = ttk.LabelFrame(main_frame, text="Printer Model", padding="10")
|
||||
model_frame.grid(row=0, column=0, pady=10, sticky=(tk.W, tk.E))
|
||||
model_frame.columnconfigure(1, weight=1)
|
||||
|
||||
self.model_var = tk.StringVar()
|
||||
ttk.Label(model_frame, text="Select Printer Model:").grid(row=0, column=0, sticky=tk.W, padx=5)
|
||||
self.model_dropdown = ttk.Combobox(model_frame, textvariable=self.model_var)
|
||||
self.model_dropdown['values'] = sorted(list(EpsonPrinter.PRINTER_CONFIG.keys()))
|
||||
self.model_dropdown.grid(row=0, column=1, pady=5, padx=5, sticky=(tk.W, tk.E))
|
||||
|
||||
# IP address entry
|
||||
ip_frame = ttk.LabelFrame(main_frame, text="Printer IP Address", padding="10")
|
||||
ip_frame.grid(row=1, column=0, pady=10, sticky=(tk.W, tk.E))
|
||||
ip_frame.columnconfigure(1, weight=1)
|
||||
|
||||
self.ip_var = tk.StringVar()
|
||||
ttk.Label(ip_frame, text="Enter Printer IP Address:").grid(row=0, column=0, sticky=tk.W, padx=5)
|
||||
self.ip_entry = ttk.Entry(ip_frame, textvariable=self.ip_var)
|
||||
self.ip_entry.grid(row=0, column=1, pady=5, padx=5, sticky=(tk.W, tk.E))
|
||||
|
||||
# buttons
|
||||
button_frame = ttk.Frame(main_frame, padding="10")
|
||||
button_frame.grid(row=2, column=0, pady=10, sticky=(tk.W, tk.E))
|
||||
button_frame.columnconfigure((0, 1, 2), weight=1)
|
||||
|
||||
self.detect_button = ttk.Button(button_frame, text="Detect Printers", command=self.start_detect_printers)
|
||||
self.detect_button.grid(row=0, column=0, padx=5, pady=5, sticky=(tk.W, tk.E))
|
||||
|
||||
self.status_button = ttk.Button(button_frame, text="Print Status", command=self.print_status)
|
||||
self.status_button.grid(row=0, column=1, padx=5, pady=5, sticky=(tk.W, tk.E))
|
||||
|
||||
self.reset_button = ttk.Button(button_frame, text="Reset Waste Ink Levels", command=self.reset_waste_ink)
|
||||
self.reset_button.grid(row=0, column=2, padx=5, pady=5, sticky=(tk.W, tk.E))
|
||||
|
||||
# status display
|
||||
status_frame = ttk.LabelFrame(main_frame, text="Status", padding="10")
|
||||
status_frame.grid(row=3, column=0, pady=10, sticky=(tk.W, tk.E, tk.N, tk.S))
|
||||
status_frame.columnconfigure(0, weight=1)
|
||||
status_frame.rowconfigure(0, weight=1)
|
||||
|
||||
self.status_text = ScrolledText(status_frame, height=10, width=50, wrap=tk.WORD)
|
||||
self.status_text.grid(row=0, column=0, pady=5, padx=5, sticky=(tk.W, tk.E, tk.N, tk.S))
|
||||
|
||||
def print_status(self):
|
||||
model = self.model_var.get()
|
||||
ip_address = self.ip_var.get()
|
||||
if not model or not self._is_valid_ip(ip_address):
|
||||
self.status_text.insert(tk.END, "[ERROR] Please select a printer model and enter a valid IP address.\n")
|
||||
return
|
||||
printer = EpsonPrinter(model=model, hostname=ip_address)
|
||||
|
||||
try:
|
||||
self.status_text.insert(tk.END, f"[INFO] {pformat(printer.stats())}\n")
|
||||
except Exception as e:
|
||||
self.status_text.insert(tk.END, f"[ERROR] {e}\n")
|
||||
|
||||
def reset_waste_ink(self):
|
||||
model = self.model_var.get()
|
||||
ip_address = self.ip_var.get()
|
||||
if not model or not self._is_valid_ip(ip_address):
|
||||
self.status_text.insert(tk.END, "[ERROR] Please select a printer model and enter a valid IP address.\n")
|
||||
return
|
||||
printer = EpsonPrinter(model=model, hostname=ip_address)
|
||||
try:
|
||||
printer.reset_waste_ink_levels()
|
||||
self.status_text.insert(tk.END, "[INFO] Waste ink levels have been reset.\n")
|
||||
except Exception as e:
|
||||
self.status_text.insert(tk.END, f"[ERROR] {e}\n")
|
||||
|
||||
def start_detect_printers(self):
|
||||
self.status_text.insert(tk.END, "[INFO] Detecting printers... (this might take a while)\n")
|
||||
self.detect_button.config(state=tk.DISABLED) # disable button while processing
|
||||
|
||||
# run printer detection in new thread, as it can take a while
|
||||
threading.Thread(target=self.detect_printers).start()
|
||||
|
||||
def detect_printers(self):
|
||||
printer_scanner=PrinterScanner()
|
||||
try:
|
||||
printers = printer_scanner.get_all_printers()
|
||||
if len(printers) > 0:
|
||||
for printer in printers:
|
||||
self.status_text.insert(tk.END, f"[INFO] {printer['name']} found at {printer['ip']} (hostname: {printer['hostname']})\n")
|
||||
else:
|
||||
self.status_text.insert(tk.END, "[WARN] No printers found.\n")
|
||||
except Exception as e:
|
||||
self.status_text.insert(tk.END, f"[ERROR] {e}\n")
|
||||
finally:
|
||||
self.detect_button.config(state=tk.NORMAL) # enable button after processing
|
||||
|
||||
def _is_valid_ip(self,ip):
|
||||
try:
|
||||
ip = ipaddress.ip_address(ip)
|
||||
return True
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = EpsonPrinterUI()
|
||||
app.mainloop()
|
Loading…
Add table
Add a link
Reference in a new issue