Update VERSION to v6.2.10

New features
Revision of the documentation
Fix bug
This commit is contained in:
Ircama
2025-06-15 16:35:28 +02:00
parent 26d9204d7e
commit 8b0d9bcc11
4 changed files with 108 additions and 13 deletions

View File

@@ -61,6 +61,10 @@ The software also includes a configurable printer dictionary, which can be easil
- Import and export printer configuration datasets in various formats: epson_print_conf pickle, Reinkpy XML, Reinkpy TOML. - Import and export printer configuration datasets in various formats: epson_print_conf pickle, Reinkpy XML, Reinkpy TOML.
- Interactive Console (API Playground).
The application includes an integrated Interactive Console. It allows Python developers to interact with the application's runtime environment, evaluate expressions, test APIs, and inspect variables. This console acts as a live API Playground, ideal for debugging, printer configuration testing and rapid prototyping.
- Access various administrative and debugging options. - Access various administrative and debugging options.
- __Available Interfaces__: - __Available Interfaces__:
@@ -80,6 +84,8 @@ cd epson_print_conf
pip install -r requirements.txt pip install -r requirements.txt
``` ```
On Linux, you might also install the tkinter module: `sudo apt install python3-tk`.
This program exploits [pysnmp v7+](https://github.com/lextudio/pysnmp) and [pysnmp-sync-adapter](https://github.com/Ircama/pysnmp-sync-adapter). This program exploits [pysnmp v7+](https://github.com/lextudio/pysnmp) and [pysnmp-sync-adapter](https://github.com/Ircama/pysnmp-sync-adapter).
It is tested with Ubuntu / Windows Subsystem for Linux, Windows. It is tested with Ubuntu / Windows Subsystem for Linux, Windows.
@@ -673,7 +679,7 @@ To return the value of the OID query: `self.fetch_oid_values(oid)[0][1]`.
Open the *epson_print_conf* application, set printer model and IP address, test printer connection. Then: Settings > Debug Shell. Open the *epson_print_conf* application, set printer model and IP address, test printer connection. Then: Settings > Debug Shell.
The following are examples of instructions to test the END4 commands: The following are examples of instructions to test the EPSON-CTRL commands:
```python ```python
# cs # cs
@@ -734,7 +740,47 @@ for i in ec_sequences:
Comprehensive, unified documentation for Epsons Remote Mode commands does not exist: support varies by model, and command references are scattered across service manuals, programming guides and third-party sources (for example, the [Developer's Guide to Gutenprint](https://gimp-print.sourceforge.io/reference-html/x952.html) or [GIMP-Print - ESC/P2 Remote Mode Commands](http://osr507doc.xinuos.com/en/OSAdminG/OSAdminG_gimp/manual-html/gimpprint_37.html)). Comprehensive, unified documentation for Epsons Remote Mode commands does not exist: support varies by model, and command references are scattered across service manuals, programming guides and third-party sources (for example, the [Developer's Guide to Gutenprint](https://gimp-print.sourceforge.io/reference-html/x952.html) or [GIMP-Print - ESC/P2 Remote Mode Commands](http://osr507doc.xinuos.com/en/OSAdminG/OSAdminG_gimp/manual-html/gimpprint_37.html)).
Check `self.printer.check_nozzles()` and `self.printer.clean_nozzles(0)` for examples of usage of remote commands. The `EpsonLpr` class is used for sending Epson LPR commands over a RAW, unidirectional TCP connection on port 9100. This channel does not support receiving responses from the printer.
| **Method** | **Description** |
| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `connect` | Opens a TCP socket connection to the printer at the specified host and port, with timeout. |
| `disconnect` | Gracefully shuts down and closes the socket connection if open. |
| `send(data)` | Sends raw `bytes` directly to the printer over the socket connection. |
| `remote_cmd(cmd, args)` | Constructs a Remote Mode command: 2-byte ASCII command + 2-byte little-endian length + arguments. |
Predefined remote mode commands in this class:
| **Command** | **Description** |
| --------------------- | ------------------------------------------------------- |
| `LF` | Line Feed (new line). |
| `FF` | Form Feed; flushes the buffer / ejects the page. |
| `EXIT_PACKET_MODE` | Exits IEEE 1284.4 (D4) packet mode. |
| `INITIALIZE_PRINTER` | Resets printer to default state (ESC @). |
| `REMOTE_MODE` | Enter Epson Remote Command mode. |
| `ENTER_REMOTE_MODE` | Initialize printer and enter Epson Remote Command mode. |
| `EXIT_REMOTE_MODE` | Exits Remote Mode. |
| `JOB_START` | Begins a print job (JS). |
| `JOB_END` | Ends a print job (JE). |
| `PRINT_NOZZLE_CHECK` | Triggers a nozzle check print pattern (NC). |
| `VERSION_INFORMATION` | Requests firmware or printer version info (VI). |
| `LD` | (unknown). |
Check `self.printer.check_nozzles()` and `self.printer.clean_nozzles(0)` for examples of usage. The following code prints the nozzle-check print pattern (copy and paste the code to the Interactive Console after selecting a printer and related host address):
```python
from epson_print_conf import EpsonLpr
lpr = EpsonLpr(self.printer.hostname)
data = (
lpr.EXIT_PACKET_MODE + # Exit packet mode
lpr.ENTER_REMOTE_MODE + # Engage remote mode commands
lpr.PRINT_NOZZLE_CHECK + # Issue nozzle-check print pattern
lpr.EXIT_REMOTE_MODE + # Disengage remote control
lpr.JOB_END # Mark maintenance job complete
)
print(f"\nDump of data:\n{self.printer.hexdump(data)}\n")
lpr.connect().send(data).disconnect()
```
## ST2 Status Reply Codes ## ST2 Status Reply Codes

View File

@@ -76,6 +76,7 @@ class EpsonLpr:
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.settimeout(self.timeout) self.sock.settimeout(self.timeout)
self.sock.connect((self.hostname, self.port)) self.sock.connect((self.hostname, self.port))
return self
def disconnect(self) -> None: def disconnect(self) -> None:
"""Shutdown and close the socket.""" """Shutdown and close the socket."""
@@ -86,12 +87,14 @@ class EpsonLpr:
pass pass
self.sock.close() self.sock.close()
self.sock = None self.sock = None
return self
def send(self, data: bytes) -> None: def send(self, data: bytes) -> None:
"""Send raw bytes to the printer.""" """Send raw bytes to the printer."""
if not self.sock: if not self.sock:
raise RuntimeError("Not connected to printer") raise RuntimeError("Not connected to printer")
self.sock.sendall(data) self.sock.sendall(data)
return self
def remote_cmd(self, cmd, args): def remote_cmd(self, cmd, args):
"Generate a Remote Mode command." "Generate a Remote Mode command."
@@ -1137,6 +1140,46 @@ class EpsonPrinter:
""" """
return(filter(lambda x: x.startswith("get_"), dir(self))) return(filter(lambda x: x.startswith("get_"), dir(self)))
def hexdump(self, data: Union[bytes, bytearray], width: int = 16) -> str:
"""
Produce a hex + ASCII dump of the given data.
Each line shows:
- 8-digit hex offset
- hex bytes (grouped by width, with extra space every 8 bytes)
- printable ASCII (non-printables as '.')
:param data: Bytes to dump.
:param width: Number of bytes per line (default: 16).
:return: The formatted hexdump.
"""
lines = []
for offset in range(0, len(data), width):
chunk = data[offset : offset + width]
# Hex part, with a space every byte and extra gap at halfwidth
hex_bytes = ' '.join(f"{b:02X}" for b in chunk)
half = width // 2
if len(chunk) > half:
# insert extra space between halves
parts = hex_bytes.split(' ')
hex_bytes = (
' '.join(parts[:half]) + ' ' + ' '.join(parts[half:])
)
# Pad hex part so ASCII column aligns
expected_len = width * 2 + (width - 1) + 2 # bytes*2 hex + spaces + extra halfsplit
hex_part = hex_bytes.ljust(expected_len)
# ASCII part: printable or '.'
ascii_part = (
''.join(chr(b) if 32 <= b < 127 else '.' for b in chunk)
)
lines.append(f"{offset:08X} {hex_part} {ascii_part}")
return "\n".join(lines)
def expand_printer_conf(self, conf): def expand_printer_conf(self, conf):
""" """
Expand "alias" and "same-as" of a printer database for all printers Expand "alias" and "same-as" of a printer database for all printers

View File

@@ -1,8 +1,8 @@
pyyaml pyyaml
pysnmp>=7.1.20 pysnmp>=7.1.20
pysnmp_sync_adapter>=1.0.6 pysnmp_sync_adapter>=1.0.8
tkcalendar tkcalendar
pyperclip pyperclip
black black
tomli tomli
text-console>=2.0.0 text-console>=2.0.2

24
ui.py
View File

@@ -37,7 +37,7 @@ from find_printers import PrinterScanner
from text_console import TextConsole from text_console import TextConsole
VERSION = "6.2.0" VERSION = "6.2.10"
NO_CONF_ERROR = ( NO_CONF_ERROR = (
" Please select a printer model and a valid IP address," " Please select a printer model and a valid IP address,"
@@ -55,7 +55,9 @@ CONFIRM_MESSAGE = (
class EpcTextConsole(TextConsole): class EpcTextConsole(TextConsole):
show_about_message = "epson_print_conf Debug Console." show_about_message = (
"Epson Printer Configuration Interactive Console (API Playground)."
)
def show_help(self): def show_help(self):
"""Open a separate window with help text.""" """Open a separate window with help text."""
@@ -67,13 +69,15 @@ class EpcTextConsole(TextConsole):
scrollbar = tk.Scrollbar(help_window) scrollbar = tk.Scrollbar(help_window)
scrollbar.pack(side="right", fill="y") scrollbar.pack(side="right", fill="y")
help_text = tk.Text(help_window, wrap="word", yscrollcommand=scrollbar.set) help_text = tk.Text(
help_window, wrap="word", yscrollcommand=scrollbar.set
)
help_text.tag_configure("title", foreground="purple") help_text.tag_configure("title", foreground="purple")
help_text.tag_configure("section", foreground="blue") help_text.tag_configure("section", foreground="blue")
help_text.insert( help_text.insert(
tk.END, tk.END,
'Welcome to the epson_print_conf Debug Console\n\n', f'Welcome to the {self.show_about_message}\n\n',
"title" "title"
) )
help_text.insert( help_text.insert(
@@ -98,7 +102,7 @@ class EpcTextConsole(TextConsole):
help_text.insert( help_text.insert(
tk.END, tk.END,
( (
"- F7: Open the debug console.\n\n" "- F7: Open the Interactive Console (API Playground).\n\n"
) )
) )
help_text.insert( help_text.insert(
@@ -370,8 +374,8 @@ class EpsonPrinterUI(tk.Tk):
help_menu.add_command(label="Clear printer list", command=self.clear_printer_list) help_menu.add_command(label="Clear printer list", command=self.clear_printer_list)
help_menu.entryconfig("Clear printer list", accelerator="F6") help_menu.entryconfig("Clear printer list", accelerator="F6")
help_menu.add_command(label="Debug shell", command=self.tk_console) help_menu.add_command(label="Interactive Console (API Playground)", command=self.tk_console)
help_menu.entryconfig("Debug shell", accelerator="F7") help_menu.entryconfig("Interactive Console (API Playground)", accelerator="F7")
help_menu.add_command(label="Remove selected printer configuration", command=self.remove_printer_conf) help_menu.add_command(label="Remove selected printer configuration", command=self.remove_printer_conf)
help_menu.entryconfig("Remove selected printer configuration", accelerator="F8") help_menu.entryconfig("Remove selected printer configuration", accelerator="F8")
@@ -1186,7 +1190,9 @@ class EpsonPrinterUI(tk.Tk):
return return
self._console_window = tk.Toplevel(self) self._console_window = tk.Toplevel(self)
self._console_window.title("Debug Console") self._console_window.title(
"Epson Printer Configuration Interactive Console (API Playground)"
)
self._console_window.geometry("800x400") self._console_window.geometry("800x400")
console = EpcTextConsole(self, self._console_window) console = EpcTextConsole(self, self._console_window)
@@ -3596,7 +3602,7 @@ def main():
import pickle import pickle
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
epilog='epson_print_conf GUI' epilog='Epson Printer Configuration GUI'
) )
parser.add_argument( parser.add_argument(
'-m', '-m',