forked from fritob/Camper-Monitor
Kühlbox: Zonen richtig erkennen, Stellbefehle nicht mehr abschneiden
Zwei Fehler am Gerät gefunden. Einzonen-Boxen als Doppelzone erkannt: Manche senden den langen Datensatz einer Zweizonen-Box mit, füllen den zweiten Block aber mit Nullen. Die Nutzlastlänge allein taugt deshalb nicht als Kriterium. Als zweizonig gilt jetzt nur, wo dieser Block überhaupt Inhalt hat, und in den Geräteeinstellungen lässt sich die Erkennung auf "Eine Zone" oder "Zwei Zonen" festnageln. Einschalten reagierte nicht: Ein/Aus und Betriebsart schicken den kompletten Einstellungsblock, der mit dem langen Datensatz auf 31 Byte kommt. Ohne ausgehandelte MTU nimmt BLE aber nur 20 Nutzbytes je Schreibvorgang an, der Rest wurde stillschweigend verworfen. Die Solltemperatur ging als kurzes Paket durch, alles Längere nicht. Lange Pakete werden jetzt anhand von maximumWriteValueLength aufgeteilt und versetzt geschrieben; die Statusabfrage danach wartet entsprechend länger. Aus dem kurzen Datensatz ist der Befehl übrigens genau 20 Byte groß und hätte gerade noch gepasst - deshalb trat es nur an dieser Box auf. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -91,6 +91,23 @@ enum AlpicoolProtocol {
|
||||
}
|
||||
|
||||
static func signed(_ byte: UInt8) -> Int { Int(Int8(bitPattern: byte)) }
|
||||
|
||||
/// Pause zwischen den Teilstücken eines aufgeteilten Pakets, damit das
|
||||
/// Gerät sie wieder zusammensetzen kann.
|
||||
static let chunkDelay: TimeInterval = 0.15
|
||||
|
||||
/// Zerlegt ein Paket in schreibbare Stücke.
|
||||
///
|
||||
/// Ohne ausgehandelte MTU nimmt BLE nur 20 Nutzbytes je Schreibvorgang an.
|
||||
/// Der Einstellungsblock einer Kühlbox ist mit bis zu 31 Byte länger und
|
||||
/// würde sonst stillschweigend verworfen.
|
||||
static func chunks(_ data: Data, limit: Int) -> [Data] {
|
||||
guard limit > 0 else { return [data] }
|
||||
guard data.count > limit else { return [data] }
|
||||
return stride(from: 0, to: data.count, by: limit).map { start in
|
||||
data.subdata(in: start..<min(start + limit, data.count))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Zustand einer Kühlbox – Messwerte und die Einstellungen, die sich ändern
|
||||
@@ -123,7 +140,29 @@ struct AlpicoolState: Equatable {
|
||||
/// deshalb wird er aufgehoben.
|
||||
var lastPayload: [UInt8] = []
|
||||
|
||||
var isDualZone: Bool { rightCurrent != nil }
|
||||
/// Übersteuerung aus den Geräteeinstellungen.
|
||||
var zoneMode: FridgeZoneMode = .automatic
|
||||
|
||||
/// Rohe Bytes der rechten Zone, für die Erkennung und die Diagnose.
|
||||
var rightZoneBytes: [UInt8] = []
|
||||
|
||||
/// Ob die Box wirklich eine zweite Zone hat.
|
||||
///
|
||||
/// Die Nutzlastlänge allein taugt nicht: Einzonen-Boxen senden den langen
|
||||
/// Datensatz teils mit, füllen den zweiten Block aber mit Nullen. Als
|
||||
/// zweizonig gilt deshalb nur, wo dieser Block überhaupt etwas enthält.
|
||||
var detectedDualZone: Bool {
|
||||
guard rightCurrent != nil, !rightZoneBytes.isEmpty else { return false }
|
||||
return rightZoneBytes.contains { $0 != 0x00 } && rightZoneBytes.contains { $0 != 0xFF }
|
||||
}
|
||||
|
||||
var isDualZone: Bool {
|
||||
switch zoneMode {
|
||||
case .automatic: return detectedDualZone
|
||||
case .single: return false
|
||||
case .dual: return rightCurrent != nil
|
||||
}
|
||||
}
|
||||
var isEco: Bool { runMode == 1 }
|
||||
var usesFahrenheit: Bool { unit == 1 }
|
||||
var hasStatus: Bool { !lastPayload.isEmpty }
|
||||
@@ -163,6 +202,11 @@ struct AlpicoolState: Equatable {
|
||||
rightTarget = AlpicoolProtocol.signed(p[18])
|
||||
rightCurrent = AlpicoolProtocol.signed(p[26])
|
||||
runningStatus = Int(p[27])
|
||||
rightZoneBytes = Array(p[18...26])
|
||||
} else {
|
||||
rightTarget = nil
|
||||
rightCurrent = nil
|
||||
rightZoneBytes = []
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user