Repository navigation
0.9.7: probe, fan rpm to Manual, Custom floor, perf read-back - #12
Merged
Merged
Conversation
0x029F answered 0x0d02 with success and its args echoed while staying in Balanced, hours into Modern Standby (2026-10-19): `auto perf mode custom` exited 0. On 2026-09-24 the tray's Battery profile, applied as the laptop woke, left the EC in Silent with no retry. confirm_perf_mode reads 0x0d82 back (one re-read after 200 ms) and returns PerfNotApplied, which is retryable and exits 5. The CLI's `auto perf mode` and `auto fan auto|manual` use it, and apply's switch to Custom uses it before writing boosts. An ignored mode no longer stops apply's lighting, brightness and charge-limit writes. The tray reads the mode back after each apply, also when another write failed, and resyncs up to three times per intended state (about 2, 4 and 8 s apart) without reopening the device. After giving up, the sync's reconcile only reads. get_perf_mode now keeps a failed zone read's typed cause, so a busy or unsupported read retries and exits as before the formatted error dropped it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The command only wrote the set point, so on a fan in Auto it changed nothing anyone could see, and a user's sweep could measure the firmware curve instead (#10). It now switches to Manual first, the order the tray uses, through the confirmed fan-mode write, and says so on stderr. If the set point write then fails, the fan goes back to its old mode. A non-multiple of 100 is rejected before anything is written, and a laptop without the mode read keeps the old set-point-only behavior. Outside Balanced it adds a note: on 0x029F Hyperboost runs its own curve when that is faster than the set point. The issue template's fan-range step now says the old behavior ends at 0.9.6. The macro only `rpm` used is gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Issue #10's Blade 17 (2022) answers 0x05 to Max Fan Enable. If Disable is rejected too, a catalogued 0x028b fails every Custom apply and reconcile, because only the generic profile learns. The generic profile doesn't learn Max Fan yet either; feat/max-fan-lacking changes that.
`razer-cli probe` reads every getter the tools use, once each through the normal retry loop, and writes what the EC answered as JSON: each attempt's status byte, reply args and timing, recorded below Device's retries. It only reads; a test checks every command is a getter. librazer::sim (tests, or --features sim) answers from an EC model: per-command rules tagged with their evidence, per-zone state, actual fan speed separate from the set point, its own clock and injectable faults. A command nobody observed fails as unmodeled instead of echoing, so it shows up as a coverage gap. The sim and Device share one retry policy (librazer::retry), and HidTransport gains pause(), so timed logic runs on simulated time in tests.
librazer/tests/ec/ holds a model for each laptop we have evidence for: the Blade 16 (2023) from hardware runs, the Blade 18 (2023) from #1, the Blade 15 (2018) Base from #2 and the Blade 17 (2022) from #10. Every rule names its source; a test checks it. The conformance suite runs each model through the same scenarios (startup read, apply each profile, Max Fan, manual fan, enforce after drift, fan reads, the probe) and compares the results with tests/ec/expected.txt. Commands nobody has observed show up as gaps, listed per laptop with the open question, so the file is also the list of what to ask each reporter. Three bugs from this project's history are replayed on the Blade 16 model: a lone-zone zero fan read, a set point below the fan floor, and an acknowledged perf write the EC ignored. The last is ignored until the read-back fix lands.
razer-cli probe --writes reads the settings it will change, saves them to a recovery file, then sets each perf mode, the boosts and Max Fan in Custom, and a manual fan sweep in Balanced over the chassis's range, reading each write back and watching the fans settle. It then restores only what moved, in dependency order (a perf write resets the fan to Auto), and verifies. It refuses to run while the tray is open, and writes nothing if the snapshot can't be read cleanly. razer-cli probe --restore FILE puts back an interrupted run. The restore is tested on the simulated EC: a clean run, a write that fails midway, a Max Fan the EC rejects, and zones that disagree.
A build with `--features sim` takes a global `--sim MODEL.json`. Every subcommand then talks to librazer::sim::SimTransport built from that EC model instead of the hardware: auto info and json, the perf, fan, boost, lighting and battery setters, manual --pid, probe and enumerate. The descriptor comes from the model's PID: a catalogued PID gets its SUPPORTED entry, any other the generic profile, which learns what the EC lacks. A model names no SKU, so an uncatalogued SKU with a catalogued PID is not modeled. A notice on stderr says the run is simulated, a simulated probe names the model in its tool field, and requests the model has no observation for are listed on stderr at the end, because json and info tolerate some failed reads. A --sim without a usable value is an error before anything is opened, never a fallback to the hardware. taskkill refuses to run simulated. Handlers took librazer::device::Device. They now take a Laptop, which holds the descriptor and either backend and implements HidTransport and Recording by delegation; command::* take `&impl HidTransport`, so a trait object would not fit. The hardware backend is gated to Windows and Linux, so a sim build compiles and runs on macOS. Without the feature the binary has no --sim option and behaves as before. The tests reach librazer's sim feature through a dev-dependency, so a plain `cargo test -p razer-cli` (the Windows CI job) runs the real handlers against an inline model: json fields, a perf mode set read back, Max Fan answering 0x05 exiting 4 on a catalogued and a generic model, and an unmodeled command failing with an error that names the gap. CONTRIBUTING.md says how to run it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
razer-cli built with --features sim takes --sim MODEL.json and runs every subcommand against librazer::sim, including probe --writes; a simulated capture says so in its tool field. A model may name its BIOS model number, and the profile is then picked the way Device::detect picks it; the Blade 18, Blade 15 and Blade 17 models now do. CI runs the CLI tests on the simulator.
From review: a write step now fails unless its read-back matches what was asked, and a step whose prerequisite didn't take is skipped (the fan sweep needs Balanced and Manual confirmed). The snapshot covers every register the write set changes, both zones' set points in any mode and both boosts included, and nothing is written if it can't be read. The restore keeps going past a failed write, verifies every field, is recorded as a step, and lists what is still different. Ctrl-C stops the run and goes straight to the restore. The recovery file names the laptop it came from; --restore refuses another laptop's, and a new run refuses to overwrite an unfinished run's file. The tray check also covers --restore. A generic profile sweeps only the middle of its guessed range unless --fan-range is given. The default settle time is 150 s, enough for a full-range move at the measured slew. Models: rules from a capture are per request; a writes capture is refused. Manual fan behavior applies only in the perf modes it was seen in, Max Fan outside Custom is a gap, and the fixtures no longer claim boost values or perf modes nobody reported. Conformance checks the state each profile leaves and lists every 0x05 answer.
From the second review: the restore reads and compares each field on its own, so one failed read only makes that field's write unconditional, and a boost snapshot with one known value restores that value. Verification checks every snapshotted field and lists each one that is unreadable or still different. razer-cli probe --writes saves the capture either way, keeps the recovery file, and exits with an error when the restore didn't verify.
On the maintainer's Blade 16 (2023), RZ09-0483U, probe --writes read back every write, held 5000, 3500 and 2000 RPM in Balanced/Manual (up at about 33 RPM/s, down at about 30), and verified its restore. The model now names that SKU and cites the run.
Measured on the maintainer's Blade 16 (2023): in Custom with the fan in Manual, the fans run at the larger of the set point and 3800 RPM, whatever the boosts. A 3000 set point reads back as 3000 while the fans hold 3800; 4500 is followed. A 90 s CPU load raised neither Custom nor Balanced above its set point. The simulator takes a manual floor per perf mode, and the Blade 16 model uses it.
On the Blade 16 (2023) a manual fan in Custom runs at 3800 RPM or more, while a lower set point still reads back as written. The CLI and tray showed that set point as if the fans followed it. A per-PID table beside KNOWN_LACKING now records the floor (0x029F, Custom, 3800, from the 2026-10-20 hardware runs). A conformance test keeps it equal to the EC models and checks that the speed it predicts is the one the simulated fans settle at. razer-cli: fan rpm below the floor still writes the value and prints one line saying what the fans run at; auto info prints the same line. auto json adds fan_floor_rpm in Manual where a floor is known, and leaves it out otherwise, so other output is unchanged. razer-tray: in Custom, presets below the floor are labeled "(runs at 3800)", and the tooltip shows "Fan 3800 floor" instead of the set point. Nothing new is enforced or counted as drift.
From review: the floor describes a manual fan, and Max Fan drives the fans past any set point, so "runs at 3800" was wrong in those states. The CLI note now needs Manual, and Max Fan off in Custom; the tray label and tooltip skip the floor while Max Fan is on. The fan handler prints through a sink, so the tests check the lines fan rpm and info print through the real handler. The README and the fan_floor_rpm comment say the speeds are settled ones with Max Fan off, and that the Black edition shares the PID untested.
Brings fix/perf-write-confirm and feat/cli-fan-rpm-manual onto the harness branch. Conflicts: razer-cli's fan handler moved into fan_handle on the harness side, so the rpm range and multiple-of-100 checks, the switch to Manual and the confirmed auto/manual writes now live there. `fan rpm` reads the modes once, to switch to Manual, and the Custom floor note reuses that read. The generic "Balanced holds a set point" note is left out for a mode with a listed floor, whose own note covers it. The replayed 10-19 ignored-write test runs again. It now checks confirm_modes, which the tray runs after apply: apply alone can't see an acknowledged write the EC ignored.
`auto fan rpm` puts the fan back to its old mode, confirmed, when the switch to Manual fails its read-back as well as when the set point fails: a switch whose read-back failed may still have taken. A laptop that can't read its perf mode can't confirm a write, so confirm_perf_mode lets the write stand there instead of failing a command that worked. A Busy read still fails. The re-read waits through HidTransport::pause, so the sim and the mocks don't sleep.
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.
Everything for the next release except the Blade 17 (2022) profile (#11), which waits on #10.
What's in it
razer-cli probe: a read-only capture of every setting on both fan zones, with the EC's raw answers, as one JSON file for model reports.probe --writes(tray closed) also changes perf mode, boosts, Max Fan and the fan, reads each back, then restores and verifies. It keeps a recovery file; Ctrl-C restores, andprobe --restore FILEreplays one.auto fan rpmswitches the fan to Manual first ([model] Razer Blade 17 2022, PID 0x028b #10), on a laptop that can read its fan mode, and puts it back if the set point fails.auto fan rpm,auto info,auto json(fan_floor_rpm) and the tray's fan presets say so.librazer::sim,librazer/tests/ec/). Unmodeled commands answer "unmodeled", never an echo. CI also runsrazer-cliagainst them.Tested
0x029F, Windows 11: razer-tray 19 tests; the full scenario run on afeb7d3 (33 steps, every write read back, restore verified, tray back); the Custom floor check (fans held 3800 at a 3000 set point, restore verified);probe --writeson the harness branch, 10-20 (every write read back, restore verified).CHANGELOG has an Unreleased section; versions are bumped at release.