Der Akku bekommt ein Gedächtnis

Verlauf über 24 Stunden, sieben Tage und dreißig Tage, aufgezeichnet alle fünf
Minuten. Als Säulen und nicht als Linie: eine Linie verbindet auch dort, wo
nichts gemessen wurde, und ein zugeklappter Rechner hatte keinen Ladestand —
eine Linie darüber behauptet einen.

Dazu Segelmodus und Hitzeschutz als reine Regeln im Helfer. Beide haben zwei
Schwellen, eine zum Anhalten und eine zum Weitermachen. Ohne diesen Abstand
hinge der Akku am Limit und würde bei jedem verlorenen Prozent nachgeladen —
hunderte winzige Ladevorgänge am Tag, und genau die altern ihn.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-09-05 21:06:06 +02:00
co-authored by Claude Opus 5
parent edb327cabe
commit f289d02c37
9 changed files with 1357 additions and 690 deletions
@@ -35,3 +35,58 @@ public enum ChargeActivity: Equatable, Sendable {
return .pluggedNotCharging
}
}
/// Was am Strom geschehen ist — und ob es überhaupt etwas war.
///
/// ``ChargeActivity`` sagt, welcher Zustand *gilt*. Das ist nicht dasselbe wie
/// „es ist etwas passiert": der Zustand gilt die ganze Zeit, gemeldet werden
/// soll er nur beim Wechsel.
public enum PowerEvent: Equatable, Sendable {
case charging
case plugged
case unplugged
case atLimit(Int)
/// Am Akku und fast leer. Schlägt `unplugged`: beides trifft zu, aber nur
/// eines davon muss man sehen.
case low
}
/// Merkt sich den letzten Zustand und meldet nur Wechsel.
///
/// **Der erste gelesene Zustand meldet nichts.** Sonst begrüßte Onyx jeden
/// Start mit „am Netz" — einer Nachricht über nichts, das gerade geschehen ist.
///
/// Eigener Typ und nicht zwei Zeilen im Programmstart, weil hier drei Regeln
/// zusammenkommen, die man einzeln prüfen können muss: die Schwelle für „fast
/// leer", der Vorrang vor „am Akku" und das Schweigen beim ersten Mal.
public struct PowerWatcher: Equatable, Sendable {
/// Ab hier heißt „am Akku" nicht mehr nur „am Akku".
public static let lowThreshold = 0.2
private var last: PowerEvent?
public init() {}
/// Der zuletzt gesehene Zustand. `nil`, solange nichts gelesen wurde.
public var current: PowerEvent? { last }
/// Nimmt eine Messung entgegen und gibt zurück, was zu melden ist — oder
/// `nil`, wenn sich nichts geändert hat.
public mutating func observe(isCharging: Bool, isPluggedIn: Bool,
charge: Double, limit: Int?) -> PowerEvent? {
let state = ChargeActivity.resolve(isCharging: isCharging,
isPluggedIn: isPluggedIn,
charge: charge, limit: limit)
let event: PowerEvent = switch state {
case .charging: .charging
case .pausedAtLimit(let value): .atLimit(value)
case .pluggedNotCharging: .plugged
case .onBattery: charge <= Self.lowThreshold ? .low : .unplugged
}
defer { last = event }
guard let previous = last else { return nil }
return event == previous ? nil : event
}
}