Skip to content

Instantly share code, notes, and snippets.

@rmuxnet
Created March 19, 2026 11:56
Show Gist options
  • Select an option

  • Save rmuxnet/105481ad0745806be4422a5ab5f3ab9b to your computer and use it in GitHub Desktop.

Select an option

Save rmuxnet/105481ad0745806be4422a5ab5f3ab9b to your computer and use it in GitHub Desktop.
ps4 icc monitor
"""
ps4_icc_monitor.py
------------------
All-in-one CLI entry point for the PS4 ICC toolkit.
Imports ps4_icc_core, ps4_icc_sysinfo, ps4_icc_fan, ps4_icc_led,
ps4_icc_nvs, and ps4_icc_i2c. All files must be in the same directory.
Hardware: PS4 Fat (Aeolia CXD90025G)
Kernel: Linux 6.18.18-Strawberry-ThinLTO-LTS+ (CachyOS x86_64)
DEVICE PERMISSIONS
------------------
If /dev/icc does not exist or you get PermissionError:
cat /proc/devices | grep icc
sudo mknod /dev/icc c <major> 0
sudo chmod 666 /dev/icc
For /dev/i2c-0:
sudo modprobe i2c-dev
sudo chmod 666 /dev/i2c-0
Permanent udev rules (survives reboots):
echo 'KERNEL=="icc", MODE="0666"' | sudo tee /etc/udev/rules.d/99-ps4.rules
echo 'KERNEL=="i2c-0", MODE="0666"' | sudo tee -a /etc/udev/rules.d/99-ps4.rules
sudo udevadm control --reload-rules
sudo udevadm trigger
COMMANDS
--------
check check /dev/icc and /dev/i2c-0 accessibility
info hardware revision, EMC version, subsystem table
temp APU temperature (single read)
trips thermal trip points (APU + GPU)
fan live fan monitor (1 Hz, Ctrl-C to stop)
fanconfig fan config block with PID flag status
threshold <C> set fan threshold (45-85 C, RMW)
led <color> set LED to named color/effect
led list list all available LED colors
nvs sweep and print full NVS directory
i2c detect probe /dev/i2c-0 for devices
i2c read <addr> <reg> read one I2C byte (hex args)
i2c write <addr> <reg> <v> write one I2C byte (hex args)
"""
import sys
import time
import os
def cmd_check():
from ps4_icc_core import check_device
check_device()
dev_i2c = '/dev/i2c-0'
if not os.path.exists(dev_i2c):
print(f"[WARN] {dev_i2c} not found. Run: sudo modprobe i2c-dev")
elif not os.access(dev_i2c, os.R_OK | os.W_OK):
print(f"[WARN] {dev_i2c} not accessible. Fix: sudo chmod 666 /dev/i2c-0")
else:
print(f"[OK] {dev_i2c} is accessible.")
def cmd_info():
from ps4_icc_sysinfo import print_sysinfo
print_sysinfo()
def cmd_temp():
from ps4_icc_fan import read_apu_temp
print(f"APU temperature: {read_apu_temp()} C")
def cmd_trips():
from ps4_icc_fan import read_trip_points
trips = read_trip_points()
print(f"APU alert: {trips['apu_alert_c']} C")
print(f"APU shutdown: {trips['apu_shutdown_c']} C")
print(f"GPU shutdown: {trips['gpu_shutdown_c']} C")
def cmd_fan():
from ps4_icc_fan import read_fan_status
print("Live fan monitor (Ctrl-C to stop)")
print(f"{'RPM':>10} {'APU temp':>10} {'tach_adc':>10}")
print('-' * 38)
try:
while True:
st = read_fan_status()
print(f"{st['rpm']:>10.1f} {st['apu_temp_c']:>9.2f}C {st['tach_adc']:>10}", end='\r', flush=True)
time.sleep(1)
except KeyboardInterrupt:
print()
def cmd_fanconfig():
from ps4_icc_fan import (read_fan_config, FAN_CONFIG_LENGTH, THRESHOLD_OFFSET,
PID_FLAG_OFFSET_A, PID_FLAG_OFFSET_B,
PID_FLAG_A_VALUE, PID_FLAG_B_VALUE)
config = read_fan_config()
print(f"Threshold: {config[THRESHOLD_OFFSET]} C (0x{config[THRESHOLD_OFFSET]:02X})")
print(f"PID flag [9]: 0x{config[PID_FLAG_OFFSET_A]:02X} {'OK' if config[PID_FLAG_OFFSET_A] == PID_FLAG_A_VALUE else 'CORRUPT'}")
print(f"PID flag [12]: 0x{config[PID_FLAG_OFFSET_B]:02X} {'OK' if config[PID_FLAG_OFFSET_B] == PID_FLAG_B_VALUE else 'CORRUPT'}")
for i in range(0, FAN_CONFIG_LENGTH, 16):
print(f" 0x{i:02x}: {' '.join(f'{b:02x}' for b in config[i:i+16])}")
def cmd_threshold(args):
if not args:
print("Usage: threshold <degrees_C> (range 45-85)")
sys.exit(1)
from ps4_icc_fan import write_fan_threshold
write_fan_threshold(int(args[0]))
def cmd_led(args):
from ps4_icc_led import set_led, COLOR_TABLE, ORANGE_OFF, ORANGE_ON, ORANGE_COMBINED, ORANGE_PULSATE
if not args or args[0] == 'list':
PROG_NAMES = {
bytes(ORANGE_OFF): 'ORANGE_OFF',
bytes(ORANGE_ON): 'ORANGE_ON',
bytes(ORANGE_COMBINED): 'ORANGE_COMBINED',
bytes(ORANGE_PULSATE): 'ORANGE_PULSATE',
}
for name in sorted(COLOR_TABLE):
bv, wv, op = COLOR_TABLE[name]
print(f" {name:<22s} blue=0x{bv:02X} white=0x{wv:02X} orange={PROG_NAMES.get(bytes(op), '?')}")
return
set_led(args[0])
print(f"LED set to: {args[0]}")
def cmd_nvs():
from ps4_icc_nvs import print_nvs_directory
print_nvs_directory()
def cmd_i2c(args):
from ps4_icc_i2c import print_device_map, I2CBus
if not args or args[0] == 'detect':
print_device_map()
elif args[0] == 'read' and len(args) == 3:
with I2CBus() as bus:
val = bus.read_byte(int(args[1], 16), int(args[2], 16))
print(f"0x{int(args[1],16):02X}[0x{int(args[2],16):02X}] = 0x{val:02X} ({val})")
elif args[0] == 'write' and len(args) == 4:
with I2CBus() as bus:
bus.write_byte(int(args[1], 16), int(args[2], 16), int(args[3], 16))
print(f"Wrote 0x{int(args[3],16):02X} to 0x{int(args[1],16):02X}[0x{int(args[2],16):02X}]")
else:
print("Usage: i2c detect | i2c read <addr> <reg> | i2c write <addr> <reg> <val>")
DISPATCH = {
'check': lambda a: cmd_check(),
'info': lambda a: cmd_info(),
'temp': lambda a: cmd_temp(),
'trips': lambda a: cmd_trips(),
'fan': lambda a: cmd_fan(),
'fanconfig': lambda a: cmd_fanconfig(),
'threshold': cmd_threshold,
'led': cmd_led,
'nvs': lambda a: cmd_nvs(),
'i2c': cmd_i2c,
}
if __name__ == '__main__':
if len(sys.argv) < 2:
print(__doc__)
sys.exit(0)
cmd = sys.argv[1].lower()
if cmd not in DISPATCH:
print(f"Unknown command: {cmd}")
print("Run without arguments to see usage.")
sys.exit(1)
try:
DISPATCH[cmd](sys.argv[2:])
except Exception as e:
print(f"Error: {e}", file=sys.stderr)
sys.exit(1)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment