Ladelimit: Key gemessen, Schreibpfad gebaut
Der Key heißt CHLT, drei Bytes. Identifiziert per Differenzmessung gegen macOS' eigene Ladebegrenzung, ohne einen einzigen Schreibzugriff: 80 % → 50 01 00 95 % → 5F 01 00 85 % → 55 01 00 Erstes Byte ist die Prozentzahl, zweites offenbar „Limit aktiv", drittes unbenutzt. Drei Messpunkte, weil einer Zufall sein kann: BACC hatte beim ersten Vergleich ebenfalls gepasst — ein Zähler, in dem gerade 50 stand. ChargerConfiguration aus ioreg sah zunächst wie eine zweite Quelle aus (niedriges Byte 0x50 = 80), blieb aber unverändert, als CHLT längst auf 95 stand. Also Zufall, und gut, dass darauf nichts gebaut wurde. Geschrieben wird nur das erste Byte, und nur zwischen 80 und 100 — dem Bereich, den macOS selbst anbietet und in dem gemessen wurde. Darunter ist ungemessenes Gebiet. Bei den Lüftern liefert die Firmware mit F0Mn/F0Mx eigene Grenzen mit, an denen sich ein Sicherheitsnetz festhalten kann; hier gibt es das nicht, hier gibt es nur die Messung. Ein falscher Wert im Lade-Subsystem ist die eine Operation in diesem Projekt, die den Akku dauerhaft beschädigen kann. Nach jedem Schreiben wird zurückgelesen und gemeldet, was tatsächlich steht — nicht, was gewünscht war. „Kein Limit" heißt 100 Prozent und nicht das Aktiv-Byte auf null: diese Kodierung hat macOS nie geschrieben, sie wäre also ungemessen. Bedienung im Lüfter-Bereich der Einstellungen und in der Lüfterkachel, also auch im Menüleisten-Popover. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -104,6 +104,12 @@ final class SMCAccess {
|
||||
write(key, bytes: [value])
|
||||
}
|
||||
|
||||
/// Schreibt beliebige Bytes — für `CHLT`, das drei davon hat.
|
||||
@discardableResult
|
||||
func writeBytes(_ key: String, _ bytes: [UInt8]) -> Bool {
|
||||
write(key, bytes: bytes)
|
||||
}
|
||||
|
||||
private func write(_ key: String, bytes: [UInt8]) -> Bool {
|
||||
guard let info = keyInfo(for: key) else { return false }
|
||||
// Nur schreiben, wenn die Größe zur Firmware passt. Ein zu kurzer oder
|
||||
@@ -134,6 +140,12 @@ final class SMCAccess {
|
||||
return output.keyInfo
|
||||
}
|
||||
|
||||
/// Liest die Rohbytes eines Keys — für `CHLT`, dessen Typ `hex_` ist und
|
||||
/// das sich damit keiner der Zahlenfassungen zuordnen lässt.
|
||||
func readBytes(_ key: String) -> [UInt8]? {
|
||||
read(key)?.bytes
|
||||
}
|
||||
|
||||
private func read(_ key: String) -> Value? {
|
||||
guard let info = keyInfo(for: key) else { return nil }
|
||||
var command = Param()
|
||||
|
||||
@@ -124,6 +124,35 @@ final class FanService: NSObject, OnyxHelperProtocol {
|
||||
reply(report())
|
||||
}
|
||||
|
||||
// MARK: - Ladelimit
|
||||
|
||||
/// Der Key trägt drei Bytes; nur das erste wird verändert. Siehe
|
||||
/// `ChargeSafety` für die Messung, auf der das beruht.
|
||||
func chargeLimit(reply: @escaping (Int) -> Void) {
|
||||
lastHeartbeat = Date()
|
||||
guard let smc, let bytes = smc.readBytes(ChargeSafety.key),
|
||||
let percent = ChargeSafety.decode(bytes) else {
|
||||
reply(-1)
|
||||
return
|
||||
}
|
||||
reply(percent)
|
||||
}
|
||||
|
||||
func setChargeLimit(percent: Int, reply: @escaping (Int) -> Void) {
|
||||
lastHeartbeat = Date()
|
||||
guard let smc else { reply(-1); return }
|
||||
|
||||
let wanted = ChargeSafety.clamp(percent)
|
||||
let written = smc.writeBytes(ChargeSafety.key, ChargeSafety.encode(wanted))
|
||||
|
||||
// Immer zurücklesen. Ein Schreibvorgang, den der SMC stillschweigend
|
||||
// verwirft, darf nicht als Erfolg gemeldet werden — sonst zeigt die
|
||||
// Oberfläche eine Grenze an, die es nicht gibt.
|
||||
let readBack = smc.readBytes(ChargeSafety.key).flatMap(ChargeSafety.decode) ?? -1
|
||||
log.notice("Ladelimit: \(percent, privacy: .public) % gewünscht, \(wanted, privacy: .public) % geschrieben (\(written, privacy: .public)), zurückgelesen \(readBack, privacy: .public) %")
|
||||
reply(readBack)
|
||||
}
|
||||
|
||||
func setFanAutomatic(index: Int, reply: @escaping (Data?) -> Void) {
|
||||
lastHeartbeat = Date()
|
||||
requests[index] = nil
|
||||
|
||||
Reference in New Issue
Block a user