diff --git a/config.yaml b/config.yaml index 84c7bd11..307e6d7f 100644 --- a/config.yaml +++ b/config.yaml @@ -1,16 +1,15 @@ ---- config: # Configuration values to set up basic communication # Set your COM port e.g. COM3 for Windows, /dev/ttyACM0 for Linux... # Use AUTO for COM port auto-discovery (may not work on every setup) # COM_PORT: "/dev/ttyACM0" # COM_PORT: "COM3" - COM_PORT: "AUTO" + COM_PORT: AUTO # Theme to use (located in res/themes) # Use the name of the folder as value # Choose a theme made for your screen size (see DISPLAY_SIZE inside theme.yaml) - THEME: 3.5inchTheme2 + THEME: Gradient # Hardware sensors reading # Choose the appropriate method for reading your hardware sensors: @@ -18,14 +17,14 @@ config: # - LHM use LibreHardwareMonitor library to read hardware sensors (Windows only - NEEDS ADMIN RIGHTS) # - STUB / STATIC use random/static data instead of real hardware sensors # - AUTO use the best method based on your OS: Windows OS will use LHM, other OS will use Python libraries - HW_SENSORS: AUTO + HW_SENSORS: PYTHON # Network interfaces # Linux/MacOS interfaces are named "eth0", "wlan0", "wlp1s0", "enp2s0"... # For Windows use the interfaces pretty name: "Ethernet 2", "Wi-Fi", ... # Leave the fields empty if the card does not exist on your setup - ETH: "" # Ethernet Card - WLO: "" # Wi-Fi Card + ETH: Ethernet # Ethernet Card + WLO: '' # Wi-Fi Card # CPU fan # For Linux/MacOS platforms, the CPU fan is amongst all fan sensors gathered from the motherboard chipset @@ -41,14 +40,14 @@ config: # OpenWeatherMap API KEY. Can be obtained by creating a free account on https://home.openweathermap.org/users/sign_up. # You need to subscribe to the 3.0 OneCallAPI that has 1000 free daily calls - WEATHER_API_KEY: "" + WEATHER_API_KEY: '' # Location from which to display the weather. Use for example https://www.latlong.net/ to get latitude/longitude - WEATHER_LATITUDE: 45.75 - WEATHER_LONGITUDE: 4.85 + WEATHER_LATITUDE: '45.75' + WEATHER_LONGITUDE: '4.85' # Units used to display temperatures (metric - °C, imperial - °F, standard - °K) WEATHER_UNITS: metric # Language is used by the API. Find more here https://openweathermap.org/api/one-call-3#multi - WEATHER_LANGUAGE: en + WEATHER_LANGUAGE: de display: # Display revision: @@ -60,7 +59,7 @@ display: # - WEACT_B for WeAct Studio Display FS V1 0.96" # - SIMU for simulated display (image written in screencap.png). Width & height will be detected from the theme # To identify your smart screen: https://github.com/mathoudebine/turing-smart-screen-python/wiki/Hardware-revisions - REVISION: A + REVISION: C_USB # Display Brightness # Set this as the desired %, 0 being completely dark and 100 being max brightness diff --git a/configure.py b/configure.py index 53b85845..8a42e897 100755 --- a/configure.py +++ b/configure.py @@ -66,7 +66,7 @@ SIZE_3_5_INCH = "3.5\"" SIZE_5_INCH = "5\"" SIZE_8_8_INCH = "8.8\"" -SIZE_8_8_INCH_USB = "8.8\" (V1.1)" +SIZE_8_8_INCH_USB = "8.8\" (V1.1) or 9.2\"" SIZE_2_1_INCH = "2.1\"" # Only for retro compatibility SIZE_2_x_INCH = "2.1\" / 2.8\"" SIZE_0_96_INCH = "0.96\"" @@ -124,20 +124,20 @@ "sk": "Slovak", "sl": "Slovenian", "sp": "Spanish", "sv": "Swedish", "th": "Thai", "tr": "Turkish", "ua": "Ukrainian", "vi": "Vietnamese", "zu": "Zulu"} -MAIN_DIRECTORY = str(Path(__file__).parent.resolve()) + "/" -THEMES_DIR = MAIN_DIRECTORY + 'res/themes' +MAIN_DIRECTORY = Path(__file__).resolve().parent +THEMES_DIR = MAIN_DIRECTORY / "res/themes" -circular_mask = Image.open(MAIN_DIRECTORY + "res/backgrounds/circular-mask.png") + +circular_mask = Image.open(MAIN_DIRECTORY / "res/backgrounds/circular-mask.png") def get_theme_data(name: str): - dir = os.path.join(THEMES_DIR, name) + dir = THEMES_DIR / name + # checking if it is a directory - if os.path.isdir(dir): - # Check if a theme.yaml file exists - theme = os.path.join(dir, 'theme.yaml') - if os.path.isfile(theme): - # Get display size from theme.yaml - with open(theme, "rt", encoding='utf8') as stream: + if dir.is_dir(): + theme = dir / "theme.yaml" + if theme.is_file(): + with open(theme, "rt", encoding="utf8") as stream: theme_data, ind, bsi = ruamel.yaml.util.load_yaml_guess_indent(stream) return theme_data return None @@ -189,8 +189,7 @@ def __init__(self): self.window = Tk() self.window.title('Turing System Monitor configuration') self.window.geometry("820x580") - self.window.iconphoto(True, PhotoImage(file=MAIN_DIRECTORY + "res/icons/monitor-icon-17865/64.png")) - # When window gets focus again, reload theme preview in case it has been updated by theme editor + self.window.iconphoto(True,PhotoImage(file=str(MAIN_DIRECTORY / "res/icons/monitor-icon-17865/64.png"))) # When window gets focus again, reload theme preview in case it has been updated by theme editor self.window.bind("", self.on_theme_change) self.window.after(0, self.on_fan_speed_update) @@ -313,14 +312,17 @@ def run(self): def load_theme_preview(self): theme_data = get_theme_data(self.theme_cb.get()) + if theme_data and theme_data['display'].get("DISPLAY_SIZE", '3.5"') == SIZE_2_1_INCH: + theme_preview.paste(circular_mask, mask=circular_mask) + try: - theme_preview = Image.open(MAIN_DIRECTORY + "res/themes/" + self.theme_cb.get() + "/preview.png") + theme_preview = Image.open(MAIN_DIRECTORY / "res" / "themes" / self.theme_cb.get() / "preview.png") if theme_data['display'].get("DISPLAY_SIZE", '3.5"') == SIZE_2_1_INCH: # This is a circular screen: apply a circle mask over the preview theme_preview.paste(circular_mask, mask=circular_mask) except: - theme_preview = Image.open(MAIN_DIRECTORY + "res/docs/no-preview.png") + theme_preview = Image.open(MAIN_DIRECTORY / "res/docs/no-preview.png") finally: theme_preview.thumbnail((320, 480), Image.Resampling.LANCZOS) self.theme_preview_img = ImageTk.PhotoImage(theme_preview) @@ -338,7 +340,7 @@ def load_theme_preview(self): self.theme_author.place(x=10, y=self.theme_preview_img.height() + 15) def load_config_values(self): - with open(MAIN_DIRECTORY + "config.yaml", "rt", encoding='utf8') as stream: + with open(MAIN_DIRECTORY / "config.yaml", "rt", encoding='utf8') as stream: self.config, ind, bsi = ruamel.yaml.util.load_yaml_guess_indent(stream) # Check if theme is valid @@ -450,7 +452,7 @@ def save_config_values(self): self.config['display']['DISPLAY_REVERSE'] = [k for k, v in reverse_map.items() if v == self.orient_cb.get()][0] self.config['display']['BRIGHTNESS'] = int(self.brightness_slider.get()) - with open(MAIN_DIRECTORY + "config.yaml", "w", encoding='utf-8') as file: + with open(MAIN_DIRECTORY / "config.yaml", "w", encoding='utf-8') as file: ruamel.yaml.YAML().dump(self.config, file) def save_additional_config(self, ping: str, api_key: str, lat: str, long: str, unit: str, lang: str): @@ -461,7 +463,7 @@ def save_additional_config(self, ping: str, api_key: str, lat: str, long: str, u self.config['config']['WEATHER_UNITS'] = unit self.config['config']['WEATHER_LANGUAGE'] = lang - with open(MAIN_DIRECTORY + "config.yaml", "w", encoding='utf-8') as file: + with open(MAIN_DIRECTORY / "config.yaml", "w", encoding='utf-8') as file: ruamel.yaml.YAML().dump(self.config, file) def on_theme_change(self, e=None): @@ -471,25 +473,44 @@ def on_weatherping_click(self): self.more_config_window.show() def on_open_theme_folder_click(self): - path = f'"{MAIN_DIRECTORY}res/themes"' + #path = f'"{MAIN_DIRECTORY}res/themes"' + #if platform.system() == "Windows": + # os.startfile(path) + #elif platform.system() == "Darwin": + # subprocess.Popen(["open", path]) + #else: + # subprocess.Popen(["xdg-open", path]) + path = MAIN_DIRECTORY / "res/themes" + if platform.system() == "Windows": os.startfile(path) elif platform.system() == "Darwin": - subprocess.Popen(["open", path]) + subprocess.Popen(["open", str(path)]) else: - subprocess.Popen(["xdg-open", path]) + subprocess.Popen(["xdg-open", str(path)]) + def on_theme_editor_click(self): - subprocess.Popen( - f'"{MAIN_DIRECTORY}{glob.glob("theme-editor.*", root_dir=MAIN_DIRECTORY)[0]}" "{self.theme_cb.get()}"', - shell=True) + theme_editor = next(MAIN_DIRECTORY.glob("theme-editor.*")) + + if platform.system() == "Windows": + subprocess.Popen([str(theme_editor), self.theme_cb.get()], shell=True) + else: + subprocess.Popen([str(theme_editor), self.theme_cb.get()]) + def on_save_click(self): self.save_config_values() def on_saverun_click(self): self.save_config_values() - subprocess.Popen(f'"{MAIN_DIRECTORY}{glob.glob("main.*", root_dir=MAIN_DIRECTORY)[0]}"', shell=True) + main_file = next(MAIN_DIRECTORY.glob("main.*")) + + if platform.system() == "Windows": + subprocess.Popen([str(main_file)], shell=True) + else: + subprocess.Popen([str(main_file)]) + self.window.destroy() def on_brightness_change(self, e=None): diff --git a/library/lcd/lcd_comm_turing_usb.py b/library/lcd/lcd_comm_turing_usb.py index 6fee436f..380641e1 100644 --- a/library/lcd/lcd_comm_turing_usb.py +++ b/library/lcd/lcd_comm_turing_usb.py @@ -37,7 +37,7 @@ from library.lcd.lcd_comm import Orientation, LcdComm VENDOR_ID = 0x1cbe -PRODUCT_ID = 0x0088 +PRODUCT_ID = [0x0088, 0x0092] # 8.8", 9.2" MAX_CHUNK_BYTES = 1024*1024 # Data sent to screen cannot exceed 1024MB or there will be a timeout @@ -71,9 +71,11 @@ def encrypt_command_packet(data: bytearray) -> bytearray: def find_usb_device(): - dev = usb.core.find(idVendor=VENDOR_ID, idProduct=PRODUCT_ID) + for pid in PRODUCT_ID: + dev = usb.core.find(idVendor=VENDOR_ID, idProduct=pid) if dev is None: - raise ValueError('USB device not found') + raise ValueError(f'USB device not found') + try: dev.set_configuration() diff --git a/main.py b/main.py index 38676496..9872f533 100755 --- a/main.py +++ b/main.py @@ -68,7 +68,7 @@ # If pystray cannot be loaded do not stop the program, just ignore it. The tray icon will not be displayed. pass -MAIN_DIRECTORY = str(Path(__file__).parent.resolve()) + "/" +MAIN_DIRECTORY = Path(__file__).resolve().parent if __name__ == "__main__": @@ -110,17 +110,20 @@ def clean_stop(tray_icon=None): except: os._exit(0) - def on_signal_caught(signum, frame=None): logger.info("Caught signal %d, exiting" % signum) clean_stop() - def on_configure_tray(tray_icon, item): logger.info("Configure from tray icon") - subprocess.Popen(f'"{MAIN_DIRECTORY}{glob.glob("configure.*", root_dir=MAIN_DIRECTORY)[0]}"', shell=True) - clean_stop(tray_icon) + configure_file = next(MAIN_DIRECTORY.glob("configure.*")) + + if platform.system() == "Windows": + subprocess.Popen([str(configure_file)], shell=True) + else: + subprocess.Popen([str(configure_file)]) + clean_stop(tray_icon) def on_exit_tray(tray_icon, item): logger.info("Exit from tray icon") @@ -165,7 +168,7 @@ def on_win32_wm_event(hWnd, msg, wParam, lParam): tray_icon = pystray.Icon( name='Turing System Monitor', title='Turing System Monitor', - icon=Image.open(MAIN_DIRECTORY + "res/icons/monitor-icon-17865/64.png"), + icon=Image.open(MAIN_DIRECTORY / "res/icons/monitor-icon-17865/64.png"), menu=pystray.Menu( pystray.MenuItem( text='Configure', diff --git a/res/backgrounds/example_1920x480 - bigger 1MB.png b/res/backgrounds/example_1920x480 - bigger 1MB.png new file mode 100644 index 00000000..65e3cad4 Binary files /dev/null and b/res/backgrounds/example_1920x480 - bigger 1MB.png differ diff --git a/res/backgrounds/example_1920x480.png b/res/backgrounds/example_1920x480.png new file mode 100644 index 00000000..356fbe75 Binary files /dev/null and b/res/backgrounds/example_1920x480.png differ diff --git a/simple-program.py b/simple-program.py index 287c92d1..e088f967 100755 --- a/simple-program.py +++ b/simple-program.py @@ -35,6 +35,7 @@ from library.lcd.lcd_comm_rev_b import LcdCommRevB from library.lcd.lcd_comm_rev_c import LcdCommRevC from library.lcd.lcd_comm_rev_d import LcdCommRevD +from library.lcd.lcd_comm_turing_usb import LcdCommTuringUSB from library.lcd.lcd_comm_weact_a import LcdCommWeActA from library.lcd.lcd_comm_weact_b import LcdCommWeActB from library.lcd.lcd_simulated import LcdSimulated @@ -52,7 +53,7 @@ # - D for Kipye Qiye Smart Display 3.5" # - SIMU for simulated display (image written in screencap.png) # To identify your smart screen: https://github.com/mathoudebine/turing-smart-screen-python/wiki/Hardware-revisions -REVISION = "A" +REVISION = "USB_C" # Display width & height in pixels for portrait orientation # /!\ Do not switch width/height here for landscape, use lcd_comm.SetOrientation below @@ -60,7 +61,7 @@ # 480x480 for 2.1" models # 480x800 for 5" models # 480x1920 for 8.8" models -WIDTH, HEIGHT = 320, 480 +WIDTH, HEIGHT = 480, 1920 assert WIDTH <= HEIGHT, "Indicate display width/height for PORTRAIT orientation: width <= height" @@ -101,6 +102,9 @@ def sighandler(signum, frame): elif REVISION == "WEACT_B": logger.info("Selected Hardware WeAct Studio Display FS V1 0.96\"") lcd_comm = LcdCommWeActB(com_port=COM_PORT, display_width=WIDTH, display_height=HEIGHT) + elif REVISION == "USB_C": + logger.info("Selected Hardware Revision USB C (Turing Smart Screen 8.8\" or 9.2\")") + lcd_comm = LcdCommTuringUSB(com_port=COM_PORT, display_width=WIDTH, display_height=HEIGHT) elif REVISION == "SIMU": logger.info("Selected Simulated LCD") lcd_comm = LcdSimulated(display_width=WIDTH, display_height=HEIGHT)