fix(tdp-control): persist user-set TDP across shutdown (#204)#205
Merged
Conversation
The manually-set TDP lived only in memory, so the user's choice was lost on every reboot (hardware power limits reset to firmware defaults). Persist the last user-requested wattage to the plugin config file (~/.config/loadout/plugins/tdp-control.json, `manualTdp` key) and re-apply it on startup. Only user-initiated setTdp()/applyProfile() calls persist — automatic applies (per-game profiles, AC transitions, resume, device changes) route through a new private applyTdp() and leave the saved value untouched. When per-game profiles is enabled the engine still owns TDP; the manual restore only runs when it's off. Co-Authored-By: Claude Opus 4.8 <[email protected]> Claude-Session: https://claude.ai/code/session_01XNKJxF3QuZjhsjn5CqKyJM
…game on The startup restore only re-applied the saved manual TDP when per-game profiles were OFF. With per-game ON, boot (and game exit) fell back to the engine's separate `defaultTdp`, so a user's manually-set TDP was lost on reboot even though it was persisted — it went to the per-game default instead (e.g. 35W set, 12W after reboot). Make the saved manual TDP authoritative for the "no game" state: - Engine gains a `getNoGameTdp` injection; game-exit, no-profile launch, and the toggle-on-while-running path use it instead of `defaultTdp`. - Backend boot restore prefers the saved manual TDP regardless of the per-game toggle; the engine `defaultTdp` remains a fallback only when no manual value was ever saved. A recognized per-game profile still takes precedence when its game launches. Co-Authored-By: Claude Opus 4.8 <[email protected]>
Follow-up to the manual-TDP restore fix, from code review: - setTdp() no longer overwrites the saved manual "no game" TDP while a per-game profile is actively governing (per-game enabled + game running). In that state the slider tunes the running game's profile (the frontend routes it to setGameProfile), so persisting here let an in-game tweak silently redefine the value restored on game exit / reboot. - removeProfile() now falls back to the manual no-game TDP (noGameTdp()), matching handleGameExit — previously deleting the running game's profile applied the raw engine default, an inconsistency with the exit path. - Add regression tests for the getNoGameTdp paths (exit, no-profile launch, profile precedence, removeProfile, toggle-on, null/omitted fallback) and the setTdp in-game persistence guard. Co-Authored-By: Claude Opus 4.8 <[email protected]>
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The manually-set TDP lived only in memory, so the user's choice was lost on
every reboot — hardware power limits reset to firmware defaults on shutdown.
Fix
Persist the last user-requested wattage to the plugin config file
(
~/.config/loadout/plugins/tdp-control.json,manualTdpkey) and re-apply iton startup. Only user-initiated
setTdp()/applyProfile()calls persist —automatic applies (per-game profiles, AC transitions, resume, device changes)
route through a private
applyTdp()and leave the saved value untouched.The "no game" TDP model
The saved manual TDP is the authoritative "no game running" value, and is
restored regardless of whether per-game profiles are enabled. Precedence:
saved manual TDP is applied.
defaultTdpremains only as a fallback for users whonever set a manual value.
Implemented via a
getNoGameTdpcallback injected into the profile engine, sohandleGameExit, the no-profile branch ofhandleGameLaunch,removeProfile,and the per-game toggle-on path all resolve the same no-game value.
Guard against in-game drift
setTdp()does not overwrite the saved no-game value while a per-gameprofile is actively governing TDP (per-game enabled + a game running). In that
state the slider tunes the running game's profile (the frontend routes it to
setGameProfile), so persisting there would let an in-game tweak silentlyredefine the value restored on game exit / reboot.
Tests
Added regression coverage for the
getNoGameTdppaths (exit, no-profile launch,profile precedence,
removeProfile, toggle-on, null/omitted-callback fallback)and the
setTdpin-game persistence guard. 90/90 tdp-control tests pass;typecheck clean.
Verification
Built and installed on hardware (OneXPlayer APEX / Ryzen AI MAX+ 395). Service
logs confirm
Restoring saved manual TDP …Won startup where it previouslyapplied the 12W per-game default.
Co-Authored-By: Claude Opus 4.8 [email protected]
Claude-Session: https://claude.ai/code/session_01XNKJxF3QuZjhsjn5CqKyJM