Change rendering method for GUI

Add installation scripts for Windows and Linux, and update README for usage instructions
This commit is contained in:
ZareMate 2025-01-15 02:25:36 +01:00
parent 1a01676d9f
commit f6aa6cd626
Signed by: zaremate
GPG Key ID: 369A0E45E03A81C3
8 changed files with 161 additions and 57 deletions

Binary file not shown.

View File

@ -11,14 +11,6 @@ This project allows you to control Discord mute and deafen status using an ESP32
- An ESP32 device
- A USB cable to connect the ESP32 to your computer
### Python Packages
Install the required Python packages using pip:
```sh
pip install pyserial keyboard pygetwindow pyautogui mss numpy pystray pillow
```
### Clone the Repository
Clone this repository to your local machine:
@ -28,14 +20,38 @@ git clone https://github.com/yourusername/ESP32-discord.git
cd ESP32-discord
```
### Windows Installation
Run the following batch file to set up the virtual environment and install the required Python packages:
```sh
install_windows.bat
```
### Linux Installation
Run the following shell script to set up the virtual environment and install the required Python packages:
```sh
./install_linux.sh
```
## Usage
### Running the Script
You can run the script with the following command:
#### Windows
```sh
python main.py
start.bat
```
#### Linux
```sh
./start.sh
```
### Specifying the COM Port
@ -43,13 +59,17 @@ python main.py
You can specify the COM port directly when running the script:
```sh
python main.py -com=COM3
pythonw main.pyw -com=COM3
```
### Hiding the Console Window
If the COM port is specified, the script will hide the console window and run in the system tray. You can quit the application from the tray icon.
### GUI
The script now includes a GUI that looks like Windows 11 dark mode. It will automatically hide the console window and show the GUI.
## Notes
- Ensure that your ESP32 device is connected to your computer via USB.

15
install_linux.sh Normal file
View File

@ -0,0 +1,15 @@
#!/bin/bash
# Create a virtual environment
python3 -m venv .venv
# Activate the virtual environment
source .venv/bin/activate
# Install the required Python packages
pip install pyserial keyboard pygetwindow pyautogui mss numpy pystray pillow
# Deactivate the virtual environment
deactivate
echo "Installation complete. You can now run the application using start.sh."

15
install_windows.bat Normal file
View File

@ -0,0 +1,15 @@
@echo off
REM Create a virtual environment
python -m venv .venv
REM Activate the virtual environment
call .venv\Scripts\activate
REM Install the required Python packages
pip install pyserial keyboard pygetwindow pyautogui mss numpy pystray pillow
REM Deactivate the virtual environment
deactivate
echo Installation complete. You can now run the application using start.bat.
pause

View File

@ -10,6 +10,25 @@ from PIL import Image, ImageDraw
import threading
import ctypes
import os
import tkinter as tk
from tkinter import messagebox
from tkinter import ttk
# Define the region where the mute and deafen icons are located
x = -1772
y = 1064
x2 = -1746
y2 = 1063
# Define the size of the region to check
width = 4
height = 4
grid = 4
# Define the expected color of the mute and deafen icons
expected_r = 242
expected_g = 63
expected_b = 67
# Set the title for the PowerShell window
ctypes.windll.kernel32.SetConsoleTitleW("ESP32 Discord Controller")
@ -26,26 +45,32 @@ def get_serial_port():
if len(sys.argv) > 1 and sys.argv[1].startswith("-com="):
return sys.argv[1].split("=")[1]
ports = list(list_ports.comports())
ports = [port for port in list(list_ports.comports()) if port.device != "COM1"]
if not ports:
print("No serial ports found. Please connect a device.")
messagebox.showerror("Error", "No serial ports found. Please connect a device.")
return None
print("Available Ports:")
for i, port in enumerate(ports):
print(f"{i}: {port.device}")
choice = int(input("Select a port by number: "))
return ports[choice].device
x = -1772
y = 1064
x2 = -1746
y2 = 1063
width = 4
height = 4
expected_r = 242
expected_g = 63
expected_b = 67
grid = 4
if len(ports) == 1:
return ports[0].device
port_choices = [port.device for port in ports]
def on_select():
selected_port.set(port_listbox.get(port_listbox.curselection()))
port_window.destroy()
port_window = tk.Tk()
port_window.title("Select Serial Port")
port_window.configure(bg="#1e1e1e")
tk.Label(port_window, text="Available Ports:", bg="#1e1e1e", fg="#ffffff").pack()
port_listbox = tk.Listbox(port_window, bg="#2d2d2d", fg="#ffffff", selectbackground="#0078d7")
for port in port_choices:
port_listbox.insert(tk.END, port)
port_listbox.pack()
selected_port = tk.StringVar()
tk.Button(port_window, text="Select", command=on_select, bg="#0078d7", fg="#ffffff").pack()
port_window.mainloop()
return selected_port.get()
def get_discord_status():
with mss.mss() as sct:
@ -61,10 +86,6 @@ def get_discord_status():
mute_image = np.array(mute_screenshot)
deafen_image = np.array(deafen_screenshot)
# # Save the screenshots to files
# mss.tools.to_png(mute_screenshot.rgb, mute_screenshot.size, output="mute_screenshot.png")
# mss.tools.to_png(deafen_screenshot.rgb, deafen_screenshot.size, output="deafen_screenshot.png")
# Check the pixel color in a 2x2 region
mute_status = any(
(mute_image[i, j, 2] == expected_r and
@ -95,32 +116,35 @@ def create_image():
return image
def toggle_console():
whnd = ctypes.windll.kernel32.GetConsoleWindow()
if whnd != 0:
if ctypes.windll.user32.IsWindowVisible(whnd):
ctypes.windll.user32.ShowWindow(whnd, 0)
else:
ctypes.windll.user32.ShowWindow(whnd, 1)
if console_window.state() == "normal":
console_window.withdraw()
else:
console_window.deiconify()
def log_message(message):
log_text.insert(tk.END, message + "\n")
log_text.see(tk.END)
def on_quit(icon, item):
icon.stop()
if 'ser' in globals() and ser.is_open:
ser.close()
print("Serial connection closed.")
log_message("Serial connection closed.")
console_window.withdraw()
console_window.quit()
sys.exit(0)
def hide_console():
whnd = ctypes.windll.kernel32.GetConsoleWindow()
if whnd != 0:
ctypes.windll.user32.ShowWindow(whnd, 0)
ctypes.windll.kernel32.CloseHandle(whnd)
log_message("Hiding console window...")
time.sleep(2)
console_window.withdraw()
def hide_in_tray():
hide_console()
icon = pystray.Icon("ESP32-discord")
icon.icon = create_image()
icon.menu = pystray.Menu(pystray.MenuItem('Quit', on_quit), pystray.MenuItem('Toggle Console', toggle_console))
threading.Thread(target=icon.run).start()
hide_console()
def main():
global ser
@ -131,20 +155,20 @@ def main():
try:
ser = serial.Serial(port, 115200, timeout=1)
time.sleep(2) # Wait for the connection to initialize
print("Listening for commands...")
log_message("Listening for commands...")
last_mute_status, last_deafen_status = get_discord_status()
print(f"Mute: {last_mute_status}, Deafen: {last_deafen_status}")
log_message(f"Mute: {last_mute_status}, Deafen: {last_deafen_status}")
ser.write(f"Mute: {last_mute_status}, Deafen: {last_deafen_status}\n".encode('utf-8'))
if len(sys.argv) > 1 and sys.argv[1].startswith("-com="):
hide_in_tray()
hide_in_tray()
while True:
try:
if ser.in_waiting > 0:
line = ser.readline().decode('utf-8').strip()
print(f"Received: {line}")
log_message(f"Received: {line}")
if line == "mute":
keyboard.send('ctrl+shift+f16')
@ -154,28 +178,39 @@ def main():
mute_status, deafen_status = get_discord_status()
ser.write(f"Mute: {mute_status}, Deafen: {deafen_status}\n".encode('utf-8'))
else:
print(f"Unknown command: {line}")
log_message(f"Unknown command: {line}")
current_mute_status, current_deafen_status = get_discord_status()
if current_mute_status != last_mute_status or current_deafen_status != last_deafen_status:
ser.write(f"Mute: {current_mute_status}, Deafen: {current_deafen_status}\n".encode('utf-8'))
print(f"Mute: {current_mute_status}, Deafen: {current_deafen_status}")
log_message(f"Mute: {current_mute_status}, Deafen: {current_deafen_status}")
last_mute_status, last_deafen_status = current_mute_status, current_deafen_status
time.sleep(0.1)
except KeyboardInterrupt:
print("Exiting program.")
log_message("Exiting program.")
break
except Exception as e:
print(f"Error during execution: {e}")
log_message(f"Error during execution: {e}")
break
except serial.SerialException as e:
print(f"Could not open serial port: {e}")
messagebox.showerror("Error", f"Could not open serial port: {e}")
finally:
if 'ser' in globals() and ser.is_open:
ser.close()
print("Serial connection closed.")
log_message("Serial connection closed.")
if __name__ == "__main__":
main()
console_window = tk.Tk()
console_window.title("ESP32 Discord Controller")
console_window.configure(bg="#1e1e1e")
style = ttk.Style()
style.theme_use('clam')
style.configure("TLabel", background="#1e1e1e", foreground="#ffffff")
style.configure("TButton", background="#0078d7", foreground="#ffffff")
style.configure("TText", background="#2d2d2d", foreground="#ffffff")
log_text = tk.Text(console_window, wrap=tk.WORD, bg="#2d2d2d", fg="#ffffff")
log_text.pack(expand=True, fill=tk.BOTH)
threading.Thread(target=main).start()
console_window.mainloop()

9
start.bat Normal file
View File

@ -0,0 +1,9 @@
@echo off
REM Activate the virtual environment
call .venv\Scripts\activate
REM Run the Python script
start pythonw main.pyw
REM Deactivate the virtual environment
deactivate

View File

@ -2,7 +2,7 @@
& .venv\Scripts\activate
# Run the Python script
python main.py -com=COM3
pythonw main.pyw
# Deactivate the virtual environment
deactivate

10
start.sh Normal file
View File

@ -0,0 +1,10 @@
#!/bin/bash
# Activate the virtual environment
source .venv/bin/activate
# Run the Python script
pythonw main.pyw &
# Deactivate the virtual environment
deactivate