diff --git a/library/sensors/sensors.py b/library/sensors/sensors.py index 90358e09..a23766b9 100644 --- a/library/sensors/sensors.py +++ b/library/sensors/sensors.py @@ -66,6 +66,11 @@ def fps() -> int: def fan_percent() -> float: pass + @staticmethod + @abstractmethod + def frequency() -> float: + pass + @staticmethod @abstractmethod def is_available() -> bool: diff --git a/library/sensors/sensors_librehardwaremonitor.py b/library/sensors/sensors_librehardwaremonitor.py index 340ff06f..47ca23ca 100644 --- a/library/sensors/sensors_librehardwaremonitor.py +++ b/library/sensors/sensors_librehardwaremonitor.py @@ -323,13 +323,34 @@ def fan_percent(cls) -> float: try: for sensor in gpu_to_use.Sensors: if sensor.SensorType == Hardware.SensorType.Control: - return float(sensor.Value) + if sensor.Value: + return float(sensor.Value) except: pass # No Fan Speed sensor for this GPU model return math.nan + @classmethod + def frequency(cls) -> float: + gpu_to_use = cls.get_gpu_to_use() + if gpu_to_use is None: + # GPU not supported + return math.nan + + try: + for sensor in gpu_to_use.Sensors: + if sensor.SensorType == Hardware.SensorType.Clock: + # Keep only real core clocks, ignore effective core clocks + if "Core" in str(sensor.Name) and "Effective" not in str(sensor.Name): + if sensor.Value: + return float(sensor.Value) + except: + pass + + # No Frequency sensor for this GPU model + return math.nan + @classmethod def is_available(cls) -> bool: cls.gpu_name = get_gpu_name() diff --git a/library/sensors/sensors_python.py b/library/sensors/sensors_python.py index 3800782f..8a70d52f 100644 --- a/library/sensors/sensors_python.py +++ b/library/sensors/sensors_python.py @@ -193,6 +193,16 @@ def fan_percent() -> float: else: return math.nan + @staticmethod + def frequency() -> float: + global DETECTED_GPU + if DETECTED_GPU == GpuType.AMD: + return GpuAmd.frequency() + elif DETECTED_GPU == GpuType.NVIDIA: + return GpuNvidia.frequency() + else: + return math.nan + @staticmethod def is_available() -> bool: global DETECTED_GPU @@ -257,13 +267,18 @@ def fan_percent() -> float: if fans: for name, entries in fans.items(): for entry in entries: - if "gpu" in (entry.label or name): + if "gpu" in (entry.label.lower() or name.lower()): return entry.percent except: pass return math.nan + @staticmethod + def frequency() -> float: + # Not supported by Python libraries + return math.nan + @staticmethod def is_available() -> bool: try: @@ -277,52 +292,43 @@ class GpuAmd(sensors.Gpu): def stats() -> Tuple[float, float, float, float]: # load (%) / used mem (%) / used mem (Mb) / temp (°C) if pyamdgpuinfo: # Unlike other sensors, AMD GPU with pyamdgpuinfo pulls in all the stats at once - i = 0 - amd_gpus = [] - while i < pyamdgpuinfo.detect_gpus(): - amd_gpus.append(pyamdgpuinfo.get_gpu(i)) - i = i + 1 + pyamdgpuinfo.detect_gpus() + amd_gpu = pyamdgpuinfo.get_gpu(0) try: - memory_used_all = [item.query_vram_usage() for item in amd_gpus] - memory_used_bytes = sum(memory_used_all) / len(memory_used_all) + memory_used_bytes = amd_gpu.query_vram_usage() memory_used = memory_used_bytes / 1000000 except: memory_used_bytes = math.nan memory_used = math.nan try: - memory_total_all = [item.memory_info["vram_size"] for item in amd_gpus] - memory_total_bytes = sum(memory_total_all) / len(memory_total_all) + memory_total_bytes = amd_gpu.memory_info["vram_size"] memory_percentage = (memory_used_bytes / memory_total_bytes) * 100 except: memory_percentage = math.nan try: - load_all = [item.query_load() for item in amd_gpus] - load = (sum(load_all) / len(load_all)) * 100 + load = amd_gpu.query_load() except: load = math.nan try: - temperature_all = [item.query_temperature() for item in amd_gpus] - temperature = sum(temperature_all) / len(temperature_all) + temperature = amd_gpu.query_temperature() except: temperature = math.nan return load, memory_percentage, memory_used, temperature elif pyadl: - amd_gpus = pyadl.ADLManager.getInstance().getDevices() + amd_gpu = pyadl.ADLManager.getInstance().getDevices()[0] try: - load_all = [item.getCurrentUsage() for item in amd_gpus] - load = (sum(load_all) / len(load_all)) + load = amd_gpu.getCurrentUsage() except: load = math.nan try: - temperature_all = [item.getCurrentTemperature() for item in amd_gpus] - temperature = sum(temperature_all) / len(temperature_all) + temperature = amd_gpu.getCurrentTemperature() except: temperature = math.nan @@ -337,17 +343,33 @@ def fps() -> int: @staticmethod def fan_percent() -> float: try: + # Try with psutil fans fans = sensors_fans() if fans: for name, entries in fans.items(): for entry in entries: - if "gpu" in (entry.label or name): - return entry.current + if "gpu" in (entry.label.lower() or name.lower()): + return entry.percent + + # Try with pyadl if psutil did not find GPU fan + if pyadl: + return pyadl.ADLManager.getInstance().getDevices()[0].getCurrentFanSpeed( + pyadl.ADL_DEVICE_FAN_SPEED_TYPE_PERCENTAGE) except: pass return math.nan + @staticmethod + def frequency() -> float: + if pyamdgpuinfo: + pyamdgpuinfo.detect_gpus() + return pyamdgpuinfo.get_gpu(0).query_sclk() / 1000000 + elif pyadl: + return pyadl.ADLManager.getInstance().getDevices()[0].getCurrentEngineClock() + else: + return math.nan + @staticmethod def is_available() -> bool: try: diff --git a/library/sensors/sensors_stub_random.py b/library/sensors/sensors_stub_random.py index 1c27383e..28a5f4b8 100644 --- a/library/sensors/sensors_stub_random.py +++ b/library/sensors/sensors_stub_random.py @@ -60,6 +60,10 @@ def fps() -> int: def fan_percent() -> float: return random.uniform(0, 100) + @staticmethod + def frequency() -> float: + return random.uniform(800, 3400) + @staticmethod def is_available() -> bool: return True diff --git a/library/sensors/sensors_stub_static.py b/library/sensors/sensors_stub_static.py index a2716e5c..e5485df0 100644 --- a/library/sensors/sensors_stub_static.py +++ b/library/sensors/sensors_stub_static.py @@ -35,6 +35,7 @@ GPU_MEM_TOTAL_SIZE_GB = 32 NETWORK_SPEED_BYTES = 1061000000 GPU_FPS = 120 +GPU_FREQ_MHZ = 1500.0 class Cpu(sensors.Cpu): @@ -73,6 +74,10 @@ def fps() -> int: def fan_percent() -> float: return PERCENTAGE_SENSOR_VALUE + @staticmethod + def frequency() -> float: + return GPU_FREQ_MHZ + @staticmethod def is_available() -> bool: return True diff --git a/library/stats.py b/library/stats.py index 9adc821b..e01157e9 100644 --- a/library/stats.py +++ b/library/stats.py @@ -241,6 +241,7 @@ class CPU: last_values_cpu_percentage = [] last_values_cpu_temperature = [] last_values_cpu_fan_speed = [] + last_values_cpu_frequency = [] @classmethod def percentage(cls): @@ -259,12 +260,26 @@ def percentage(cls): @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=config.THEME_DATA['STATS']['CPU']['FREQUENCY']['TEXT'], - value=f'{sensors.Cpu.frequency() / 1000:.2f}', + 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): @@ -344,12 +359,14 @@ class Gpu: 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, 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'] @@ -363,6 +380,8 @@ def stats(cls): 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'] @@ -520,6 +539,26 @@ def stats(cls): 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() diff --git a/res/themes/Cyberdeck/theme.yaml b/res/themes/Cyberdeck/theme.yaml index 88e9c76d..bd57730a 100644 --- a/res/themes/Cyberdeck/theme.yaml +++ b/res/themes/Cyberdeck/theme.yaml @@ -185,6 +185,7 @@ STATS: FPS: TEXT: SHOW: True + SHOW_UNIT: False X: 46 Y: 279 MIN_SIZE: 4 diff --git a/res/themes/default.yaml b/res/themes/default.yaml index 265173bc..1e1d2d5f 100644 --- a/res/themes/default.yaml +++ b/res/themes/default.yaml @@ -3,7 +3,7 @@ STATS: CPU: PERCENTAGE: - INTERVAL: 1 + INTERVAL: 100 TEXT: SHOW: False GRAPH: @@ -13,11 +13,17 @@ STATS: LINE_GRAPH: SHOW: False FREQUENCY: - INTERVAL: 5 + INTERVAL: 100 TEXT: SHOW: False + GRAPH: + SHOW: False + RADIAL: + SHOW: False + LINE_GRAPH: + SHOW: False LOAD: - INTERVAL: 5 + INTERVAL: 100 ONE: TEXT: SHOW: False @@ -28,7 +34,7 @@ STATS: TEXT: SHOW: False TEMPERATURE: - INTERVAL: 5 + INTERVAL: 100 TEXT: SHOW: False GRAPH: @@ -38,10 +44,9 @@ STATS: LINE_GRAPH: SHOW: False FAN_SPEED: - INTERVAL: 1 + INTERVAL: 100 TEXT: SHOW: False - SHOW_UNIT: False GRAPH: SHOW: False RADIAL: @@ -49,7 +54,7 @@ STATS: LINE_GRAPH: SHOW: False GPU: - INTERVAL: 1 + INTERVAL: 100 PERCENTAGE: GRAPH: SHOW: False @@ -66,13 +71,11 @@ STATS: SHOW: False TEXT: SHOW: False - SHOW_UNIT: False LINE_GRAPH: SHOW: False MEMORY_USED: TEXT: SHOW: False - SHOW_UNIT: False MEMORY: # Deprecated, do not use GRAPH: SHOW: False @@ -80,11 +83,9 @@ STATS: SHOW: False TEXT: SHOW: False - SHOW_UNIT: False TEMPERATURE: TEXT: SHOW: False - SHOW_UNIT: False GRAPH: SHOW: False RADIAL: @@ -94,7 +95,6 @@ STATS: FPS: TEXT: SHOW: False - SHOW_UNIT: False GRAPH: SHOW: False RADIAL: @@ -104,7 +104,15 @@ STATS: FAN_SPEED: TEXT: SHOW: False - SHOW_UNIT: False + GRAPH: + SHOW: False + RADIAL: + SHOW: False + LINE_GRAPH: + SHOW: False + FREQUENCY: + TEXT: + SHOW: False GRAPH: SHOW: False RADIAL: @@ -112,7 +120,7 @@ STATS: LINE_GRAPH: SHOW: False MEMORY: - INTERVAL: 5 + INTERVAL: 100 SWAP: GRAPH: SHOW: False @@ -129,18 +137,14 @@ STATS: SHOW: False USED: SHOW: False - SHOW_UNIT: False FREE: SHOW: False - SHOW_UNIT: False TOTAL: SHOW: False - SHOW_UNIT: False PERCENT_TEXT: SHOW: False - SHOW_UNIT: False DISK: - INTERVAL: 10 + INTERVAL: 100 USED: GRAPH: SHOW: False @@ -150,20 +154,16 @@ STATS: SHOW: False TEXT: SHOW: False - SHOW_UNIT: False PERCENT_TEXT: SHOW: False - SHOW_UNIT: False TOTAL: TEXT: SHOW: False - SHOW_UNIT: False FREE: TEXT: SHOW: False - SHOW_UNIT: False NET: - INTERVAL: 1 + INTERVAL: 100 WLO: UPLOAD: TEXT: @@ -199,7 +199,7 @@ STATS: TEXT: SHOW: False DATE: - INTERVAL: 1 + INTERVAL: 100 DAY: TEXT: SHOW: False @@ -207,4 +207,4 @@ STATS: TEXT: SHOW: False CUSTOM: - INTERVAL: 3 + INTERVAL: 100 diff --git a/res/themes/theme_example.yaml b/res/themes/theme_example.yaml index e982b7e4..fd56d91f 100644 --- a/res/themes/theme_example.yaml +++ b/res/themes/theme_example.yaml @@ -139,6 +139,54 @@ STATS: FONT_COLOR: 200, 200, 200 # BACKGROUND_COLOR: 50, 50, 50 BACKGROUND_IMAGE: background.png + GRAPH: + SHOW: False + X: 115 + Y: 71 + WIDTH: 178 + HEIGHT: 13 + MIN_VALUE: 0 + MAX_VALUE: 100 + BAR_COLOR: 255, 0, 0 + BAR_OUTLINE: False + # BACKGROUND_COLOR: 0, 0, 0 + BACKGROUND_IMAGE: background.png + RADIAL: + SHOW: False + X: 100 + Y: 110 + RADIUS: 40 + WIDTH: 10 + MIN_VALUE: 0 + MAX_VALUE: 100 + ANGLE_START: 120 + ANGLE_END: 60 + ANGLE_STEPS: 20 + ANGLE_SEP: 5 + CLOCKWISE: True + BAR_COLOR: 0, 255, 0 + SHOW_TEXT: True + SHOW_UNIT: True + FONT: roboto/Roboto-Bold.ttf + FONT_SIZE: 13 + FONT_COLOR: 200, 200, 200 + # BACKGROUND_COLOR: 0, 0, 0 + BACKGROUND_IMAGE: background.png + LINE_GRAPH: + SHOW: False + X: 300 + Y: 220 + WIDTH: 133 + HEIGHT: 70 + MIN_VALUE: 0 + MAX_VALUE: 100 + HISTORY_SIZE: 10 + AUTOSCALE: False + LINE_COLOR: 61, 184, 225 + AXIS: True + AXIS_COLOR: 255, 135, 0 + # BACKGROUND_COLOR: 0, 0, 0 + BACKGROUND_IMAGE: background.png LOAD: # In seconds. Longer intervals cause this to refresh more slowly. # Setting to lower values will display near real time data, @@ -614,6 +662,65 @@ STATS: AXIS_COLOR: 255, 135, 0 # BACKGROUND_COLOR: 0, 0, 0 BACKGROUND_IMAGE: background.png + FREQUENCY: + TEXT: + SHOW: False + SHOW_UNIT: True + X: 100 + Y: 87 + FONT: roboto/Roboto-Bold.ttf + FONT_SIZE: 13 + FONT_COLOR: 200, 200, 200 + # BACKGROUND_COLOR: 50, 50, 50 + BACKGROUND_IMAGE: background.png + GRAPH: + SHOW: False + X: 115 + Y: 71 + WIDTH: 178 + HEIGHT: 13 + MIN_VALUE: 0 + MAX_VALUE: 100 + BAR_COLOR: 255, 0, 0 + BAR_OUTLINE: False + # BACKGROUND_COLOR: 0, 0, 0 + BACKGROUND_IMAGE: background.png + RADIAL: + SHOW: False + X: 100 + Y: 110 + RADIUS: 40 + WIDTH: 10 + MIN_VALUE: 0 + MAX_VALUE: 100 + ANGLE_START: 120 + ANGLE_END: 60 + ANGLE_STEPS: 20 + ANGLE_SEP: 5 + CLOCKWISE: True + BAR_COLOR: 0, 255, 0 + SHOW_TEXT: True + SHOW_UNIT: True + FONT: roboto/Roboto-Bold.ttf + FONT_SIZE: 13 + FONT_COLOR: 200, 200, 200 + # BACKGROUND_COLOR: 0, 0, 0 + BACKGROUND_IMAGE: background.png + LINE_GRAPH: + SHOW: False + X: 300 + Y: 220 + WIDTH: 133 + HEIGHT: 70 + MIN_VALUE: 0 + MAX_VALUE: 100 + HISTORY_SIZE: 10 + AUTOSCALE: False + LINE_COLOR: 61, 184, 225 + AXIS: True + AXIS_COLOR: 255, 135, 0 + # BACKGROUND_COLOR: 0, 0, 0 + BACKGROUND_IMAGE: background.png MEMORY: # In seconds. Longer intervals cause this to refresh more slowly. # Setting to lower values will display near real time data,