import serial import time import keyboard import serial.tools.list_ports as list_ports import mss import numpy as np import sys import pystray 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") # Set the process name for Task Manager try: import ctypes ctypes.windll.kernel32.SetConsoleTitleW("ESP32 Discord Controller") os.system("title ESP32 Discord Controller") except: pass def get_serial_port(): if len(sys.argv) > 1 and sys.argv[1].startswith("-com="): return sys.argv[1].split("=")[1] ports = [port for port in list(list_ports.comports()) if port.device != "COM1"] if not ports: messagebox.showerror("Error", "No serial ports found. Please connect a device.") return None 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: # Define the region where the mute and deafen icons are located mute_region = {"top": y, "left": x, "width": width, "height": height} deafen_region = {"top": y2, "left": x2, "width": width, "height": height} # Capture the screen region mute_screenshot = sct.grab(mute_region) deafen_screenshot = sct.grab(deafen_region) # Convert the screenshots to numpy arrays mute_image = np.array(mute_screenshot) deafen_image = np.array(deafen_screenshot) # Check the pixel color in a 2x2 region mute_status = any( (mute_image[i, j, 2] == expected_r and mute_image[i, j, 1] == expected_g and mute_image[i, j, 0] == expected_b) for i in range(grid) for j in range(grid) ) deafen_status = any( (deafen_image[i, j, 2] == expected_r and deafen_image[i, j, 1] == expected_g and deafen_image[i, j, 0] == expected_b) for i in range(grid) for j in range(grid) ) return mute_status, deafen_status def create_image(): # Generate an image for the system tray icon width = 64 height = 64 image = Image.new('RGB', (width, height), (255, 255, 255)) dc = ImageDraw.Draw(image) dc.rectangle( [(width // 2, 0), (width, height // 2)], fill="black") dc.rectangle( [(0, height // 2), (width // 2, height)], fill="black") return image def toggle_console(): 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() log_message("Serial connection closed.") console_window.withdraw() console_window.quit() sys.exit(0) def hide_console(): log_message("Hiding console window...") time.sleep(2) console_window.withdraw() def hide_in_tray(): 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 port = get_serial_port() if not port: return try: ser = serial.Serial(port, 115200, timeout=1) time.sleep(2) # Wait for the connection to initialize log_message("Listening for commands...") last_mute_status, last_deafen_status = get_discord_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')) hide_in_tray() while True: try: if ser.in_waiting > 0: line = ser.readline().decode('utf-8').strip() log_message(f"Received: {line}") if line == "mute": keyboard.send('ctrl+shift+f16') elif line == "deafen": keyboard.send('ctrl+shift+f15') elif line == "status": mute_status, deafen_status = get_discord_status() ser.write(f"Mute: {mute_status}, Deafen: {deafen_status}\n".encode('utf-8')) else: 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')) 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: log_message("Exiting program.") break except Exception as e: log_message(f"Error during execution: {e}") break except serial.SerialException as e: messagebox.showerror("Error", f"Could not open serial port: {e}") finally: if 'ser' in globals() and ser.is_open: ser.close() log_message("Serial connection closed.") if __name__ == "__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()