Replies: 15 comments 9 replies
-
stats5.py import time
import logging
import os
import sys
import clr
import ctypes
import re
from datetime import datetime
from library.lcd.lcd_comm import Orientation
from library.lcd.lcd_comm_rev_c import LcdCommRevC # For 5″ monitor
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s.%(msecs)03d [%(levelname)s] %(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
force=True
)
# ====== Display Settings ======
# In landscape mode, physical resolution is 800x480.
DISPLAY_WIDTH = 480
DISPLAY_HEIGHT = 800
FONT_PATH = "res/fonts/jetbrains-mono/JetBrainsMono-Bold.ttf"
FONT_SIZE = 20
BG_COLOR = (0, 0, 0) # Solid black background
CPU_COLOR = (255, 0, 0) # Red for CPU (and RAM)
GPU_COLOR = (0, 128, 0) # Dark green for GPU stats
TEXT_COLOR = (255, 255, 255) # White text
UPDATE_INTERVAL = .5 # seconds
BACKGROUND_IMG = "background.png"
# Bar vertical offset (in pixels) to align bars with text.
BAR_OFFSET = 5
# ====== Setup LibreHardwareMonitor ======
clr.AddReference(os.path.join(os.getcwd(), 'external', 'LibreHardwareMonitor', 'LibreHardwareMonitorLib.dll'))
clr.AddReference(os.path.join(os.getcwd(), 'external', 'LibreHardwareMonitor', 'HidSharp.dll'))
from LibreHardwareMonitor import Hardware
handle = Hardware.Computer()
handle.IsCpuEnabled = True
handle.IsGpuEnabled = True
handle.IsMemoryEnabled = True
handle.IsMotherboardEnabled = True
handle.IsControllerEnabled = True
handle.IsNetworkEnabled = True
handle.IsStorageEnabled = True
handle.IsPsuEnabled = True
handle.Open()
# ====== Sensor Query Functions ======
def get_sensor_value(hw_list, hw_type, sensor_type, sensor_name, hw_name=None):
for hw in hw_list:
if hw.HardwareType == hw_type:
if hw_name and (hw_name.lower() not in hw.Name.lower()):
continue
hw.Update()
for sensor in hw.Sensors:
if str(sensor.SensorType) == sensor_type and sensor.Name == sensor_name:
return sensor.Value
for subhw in hw.SubHardware:
subhw.Update()
for sensor in subhw.Sensors:
if str(sensor.SensorType) == sensor_type and sensor.Name == sensor_name:
return sensor.Value
return None
def get_hardware_name(hw_list, hw_type, skip_first=False):
skipped = False
for hw in hw_list:
if hw.HardwareType == hw_type:
if skip_first and not skipped:
skipped = True
continue
return hw.Name
return None
def truncate_first_word(name_str):
parts = name_str.split()
if len(parts) > 1:
return " ".join(parts[1:])
return name_str
# Store CPU name globally.
def initialize_hardware_names():
global CPU_NAME
hw_list = handle.Hardware
cpu_full_name = get_hardware_name(hw_list, Hardware.HardwareType.Cpu) or "Unknown CPU"
CPU_NAME = truncate_first_word(cpu_full_name)
# Get GPU stats for a given filter (e.g. "4090" or "1030")
def get_gpu_stats(hw_list, filter_str):
stats = {}
for hw in hw_list:
if hw.HardwareType == Hardware.HardwareType.GpuNvidia and filter_str.lower() in hw.Name.lower():
stats["name"] = hw.Name
stats["util"] = get_sensor_value(hw_list, Hardware.HardwareType.GpuNvidia, "Load", "GPU Core", hw_name=filter_str) or 0.0
stats["temp"] = get_sensor_value(hw_list, Hardware.HardwareType.GpuNvidia, "Temperature", "GPU Core", hw_name=filter_str) or 0.0
stats["clock"] = get_sensor_value(hw_list, Hardware.HardwareType.GpuNvidia, "Clock", "GPU Core", hw_name=filter_str) or 0.0
stats["mem_used"] = get_sensor_value(hw_list, Hardware.HardwareType.GpuNvidia, "SmallData", "GPU Memory Used", hw_name=filter_str) or 0.0
stats["mem_total"] = get_sensor_value(hw_list, Hardware.HardwareType.GpuNvidia, "SmallData", "GPU Memory Total", hw_name=filter_str) or 1.0
stats["mem_percent"] = (stats["mem_used"] / stats["mem_total"]) * 100
return stats
return None
# Draw a GPU section starting at (x,y) for a given GPU's stats.
def draw_gpu_section(lcd, x, y, stats):
# Fix GPU progress bar's right edge at x=780.
bar_width = 300
bar_x = 780 - bar_width
# Header: display GPU name.
lcd.DisplayText(stats["name"], x=x, y=y, font=FONT_PATH, font_size=20,
font_color=GPU_COLOR, background_color=BG_COLOR)
y += 20
# Utilization text: pad to 3 characters.
util_str = f"Util: {int(stats['util']):3d}%"
lcd.DisplayText(util_str, x=x, y=y, font=FONT_PATH, font_size=16,
font_color=GPU_COLOR, background_color=BG_COLOR)
y += 20
# Utilization progress bar.
lcd.DisplayProgressBar(x=bar_x, y=y + BAR_OFFSET, width=bar_width, height=20,
min_value=0, max_value=100, value=int(stats["util"]),
bar_color=GPU_COLOR, bar_outline=True, background_color=BG_COLOR)
y += 30
# Temperature and clock on one line.
temp_freq_str = f"Temp: {int(stats['temp']):2d}°C Freq: {int(stats['clock']):4d}MHz"
lcd.DisplayText(temp_freq_str, x=x, y=y, font=FONT_PATH, font_size=16,
font_color=GPU_COLOR, background_color=BG_COLOR)
y += 20
# Memory usage text: pad to 5 digits.
mem_str = f"Mem: {int(stats['mem_used']):5d}MB/{int(stats['mem_total']):5d}MB"
lcd.DisplayText(mem_str, x=x, y=y, font=FONT_PATH, font_size=16,
font_color=GPU_COLOR, background_color=BG_COLOR)
y += 20
# Memory usage progress bar.
lcd.DisplayProgressBar(x=bar_x, y=y + BAR_OFFSET, width=bar_width, height=20,
min_value=0, max_value=100, value=int(stats["mem_percent"]),
bar_color=GPU_COLOR, bar_outline=True, background_color=BG_COLOR)
y += 30
return y
def get_sorted_core_loads(hw_list):
core_loads = []
for hw in hw_list:
if hw.HardwareType == Hardware.HardwareType.Cpu:
hw.Update()
for sensor in hw.Sensors:
if str(sensor.SensorType) == "Load" and "Core" in sensor.Name:
m = re.search(r'#(\d+)', sensor.Name)
core_index = int(m.group(1)) if m else 99
core_loads.append((core_index, sensor.Name, sensor.Value))
for subhw in hw.SubHardware:
subhw.Update()
for sensor in subhw.Sensors:
if str(sensor.SensorType) == "Load" and "Core" in sensor.Name:
m = re.search(r'#(\d+)', sensor.Name)
core_index = int(m.group(1)) if m else 99
core_loads.append((core_index, sensor.Name, sensor.Value))
core_loads.sort(key=lambda x: x[0])
return core_loads
def initialize_display():
lcd = LcdCommRevC(
com_port="AUTO",
display_width=DISPLAY_WIDTH,
display_height=DISPLAY_HEIGHT
)
lcd.Reset()
lcd.InitializeComm()
lcd.SetBrightness(50)
lcd.SetOrientation(Orientation.LANDSCAPE)
logging.debug("Displaying initial background...")
lcd.DisplayBitmap(BACKGROUND_IMG)
logging.debug("Initial background displayed.")
return lcd
def draw_static_text(lcd):
# Left side: CPU header and CPU name.
lcd.DisplayText("CPU Stats", x=10, y=10, font=FONT_PATH, font_size=22,
font_color=TEXT_COLOR, background_color=BG_COLOR)
lcd.DisplayText(CPU_NAME, x=10, y=40, font=FONT_PATH, font_size=20,
font_color=CPU_COLOR, background_color=BG_COLOR)
# Right side: GPU Stats header.
lcd.DisplayText("GPU Stats", x=420, y=10, font=FONT_PATH, font_size=22,
font_color=TEXT_COLOR, background_color=BG_COLOR)
def draw_dynamic_stats(lcd):
hw_list = handle.Hardware
# --- CPU Stats (Left Side) ---
cpu_load = get_sensor_value(hw_list, Hardware.HardwareType.Cpu, "Load", "CPU Total") or 0.0
cpu_temp = get_sensor_value(hw_list, Hardware.HardwareType.Cpu, "Temperature", "Core (Tctl/Tdie)")
if cpu_temp is None:
cpu_temp = get_sensor_value(hw_list, Hardware.HardwareType.Cpu, "Temperature", "Package") or 0.0
cpu_freq = get_sensor_value(hw_list, Hardware.HardwareType.Cpu, "Clock", "Core #1") or 0.0
y_cpu = 70
# Total percentage: pad to 3 digits.
lcd.DisplayText(f"Total: {int(cpu_load):3d}%", x=10, y=y_cpu, font=FONT_PATH, font_size=20,
font_color=CPU_COLOR, background_color=BG_COLOR)
cpu_bar_width = 170
cpu_bar_x = 320 - cpu_bar_width # = 180.
lcd.DisplayProgressBar(x=cpu_bar_x, y=y_cpu + BAR_OFFSET, width=cpu_bar_width, height=20,
min_value=0, max_value=100, value=int(cpu_load),
bar_color=CPU_COLOR, bar_outline=True, background_color=BG_COLOR)
y_cpu += 30
lcd.DisplayText(f"Temp: {int(cpu_temp):2d}°C Freq: {int(cpu_freq):4d}MHz", x=10, y=y_cpu,
font=FONT_PATH, font_size=20, font_color=CPU_COLOR, background_color=BG_COLOR)
y_cpu += 30
core_loads = get_sorted_core_loads(hw_list)
for core_index, sensor_name, load in core_loads:
core_label = f"Core {core_index}:" if core_index != 99 else "Core (top):"
lcd.DisplayText(core_label, x=10, y=y_cpu, font=FONT_PATH, font_size=18,
font_color=CPU_COLOR, background_color=BG_COLOR)
lcd.DisplayProgressBar(x=cpu_bar_x, y=y_cpu + BAR_OFFSET, width=cpu_bar_width, height=15,
min_value=0, max_value=100, value=int(load),
bar_color=CPU_COLOR, bar_outline=True, background_color=BG_COLOR)
lcd.DisplayText(f"{int(load):3d}%", x=330, y=y_cpu, font=FONT_PATH, font_size=18,
font_color=CPU_COLOR, background_color=BG_COLOR)
y_cpu += 20
# --- RAM Stats (Left Side, below CPU) ---
mem_used = get_sensor_value(hw_list, Hardware.HardwareType.Memory, "Data", "Memory Used") or 0.0
mem_avail = get_sensor_value(hw_list, Hardware.HardwareType.Memory, "Data", "Memory Available") or 0.0
mem_total = mem_used + mem_avail
mem_pct = (mem_used / mem_total) * 100 if mem_total > 0 else 0
y_ram = y_cpu + 20
lcd.DisplayText("RAM Stats", x=10, y=y_ram, font=FONT_PATH, font_size=22,
font_color=TEXT_COLOR, background_color=BG_COLOR)
y_ram += 30
# Convert values from GB to MB.
mem_used_mb = int(round(mem_used * 1024))
mem_total_mb = int(round(mem_total * 1024))
# System RAM values: pad to 6 characters.
lcd.DisplayText(f"{mem_used_mb:6d}MB / {mem_total_mb:6d}MB", x=10, y=y_ram, font=FONT_PATH, font_size=20,
font_color=CPU_COLOR, background_color=BG_COLOR)
ram_bar_width = 140
ram_bar_x = 420 - ram_bar_width # = 280.
lcd.DisplayProgressBar(x=ram_bar_x, y=y_ram + BAR_OFFSET, width=ram_bar_width, height=20,
min_value=0, max_value=100, value=int(mem_pct),
bar_color=CPU_COLOR, bar_outline=True, background_color=BG_COLOR)
# --- GPU Stats (Right Side) ---
gpu_x = 420 # left margin for GPU section.
gpu_stats_4090 = get_gpu_stats(hw_list, "4090")
if gpu_stats_4090 is not None:
y_gpu1 = 40
y_gpu1 = draw_gpu_section(lcd, gpu_x, y_gpu1, gpu_stats_4090)
gpu_stats_1030 = get_gpu_stats(hw_list, "1030")
if gpu_stats_1030 is not None:
y_gpu2 = 180 # vertical gap.
y_gpu2 = draw_gpu_section(lcd, gpu_x, y_gpu2, gpu_stats_1030)
# --- Uptime and Clock (Centered at Bottom) ---
now = datetime.now()
clock_str = now.strftime("%a %m/%d/%Y %I:%M:%S %p")
uptime_str = get_uptime_str()
lcd.DisplayText(uptime_str, x=400, y=440, font=FONT_PATH, font_size=20,
font_color=TEXT_COLOR, background_color=BG_COLOR, anchor="mt")
lcd.DisplayText(clock_str, x=400, y=460, font=FONT_PATH, font_size=20,
font_color=TEXT_COLOR, background_color=BG_COLOR, anchor="mt")
def get_uptime_str():
uptime_ms = ctypes.windll.kernel32.GetTickCount64()
uptime_sec = uptime_ms // 1000
days = uptime_sec // 86400
hours = (uptime_sec % 86400) // 3600
minutes = (uptime_sec % 3600) // 60
seconds = uptime_sec % 60
return f"Uptime: {days}d {hours:02d}:{minutes:02d}:{seconds:02d}"
def main():
initialize_hardware_names()
lcd = initialize_display()
draw_static_text(lcd)
while True:
draw_dynamic_stats(lcd)
time.sleep(UPDATE_INTERVAL)
if __name__ == "__main__":
try:
main()
finally:
handle.Close() |
Beta Was this translation helpful? Give feedback.
-
![]() Oh, you will need this file in the root of your directory as well... it is just a black image called background.png |
Beta Was this translation helpful? Give feedback.
-
Thank you @majormer nice to see something a little different than themes with System Monitor! |
Beta Was this translation helpful? Give feedback.
-
Hey @majormer This looks really awesome, and it's almost exactly what I was hoping for. I'm still pretty new to coding, so some of this is a bit confusing for me. I can probably use ChatGPT to tweak things a little if needed, but I was wondering if you could help me figure out how to use a custom Python theme instead of the regular .yaml files. I've already downloaded the repository and was able to run the configure.yaml file and load themes from there without any problems. What I'm stuck on is how to get my screen to load up using one of these custom .py themes instead of the .yaml ones. Any guidance you could offer would be super helpful! |
Beta Was this translation helpful? Give feedback.
-
The trick is have the script in the root of the repository, and run it with
Python directly. You don't use the app or configuration. Instead, the
python script can just use the files directly to access the screen. The
screen size also matters, and needs to set up in the script.
…On Thu, Apr 3, 2025, 8:28 PM SithLord_Sylar ***@***.***> wrote:
Hey @majormer <https://github.com/majormer> This looks really awesome,
and it's almost exactly what I was hoping for. I'm still pretty new to
coding, so some of this is a bit confusing for me. I can probably use
ChatGPT to tweak things a little if needed, but I was wondering if you
could help me figure out how to use a custom Python theme instead of the
regular .yaml files. I've already downloaded the repository and was able to
run the configure.yaml file and load themes from there without any
problems. What I'm stuck on is how to get my screen to load up using one of
these custom .py themes instead of the .yaml ones. Any guidance you could
offer would be super helpful!
—
Reply to this email directly, view it on GitHub
<#664 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AB2XMTKSP6PMEJS5JITO77T2XXN2JAVCNFSM6AAAAABWR3PEU6VHI2DSMVQWIX3LMV43URDJONRXK43TNFXW4Q3PNVWWK3TUHMYTENZSGAYTCOI>
.
You are receiving this because you were mentioned.Message ID:
<mathoudebine/turing-smart-screen-python/repo-discussions/664/comments/12720119
@github.com>
|
Beta Was this translation helpful? Give feedback.
-
I modified your script for my own 3.5" screen and added code to detect AMD GPUs:
|
Beta Was this translation helpful? Give feedback.
-
Cool! Got a picture?
…On Thu, Apr 10, 2025, 2:45 PM CanHasDIY ***@***.***> wrote:
I modified your script for my own 3.5" screen and added code to detect AMD
GPUs:
import time
import logging
import os
import sys
import clr
import ctypes
import re
from datetime import datetime
from library.lcd.lcd_comm import Orientation
from library.lcd.lcd_comm_rev_a import LcdCommRevA # For 3.5″ monitor
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s.%(msecs)03d [%(levelname)s] %(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
force=True
)
# ====== Display Settings ======
# In landscape mode, physical resolution is 800x480.
DISPLAY_WIDTH = 320
DISPLAY_HEIGHT = 480
FONT_PATH = "res/fonts/jetbrains-mono/JetBrainsMono-Bold.ttf"
FONT_SIZE = 5
BG_COLOR = (0, 0, 0) # Solid black background
CPU_COLOR = (255, 0, 0) # Red for CPU (and RAM)
GPU_COLOR = (0, 128, 0) # Dark green for GPU stats
TEXT_COLOR = (255, 255, 255) # White text
UPDATE_INTERVAL = .5 # seconds
BACKGROUND_IMG = "background.png"
# Bar vertical offset (in pixels) to align bars with text.
BAR_OFFSET = 1
# ====== Setup LibreHardwareMonitor ======
clr.AddReference(os.path.join(os.getcwd(), 'external', 'LibreHardwareMonitor', 'LibreHardwareMonitorLib.dll'))
clr.AddReference(os.path.join(os.getcwd(), 'external', 'LibreHardwareMonitor', 'HidSharp.dll'))
from LibreHardwareMonitor import Hardware
handle = Hardware.Computer()
handle.IsCpuEnabled = True
handle.IsGpuEnabled = True
handle.IsMemoryEnabled = True
handle.IsMotherboardEnabled = True
handle.IsControllerEnabled = True
handle.IsNetworkEnabled = True
handle.IsStorageEnabled = True
handle.IsPsuEnabled = True
handle.Open()
# ====== Sensor Query Functions ======
def get_sensor_value(hw_list, hw_type, sensor_type, sensor_name, hw_name=None):
for hw in hw_list:
if hw.HardwareType == hw_type:
if hw_name and (hw_name.lower() not in hw.Name.lower()):
continue
hw.Update()
for sensor in hw.Sensors:
if str(sensor.SensorType) == sensor_type and sensor.Name == sensor_name:
return sensor.Value
for subhw in hw.SubHardware:
subhw.Update()
for sensor in subhw.Sensors:
if str(sensor.SensorType) == sensor_type and sensor.Name == sensor_name:
return sensor.Value
return None
def get_hardware_name(hw_list, hw_type, skip_first=False):
skipped = False
for hw in hw_list:
if hw.HardwareType == hw_type:
if skip_first and not skipped:
skipped = True
continue
return hw.Name
return None
def truncate_first_word(name_str):
parts = name_str.split()
if len(parts) > 1:
return " ".join(parts[1:])
return name_str
# Store CPU name globally.
def initialize_hardware_names():
global CPU_NAME
hw_list = handle.Hardware
cpu_full_name = get_hardware_name(hw_list, Hardware.HardwareType.Cpu) or "Unknown CPU"
CPU_NAME = truncate_first_word(cpu_full_name)
# Get GPU stats for a given filter (e.g. "amd" or "Nvidia")
def get_gpu_stats(hw_list, filter_str):
stats = {}
for hw in hw_list:
if hw.HardwareType == Hardware.HardwareType.GpuNvidia and filter_str.lower() in hw.Name.lower():
stats["name"] = hw.Name
stats["util"] = get_sensor_value(hw_list, Hardware.HardwareType.GpuNvidia, "Load", "GPU Core", hw_name=filter_str) or 0.0
stats["temp"] = get_sensor_value(hw_list, Hardware.HardwareType.GpuNvidia, "Temperature", "GPU Core", hw_name=filter_str) or 0.0
stats["clock"] = get_sensor_value(hw_list, Hardware.HardwareType.GpuNvidia, "Clock", "GPU Core", hw_name=filter_str) or 0.0
stats["mem_used"] = get_sensor_value(hw_list, Hardware.HardwareType.GpuNvidia, "SmallData", "GPU Memory Used", hw_name=filter_str) or 0.0
stats["mem_total"] = get_sensor_value(hw_list, Hardware.HardwareType.GpuNvidia, "SmallData", "GPU Memory Total", hw_name=filter_str) or 1.0
stats["mem_percent"] = (stats["mem_used"] / stats["mem_total"]) * 100
return stats
elif hw.HardwareType == Hardware.HardwareType.GpuAmd and filter_str.lower() in hw.Name.lower():
stats["name"] = hw.Name
stats["util"] = get_sensor_value(hw_list, Hardware.HardwareType.GpuAmd, "Load", "GPU Core", hw_name=filter_str) or 0.0
stats["temp"] = get_sensor_value(hw_list, Hardware.HardwareType.GpuAmd, "Temperature", "GPU Core", hw_name=filter_str) or 0.0
stats["clock"] = get_sensor_value(hw_list, Hardware.HardwareType.GpuAmd, "Clock", "GPU Core", hw_name=filter_str) or 0.0
stats["mem_used"] = get_sensor_value(hw_list, Hardware.HardwareType.GpuAmd, "SmallData", "GPU Memory Used", hw_name=filter_str) or 0.0
stats["mem_total"] = get_sensor_value(hw_list, Hardware.HardwareType.GpuAmd, "SmallData", "GPU Memory Total", hw_name=filter_str) or 1.0
stats["mem_percent"] = (stats["mem_used"] / stats["mem_total"]) * 100
return stats
return None
# Draw a GPU section starting at (x,y) for a given GPU's stats.
def draw_gpu_section(lcd, x, y, stats):
# Fix GPU progress bar's right edge at x=780.
bar_width = 150
bar_x = 360 - bar_width
# Header: display GPU name.
lcd.DisplayText(stats["name"], x=x, y=y, font=FONT_PATH, font_size=12,
font_color=GPU_COLOR, background_color=BG_COLOR)
y += 20
# Utilization text: pad to 3 characters.
util_str = f"Util: {int(stats['util']):3d}%"
lcd.DisplayText(util_str, x=x, y=y, font=FONT_PATH, font_size=10,
font_color=GPU_COLOR, background_color=BG_COLOR)
y += 20
# Utilization progress bar.
lcd.DisplayProgressBar(x=bar_x, y=y + BAR_OFFSET, width=bar_width, height=20,
min_value=0, max_value=100, value=int(stats["util"]),
bar_color=GPU_COLOR, bar_outline=True, background_color=BG_COLOR)
y += 20
# Temperature and clock on one line.
temp_freq_str = f"Temp: {int(stats['temp']):2d}°C Freq: {int(stats['clock']):4d}MHz"
lcd.DisplayText(temp_freq_str, x=x, y=y, font=FONT_PATH, font_size=10,
font_color=GPU_COLOR, background_color=BG_COLOR)
y += 20
# Memory usage text: pad to 5 digits.
mem_str = f"Mem: {int(stats['mem_used']):5d}MB/{int(stats['mem_total']):5d}MB"
lcd.DisplayText(mem_str, x=x, y=y, font=FONT_PATH, font_size=10,
font_color=GPU_COLOR, background_color=BG_COLOR)
y += 20
# Memory usage progress bar.
lcd.DisplayProgressBar(x=bar_x, y=y + BAR_OFFSET, width=bar_width, height=20,
min_value=0, max_value=100, value=int(stats["mem_percent"]),
bar_color=GPU_COLOR, bar_outline=True, background_color=BG_COLOR)
y += 10
return y
def get_sorted_core_loads(hw_list):
core_loads = []
for hw in hw_list:
if hw.HardwareType == Hardware.HardwareType.Cpu:
hw.Update()
for sensor in hw.Sensors:
if str(sensor.SensorType) == "Load" and "Core" in sensor.Name:
m = re.search(r'#(\d+)', sensor.Name)
core_index = int(m.group(1)) if m else 99
core_loads.append((core_index, sensor.Name, sensor.Value))
for subhw in hw.SubHardware:
subhw.Update()
for sensor in subhw.Sensors:
if str(sensor.SensorType) == "Load" and "Core" in sensor.Name:
m = re.search(r'#(\d+)', sensor.Name)
core_index = int(m.group(1)) if m else 99
core_loads.append((core_index, sensor.Name, sensor.Value))
core_loads.sort(key=lambda x: x[0])
return core_loads
def initialize_display():
lcd = LcdCommRevA(
com_port="AUTO",
display_width=DISPLAY_WIDTH,
display_height=DISPLAY_HEIGHT
)
lcd.Reset()
lcd.InitializeComm()
lcd.SetBrightness(50)
lcd.SetOrientation(Orientation.LANDSCAPE)
logging.debug("Displaying initial background...")
lcd.DisplayBitmap(BACKGROUND_IMG)
logging.debug("Initial background displayed.")
return lcd
def draw_static_text(lcd):
# Left side: CPU header and CPU name.
lcd.DisplayText("CPU Stats", x=1, y=1, font=FONT_PATH, font_size=12,
font_color=TEXT_COLOR, background_color=BG_COLOR)
lcd.DisplayText(CPU_NAME, x=1, y=15, font=FONT_PATH, font_size=10,
font_color=CPU_COLOR, background_color=BG_COLOR)
# Right side: GPU Stats header.
lcd.DisplayText("GPU Stats", x=220, y=1, font=FONT_PATH, font_size=12,
font_color=TEXT_COLOR, background_color=BG_COLOR)
def draw_dynamic_stats(lcd):
hw_list = handle.Hardware
# --- CPU Stats (Left Side) ---
cpu_load = get_sensor_value(hw_list, Hardware.HardwareType.Cpu, "Load", "CPU Total") or 0.0
cpu_temp = get_sensor_value(hw_list, Hardware.HardwareType.Cpu, "Temperature", "Core (Tctl/Tdie)")
if cpu_temp is None:
cpu_temp = get_sensor_value(hw_list, Hardware.HardwareType.Cpu, "Temperature", "Package") or 0.0
cpu_freq = get_sensor_value(hw_list, Hardware.HardwareType.Cpu, "Clock", "Core #1") or 0.0
y_cpu = 35
# Total percentage: pad to 3 digits.
lcd.DisplayText(f"Total: {int(cpu_load):3d}%", x=5, y=y_cpu, font=FONT_PATH, font_size=10,
font_color=CPU_COLOR, background_color=BG_COLOR)
cpu_bar_width = 70
cpu_bar_x = 120 - cpu_bar_width # = 180.
lcd.DisplayProgressBar(x=cpu_bar_x, y=y_cpu + BAR_OFFSET, width=cpu_bar_width, height=5,
min_value=0, max_value=100, value=int(cpu_load),
bar_color=CPU_COLOR, bar_outline=True, background_color=BG_COLOR)
y_cpu += 15
lcd.DisplayText(f"Temp: {int(cpu_temp):2d}°C Freq: {int(cpu_freq):4d}MHz", x=5, y=y_cpu,
font=FONT_PATH, font_size=10, font_color=CPU_COLOR, background_color=BG_COLOR)
y_cpu += 15
core_loads = get_sorted_core_loads(hw_list)
for core_index, sensor_name, load in core_loads:
core_label = f"Core {core_index}:" if core_index != 99 else "Core (top):"
lcd.DisplayText(core_label, x=10, y=y_cpu, font=FONT_PATH, font_size=8,
font_color=CPU_COLOR, background_color=BG_COLOR)
lcd.DisplayProgressBar(x=cpu_bar_x, y=y_cpu + BAR_OFFSET, width=cpu_bar_width, height=5,
min_value=0, max_value=100, value=int(load),
bar_color=CPU_COLOR, bar_outline=True, background_color=BG_COLOR)
lcd.DisplayText(f"{int(load):3d}%", x=120, y=y_cpu, font=FONT_PATH, font_size=10,
font_color=CPU_COLOR, background_color=BG_COLOR)
y_cpu += 10
# --- RAM Stats (Left Side, below CPU) ---
mem_used = get_sensor_value(hw_list, Hardware.HardwareType.Memory, "Data", "Memory Used") or 0.0
mem_avail = get_sensor_value(hw_list, Hardware.HardwareType.Memory, "Data", "Memory Available") or 0.0
mem_total = mem_used + mem_avail
mem_pct = (mem_used / mem_total) * 100 if mem_total > 0 else 0
y_ram = y_cpu + 10
lcd.DisplayText("RAM Stats", x=10, y=y_ram, font=FONT_PATH, font_size=12,
font_color=TEXT_COLOR, background_color=BG_COLOR)
y_ram += 15
# Convert values from GB to MB.
mem_used_mb = int(round(mem_used * 1024))
mem_total_mb = int(round(mem_total * 1024))
# System RAM values: pad to 6 characters.
lcd.DisplayText(f"{mem_used_mb:6d}MB / {mem_total_mb:6d}MB", x=1, y=y_ram, font=FONT_PATH, font_size=10,
font_color=CPU_COLOR, background_color=BG_COLOR)
ram_bar_width = 100
ram_bar_x = 10 - ram_bar_width # = 280.
lcd.DisplayProgressBar(x=10, y=y_ram + 15, width=ram_bar_width, height=8,
min_value=0, max_value=100, value=int(mem_pct),
bar_color=CPU_COLOR, bar_outline=True, background_color=BG_COLOR)
# --- GPU Stats (Right Side) ---
gpu_x = 220 # left margin for GPU section.
gpu_stats_amd = get_gpu_stats(hw_list, "AMD")
if gpu_stats_amd is not None:
y_gpu1 = 20
y_gpu1 = draw_gpu_section(lcd, gpu_x, y_gpu1, gpu_stats_amd)
gpu_stats_Nvidia = get_gpu_stats(hw_list, "NVIDIA")
if gpu_stats_Nvidia is not None:
y_gpu2 = 145 # vertical gap.
y_gpu2 = draw_gpu_section(lcd, gpu_x, y_gpu2, gpu_stats_Nvidia)
# --- Uptime and Clock (Centered at Bottom) ---
now = datetime.now()
clock_str = now.strftime("%a %m/%d/%Y %I:%M:%S %p")
uptime_str = get_uptime_str()
lcd.DisplayText(uptime_str, x=400, y=280, font=FONT_PATH, font_size=10,
font_color=TEXT_COLOR, background_color=BG_COLOR, anchor="mt")
lcd.DisplayText(clock_str, x=400, y=300, font=FONT_PATH, font_size=10,
font_color=TEXT_COLOR, background_color=BG_COLOR, anchor="mt")
def get_uptime_str():
uptime_ms = ctypes.windll.kernel32.GetTickCount64()
uptime_sec = uptime_ms // 1000
days = uptime_sec // 86400
hours = (uptime_sec % 86400) // 3600
minutes = (uptime_sec % 3600) // 60
seconds = uptime_sec % 60
return f"Uptime: {days}d {hours:02d}:{minutes:02d}:{seconds:02d}"
def main():
initialize_hardware_names()
lcd = initialize_display()
draw_static_text(lcd)
while True:
draw_dynamic_stats(lcd)
time.sleep(UPDATE_INTERVAL)
if __name__ == "__main__":
try:
main()
finally:
handle.Close()
—
Reply to this email directly, view it on GitHub
<#664 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AB2XMTMFB6D75ZH5O2HVT5L2Y3C7NAVCNFSM6AAAAABWR3PEU6VHI2DSMVQWIX3LMV43URDJONRXK43TNFXW4Q3PNVWWK3TUHMYTENZZGU4DKMA>
.
You are receiving this because you were mentioned.Message ID:
<mathoudebine/turing-smart-screen-python/repo-discussions/664/comments/12795850
@github.com>
|
Beta Was this translation helpful? Give feedback.
-
Excellent! Well done!
…On Thu, Apr 10, 2025, 3:03 PM CanHasDIY ***@***.***> wrote:
Here you go! I still need to fix some spacing and alignment issues:
example.png (view on web)
<https://github.com/user-attachments/assets/1f154d13-21d3-4f07-8f88-41b299fb5d28>
—
Reply to this email directly, view it on GitHub
<#664 (reply in thread)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AB2XMTPR4NMV7LT5YVFZCWL2Y3FA7AVCNFSM6AAAAABWR3PEU6VHI2DSMVQWIX3LMV43URDJONRXK43TNFXW4Q3PNVWWK3TUHMYTENZZGU4TQNI>
.
You are receiving this because you were mentioned.Message ID:
<mathoudebine/turing-smart-screen-python/repo-discussions/664/comments/12795985
@github.com>
|
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
-
Love it. Love seeing people inspired to create their own data-driven displays. Themes are cool looking, but power users want to squeeze every last bit of screen space to get the data they want! Well done! |
Beta Was this translation helpful? Give feedback.
-
This looks like something I've wanted/have been passively working on for a while in my free time but I'm trying to use data from a different machine on my local network via either the Glances' API or maybe Zabbix. But this has definitely inspired me and given me some ideas. |
Beta Was this translation helpful? Give feedback.
-
Dunno. Might need to update Com port or something.
…On Thu, Apr 24, 2025, 1:41 PM reactor333-debug ***@***.***> wrote:
I get this error in the terminal: [CRITICAL] Critical error in main:
cannot open resource.
—
Reply to this email directly, view it on GitHub
<#664 (reply in thread)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AB2XMTP2U6YEBJVY4EJ5ZNT23EV7NAVCNFSM6AAAAABWR3PEU6VHI2DSMVQWIX3LMV43URDJONRXK43TNFXW4Q3PNVWWK3TUHMYTEOJTHA4DQOA>
.
You are receiving this because you were mentioned.Message ID:
<mathoudebine/turing-smart-screen-python/repo-discussions/664/comments/12938888
@github.com>
|
Beta Was this translation helpful? Give feedback.
-
I have created an update to this that I am testing before sharing. The CPU and GPU queries are run asynchronously. The GPU query was taking like 1 to 2 seconds, so I spun that off and let the main loop just check if there is an update from the GPU process. The result is multiple refreshes a second now, allowing the processor updates to happen faster. Runs MUCH better! |
Beta Was this translation helpful? Give feedback.
-
Here is my version with the updated asynchronous stat reading. The result is much higher refresh rates of the screen. In this case, the GPU details are pretty slow, so we have a separate thread that handles that lookup and updates the main thread when the result comes back. That way, the refresh isn't waiting for the GPU reads each time. I went ahead and got the CPU lookup into a separate thread as well, but didn't get that much performance increase... perhaps someone else might see more? Once again, I use a 5 inch turing screen for mine. If you have tweaked yours to use different GPU/s or use a 3.5" turing (or other supported screens), you might want to just steal the logic from this. Anyway, feel free to modify or enhance your own versions using this as a template! Cheers!
|
Beta Was this translation helpful? Give feedback.
-
20250425_091632.mp4 |
Beta Was this translation helpful? Give feedback.
-
I know a lot use the themes. I went with the Python script method of interacting, and leveraged ChatGPT some to help me get it working just right for me. Basically, I want a clean, simple interface for monitoring my RAM, GPU, and CPU usage. In my case, I have two GPUs (my 4090 could only drive 2x120hz monitors, so I have a second one driving other 4K monitors. So, I created something to help me watch how my games run. I also run AI inference on my computer, sometimes, and I have to watch my vRAM closely when I do.
If you want to tweak this, you can do it pretty easily. Since I have two GPUs, I had to use part off the name to identify which one I wanted, like 4090 or 1030. If you have different cards, you can just modify that part.
Also, the setup of the device (5 inch Turing in my case) is in the beginning of the script. You might need to tweak it some to get it working.
Beta Was this translation helpful? Give feedback.
All reactions