Repository navigation
Automations
Automations manage hardware power profiles dynamically based on external power delivery and platform sleep states. ZenTune switches between premade and custom presets when the AC power adapter is connected or disconnected, and re-applies designated profiles upon system resume. The automation engine operates as an unprivileged client configuration driving background threads within the root daemon.
For static preset definitions, refer to Custom Presets and Premade Presets. For real-time closed-loop tuning, refer to Adaptive Mode. For configuration keys and daemon settings, refer to Configuration.
Access the automations interface by selecting tab 4 in the ZenTune TUI or clicking Automations.
The interface provides three configuration dropdowns:
| UI Slot Label | Config Key | Trigger Condition |
|---|---|---|
| Preset on Battery Charge | OnAC |
Mains power connected (AC state active). |
| Preset on Battery Discharge | OnBattery |
Mains power disconnected (operating on battery reserve). |
| Preset on System Resume | OnResume |
System wake from suspend-to-RAM, sleep, or hibernation. |
Each slot accepts:
-
None: Leaves the slot unassigned. -
Built-in Premade Presets:
Eco,Balanced,Performance, orExtreme. -
Saved Custom Presets: Any user-created profile stored in
custom.json.
Selecting a preset updates config.ini under [Automations] immediately. If the assigned trigger condition matches current hardware state, the preset applies without requiring a system restart.
Automations are executed within the daemon (daemon/loops.py) by two independent monitoring threads:
┌──────────────────────────────────────────────────────────┐
│ ZenTune Daemon │
├─────────────────────────────┬────────────────────────────┤
│ Power-State Monitor │ Suspend Monitor │
│ Poll Interval: 2.0s │ Poll Interval: 1.0s │
│ Reads /sys/.../power_supply│ Compares CLOCK_BOOTTIME │
│ Enforces 3.0s Anti-Flap │ vs. time.monotonic() │
└──────────────┬──────────────┴─────────────┬──────────────┘
│ │
│ ▼
│ Wake Detected (>1.0s)
│ │
│ 5.0s Settling Delay
│ │
▼ ▼
┌─────────────────────────────────────────────┐
│ SMU Dispatch Queue │
│ zenmaster.apply / platformctl.py │
└─────────────────────────────────────────────┘
-
Poll Interval: Evaluates power delivery every 2.0 seconds (
_POWER_MONITOR_POLL_S = 2). -
Linux AC Detection: Inspects kernel sysfs attributes across all online power supply nodes:
/sys/class/power_supply/AC*/onlineor/sys/class/power_supply/ACAD*/online
A value of1indicates AC mains operation;0indicates battery discharge. -
macOS Detection: Parses output from the native power management utility:
pmset -g batt -
State Transition Execution: When the monitor detects a transition between AC and battery, it verifies the anti-flap window and calls
_apply_once().
-
Poll Interval: Samples hardware monotonic timers every 1.0 second (
_SUSPEND_MONITOR_POLL_S = 1). -
Timer Divergence Logic: The monitor calculates the offset between
_clock_boottime()(CLOCK_BOOTTIMEunder Linux, which continues incrementing while the CPU suspends) andtime.monotonic()(which halts execution while suspended). -
Wake Identification: If timer offset divergence exceeds 1.0 second (
_SUSPEND_GAP_THRESHOLD_S = 1.0), the daemon flags a platform wake event. -
Resume Preset Fallback: If the
OnResumeslot is configured asNone, the resume handler reads current AC state and applies the correspondingOnACorOnBatteryprofile.
To prevent bus saturation and hardware race conditions during platform transitions, ZenTune enforces two timing guards:
Upon detecting a wake event, the daemon enforces a 5.0s post-suspend settling delay (_POST_SUSPEND_WAIT_S = 5.0) before applying SMU profiles.
This interval ensures that:
- Motherboard embedded controllers (EC) complete ACPI wake handlers.
- Discrete GPU and display bridge drivers restore PCIe links.
- Voltage regulator modules establish stable output voltages.
- Storage devices and filesystems resume read/write availability.
The daemon enforces a 3.0s anti-flap cooldown delay between consecutive SMU command dispatches. If an AC/Battery transition or resume event occurs within 3.0 seconds of a prior SMU command, the monitor suppresses the execution cycle:
if time.monotonic() - last_smu_apply < 3.0:
log.debug("Power monitor skipped: SMU command applied %.1fs ago.", time.monotonic() - last_smu_apply)
continueThis protects voltage rails from rapid toggling caused by loose charging cables or transient power fluctuations.
When an automation slot is set to None, or references a preset that is unavailable on disk, the daemon executes with keep_on_empty=True.
Rather than resetting hardware registers to factory defaults or falling back to a hardcoded profile, ZenTune retains the parameters already programmed into the SMU. For example, if a user configures OnBattery to Eco but leaves OnAC as None, disconnecting the charger applies Eco, while reconnecting the charger maintains the active limits without interruption.
ZenTune synchronizes custom preset modifications across automation slots automatically:
-
Preset Rename Cascade: Renaming a custom preset within the Custom Preset Editor updates all matching slot entries in
config.ini([Automations] OnAC,[Automations] OnBattery) and[User] Mode. -
Preset Deletion Cascade: Deleting a custom preset assigned to an automation slot clears the slot (
""), logs a warning, and executesclient.apply_saved()to restore safe baseline settings.
- Adaptive Mode Priority: When Adaptive Mode starts, the daemon automatically suspends the Power-State Monitor and the Periodic Reapply loop. Hardware power allocation is driven exclusively by the real-time adaptive state machine. Automations resume when Adaptive Mode stops.
- Manual Overrides: Applying a preset manually from the Premade Presets or Custom Presets tab takes effect immediately. The automation engine re-engages and applies its scheduled preset on the subsequent power state change or resume event.
-
Periodic Reapply Loop (
[Settings] ReApply = 1): If the periodic reapply loop is active, each interval tick re-checks AC state dynamically, applying the active automation preset if power delivery changed between polling periods.
To verify that the daemon detects power transitions and hardware nodes correctly:
Verify daemon service status:
systemctl is-active zentune.serviceInspect power supply sysfs attributes:
cat /sys/class/power_supply/AC*/online 2>/dev/null || cat /sys/class/power_supply/ACAD*/online 2>/dev/null- Returns
1: System operating on AC mains power. - Returns
0: System operating on battery power.
Inspect daemon log stream during power transitions:
journalctl -u zentune.service -fVerify background launchd service status:
launchctl print system/com.horizonunix.zentune | grep stateVerify power source reporting:
pmset -g battGetting started
Using the app
Internals