Created
March 19, 2026 11:56
-
-
Save rmuxnet/105481ad0745806be4422a5ab5f3ab9b to your computer and use it in GitHub Desktop.
ps4 icc monitor
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
| """ | |
| 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