import SwiftUI /// Eingabe des Victron-Verschlüsselungsschlüssels. /// /// Der Schlüssel wird einmal eingetragen und danach nie wieder angefasst – /// deshalb steht er hier und nicht in der Geräteübersicht. Nur solange er /// fehlt oder nicht passt, weist die Übersicht darauf hin. struct VictronKeyView: View { let device: ConfiguredDevice @Environment(DeviceStore.self) private var store @Environment(BluetoothManager.self) private var bluetooth @State private var keyInput = "" var body: some View { List { Section { TextField("32 Hex-Zeichen", text: $keyInput) .font(.system(.body, design: .monospaced)) .textInputAutocapitalization(.never) .autocorrectionDisabled() .onSubmit(save) Button("Schlüssel speichern", action: save) .disabled(keyInput.hexBytes?.count != 16) } footer: { Text("In VictronConnect: Gerät öffnen → Zahnrad → ⋮ → Produkt-Info → " + "„Instant Readout“ einschalten → Verschlüsselungsdaten anzeigen. " + "Der Schlüssel ist 16 Byte lang (32 Hex-Zeichen).") } // Das erste Byte sendet das Gerät unverschlüsselt mit. Stimmt es // nicht mit dem eingetragenen überein, gehört der Schlüssel zu // einem anderen Victron-Gerät – der häufigste Fehler überhaupt. if let expected = bluetooth.diagnostics[device.id]?.expectedKeyText { Section { LabeledContent("Gerät sendet") { Text(expected) .font(.body.monospaced()) .foregroundStyle(bytesAgree == false ? .red : .primary) } LabeledContent("Eingetragen") { Text(enteredKeyText) .font(.body.monospaced()) .foregroundStyle(bytesAgree == false ? .red : .secondary) } } header: { Text("Erstes Schlüsselbyte") } footer: { if bytesAgree == false { Text("Die beiden Bytes müssen übereinstimmen. Tun sie das " + "nicht, stammt der Schlüssel von einem anderen " + "Victron-Gerät – in VictronConnect prüfen, ob wirklich " + "dieses Gerät geöffnet war.") } else { Text("Zum Vergleichen: dieses Byte sendet das Gerät " + "unverschlüsselt mit.") } } } } .navigationTitle("Verschlüsselung") .navigationBarTitleDisplayMode(.inline) .onAppear { keyInput = store.victronKeyText(for: device.id) ?? "" } } /// nil, solange kein vollständiger Schlüssel eingetragen ist. private var bytesAgree: Bool? { guard let expected = bluetooth.diagnostics[device.id]?.expectedKeyFirstByte, let entered = keyInput.hexBytes?.first else { return nil } return expected == entered } private var enteredKeyText: String { guard let byte = keyInput.hexBytes?.first else { return "–" } return String(format: "0x%02X", byte) } private func save() { store.setVictronKey(keyInput, for: device.id) bluetooth.refreshConfiguration() } }