Description
I am getting this error when following the python method, the problem lies on line 203 - casting the "value" variable into int, when I changed the line from float to int the error did not occur but I had very strange numbers on my ram readings.
This is the error description:
File "D:\Downloads\turing-smart-screen-python-3.9.1\library\scheduler.py", line 135, in MemoryStats
stats.Memory.stats()
File "D:\Downloads\turing-smart-screen-python-3.9.1\library\stats.py", line 612, in stats
display_themed_percent_radial_bar(memory_stats_theme_data['SWAP']['RADIAL'], swap_percent)
File "D:\Downloads\turing-smart-screen-python-3.9.1\library\stats.py", line 203, in display_themed_percent_radial_bar
value=int(value),
^^^^^^^^^^
ValueError: cannot convert float NaN to integer
This is my main.py:
def display_themed_percent_radial_bar(theme_data, value):
display_themed_radial_bar(
theme_data=theme_data,
value=int(value),
unit="%",
min_size=3
)
`# turing-smart-screen-python - a Python system monitor and library for USB-C displays like Turing Smart Screen or XuanFang
https://github.com/mathoudebine/turing-smart-screen-python/
Copyright (C) 2021-2023 Matthieu Houdebine (mathoudebine)
Copyright (C) 2022-2023 Rollbacke
Copyright (C) 2022-2023 Ebag333
Copyright (C) 2022-2023 w1ld3r
Copyright (C) 2022-2023 Charles Ferguson (gerph)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see https://www.gnu.org/licenses/.
import datetime
import locale
import math
import os
import platform
import sys
from typing import List
import babel.dates
import requests
from ping3 import ping
from psutil._common import bytes2human
from uptime import uptime
import library.config as config
from library.display import display
from library.log import logger
DEFAULT_HISTORY_SIZE = 10
ETH_CARD = config.CONFIG_DATA["config"].get("ETH", "")
WLO_CARD = config.CONFIG_DATA["config"].get("WLO", "")
HW_SENSORS = config.CONFIG_DATA["config"].get("HW_SENSORS", "AUTO")
CPU_FAN = config.CONFIG_DATA["config"].get("CPU_FAN", "AUTO")
PING_DEST = config.CONFIG_DATA["config"].get("PING", "127.0.0.1")
if HW_SENSORS == "PYTHON":
if platform.system() == 'Windows':
logger.warning("It is recommended to use LibreHardwareMonitor integration for Windows instead of Python "
"libraries (require admin. rights)")
import library.sensors.sensors_python as sensors
elif HW_SENSORS == "LHM":
if platform.system() == 'Windows':
import library.sensors.sensors_librehardwaremonitor as sensors
else:
logger.error("LibreHardwareMonitor integration is only available on Windows")
try:
sys.exit(0)
except:
os._exit(0)
elif HW_SENSORS == "STUB":
logger.warning("Stub sensors, not real HW sensors")
import library.sensors.sensors_stub_random as sensors
elif HW_SENSORS == "STATIC":
logger.warning("Stub sensors, not real HW sensors")
import library.sensors.sensors_stub_static as sensors
elif HW_SENSORS == "AUTO":
if platform.system() == 'Windows':
import library.sensors.sensors_librehardwaremonitor as sensors
else:
import library.sensors.sensors_python as sensors
else:
logger.error("Unsupported HW_SENSORS value in config.yaml")
try:
sys.exit(0)
except:
os._exit(0)
import library.sensors.sensors_custom as sensors_custom
def get_theme_file_path(name):
if name:
return os.path.join(config.THEME_DATA['PATH'], name)
else:
return None
def display_themed_value(theme_data, value, min_size=0, unit=''):
if not theme_data.get("SHOW", False):
return
if value is None:
return
# overridable MIN_SIZE from theme with backward compatibility
min_size = theme_data.get("MIN_SIZE", min_size)
text = f"{{:>{min_size}}}".format(value)
if theme_data.get("SHOW_UNIT", True) and unit:
text += str(unit)
display.lcd.DisplayText(
text=text,
x=theme_data.get("X", 0),
y=theme_data.get("Y", 0),
width=theme_data.get("WIDTH", 0),
height=theme_data.get("HEIGHT", 0),
font=config.FONTS_DIR + theme_data.get("FONT", "roboto-mono/RobotoMono-Regular.ttf"),
font_size=theme_data.get("FONT_SIZE", 10),
font_color=theme_data.get("FONT_COLOR", (0, 0, 0)),
background_color=theme_data.get("BACKGROUND_COLOR", (255, 255, 255)),
background_image=get_theme_file_path(theme_data.get("BACKGROUND_IMAGE", None)),
align=theme_data.get("ALIGN", "left"),
anchor=theme_data.get("ANCHOR", "lt"),
)
def display_themed_percent_value(theme_data, value):
display_themed_value(
theme_data=theme_data,
value=int(value),
min_size=3,
unit="%"
)
def display_themed_temperature_value(theme_data, value):
display_themed_value(
theme_data=theme_data,
value=int(value),
min_size=3,
unit="°C"
)
def display_themed_progress_bar(theme_data, value):
if not theme_data.get("SHOW", False):
return
display.lcd.DisplayProgressBar(
x=theme_data.get("X", 0),
y=theme_data.get("Y", 0),
width=theme_data.get("WIDTH", 0),
height=theme_data.get("HEIGHT", 0),
value=int(value),
min_value=theme_data.get("MIN_VALUE", 0),
max_value=theme_data.get("MAX_VALUE", 100),
bar_color=theme_data.get("BAR_COLOR", (0, 0, 0)),
bar_outline=theme_data.get("BAR_OUTLINE", False),
background_color=theme_data.get("BACKGROUND_COLOR", (255, 255, 255)),
background_image=get_theme_file_path(theme_data.get("BACKGROUND_IMAGE", None))
)
def display_themed_radial_bar(theme_data, value, min_size=0, unit='', custom_text=None):
if not theme_data.get("SHOW", False):
return
if theme_data.get("SHOW_TEXT", False):
if custom_text:
text = custom_text
else:
text = f"{{:>{min_size}}}".format(value)
if theme_data.get("SHOW_UNIT", True) and unit:
text += str(unit)
else:
text = ""
display.lcd.DisplayRadialProgressBar(
xc=theme_data.get("X", 0),
yc=theme_data.get("Y", 0),
radius=theme_data.get("RADIUS", 1),
bar_width=theme_data.get("WIDTH", 1),
min_value=theme_data.get("MIN_VALUE", 0),
max_value=theme_data.get("MAX_VALUE", 100),
angle_start=theme_data.get("ANGLE_START", 0),
angle_end=theme_data.get("ANGLE_END", 360),
angle_steps=theme_data.get("ANGLE_STEPS", 1),
angle_sep=theme_data.get("ANGLE_SEP", 0),
clockwise=theme_data.get("CLOCKWISE", False),
value=value,
bar_color=theme_data.get("BAR_COLOR", (0, 0, 0)),
text=text,
font=config.FONTS_DIR + theme_data.get("FONT", "roboto-mono/RobotoMono-Regular.ttf"),
font_size=theme_data.get("FONT_SIZE", 10),
font_color=theme_data.get("FONT_COLOR", (0, 0, 0)),
background_color=theme_data.get("BACKGROUND_COLOR", (0, 0, 0)),
background_image=get_theme_file_path(theme_data.get("BACKGROUND_IMAGE", None)),
custom_bbox=theme_data.get("CUSTOM_BBOX", (0, 0, 0, 0)),
text_offset=theme_data.get("TEXT_OFFSET", (0, 0)),
bar_background_color = theme_data.get("BAR_BACKGROUND_COLOR", (0, 0, 0)),
draw_bar_background = theme_data.get("DRAW_BAR_BACKGROUND", False),
bar_decoration = theme_data.get("BAR_DECORATION", "")
)
def display_themed_percent_radial_bar(theme_data, value):
display_themed_radial_bar(
theme_data=theme_data,
value=int(value),
unit="%",
min_size=3
)
def display_themed_temperature_radial_bar(theme_data, value):
display_themed_radial_bar(
theme_data=theme_data,
value=int(value),
min_size=3,
unit="°C"
)
def display_themed_line_graph(theme_data, values):
if not theme_data.get("SHOW", False):
return
line_color = theme_data.get("LINE_COLOR", (0, 0, 0))
display.lcd.DisplayLineGraph(
x=theme_data.get("X", 0),
y=theme_data.get("Y", 0),
width=theme_data.get("WIDTH", 1),
height=theme_data.get("HEIGHT", 1),
values=values,
min_value=theme_data.get("MIN_VALUE", 0),
max_value=theme_data.get("MAX_VALUE", 100),
autoscale=theme_data.get("AUTOSCALE", False),
line_color=line_color,
line_width=theme_data.get("LINE_WIDTH", 2),
graph_axis=theme_data.get("AXIS", False),
axis_color=theme_data.get("AXIS_COLOR", line_color), # If no color specified, use line color for axis
axis_font=config.FONTS_DIR + theme_data.get("AXIS_FONT", "roboto/Roboto-Black.ttf"),
axis_font_size=theme_data.get("AXIS_FONT_SIZE", 10),
background_color=theme_data.get("BACKGROUND_COLOR", (0, 0, 0)),
background_image=get_theme_file_path(theme_data.get("BACKGROUND_IMAGE", None))
)
def save_last_value(value: float, last_values: List[float], history_size: int):
# Initialize last values list the first time with given size
if len(last_values) != history_size:
last_values[:] = last_values_list(size=history_size)
# Store the value to the list that can then be used for line graph
last_values.append(value)
# Also remove the oldest value from list
last_values.pop(0)
def last_values_list(size: int) -> List[float]:
return [math.nan] * size
class CPU:
last_values_cpu_percentage = []
last_values_cpu_temperature = []
last_values_cpu_fan_speed = []
last_values_cpu_frequency = []
@classmethod
def percentage(cls):
theme_data = config.THEME_DATA['STATS']['CPU']['PERCENTAGE']
cpu_percentage = sensors.Cpu.percentage(
interval=theme_data.get("INTERVAL", None)
)
save_last_value(cpu_percentage, cls.last_values_cpu_percentage,
theme_data['LINE_GRAPH'].get("HISTORY_SIZE", DEFAULT_HISTORY_SIZE))
# logger.debug(f"CPU Percentage: {cpu_percentage}")
display_themed_progress_bar(theme_data['GRAPH'], cpu_percentage)
display_themed_percent_radial_bar(theme_data['RADIAL'], cpu_percentage)
display_themed_percent_value(theme_data['TEXT'], cpu_percentage)
display_themed_line_graph(theme_data['LINE_GRAPH'], cls.last_values_cpu_percentage)
@classmethod
def frequency(cls):
freq_ghz = sensors.Cpu.frequency() / 1000
theme_data = config.THEME_DATA['STATS']['CPU']['FREQUENCY']
save_last_value(freq_ghz, cls.last_values_cpu_frequency,
theme_data['LINE_GRAPH'].get("HISTORY_SIZE", DEFAULT_HISTORY_SIZE))
display_themed_value(
theme_data=theme_data['TEXT'],
value=f'{freq_ghz:.2f}',
unit=" GHz",
min_size=4
)
display_themed_progress_bar(theme_data['GRAPH'], freq_ghz)
display_themed_radial_bar(
theme_data=theme_data['RADIAL'],
value=f'{freq_ghz:.2f}',
unit=" GHz",
min_size=4
)
display_themed_line_graph(theme_data['LINE_GRAPH'], cls.last_values_cpu_frequency)
@classmethod
def load(cls):
cpu_load = sensors.Cpu.load()
# logger.debug(f"CPU Load: ({cpu_load[0]},{cpu_load[1]},{cpu_load[2]})")
load_theme_data = config.THEME_DATA['STATS']['CPU']['LOAD']
display_themed_percent_value(load_theme_data['ONE']['TEXT'], cpu_load[0])
display_themed_percent_value(load_theme_data['FIVE']['TEXT'], cpu_load[1])
display_themed_percent_value(load_theme_data['FIFTEEN']['TEXT'], cpu_load[2])
@classmethod
def temperature(cls):
temperature = sensors.Cpu.temperature()
save_last_value(temperature, cls.last_values_cpu_temperature,
config.THEME_DATA['STATS']['CPU']['TEMPERATURE']['LINE_GRAPH'].get("HISTORY_SIZE",
DEFAULT_HISTORY_SIZE))
cpu_temp_text_data = config.THEME_DATA['STATS']['CPU']['TEMPERATURE']['TEXT']
cpu_temp_radial_data = config.THEME_DATA['STATS']['CPU']['TEMPERATURE']['RADIAL']
cpu_temp_graph_data = config.THEME_DATA['STATS']['CPU']['TEMPERATURE']['GRAPH']
cpu_temp_line_graph_data = config.THEME_DATA['STATS']['CPU']['TEMPERATURE']['LINE_GRAPH']
if math.isnan(temperature):
temperature = 0
if cpu_temp_text_data['SHOW'] or cpu_temp_radial_data['SHOW'] or cpu_temp_graph_data[
'SHOW'] or cpu_temp_line_graph_data['SHOW']:
logger.warning("Your CPU temperature is not supported yet")
cpu_temp_text_data['SHOW'] = False
cpu_temp_radial_data['SHOW'] = False
cpu_temp_graph_data['SHOW'] = False
cpu_temp_line_graph_data['SHOW'] = False
display_themed_temperature_value(cpu_temp_text_data, temperature)
display_themed_progress_bar(cpu_temp_graph_data, temperature)
display_themed_temperature_radial_bar(cpu_temp_radial_data, temperature)
display_themed_line_graph(cpu_temp_line_graph_data, cls.last_values_cpu_temperature)
@classmethod
def fan_speed(cls):
if CPU_FAN != "AUTO":
fan_percent = sensors.Cpu.fan_percent(CPU_FAN)
else:
fan_percent = sensors.Cpu.fan_percent()
save_last_value(fan_percent, cls.last_values_cpu_fan_speed,
config.THEME_DATA['STATS']['CPU']['FAN_SPEED']['LINE_GRAPH'].get("HISTORY_SIZE",
DEFAULT_HISTORY_SIZE))
cpu_fan_text_data = config.THEME_DATA['STATS']['CPU']['FAN_SPEED']['TEXT']
cpu_fan_radial_data = config.THEME_DATA['STATS']['CPU']['FAN_SPEED']['RADIAL']
cpu_fan_graph_data = config.THEME_DATA['STATS']['CPU']['FAN_SPEED']['GRAPH']
cpu_fan_line_graph_data = config.THEME_DATA['STATS']['CPU']['FAN_SPEED']['LINE_GRAPH']
if math.isnan(fan_percent):
fan_percent = 0
if cpu_fan_text_data['SHOW'] or cpu_fan_radial_data['SHOW'] or cpu_fan_graph_data[
'SHOW'] or cpu_fan_line_graph_data['SHOW']:
if sys.platform == "win32":
logger.warning("Your CPU Fan sensor could not be auto-detected")
else:
logger.warning("Your CPU Fan sensor could not be auto-detected. Select it from Configuration UI.")
cpu_fan_text_data['SHOW'] = False
cpu_fan_radial_data['SHOW'] = False
cpu_fan_graph_data['SHOW'] = False
cpu_fan_line_graph_data['SHOW'] = False
display_themed_percent_value(cpu_fan_text_data, fan_percent)
display_themed_progress_bar(cpu_fan_graph_data, fan_percent)
display_themed_percent_radial_bar(cpu_fan_radial_data, fan_percent)
display_themed_line_graph(cpu_fan_line_graph_data, cls.last_values_cpu_fan_speed)
class Gpu:
last_values_gpu_percentage = []
last_values_gpu_mem_percentage = []
last_values_gpu_temperature = []
last_values_gpu_fps = []
last_values_gpu_fan_speed = []
last_values_gpu_frequency = []
@classmethod
def stats(cls):
load, memory_percentage, memory_used_mb, total_memory_mb, temperature = sensors.Gpu.stats()
fps = sensors.Gpu.fps()
fan_percent = sensors.Gpu.fan_percent()
freq_ghz = sensors.Gpu.frequency() / 1000
theme_gpu_data = config.THEME_DATA['STATS']['GPU']
save_last_value(load, cls.last_values_gpu_percentage,
theme_gpu_data['PERCENTAGE']['LINE_GRAPH'].get("HISTORY_SIZE", DEFAULT_HISTORY_SIZE))
save_last_value(memory_percentage, cls.last_values_gpu_mem_percentage,
theme_gpu_data['MEMORY_PERCENT']['LINE_GRAPH'].get("HISTORY_SIZE", DEFAULT_HISTORY_SIZE))
save_last_value(temperature, cls.last_values_gpu_temperature,
theme_gpu_data['TEMPERATURE']['LINE_GRAPH'].get("HISTORY_SIZE", DEFAULT_HISTORY_SIZE))
save_last_value(fps, cls.last_values_gpu_fps,
theme_gpu_data['FPS']['LINE_GRAPH'].get("HISTORY_SIZE", DEFAULT_HISTORY_SIZE))
save_last_value(fan_percent, cls.last_values_gpu_fan_speed,
theme_gpu_data['FAN_SPEED']['LINE_GRAPH'].get("HISTORY_SIZE", DEFAULT_HISTORY_SIZE))
save_last_value(freq_ghz, cls.last_values_gpu_frequency,
theme_gpu_data['FREQUENCY']['LINE_GRAPH'].get("HISTORY_SIZE", DEFAULT_HISTORY_SIZE))
################################ for backward compatibility only
gpu_mem_graph_data = theme_gpu_data['MEMORY']['GRAPH']
gpu_mem_radial_data = theme_gpu_data['MEMORY']['RADIAL']
if math.isnan(memory_percentage):
memory_percentage = 0
if gpu_mem_graph_data['SHOW'] or gpu_mem_radial_data['SHOW']:
logger.warning("Your GPU memory relative usage (%) is not supported yet")
gpu_mem_graph_data['SHOW'] = False
gpu_mem_radial_data['SHOW'] = False
gpu_mem_text_data = theme_gpu_data['MEMORY']['TEXT']
if math.isnan(memory_used_mb):
memory_used_mb = 0
if gpu_mem_text_data['SHOW']:
logger.warning("Your GPU memory absolute usage (M) is not supported yet")
gpu_mem_text_data['SHOW'] = False
display_themed_progress_bar(gpu_mem_graph_data, memory_percentage)
display_themed_percent_radial_bar(gpu_mem_radial_data, memory_percentage)
display_themed_value(
theme_data=gpu_mem_text_data,
value=int(memory_used_mb),
min_size=5,
unit=" M"
)
################################ end of backward compatibility only
# GPU usage (%)
gpu_percent_graph_data = theme_gpu_data['PERCENTAGE']['GRAPH']
gpu_percent_radial_data = theme_gpu_data['PERCENTAGE']['RADIAL']
gpu_percent_text_data = theme_gpu_data['PERCENTAGE']['TEXT']
gpu_percent_line_graph_data = theme_gpu_data['PERCENTAGE']['LINE_GRAPH']
if math.isnan(load):
load = 0
if gpu_percent_graph_data['SHOW'] or gpu_percent_text_data['SHOW'] or gpu_percent_radial_data['SHOW'] or \
gpu_percent_line_graph_data['SHOW']:
logger.warning("Your GPU load is not supported yet")
gpu_percent_graph_data['SHOW'] = False
gpu_percent_text_data['SHOW'] = False
gpu_percent_radial_data['SHOW'] = False
gpu_percent_line_graph_data['SHOW'] = False
display_themed_progress_bar(gpu_percent_graph_data, load)
display_themed_percent_radial_bar(gpu_percent_radial_data, load)
display_themed_percent_value(gpu_percent_text_data, load)
display_themed_line_graph(gpu_percent_line_graph_data, cls.last_values_gpu_percentage)
# GPU mem. usage (%)
gpu_mem_percent_graph_data = theme_gpu_data['MEMORY_PERCENT']['GRAPH']
gpu_mem_percent_radial_data = theme_gpu_data['MEMORY_PERCENT']['RADIAL']
gpu_mem_percent_text_data = theme_gpu_data['MEMORY_PERCENT']['TEXT']
gpu_mem_percent_line_graph_data = theme_gpu_data['MEMORY_PERCENT']['LINE_GRAPH']
if math.isnan(memory_percentage):
memory_percentage = 0
if gpu_mem_percent_graph_data['SHOW'] or gpu_mem_percent_radial_data['SHOW'] or gpu_mem_percent_text_data[
'SHOW'] or gpu_mem_percent_line_graph_data['SHOW']:
logger.warning("Your GPU memory relative usage (%) is not supported yet")
gpu_mem_percent_graph_data['SHOW'] = False
gpu_mem_percent_radial_data['SHOW'] = False
gpu_mem_percent_text_data['SHOW'] = False
display_themed_progress_bar(gpu_mem_percent_graph_data, memory_percentage)
display_themed_percent_radial_bar(gpu_mem_percent_radial_data, memory_percentage)
display_themed_percent_value(gpu_mem_percent_text_data, memory_percentage)
display_themed_line_graph(gpu_mem_percent_line_graph_data, cls.last_values_gpu_mem_percentage)
# GPU mem. absolute usage (M)
gpu_mem_used_text_data = theme_gpu_data['MEMORY_USED']['TEXT']
if math.isnan(memory_used_mb):
memory_used_mb = 0
if gpu_mem_used_text_data['SHOW']:
logger.warning("Your GPU memory absolute usage (M) is not supported yet")
gpu_mem_used_text_data['SHOW'] = False
display_themed_value(
theme_data=gpu_mem_used_text_data,
value=int(memory_used_mb),
min_size=5,
unit=" M"
)
# GPU mem. total memory (M)
gpu_mem_total_text_data = theme_gpu_data['MEMORY_TOTAL']['TEXT']
if math.isnan(total_memory_mb):
total_memory_mb = 0
if gpu_mem_total_text_data['SHOW']:
logger.warning("Your GPU total memory capacity (M) is not supported yet")
gpu_mem_total_text_data['SHOW'] = False
display_themed_value(
theme_data=gpu_mem_total_text_data,
value=int(total_memory_mb),
min_size=5, # Adjust min_size as necessary for your display
unit=" M" # Assuming the unit is in Megabytes
)
# GPU temperature (°C)
gpu_temp_text_data = theme_gpu_data['TEMPERATURE']['TEXT']
gpu_temp_radial_data = theme_gpu_data['TEMPERATURE']['RADIAL']
gpu_temp_graph_data = theme_gpu_data['TEMPERATURE']['GRAPH']
gpu_temp_line_graph_data = theme_gpu_data['TEMPERATURE']['LINE_GRAPH']
if math.isnan(temperature):
temperature = 0
if gpu_temp_text_data['SHOW'] or gpu_temp_radial_data['SHOW'] or gpu_temp_graph_data[
'SHOW'] or gpu_temp_line_graph_data['SHOW']:
logger.warning("Your GPU temperature is not supported yet")
gpu_temp_text_data['SHOW'] = False
gpu_temp_radial_data['SHOW'] = False
gpu_temp_graph_data['SHOW'] = False
gpu_temp_line_graph_data['SHOW'] = False
display_themed_temperature_value(gpu_temp_text_data, temperature)
display_themed_progress_bar(gpu_temp_graph_data, temperature)
display_themed_temperature_radial_bar(gpu_temp_radial_data, temperature)
display_themed_line_graph(gpu_temp_line_graph_data, cls.last_values_gpu_temperature)
# GPU FPS
gpu_fps_text_data = theme_gpu_data['FPS']['TEXT']
gpu_fps_radial_data = theme_gpu_data['FPS']['RADIAL']
gpu_fps_graph_data = theme_gpu_data['FPS']['GRAPH']
gpu_fps_line_graph_data = theme_gpu_data['FPS']['LINE_GRAPH']
if fps < 0:
fps = 0
if gpu_fps_text_data['SHOW'] or gpu_fps_radial_data['SHOW'] or gpu_fps_graph_data[
'SHOW'] or gpu_fps_line_graph_data['SHOW']:
logger.warning("Your GPU FPS is not supported yet")
gpu_fps_text_data['SHOW'] = False
gpu_fps_radial_data['SHOW'] = False
gpu_fps_graph_data['SHOW'] = False
gpu_fps_line_graph_data['SHOW'] = False
display_themed_progress_bar(gpu_fps_graph_data, fps)
display_themed_value(
theme_data=gpu_fps_text_data,
value=int(fps),
min_size=4,
unit=" FPS"
)
display_themed_radial_bar(
theme_data=gpu_fps_radial_data,
value=int(fps),
min_size=4,
unit=" FPS"
)
display_themed_line_graph(gpu_fps_line_graph_data, cls.last_values_gpu_fps)
# GPU Fan Speed (%)
gpu_fan_text_data = theme_gpu_data['FAN_SPEED']['TEXT']
gpu_fan_radial_data = theme_gpu_data['FAN_SPEED']['RADIAL']
gpu_fan_graph_data = theme_gpu_data['FAN_SPEED']['GRAPH']
gpu_fan_line_graph_data = theme_gpu_data['FAN_SPEED']['LINE_GRAPH']
if math.isnan(fan_percent):
fan_percent = 0
if gpu_fan_text_data['SHOW'] or gpu_fan_radial_data['SHOW'] or gpu_fan_graph_data[
'SHOW'] or gpu_fan_line_graph_data['SHOW']:
logger.warning("Your GPU Fan Speed is not supported yet")
gpu_fan_text_data['SHOW'] = False
gpu_fan_radial_data['SHOW'] = False
gpu_fan_graph_data['SHOW'] = False
gpu_fan_line_graph_data['SHOW'] = False
display_themed_percent_value(gpu_fan_text_data, fan_percent)
display_themed_progress_bar(gpu_fan_graph_data, fan_percent)
display_themed_percent_radial_bar(gpu_fan_radial_data, fan_percent)
display_themed_line_graph(gpu_fan_line_graph_data, cls.last_values_gpu_fan_speed)
# GPU Frequency (Ghz)
gpu_freq_text_data = theme_gpu_data['FREQUENCY']['TEXT']
gpu_freq_radial_data = theme_gpu_data['FREQUENCY']['RADIAL']
gpu_freq_graph_data = theme_gpu_data['FREQUENCY']['GRAPH']
gpu_freq_line_graph_data = theme_gpu_data['FREQUENCY']['LINE_GRAPH']
display_themed_value(
theme_data=gpu_freq_text_data,
value=f'{freq_ghz:.2f}',
unit=" GHz",
min_size=4
)
display_themed_progress_bar(gpu_freq_graph_data, freq_ghz)
display_themed_radial_bar(
theme_data=gpu_freq_radial_data,
value=f'{freq_ghz:.2f}',
unit=" GHz",
min_size=4
)
display_themed_line_graph(gpu_freq_line_graph_data, cls.last_values_gpu_frequency)
@staticmethod
def is_available():
return sensors.Gpu.is_available()
class Memory:
last_values_memory_swap = []
last_values_memory_virtual = []
@classmethod
def stats(cls):
memory_stats_theme_data = config.THEME_DATA['STATS']['MEMORY']
swap_percent = sensors.Memory.swap_percent()
save_last_value(swap_percent, cls.last_values_memory_swap,
memory_stats_theme_data['SWAP']['LINE_GRAPH'].get("HISTORY_SIZE", DEFAULT_HISTORY_SIZE))
display_themed_progress_bar(memory_stats_theme_data['SWAP']['GRAPH'], swap_percent)
display_themed_percent_radial_bar(memory_stats_theme_data['SWAP']['RADIAL'], swap_percent)
display_themed_line_graph(memory_stats_theme_data['SWAP']['LINE_GRAPH'], cls.last_values_memory_swap)
virtual_percent = sensors.Memory.virtual_percent()
save_last_value(virtual_percent, cls.last_values_memory_virtual,
memory_stats_theme_data['VIRTUAL']['LINE_GRAPH'].get("HISTORY_SIZE", DEFAULT_HISTORY_SIZE))
display_themed_progress_bar(memory_stats_theme_data['VIRTUAL']['GRAPH'], virtual_percent)
display_themed_percent_radial_bar(memory_stats_theme_data['VIRTUAL']['RADIAL'], virtual_percent)
display_themed_percent_value(memory_stats_theme_data['VIRTUAL']['PERCENT_TEXT'], virtual_percent)
display_themed_line_graph(memory_stats_theme_data['VIRTUAL']['LINE_GRAPH'], cls.last_values_memory_virtual)
display_themed_value(
theme_data=memory_stats_theme_data['VIRTUAL']['USED'],
value=int(sensors.Memory.virtual_used() / 1024 ** 2),
min_size=5,
unit=" M"
)
display_themed_value(
theme_data=memory_stats_theme_data['VIRTUAL']['FREE'],
value=int(sensors.Memory.virtual_free() / 1024 ** 2),
min_size=5,
unit=" M"
)
display_themed_value(
theme_data=memory_stats_theme_data['VIRTUAL']['TOTAL'],
value=int((sensors.Memory.virtual_free() + sensors.Memory.virtual_used()) / 1024 ** 2),
min_size=5,
unit=" M"
)
class Disk:
last_values_disk_usage = []
@classmethod
def stats(cls):
used = sensors.Disk.disk_used()
free = sensors.Disk.disk_free()
disk_theme_data = config.THEME_DATA['STATS']['DISK']
disk_usage_percent = sensors.Disk.disk_usage_percent()
save_last_value(disk_usage_percent, cls.last_values_disk_usage,
disk_theme_data['USED']['LINE_GRAPH'].get("HISTORY_SIZE", DEFAULT_HISTORY_SIZE))
display_themed_progress_bar(disk_theme_data['USED']['GRAPH'], disk_usage_percent)
display_themed_percent_radial_bar(disk_theme_data['USED']['RADIAL'], disk_usage_percent)
display_themed_percent_value(disk_theme_data['USED']['PERCENT_TEXT'], disk_usage_percent)
display_themed_line_graph(disk_theme_data['USED']['LINE_GRAPH'], cls.last_values_disk_usage)
display_themed_value(
theme_data=disk_theme_data['USED']['TEXT'],
value=int(used / 1000000000),
min_size=5,
unit=" G"
)
display_themed_value(
theme_data=disk_theme_data['TOTAL']['TEXT'],
value=int((free + used) / 1000000000),
min_size=5,
unit=" G"
)
display_themed_value(
theme_data=disk_theme_data['FREE']['TEXT'],
value=int(free / 1000000000),
min_size=5,
unit=" G"
)
class Net:
last_values_wlo_upload = []
last_values_wlo_download = []
last_values_eth_upload = []
last_values_eth_download = []
@classmethod
def stats(cls):
net_theme_data = config.THEME_DATA['STATS']['NET']
interval = net_theme_data.get("INTERVAL", None)
upload_wlo, uploaded_wlo, download_wlo, downloaded_wlo = sensors.Net.stats(WLO_CARD, interval)
save_last_value(upload_wlo, cls.last_values_wlo_upload,
net_theme_data['WLO']['UPLOAD']['LINE_GRAPH'].get("HISTORY_SIZE", DEFAULT_HISTORY_SIZE))
Net._show_themed_tax_rate(net_theme_data['WLO']['UPLOAD']['TEXT'], upload_wlo)
Net._show_themed_total_data(net_theme_data['WLO']['UPLOADED']['TEXT'], uploaded_wlo)
display_themed_line_graph(net_theme_data['WLO']['UPLOAD']['LINE_GRAPH'], cls.last_values_wlo_upload)
save_last_value(download_wlo, cls.last_values_wlo_download,
net_theme_data['WLO']['DOWNLOAD']['LINE_GRAPH'].get("HISTORY_SIZE", DEFAULT_HISTORY_SIZE))
Net._show_themed_tax_rate(net_theme_data['WLO']['DOWNLOAD']['TEXT'], download_wlo)
Net._show_themed_total_data(net_theme_data['WLO']['DOWNLOADED']['TEXT'], downloaded_wlo)
display_themed_line_graph(net_theme_data['WLO']['DOWNLOAD']['LINE_GRAPH'], cls.last_values_wlo_download)
upload_eth, uploaded_eth, download_eth, downloaded_eth = sensors.Net.stats(ETH_CARD, interval)
save_last_value(upload_eth, cls.last_values_eth_upload,
net_theme_data['ETH']['UPLOAD']['LINE_GRAPH'].get("HISTORY_SIZE", DEFAULT_HISTORY_SIZE))
Net._show_themed_tax_rate(net_theme_data['ETH']['UPLOAD']['TEXT'], upload_eth)
Net._show_themed_total_data(net_theme_data['ETH']['UPLOADED']['TEXT'], uploaded_eth)
display_themed_line_graph(net_theme_data['ETH']['UPLOAD']['LINE_GRAPH'], cls.last_values_eth_upload)
save_last_value(download_eth, cls.last_values_eth_download,
net_theme_data['ETH']['DOWNLOAD']['LINE_GRAPH'].get("HISTORY_SIZE", DEFAULT_HISTORY_SIZE))
Net._show_themed_tax_rate(net_theme_data['ETH']['DOWNLOAD']['TEXT'], download_eth)
Net._show_themed_total_data(net_theme_data['ETH']['DOWNLOADED']['TEXT'], downloaded_eth)
display_themed_line_graph(net_theme_data['ETH']['DOWNLOAD']['LINE_GRAPH'], cls.last_values_eth_download)
@staticmethod
def _show_themed_total_data(theme_data, amount):
display_themed_value(
theme_data=theme_data,
value=f"{bytes2human(amount)}",
min_size=6
)
@staticmethod
def _show_themed_tax_rate(theme_data, rate):
display_themed_value(
theme_data=theme_data,
value=f"{bytes2human(rate, '%(value).1f %(symbol)s/s')}",
min_size=10
)
class Date:
@staticmethod
def stats():
if HW_SENSORS == "STATIC":
# For static sensors, use predefined date/time
date_now = datetime.datetime.fromtimestamp(1694014609)
else:
date_now = datetime.datetime.now()
if platform.system() == "Windows":
# Windows does not have LC_TIME environment variable, use deprecated getdefaultlocale() that returns language code following RFC 1766
lc_time = locale.getdefaultlocale()[0]
else:
lc_time = babel.dates.LC_TIME
date_theme_data = config.THEME_DATA['STATS']['DATE']
day_theme_data = date_theme_data['DAY']['TEXT']
date_format = day_theme_data.get("FORMAT", 'medium')
display_themed_value(
theme_data=day_theme_data,
value=f"{babel.dates.format_date(date_now, format=date_format, locale=lc_time)}"
)
hour_theme_data = date_theme_data['HOUR']['TEXT']
time_format = hour_theme_data.get("FORMAT", 'medium')
display_themed_value(
theme_data=hour_theme_data,
value=f"{babel.dates.format_time(date_now, format=time_format, locale=lc_time)}"
)
class SystemUptime:
@staticmethod
def stats():
if HW_SENSORS == "STATIC":
# For static sensors, use predefined uptime
uptimesec = 4294036
else:
uptimesec = int(uptime())
uptimeformatted = str(datetime.timedelta(seconds=uptimesec))
systemuptime_theme_data = config.THEME_DATA['STATS']['UPTIME']
systemuptime_sec_theme_data = systemuptime_theme_data['SECONDS']['TEXT']
display_themed_value(
theme_data=systemuptime_sec_theme_data,
value=uptimesec
)
systemuptime_formatted_theme_data = systemuptime_theme_data['FORMATTED']['TEXT']
display_themed_value(
theme_data=systemuptime_formatted_theme_data,
value=uptimeformatted
)
class Custom:
@staticmethod
def stats():
for custom_stat in config.THEME_DATA['STATS']['CUSTOM']:
if custom_stat != "INTERVAL":
# Load the custom sensor class from sensors_custom.py based on the class name
try:
custom_stat_class = getattr(sensors_custom, str(custom_stat))()
numeric_value = custom_stat_class.as_numeric()
string_value = custom_stat_class.as_string()
last_values = custom_stat_class.last_values()
except Exception as e:
logger.error(
"Error loading custom sensor class " + str(custom_stat) + " from sensors_custom.py : " + str(e))
return
if string_value is None:
string_value = str(numeric_value)
# Display text
theme_data = config.THEME_DATA['STATS']['CUSTOM'][custom_stat].get("TEXT", None)
if theme_data is not None and string_value is not None:
display_themed_value(theme_data=theme_data, value=string_value)
# Display graph from numeric value
theme_data = config.THEME_DATA['STATS']['CUSTOM'][custom_stat].get("GRAPH", None)
if theme_data is not None and numeric_value is not None and not math.isnan(numeric_value):
display_themed_progress_bar(theme_data=theme_data, value=numeric_value)
# Display radial from numeric and text value
theme_data = config.THEME_DATA['STATS']['CUSTOM'][custom_stat].get("RADIAL", None)
if theme_data is not None and numeric_value is not None and not math.isnan(
numeric_value) and string_value is not None:
display_themed_radial_bar(
theme_data=theme_data,
value=numeric_value,
custom_text=string_value
)
# Display plot graph from histo values
theme_data = config.THEME_DATA['STATS']['CUSTOM'][custom_stat].get("LINE_GRAPH", None)
if theme_data is not None and last_values is not None:
display_themed_line_graph(theme_data=theme_data, values=last_values)
class Weather:
@staticmethod
def stats():
WEATHER_UNITS = {'metric': '°C', 'imperial': '°F', 'standard': '°K'}
weather_theme_data = config.THEME_DATA['STATS'].get('WEATHER', {})
wtemperature_theme_data = weather_theme_data.get('TEMPERATURE', {}).get('TEXT', {})
wfelt_theme_data = weather_theme_data.get('TEMPERATURE_FELT', {}).get('TEXT', {})
wupdatetime_theme_data = weather_theme_data.get('UPDATE_TIME', {}).get('TEXT', {})
wdescription_theme_data = weather_theme_data.get('WEATHER_DESCRIPTION', {}).get('TEXT', {})
whumidity_theme_data = weather_theme_data.get('HUMIDITY', {}).get('TEXT', {})
activate = True if wtemperature_theme_data.get("SHOW") or wfelt_theme_data.get(
"SHOW") or wupdatetime_theme_data.get("SHOW") or wdescription_theme_data.get(
"SHOW") or whumidity_theme_data.get("SHOW") else False
if activate:
temp = None
feel = None
time = None
humidity = None
if HW_SENSORS in ["STATIC", "STUB"]:
temp = "17.5°C"
feel = "(17.2°C)"
desc = "Cloudy"
time = "@15:33"
humidity = "45%"
else:
# API Parameters
lat = config.CONFIG_DATA['config'].get('WEATHER_LATITUDE', "")
lon = config.CONFIG_DATA['config'].get('WEATHER_LONGITUDE', "")
api_key = config.CONFIG_DATA['config'].get('WEATHER_API_KEY', "")
units = config.CONFIG_DATA['config'].get('WEATHER_UNITS', "metric")
lang = config.CONFIG_DATA['config'].get('WEATHER_LANGUAGE', "en")
deg = WEATHER_UNITS.get(units, '°?')
if api_key:
url = f'https://api.openweathermap.org/data/3.0/onecall?lat={lat}&lon={lon}&exclude=minutely,hourly,daily,alerts&appid={api_key}&units={units}&lang={lang}'
try:
response = requests.get(url)
if response.status_code == 200:
data = response.json()
temp = f"{data['current']['temp']:.1f}{deg}"
feel = f"({data['current']['feels_like']:.1f}{deg})"
desc = data['current']['weather'][0]['description'].capitalize()
humidity = f"{data['current']['humidity']:.0f}%"
now = datetime.datetime.now()
time = f"@{now.hour:02d}:{now.minute:02d}"
else:
logger.error(f"Error {response.status_code} fetching OpenWeatherMap API:")
# logger.error(f"Response content: {response.content}")
# logger.error(response.text)
desc = response.json().get('message')
except Exception as e:
logger.error(f"Error fetching OpenWeatherMap API: {str(e)}")
desc = "Error fetching OpenWeatherMap API"
else:
logger.warning("No OpenWeatherMap API key provided in config.yaml")
desc = "No OpenWeatherMap API key"
if activate:
# Display Temperature
display_themed_value(theme_data=wtemperature_theme_data, value=temp)
# Display Temperature Felt
display_themed_value(theme_data=wfelt_theme_data, value=feel)
# Display Update Time
display_themed_value(theme_data=wupdatetime_theme_data, value=time)
# Display Humidity
display_themed_value(theme_data=whumidity_theme_data, value=humidity)
# Display Weather Description (or error message)
display_themed_value(theme_data=wdescription_theme_data, value=desc)
class Ping:
last_values_ping = []
@classmethod
def stats(cls):
theme_data = config.THEME_DATA['STATS']['PING']
delay = ping(dest_addr=PING_DEST, unit="ms")
save_last_value(delay, cls.last_values_ping,
theme_data['LINE_GRAPH'].get("HISTORY_SIZE", DEFAULT_HISTORY_SIZE))
# logger.debug(f"Ping delay: {delay}ms")
display_themed_progress_bar(theme_data['GRAPH'], delay)
display_themed_radial_bar(
theme_data=theme_data['RADIAL'],
value=int(delay),
unit="ms",
min_size=6
)
display_themed_value(
theme_data=theme_data['TEXT'],
value=int(delay),
unit="ms",
min_size=6
)
display_themed_line_graph(theme_data['LINE_GRAPH'], cls.last_values_ping)
`