Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 10 additions & 11 deletions config.yaml
Original file line number Diff line number Diff line change
@@ -1,31 +1,30 @@
---
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:
# - PYTHON use Python libraries (psutils, GPUtil...) to read hardware sensors (supports all OS but not all HW)
# - 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
Expand All @@ -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:
Expand All @@ -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
Expand Down
71 changes: 46 additions & 25 deletions configure.py
Original file line number Diff line number Diff line change
Expand Up @@ -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\""
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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("<FocusIn>", self.on_theme_change)
self.window.after(0, self.on_fan_speed_update)

Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand Down Expand Up @@ -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):
Expand All @@ -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):
Expand All @@ -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):
Expand Down
8 changes: 5 additions & 3 deletions library/lcd/lcd_comm_turing_usb.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()
Expand Down
15 changes: 9 additions & 6 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -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__":

Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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',
Expand Down
Binary file added res/backgrounds/example_1920x480 - bigger 1MB.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added res/backgrounds/example_1920x480.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 6 additions & 2 deletions simple-program.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -52,15 +53,15 @@
# - 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
# 320x480 for 3.5" models
# 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"

Expand Down Expand Up @@ -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)
Expand Down