import Foundation /// Das Ladelimit — Kodierung und harte Grenzen. /// /// Der SMC-Key heißt `CHLT`, drei Bytes. Gemessen am 11.08.2026 auf Mac17,9, /// rein lesend, gegen macOS' eigene Ladebegrenzung: /// /// | Einstellung | `CHLT` | /// |---|---| /// | 80 % | `50 01 00` | /// | 85 % | `55 01 00` | /// | 95 % | `5F 01 00` | /// /// Erstes Byte ist die Prozentzahl, das zweite offenbar „Limit aktiv", das /// dritte unbenutzt. Mehr ist nicht belegt, und mehr wird hier auch nicht /// angenommen. /// /// **Warum das strenger gehandhabt wird als die Lüfter:** dort liefert die /// Firmware mit `F0Mn`/`F0Mx` ihre eigenen Grenzen mit, an denen sich das /// Sicherheitsnetz festhalten kann. Hier gibt es nichts dergleichen — es gibt /// nur die Werte, die wir gemessen haben. Ein falscher Wert im Lade-Subsystem /// ist die eine Operation in diesem Projekt, die den Akku dauerhaft /// beschädigen kann. public enum ChargeSafety { /// Der SMC-Key. public static let key = "CHLT" /// Untergrenze. macOS selbst bietet nichts darunter an; darunter ist /// ungemessenes Gebiet. public static let minimumPercent = 80 public static let maximumPercent = 100 /// „Kein Limit" heißt hundert Prozent. /// /// Und nicht: das Aktiv-Byte auf null setzen. Diese Kodierung hat macOS nie /// geschrieben, sie ist also ungemessen — hundert Prozent erreicht dasselbe /// innerhalb dessen, was belegt ist. public static let noLimit = 100 public static func clamp(_ percent: Int) -> Int { // Anheben statt ablehnen: ein Regler, der bei einer Fehleingabe gar // nichts tut, wirkt kaputt. min(max(percent, minimumPercent), maximumPercent) } /// Die drei Bytes für einen Prozentwert. /// /// Nur das erste Byte trägt den Wunsch. Die beiden anderen bekommen exakt /// das, was macOS dort schreibt — ungemessene Bytes im Ladepfad werden /// nicht angefasst. public static func encode(_ percent: Int) -> [UInt8] { [UInt8(clamp(percent)), 0x01, 0x00] } /// Der Prozentwert aus den gelesenen Bytes. `nil`, wenn kein Limit gilt /// oder die Daten nicht plausibel sind. public static func decode(_ bytes: [UInt8]) -> Int? { guard bytes.count >= 2 else { return nil } // Zweites Byte null: kein Limit, egal was im ersten steht. guard bytes[1] != 0 else { return nil } let percent = Int(bytes[0]) // Ein Wert außerhalb des Möglichen wird nicht angezeigt. „Unbekannt" // ist eine ehrlichere Auskunft als eine Zahl, die es nicht geben kann. guard (1...100).contains(percent) else { return nil } return percent } }