diff --git a/.gitignore b/.gitignore index 4f2f775..7c02179 100644 --- a/.gitignore +++ b/.gitignore @@ -160,3 +160,5 @@ cython_debug/ #.idea/ devices.xml *.toml +*.srs +*.pickle diff --git a/README.md b/README.md index 6b10fb2..08b6096 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,8 @@ The *Epson Printer Configuration Tool* simplifies the management of Epson printe A range of features are offered for both end-users and developers, making it easier to administer and maintain Epson printers. +The software provides a configurable printer dictionary, which can be easily extended. In addition, it is possible to import and convert external Epson printer configuration databases. + ## Key Features - __SNMP Interface__: Seamlessly connect and manage Epson printers using SNMP over TCP/IP, supporting Wi-Fi connections (not USB). @@ -24,36 +26,27 @@ A range of features are offered for both end-users and developers, making it eas - Reset the ink waste counter. The ink waste counters track the amount of ink discarded during maintenance tasks to prevent overflow in the waste ink pads. Once the counters indicate that one of the printer pads is full, the printer will stop working to avoid potential damage or ink spills. Resetting the ink waste counter extends the printer operation while a pad maintenance or tank replacement is programmed. - - Adjust the power-off timer for more accurate energy efficiency. + - Adjust the power-off timer (for more accurate energy efficiency). + - Change the _First TI Received Time_, + + The *First TI Received Time* in Epson printers typically refers to the timestamp of the first transmission instruction to the printer when it was first set up. This feature tracks when the printer first operated. + - Change the printer WiFi MAC address and the printer serial number (typically used in specialized scenarios where specific device identifiers are required). - - Access various administrative and debugging options. - Read and write to EEPROM addresses for advanced configurations. - Dump and analyze sets of EEPROM addresses. + - Detect the access key (*read_key* and *write_key*) and some attributes of the printer configuration. + + The GUI includes some features that attempt to detect the attributes of an Epson printer whose model is not included in the configuration, which can also be used with known printers, to detect additional parameters. + + - Import and export printer configuration datasets in various formats: epson_print_conf pickle, Reinkpy XML, Reinkpy TOML. + + - Access various administrative and debugging options. - __User-Friendly Interfaces__: - __Graphical User Interface (GUI)__: Intuitive interface with an autodiscovery function that detects printer IP addresses and model names. - __Command Line Tool__: For users who prefer command-line interactions, providing the full set of features. - __Python API Interface__: For developers to integrate and automate printer management tasks. -The GUI can automatically find and display printer IP addresses and model names, allowing users to: - -- Check printer status -- Open the printer web interface -- Change the printer power-off timer, the _First TI Received Time_, the printer WiFi MAC address, and the printer serial number -- Read and write the EEPROM -- Detect the access key (*read_key* and *write_key*) and some attributes of the printer configuration -- Reset the ink waste counter - -Special features allow showing the internal configuration settings. - -The *First TI Received Time* in Epson printers typically refers to the timestamp of the first transmission instruction to the printer when it was first set up. This feature tracks when the printer first operated. - -The software provides a configurable printer dictionary, which can be easily extended. In addition, a tool allows importing and converting an extensive Epson printer configuration DB. - -The GUI includes some features that attempt to detect the attributes of an Epson printer whose model is not included in the configuration. First press "Detect Printers". If the printer is not in the configuration, press "Detect Access Keys". If the output does not show errors, press "Detect Configuration". These commands produce a tree view and a text view, which are useful to analyze whether there is a configured model that might be close or possibly same to target one. Notice that these operations take many minutes to complete and the printer shall be kept switched on for the whole period. Temporarily disabling the auto power-off timer is suggested. - -"Detect Configuration" can also be used with known printers, to detect additional parameters. - Note on the ink waste counter reset feature: resetting the ink waste counter is just removing a lock; not replacing the tank will reduce the print quality and make the ink spill. ## Installation @@ -75,6 +68,8 @@ pip3 install pyasn1==0.4.8 pip3 install git+https://github.com/etingof/pysnmp.git pip3 install tkcalendar pip3 install pyperclip +pip3 install black +pip3 install tomli cd epson_print_conf ``` @@ -125,6 +120,28 @@ optional arguments: epson_print_conf GUI ``` +### How to import an external printer configuration DB + +With the GUI, the following operations are possible (from the file menu): + +- Load a PICKLE configuration file or web URL. + + This operation allows to open a file saved with the GUI ("Save the selected printer configuration to a PICKLE file") or with the *parse_devices.py* utility. In addition to the printer configuration DB, this file includes the last used IP address and printer model in order to simplify the GUI usage. + +- Import an XML configuration file or web URL + + This option allows to import the XML configuration file downloaded from https://codeberg.org/attachments/147f41a3-a6ea-45f6-8c2a-25bac4495a1d. Alternatively, this option directly accepts the [source Web URL](https://codeberg.org/attachments/147f41a3-a6ea-45f6-8c2a-25bac4495a1d) of this file, incorporating the download operation into the GUI. + +- Import a TOML configuration file or web URL + + Similar to the XML import, this option allows to load the TOML configuration file downloaded from https://codeberg.org/atufi/reinkpy/raw/branch/main/reinkpy/epson.toml and also accepts the [source Web URL](https://codeberg.org/atufi/reinkpy/raw/branch/main/reinkpy/epson.toml) of this file, incorporating the download operation into the GUI. + +Other menu options allow to filter or clean up the configuration list, as well as select a specific printer model and then save data to a PICKLE file. + +### How to detect parameters of an unknown printer + +First press "Detect Printers". If the printer is not in the configuration, press "Detect Access Keys". If the output does not show errors, press "Detect Configuration". These commands produce a tree view and a text view, which are useful to analyze whether there is a configured model that might be close or possibly same to target one. Notice that these operations take many minutes to complete and the printer shall be kept switched on for the whole period. Temporarily disabling the auto power-off timer is suggested. + ### How to revert a change performed through the GUI The GUI displays a `[NOTE]` in the status box before performing any change, specifying the current EEPROM values before the rewrite operation. This line can be copied and pasted as is into the text box that appears when the "Write EEPROM" button is pressed; the execution of the related action reverts the changes to their original values. @@ -649,14 +666,16 @@ Example of advanced printer status with an XP-205 printer: ## Resources -### snmpget (Linux) +### snmpget -Installation: +Installation with Linux: ``` sudo apt-get install snmp ``` +There are also [binaries for Windows](https://netcologne.dl.sourceforge.net/project/net-snmp/net-snmp%20binaries/5.7-binaries/net-snmp-5.7.0-1.x86.exe?viasf=1) which include snmpget.exe, running with the same arguments. + Usage: ``` @@ -695,7 +714,7 @@ emanage x900: https://github.com/abrasive/x900-otsakupuhastajat/ - Epson Maintenance Reset Utility: https://epson.com/epsonstorefront/orbeon/fr/us_regular_s03/us_ServiceInk_Pad_Reset/new - Epson Ink Pads Reset Utility Terms and Conditions: https://epson.com/Support/wa00370 - Epson Adjustment Program (developed by EPSON) -- WIC-Reset: https://wic-reset.com / https://www.2manuals.com / https://resetters.com (Use at your risk) +- WIC-Reset: https://www.wic.support/download/ / https://www.2manuals.com / (Use at your risk) - PrintHelp: https://printhelp.info/ (Use at your risk) ### Other resources diff --git a/epson_print_conf.py b/epson_print_conf.py index 588f836..d1367a6 100644 --- a/epson_print_conf.py +++ b/epson_print_conf.py @@ -16,6 +16,7 @@ import os import yaml from pathlib import Path +import pickle # The pysnmp module uses functionality from importlib.util and # importlib.machinery, which were seperated from the importlib module @@ -262,6 +263,7 @@ class EpsonPrinter: }, "serial_number": range(192, 202), }, + """ "L3250": { "alias": ["L3251", "L3253", "L3255"], "read_key": [74, 54], @@ -290,6 +292,7 @@ class EpsonPrinter: "Total scan counter": [1843, 1842, 1841, 1840], }, }, + """ "ET-2400": { "alias": ["ET-2401", "ET-2403", "ET-2405"], "read_key": [74, 54], @@ -2480,6 +2483,41 @@ def write_simdata(self, file): return True +def get_printer_models(input_string): + # Tokenize the string + tokens = re.split(" |/", input_string) + if not len(tokens): + return [] + + # Define the words to remove (uppercase, then case insensitive) + remove_tokens = {"EPSON", "SERIES"} + + # Process tokens + processed_tokens = [] + non_numeric_part = "" + pre_model = "" + for token in tokens: + upper_token = token.upper() + + # Remove tokens that match remove_tokens + if any(word == upper_token for word in remove_tokens): + continue + if not any(char.isdigit() for char in token): # no alphanum inside + pre_model = pre_model + token + " " + continue + # Identify the non-numeric part of the first token + if not token.isnumeric() and not non_numeric_part: + non_numeric_part = "".join(c for c in token if not c.isdigit()) + # if token is numeric, prepend the non-numeric part + if token.isnumeric(): + processed_tokens.append(f"{pre_model}{non_numeric_part}{token}") + else: + processed_tokens.append(f"{pre_model}{token}") + if not processed_tokens and pre_model: + processed_tokens.append(pre_model.strip()) + return processed_tokens + + if __name__ == "__main__": import argparse from pprint import pprint @@ -2687,7 +2725,11 @@ def auto_int(x): conf_dict = {} if args.pickle: - conf_dict = pickle.load(args.pickle[0]) + try: + conf_dict = pickle.load(args.pickle[0]) + except Exception as e: + print("Error while loading the pickle file:", e) + quit(1) printer = EpsonPrinter( conf_dict=conf_dict, @@ -2733,7 +2775,7 @@ def auto_int(x): print("List of known keys:") print("\n".join(printer.list_known_keys())) else: - print(f"Cannot found read_key") + print(f"Could not detect read_key.") if args.ftrt: print_opt = True if printer.write_first_ti_received_time( diff --git a/parse_devices.py b/parse_devices.py index d4bdfed..bbaff16 100644 --- a/parse_devices.py +++ b/parse_devices.py @@ -7,7 +7,7 @@ import textwrap import tomli -from ui import get_printer_models +from epson_print_conf import get_printer_models, EpsonPrinter WASTE_LABELS = [ "main_waste", "borderless_waste", "third_waste", "fourth_waste", @@ -53,7 +53,7 @@ def traverse_data(element, depth=0): def generate_config_from_xml( - config, traverse, add_fatal_errors, full, printer_model + config, traverse=False, add_fatal_errors=False, full=False, printer_model=False ): irc_pattern = [ r'Ink replacement counter %-% (\w+) % \((\w+)\)', @@ -231,12 +231,12 @@ def generate_config_from_xml( def normalize_config( config, - remove_invalid, - expand_names, - add_alias, - aggregate_alias, - maint_level, - add_same_as, + remove_invalid=True, + expand_names=True, + add_alias=True, + aggregate_alias=True, + maint_level=True, + add_same_as=True, ): logging.info("Number of configuration entries before removing invalid ones: %s", len(config)) # Remove printers without write_key or without read_key @@ -360,7 +360,7 @@ def normalize_config( return config def generate_config_from_toml( - config, printer_model, full, + config, printer_model=None, full=False, ): # Generate "read_key" values @@ -650,8 +650,6 @@ def main(): if args.pickle: pickle.dump(normalized_config, args.pickle[0]) # serialize the list args.pickle[0].close() - - from epson_print_conf import EpsonPrinter ep = EpsonPrinter(conf_dict=normalized_config, replace_conf=True) logging.info("Number of expanded configuration entries: %s", len(ep.PRINTER_CONFIG)) quit() diff --git a/ui.py b/ui.py index e55fb15..1129cfe 100644 --- a/ui.py +++ b/ui.py @@ -5,6 +5,7 @@ Epson Printer Configuration via SNMP (TCP/IP) - GUI """ +import os import sys import re import threading @@ -15,6 +16,7 @@ import traceback import logging import webbrowser +import pickle import black import tkinter as tk @@ -22,14 +24,15 @@ from tkinter.scrolledtext import ScrolledText import tkinter.font as tkfont from tkcalendar import DateEntry # Ensure you have: pip install tkcalendar -from tkinter import simpledialog, messagebox +from tkinter import simpledialog, messagebox, filedialog import pyperclip -from epson_print_conf import EpsonPrinter +from epson_print_conf import EpsonPrinter, get_printer_models +from parse_devices import generate_config_from_toml, generate_config_from_xml, normalize_config from find_printers import PrinterScanner -VERSION = "4.0" +VERSION = "5.0" NO_CONF_ERROR = ( "[ERROR] Please select a printer model and a valid IP address," @@ -44,40 +47,6 @@ "Are you sure you want to proceed?" ) -def get_printer_models(input_string): - # Tokenize the string - tokens = re.split(" |/", input_string) - if not len(tokens): - return [] - - # Define the words to remove (uppercase, then case insensitive) - remove_tokens = {"EPSON", "SERIES"} - - # Process tokens - processed_tokens = [] - non_numeric_part = "" - pre_model = "" - for token in tokens: - upper_token = token.upper() - - # Remove tokens that match remove_tokens - if any(word == upper_token for word in remove_tokens): - continue - if not any(char.isdigit() for char in token): # no alphanum inside - pre_model = pre_model + token + " " - continue - # Identify the non-numeric part of the first token - if not token.isnumeric() and not non_numeric_part: - non_numeric_part = "".join(c for c in token if not c.isdigit()) - # if token is numeric, prepend the non-numeric part - if token.isnumeric(): - processed_tokens.append(f"{pre_model}{non_numeric_part}{token}") - else: - processed_tokens.append(f"{pre_model}{token}") - if not processed_tokens and pre_model: - processed_tokens.append(pre_model.strip()) - return processed_tokens - class MultiLineInputDialog(simpledialog.Dialog): def __init__(self, parent, title=None, text=""): @@ -205,7 +174,7 @@ def __init__( self, model: str = None, hostname: str = None, - conf_dict={}, + conf_dict = {}, replace_conf=False ): super().__init__() @@ -225,6 +194,89 @@ def __init__( self.columnconfigure(0, weight=1) self.rowconfigure(0, weight=1) + # Setup the menu + menubar = tk.Menu(self) + self.config(menu=menubar) + + # Create File menu + file_menu = tk.Menu(menubar, tearoff=0) + menubar.add_cascade(label="File", menu=file_menu) + LOAD_LABEL_NAME = "%s printer configuration file or web URL..." + LOAD_LABEL_TITLE = "Select a %s printer configuration file, or enter a Web URL" + LOAD_LABEL_TYPE = "%s files" + file_menu.add_command( + label=LOAD_LABEL_NAME % "Load a PICKLE", + command=lambda: self.load_from_file( + file_type={ + "title": LOAD_LABEL_TITLE % "PICKLE", + "filetypes": [ + (LOAD_LABEL_TYPE % "PICKLE", "*.pickle"), + ("All files", "*.*") + ] + }, + type=0 + ) + ) + file_menu.add_command( + label=LOAD_LABEL_NAME % "Import a XML", + command=lambda: self.load_from_file( + file_type={ + "title": LOAD_LABEL_TITLE % "XML", + "filetypes": [ + (LOAD_LABEL_TYPE % "XML", "*.xml"), + ("All files", "*.*") + ] + }, + type=1 + ) + ) + file_menu.add_command( + label=LOAD_LABEL_NAME % "Import a TOML", + command=lambda: self.load_from_file( + file_type={ + "title": LOAD_LABEL_TITLE % "TOML", + "filetypes": [ + (LOAD_LABEL_TYPE % "TOML", "*.toml"), + ("All files", "*.*") + ] + }, + type=2 + ) + ) + file_menu.add_command( + label="Save the selected printer configuration to a PICKLE file...", + command=self.save_to_file + ) + + # Create Help menu + help_menu = tk.Menu(menubar, tearoff=0) + menubar.add_cascade(label="Settings", menu=help_menu) + + help_menu.add_command(label="Show printer parameters of the selected model", command=self.printer_config) + help_menu.entryconfig("Show printer parameters of the selected model", accelerator="F2") + + help_menu.add_command(label="Show printer keys of the selected model", command=self.key_values) + help_menu.entryconfig("Show printer keys of the selected model", accelerator="F3") + + help_menu.add_command(label="Remove selected printer configuration", command=self.remove_printer_conf) + help_menu.entryconfig("Remove selected printer configuration", accelerator="F4") + + help_menu.add_command(label="Keep only selected printer configuration", command=self.keep_printer_conf) + help_menu.entryconfig("Keep only selected printer configuration", accelerator="F5") + + help_menu.add_command(label="Clear printer list", command=self.clear_printer_list) + help_menu.entryconfig("Clear printer list", accelerator="F6") + + help_menu.add_command(label="Get next local IP addresss", command=lambda: self.next_ip(0)) + help_menu.entryconfig("Get next local IP addresss", accelerator="F9") + + # Create Help menu + help_menu = tk.Menu(menubar, tearoff=0) + menubar.add_cascade(label="Help", menu=help_menu) + help_menu.add_command(label="Help", command=self.open_help_browser) + help_menu.add_command(label="Program Information", command=self.show_program_info) + + # Setup frames FRAME_PAD = 10 PAD = (3, 0) PADX = 4 @@ -277,12 +329,14 @@ def __init__( ) ToolTip( self.model_dropdown, - "Select the model of the printer, or press 'Detect Printers'.\n" - "Press F2 to dump the parameters associated to the printer model." - " Press F3 to get the values of the keys from the configuration.", + "Select the model of the printer, or press 'Detect Printers'." + " Special features are allowed via F2, F3, F4, F5, or F6.\n" ) self.model_dropdown.bind("", self.printer_config) self.model_dropdown.bind("", self.key_values) + self.model_dropdown.bind("", lambda event: self.remove_printer_conf()) + self.model_dropdown.bind("", lambda event: self.keep_printer_conf()) + self.model_dropdown.bind("", lambda event: self.clear_printer_list()) # BOX IP address ip_frame = ttk.LabelFrame( @@ -310,13 +364,13 @@ def __init__( self.ip_entry.grid( row=0, column=1, pady=PADY, padx=PADX, sticky=(tk.W, tk.E) ) - self.ip_entry.bind("", self.next_ip) + self.ip_entry.bind("", self.next_ip) ToolTip( self.ip_entry, "Enter the IP address, or press 'Detect Printers'" " (you can also enter part of the IP address" " to speed up the detection)," - " or press F2 more times to get the next local IP address," + " or press F9 more times to get the next local IP address," " which can then be edited" " (by removing the last part before pressing 'Detect Printers').", ) @@ -779,6 +833,219 @@ def __init__( self.ip_var.trace('w', self.change_widget_states) self.change_widget_states() + def save_to_file(self): + if not self.model_var.get(): + self.show_status_text_view() + self.status_text.insert( + tk.END, + '[ERROR]: Unknown printer model.' + ) + return + if not self.printer: + self.printer = EpsonPrinter( + conf_dict=self.conf_dict, + model=self.model_var.get(), + ) + if not self.printer or not self.printer.parm: + self.show_status_text_view() + self.status_text.insert( + tk.END, + '[ERROR]: No printer configuration defined.' + ) + return + # Open file dialog to enter the file + file_path = filedialog.asksaveasfilename( + defaultextension=".pickle", + title="PICKLE file name", + initialfile=self.model_var.get(), + filetypes=[("PICKLE files", "*.pickle")] + ) + if not file_path: + self.show_status_text_view() + self.status_text.insert( + tk.END, + f"[WARNING] File save operation aborted.\n" + ) + return + # Ensure the file has the desired extension + if "." not in file_path and not file_path.endswith(".pickle"): + file_path += ".pickle" + normalized_config = { self.model_var.get(): self.printer.parm.copy() } + normalized_config["internal_data"] = {} + normalized_config["internal_data"]["default_model"] = self.model_var.get() + if self.ip_var.get(): + normalized_config["internal_data"]["hostname"] = self.ip_var.get() + try: + with open(file_path, "wb") as file: + pickle.dump(normalized_config, file) # serialize the list + except Exception: + self.show_status_text_view() + self.status_text.insert( + tk.END, + f"[ERROR] File save operation failed.\n" + ) + return + self.status_text.insert( + tk.END, + f'[INFO] "{os.path.basename(file_path)}" file save operation completed.\n' + ) + + def load_from_file(self, file_type, type): + # Open file dialog to select the file + file_path = filedialog.askopenfilename(**file_type) + if not file_path: + self.show_status_text_view() + self.status_text.insert( + tk.END, + f"[WARNING] File load operation aborted.\n" + ) + return + if type == 0: + try: + with open(file_path, 'rb') as pickle_file: + self.conf_dict = pickle.load(pickle_file) + except Exception as e: + self.show_status_text_view() + if not file_path.tell(): + self.status_text.insert( + tk.END, + f"[ERROR] Empty PICKLE FILE {file_path}.\n" + ) + else: + self.status_text.insert( + tk.END, + f"[ERROR] Cannot load PICKLE file {file_path}. {e}\n" + ) + return + if ( + "internal_data" in self.conf_dict + and "hostname" in self.conf_dict["internal_data"] + ): + self.ip_var.set(self.conf_dict["internal_data"]["hostname"]) + if ( + "internal_data" in self.conf_dict + and "default_model" in self.conf_dict["internal_data"] + ): + self.model_var.set(self.conf_dict["internal_data"]["default_model"]) + else: + self.status_text.insert( + tk.END, + f"[INFO] Converting file, please wait...\n" + ) + self.update_idletasks() + if type == 1: + printer_config = generate_config_from_xml(config=file_path) + if type == 2: + printer_config = generate_config_from_toml(config=file_path) + if not printer_config: + self.show_status_text_view() + self.status_text.insert( + tk.END, + f"[ERROR] Cannot load file {file_path}\n" + ) + return + self.conf_dict = normalize_config(config=printer_config) + self.model_dropdown["values"] = sorted(EpsonPrinter( + conf_dict=self.conf_dict, + replace_conf=self.replace_conf + ).valid_printers) + if file_path: + self.show_status_text_view() + self.status_text.insert( + tk.END, + f"[INFO] Loaded file {os.path.basename(file_path)}.\n" + ) + + def keep_printer_conf(self): + self.show_status_text_view() + if not self.model_var.get(): + self.status_text.insert( + tk.END, + '[ERROR]: Select a valid printer model.\n' + ) + return + keep_model = self.model_var.get() + self.model_dropdown["values"] = tuple( + model for model in self.model_dropdown["values"] if model == keep_model + ) + self.replace_conf = True + self.show_status_text_view() + self.update_idletasks() + self.status_text.insert( + tk.END, + f"[INFO] Printer {keep_model} is the only one in the list.\n" + ) + + def remove_printer_conf(self): + self.show_status_text_view() + if not self.model_var.get(): + self.status_text.insert( + tk.END, + '[ERROR]: Select a valid printer model.\n' + ) + return + remove_model = self.model_var.get() + self.model_var.set("") + self.model_dropdown["values"] = tuple( + model for model in self.model_dropdown["values"] if model != remove_model + ) + self.replace_conf = True + self.show_status_text_view() + self.update_idletasks() + self.status_text.insert( + tk.END, + f"[INFO] Configuation of printer {remove_model} removed.\n" + ) + + def clear_printer_list(self): + self.conf_dict = {} + self.model_var.set("") + self.model_dropdown["values"] = {} + self.replace_conf = True + self.show_status_text_view() + self.update_idletasks() + self.status_text.insert( + tk.END, + f"[INFO] Printer list cleared.\n" + ) + + def open_help_browser(self): + # Opens a web browser to a help URL + url = "https://github.com/Ircama/epson_print_conf/?tab=readme-ov-file#epson_print_conf" + self.show_status_text_view() + try: + ret = webbrowser.open(url) + if ret: + self.status_text.insert( + tk.END, f"[INFO] The browser is being opened.\n" + ) + else: + self.status_text.insert( + tk.END, f"[ERROR] Cannot open browser.\n" + ) + except Exception as e: + self.status_text.insert( + tk.END, f"[ERROR] Cannot open web browser: {e}\n" + ) + finally: + self.config(cursor="") + self.update_idletasks() + + def show_program_info(self): + # Show program information in a popup + program_version = "1.0.0" # Specify your program version + description = """ +Epson Printer Configuration tool via SNMP (TCP/IP). + +A tool for managing settings of Epson printers connected via Wi-Fi over the SNMP protocol. + +Web site: https://github.com/Ircama/epson_print_conf +""" + self.title("Epson Printer Configuration - v" + VERSION) + messagebox.showinfo("Program Information", + f"Version: {VERSION}\n{description}" + ) + def focus_next(self, event): event.widget.tk_focusNext().focus() return("break") @@ -1783,6 +2050,7 @@ def run_detection(): return if not self.printer: self.printer = EpsonPrinter( + conf_dict=self.conf_dict, hostname=self.ip_var.get() ) self.printer.parm = {'read_key': None} @@ -1796,16 +2064,17 @@ def run_detection(): read_key = None try: read_key = self.printer.brute_force_read_key() - self.status_text.insert( - tk.END, f"[INFO] Detected read_key: {read_key}.\n" - ) except Exception as e: self.handle_printer_error(e) logging.getLogger().setLevel(current_log_level) self.config(cursor="") self.update_idletasks() return - if not read_key: + if read_key: + self.status_text.insert( + tk.END, f"[INFO] Detected read_key: {read_key}.\n" + ) + else: self.status_text.insert( tk.END, f"[ERROR] Could not detect read_key.\n" ) @@ -2163,13 +2432,14 @@ def detect_sequence(eeprom, sequence): try: addr = range(0, 2048) eeprom = { - k: int(v, 16) for k, v in zip( - addr,self.printer.read_eeprom_many( + k: None if v == None else int(v, 16) for k, v in zip( + addr, + self.printer.read_eeprom_many( addr, label="dump_EEPROM" ) ) } - if not eeprom: + if not eeprom or eeprom == {0: None}: self.status_text.insert( tk.END, '[ERROR] Cannot read EEPROM values' @@ -2658,6 +2928,10 @@ def print_items(self, text): ip_address = self.ip_var.get() if not self._is_valid_ip(ip_address): + self.show_status_text_view() + self.status_text.insert( + tk.END, f"[ERROR] Missing IP address or printer host name.\n" + ) return try: with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: @@ -2670,7 +2944,10 @@ def print_items(self, text): + form_feed ) except Exception as e: - self.handle_printer_error(e) + self.show_status_text_view() + self.status_text.insert( + tk.END, f"[ERROR] Printer is unreachable or offline.\n" + ) def main(): @@ -2726,7 +3003,14 @@ def main(): logging.getLogger().setLevel(logging.DEBUG) conf_dict = {} if args.pickle: - conf_dict = pickle.load(args.pickle[0]) + try: + conf_dict = pickle.load(args.pickle[0]) + except Exception as e: + if not args.pickle[0].tell(): + print("Error. Empty PICKLE FILE.") + else: + print("Error. Cannot load PICKLE FILE:", e) + quit() return EpsonPrinterUI( model=args.model,