Last active
June 2, 2026 14:27
-
-
Save kwilczynski/f98a6ec07cb6a5d55f2f98b43509f7cb to your computer and use it in GitHub Desktop.
System Sensors Summary on B850 based motherboard
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| System Sensors Summary | |
| CPU (AMD Ryzen) | |
| ────────────────────────────────────────────────── | |
| Tctl: 47.6°C | |
| CCD1: 39.6°C | |
| CCD2: 37.6°C | |
| Motherboard (ASUS) | |
| ────────────────────────────────────────────────── | |
| CPU: 38.0°C | |
| CPU Package: 47.0°C | |
| Motherboard: 39.0°C | |
| VRM: 41.0°C | |
| GPU (AMD Radeon) | |
| ────────────────────────────────────────────────── | |
| Edge: 50.0°C | |
| Junction: 52.0°C | |
| Memory: 52.0°C | |
| Power: 24.0W / 220W (10.9%) | |
| Fan: 0 RPM @ 0% | |
| Core: 56 MHz | |
| Memory: 772 MHz | |
| VDD: 91 mV | |
| AIO Cooler (NZXT Kraken) | |
| ────────────────────────────────────────────────── | |
| Coolant: 35.1°C | |
| Pump: 2040 RPM @ 55% | |
| Fan: 1098 RPM @ 55% | |
| Memory (DDR5) | |
| ────────────────────────────────────────────────── | |
| DIMM 1: 45.2°C | |
| DIMM 2: 45.2°C | |
| Storage (NVMe) | |
| ────────────────────────────────────────────────── | |
| Composite: 48.9°C | |
| NAND: 48.9°C | |
| Ctrl: 50.9°C | |
| Wireless (MediaTek MT7925) | |
| ────────────────────────────────────────────────── | |
| WiFi: 38.0°C | |
| Chassis (Thermistor) | |
| ────────────────────────────────────────────────── | |
| Rear: 30.0°C | |
| Case Fans (Super I/O) | |
| ────────────────────────────────────────────────── | |
| FAN1: 818 RPM @ 33% | |
| FAN2: 1909 RPM @ 33% | |
| FAN6: 2915 RPM @ 52% | |
| FAN7: 2000 RPM @ 100% | |
| Voltage Rails (Super I/O) | |
| ────────────────────────────────────────────────── | |
| +12V: 12.29 V | |
| +5V: 4.96 V | |
| +3.3V: 3.39 V | |
| Standby: 3.39 V | |
| VCore: 840 mV | |
| SoC: 1.00 V | |
| VDD: 1.10 V | |
| DRAM: 1.38 V | |
| DRAM VDDP: 752 mV | |
| DRAM VPP: 1.26 V |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| import json | |
| import subprocess | |
| import sys | |
| import time | |
| RESET = "" | |
| BOLD = "" | |
| DIM = "" | |
| GREEN = "" | |
| YELLOW = "" | |
| RED = "" | |
| CYAN = "" | |
| CLEAR = "" | |
| if sys.stdout.isatty(): | |
| RESET = "\033[0m" | |
| BOLD = "\033[1m" | |
| DIM = "\033[2m" | |
| GREEN = "\033[32m" | |
| YELLOW = "\033[33m" | |
| RED = "\033[31m" | |
| CYAN = "\033[36m" | |
| CLEAR = "\033[H\033[2J" | |
| def get_sensor_data(): | |
| result = subprocess.run(["sensors", "-j"], capture_output=True, text=True) | |
| if result.returncode != 0: | |
| sys.exit(f"sensors command failed: {result.stderr}") | |
| return json.loads(result.stdout) | |
| def find_sensor(data, prefix): | |
| for key in data: | |
| if key.startswith(prefix): | |
| return data[key] | |
| return None | |
| def format_voltage(volts): | |
| if volts >= 1.0: | |
| return f"{volts:5.2f} V" | |
| return f"{volts*1000:5.0f} mV" | |
| def voltage_color(volts, nominal, tolerance=0.05): | |
| deviation = abs(volts - nominal) / nominal | |
| if deviation > tolerance * 2: | |
| return RED | |
| if deviation > tolerance: | |
| return YELLOW | |
| return GREEN | |
| def format_voltage_rail(volts, nominal, name_width=8): | |
| color = voltage_color(volts, nominal) | |
| return f"{color}{format_voltage(volts)}{RESET}" | |
| def temp_color(temp, warn=70, crit=85): | |
| if temp >= crit: | |
| return RED | |
| if temp >= warn: | |
| return YELLOW | |
| return GREEN | |
| def format_temp(temp, warn=70, crit=85): | |
| color = temp_color(temp, warn, crit) | |
| return f"{color}{temp:5.1f}°C{RESET}" | |
| def format_rpm(rpm): | |
| return f"{int(rpm):5d} RPM" | |
| def format_power(watts, cap=None): | |
| if cap: | |
| pct = (watts / cap) * 100 | |
| return f"{watts:5.1f}W / {cap:.0f}W ({pct:4.1f}%)" | |
| return f"{watts:5.1f}W" | |
| def format_freq(hz): | |
| mhz = hz / 1_000_000 | |
| if mhz >= 1000: | |
| return f"{mhz/1000:5.2f} GHz" | |
| return f"{mhz:5.0f} MHz" | |
| def extract_temp(data, key_prefix="temp"): | |
| for k, v in data.items(): | |
| if k.startswith(key_prefix) and k.endswith("_input"): | |
| return v | |
| return None | |
| def extract_val(data, key_prefix): | |
| for k, v in data.items(): | |
| if k.startswith(key_prefix) and k.endswith("_input"): | |
| return v | |
| return None | |
| def print_section(title): | |
| print(f"\n{BOLD}{CYAN}{title}{RESET}") | |
| print(f"{DIM}{'─' * 50}{RESET}") | |
| def main(): | |
| data = get_sensor_data() | |
| print(f"{BOLD}System Sensors Summary{RESET}") | |
| # CPU (k10temp) | |
| k10 = find_sensor(data, "k10temp") | |
| if k10: | |
| print_section("CPU (AMD Ryzen)") | |
| tctl = k10.get("Tctl", {}).get("temp1_input") | |
| tccd1 = k10.get("Tccd1", {}).get("temp3_input") | |
| tccd2 = k10.get("Tccd2", {}).get("temp4_input") | |
| if tctl: | |
| print(f" Tctl: {format_temp(tctl)}") | |
| if tccd1: | |
| print(f" CCD1: {format_temp(tccd1)}") | |
| if tccd2: | |
| print(f" CCD2: {format_temp(tccd2)}") | |
| # Motherboard (asusec) | |
| asus = find_sensor(data, "asusec") | |
| if asus: | |
| print_section("Motherboard (ASUS)") | |
| for name in ["CPU", "CPU Package", "Motherboard", "VRM"]: | |
| sensor = asus.get(name, {}) | |
| temp = extract_temp(sensor) | |
| if temp: | |
| print(f" {name + ':':<13s} {format_temp(temp)}") | |
| # GPU (amdgpu) | |
| gpu = find_sensor(data, "amdgpu") | |
| if gpu: | |
| print_section("GPU (AMD Radeon)") | |
| edge = gpu.get("edge", {}).get("temp1_input") | |
| junction = gpu.get("junction", {}).get("temp2_input") | |
| mem = gpu.get("mem", {}).get("temp3_input") | |
| if edge: | |
| print(f" Edge: {format_temp(edge, warn=80, crit=100)}") | |
| if junction: | |
| print(f" Junction: {format_temp(junction, warn=90, crit=105)}") | |
| if mem: | |
| print(f" Memory: {format_temp(mem, warn=85, crit=100)}") | |
| ppt = gpu.get("PPT", {}) | |
| power = ppt.get("power1_average") | |
| cap = ppt.get("power1_cap") | |
| if power: | |
| print(f" Power: {format_power(power, cap)}") | |
| fan = gpu.get("fan1", {}).get("fan1_input") | |
| pwm = gpu.get("pwm1", {}).get("pwm1") | |
| if fan is not None: | |
| if pwm is not None: | |
| pwm_pct = min(pwm / 127.5 * 100, 100) | |
| print(f" Fan: {format_rpm(fan)} @ {pwm_pct:4.0f}%") | |
| else: | |
| print(f" Fan: {format_rpm(fan)}") | |
| sclk = gpu.get("sclk", {}).get("freq1_input") | |
| mclk = gpu.get("mclk", {}).get("freq2_input") | |
| if sclk: | |
| print(f" Core: {format_freq(sclk)}") | |
| if mclk: | |
| print(f" Memory: {format_freq(mclk)}") | |
| vddgfx = gpu.get("vddgfx", {}).get("in0_input") | |
| if vddgfx is not None: | |
| print(f" VDD: {format_voltage(vddgfx)}") | |
| # AIO Cooler (kraken) | |
| kraken = find_sensor(data, "kraken2023elite") | |
| if kraken: | |
| print_section("AIO Cooler (NZXT Kraken)") | |
| coolant = kraken.get("Coolant temp", {}).get("temp1_input") | |
| pump = kraken.get("Pump speed", {}).get("fan1_input") | |
| pump_pwm = kraken.get("pwm1", {}).get("pwm1") | |
| fan = kraken.get("Fan speed", {}).get("fan2_input") | |
| fan_pwm = kraken.get("pwm2", {}).get("pwm2") | |
| if coolant: | |
| print(f" Coolant: {format_temp(coolant, warn=40, crit=50)}") | |
| if pump: | |
| if pump_pwm is not None: | |
| pump_pct = min(pump_pwm / 127.5 * 100, 100) | |
| print(f" Pump: {format_rpm(pump)} @ {pump_pct:4.0f}%") | |
| else: | |
| print(f" Pump: {format_rpm(pump)}") | |
| if fan: | |
| if fan_pwm is not None: | |
| fan_pct = min(fan_pwm / 127.5 * 100, 100) | |
| print(f" Fan: {format_rpm(fan)} @ {fan_pct:4.0f}%") | |
| else: | |
| print(f" Fan: {format_rpm(fan)}") | |
| # RAM (spd5118) | |
| ram_sensors = [(k, v) for k, v in data.items() if k.startswith("spd5118")] | |
| if ram_sensors: | |
| print_section("Memory (DDR5)") | |
| for i, (name, sensor) in enumerate(sorted(ram_sensors)): | |
| temp = sensor.get("temp1", {}).get("temp1_input") | |
| if temp: | |
| print(f" DIMM {i+1}: {format_temp(temp, warn=50, crit=80)}") | |
| # NVMe | |
| nvme = find_sensor(data, "nvme") | |
| if nvme: | |
| print_section("Storage (NVMe)") | |
| composite = nvme.get("Composite", {}).get("temp1_input") | |
| sensor1 = nvme.get("Sensor 1", {}).get("temp2_input") | |
| sensor2 = nvme.get("Sensor 2", {}).get("temp3_input") | |
| if composite: | |
| print(f" Composite: {format_temp(composite, warn=70, crit=80)}") | |
| if sensor1: | |
| print(f" NAND: {format_temp(sensor1, warn=70, crit=80)}") | |
| if sensor2: | |
| print(f" Ctrl: {format_temp(sensor2, warn=70, crit=80)}") | |
| # WiFi | |
| wifi = find_sensor(data, "mt7925") | |
| if wifi: | |
| print_section("Wireless (MediaTek MT7925)") | |
| temp = wifi.get("temp1", {}).get("temp1_input") | |
| if temp: | |
| print(f" WiFi: {format_temp(temp)}") | |
| # Super I/O (nct6799) - optional, may not be loaded | |
| nct = find_sensor(data, "nct6799") | |
| if nct: | |
| rear_chassis = nct.get("AUXTIN3", {}).get("temp6_input") | |
| if rear_chassis is not None: | |
| print_section("Chassis (Thermistor)") | |
| print(f" Rear: {format_temp(rear_chassis, warn=40, crit=50)}") | |
| fans = [] | |
| for fan_name in ["fan1", "fan2", "fan6", "fan7"]: | |
| fan_data = nct.get(fan_name, {}) | |
| rpm = fan_data.get(f"{fan_name}_input") | |
| if rpm and rpm > 0: | |
| fans.append((fan_name.upper(), rpm)) | |
| if fans: | |
| print_section("Case Fans (Super I/O)") | |
| for name, rpm in fans: | |
| pwm_key = name.lower().replace("fan", "pwm") | |
| pwm_data = nct.get(pwm_key, {}) | |
| pwm_raw = pwm_data.get(pwm_key) | |
| if pwm_raw is not None: | |
| pwm_pct = min(pwm_raw / 127.5 * 100, 100) | |
| print(f" {name + ':':<9s} {format_rpm(rpm)} @ {pwm_pct:4.0f}%") | |
| else: | |
| print(f" {name + ':':<9s} {format_rpm(rpm)}") | |
| print_section("Voltage Rails (Super I/O)") | |
| rails = [ | |
| ("in12", "+12V", 12.0, 12.0), | |
| ("in1", "+5V", 5.0, 5.0), | |
| ("in2", "+3.3V", 3.3, 1.0), | |
| ("in7", "Standby", 3.3, 1.0), | |
| ("in0", "VCore", None, 1.0), | |
| ("in4", "SoC", None, 1.0), | |
| ("in11", "VDD", None, 1.0), | |
| ("in10", "DRAM", None, 1.0), | |
| ("in15", "DRAM VDDP", None, 1.0), | |
| ("in17", "DRAM VPP", None, 1.0), | |
| ] | |
| for key, label, nominal, mult in rails: | |
| rail_data = nct.get(key, {}) | |
| volts = rail_data.get(f"{key}_input") | |
| if volts is not None: | |
| volts *= mult | |
| if nominal: | |
| print(f" {label + ':':<11s} {format_voltage_rail(volts, nominal)}") | |
| else: | |
| print(f" {label + ':':<11s} {format_voltage(volts)}") | |
| print() | |
| if __name__ == "__main__": | |
| interval = None | |
| if len(sys.argv) > 1: | |
| try: | |
| interval = float(sys.argv[1]) | |
| except ValueError: | |
| sys.exit(f"Usage: {sys.argv[0]} [INTERVAL SECONDS]") | |
| if interval is None: | |
| main() | |
| else: | |
| try: | |
| while True: | |
| print(CLEAR, end="") | |
| main() | |
| sys.stdout.flush() | |
| time.sleep(interval) | |
| except KeyboardInterrupt: | |
| pass |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment