1 contributor
//
// DataStore.swift
// USB Meter
//
// Created by Bogdan Timofte on 03/03/2020.
// Copyright © 2020 Bogdan Timofte. All rights reserved.
//
import SwiftUI
import Combine
import CoreBluetooth
final class AppData : ObservableObject {
struct KnownMeterSummary: Identifiable {
let macAddress: String
let displayName: String
let modelSummary: String
let advertisedName: String?
let lastSeen: Date?
let lastConnected: Date?
let meter: Meter?
var id: String {
macAddress
}
}
private var bluetoothManagerNotification: AnyCancellable?
private var meterStoreObserver: AnyCancellable?
private var meterStoreCloudObserver: AnyCancellable?
private let meterStore = MeterNameStore.shared
init() {
bluetoothManagerNotification = bluetoothManager.objectWillChange.sink { [weak self] _ in
self?.scheduleObjectWillChange()
}
meterStoreObserver = NotificationCenter.default.publisher(for: .meterNameStoreDidChange)
.receive(on: DispatchQueue.main)
.sink { [weak self] _ in
self?.refreshMeterMetadata()
}
meterStoreCloudObserver = NotificationCenter.default.publisher(for: .meterNameStoreCloudStatusDidChange)
.receive(on: DispatchQueue.main)
.sink { [weak self] _ in
self?.scheduleObjectWillChange()
}
}
let bluetoothManager = BluetoothManager()
@Published var enableRecordFeature: Bool = true
@Published var meters: [UUID:Meter] = [UUID:Meter]()
var cloudAvailability: MeterNameStore.CloudAvailability {
meterStore.currentCloudAvailability
}
func meterName(for macAddress: String) -> String? {
meterStore.name(for: macAddress)
}
func setMeterName(_ name: String, for macAddress: String) {
meterStore.upsert(macAddress: macAddress, name: name, temperatureUnitRawValue: nil)
}
func temperatureUnitPreference(for macAddress: String) -> TemperatureUnitPreference {
let rawValue = meterStore.temperatureUnitRawValue(for: macAddress) ?? TemperatureUnitPreference.celsius.rawValue
return TemperatureUnitPreference(rawValue: rawValue) ?? .celsius
}
func setTemperatureUnitPreference(_ preference: TemperatureUnitPreference, for macAddress: String) {
meterStore.upsert(macAddress: macAddress, name: nil, temperatureUnitRawValue: preference.rawValue)
}
func registerKnownMeter(macAddress: String, modelName: String?, advertisedName: String?) {
meterStore.registerKnownMeter(macAddress: macAddress, modelName: modelName, advertisedName: advertisedName)
}
func noteMeterSeen(at date: Date, macAddress: String) {
meterStore.noteLastSeen(date, for: macAddress)
}
func noteMeterConnected(at date: Date, macAddress: String) {
meterStore.noteLastConnected(date, for: macAddress)
}
func lastSeen(for macAddress: String) -> Date? {
meterStore.lastSeen(for: macAddress)
}
func lastConnected(for macAddress: String) -> Date? {
meterStore.lastConnected(for: macAddress)
}
var knownMeters: [KnownMeterSummary] {
let liveMetersByMAC = Dictionary(uniqueKeysWithValues: meters.values.map { ($0.btSerial.macAddress.description, $0) })
let recordsByMAC = Dictionary(uniqueKeysWithValues: meterStore.allRecords().map { ($0.macAddress, $0) })
let macAddresses = Set(recordsByMAC.keys).union(liveMetersByMAC.keys)
return macAddresses.map { macAddress in
let liveMeter = liveMetersByMAC[macAddress]
let record = recordsByMAC[macAddress]
return KnownMeterSummary(
macAddress: macAddress,
displayName: liveMeter?.name ?? record?.customName ?? macAddress,
modelSummary: liveMeter?.deviceModelSummary ?? record?.modelName ?? "Known meter",
advertisedName: liveMeter?.modelString ?? record?.advertisedName,
lastSeen: liveMeter?.lastSeen ?? record?.lastSeen,
lastConnected: liveMeter?.lastConnectedAt ?? record?.lastConnected,
meter: liveMeter
)
}
.sorted { lhs, rhs in
let byName = lhs.displayName.localizedCaseInsensitiveCompare(rhs.displayName)
if byName != .orderedSame {
return byName == .orderedAscending
}
return lhs.macAddress < rhs.macAddress
}
}
private func scheduleObjectWillChange() {
DispatchQueue.main.async { [weak self] in
self?.objectWillChange.send()
}
}
private func refreshMeterMetadata() {
DispatchQueue.main.async { [weak self] in
guard let self else { return }
var didUpdateAnyMeter = false
for meter in self.meters.values {
let mac = meter.btSerial.macAddress.description
let displayName = self.meterName(for: mac) ?? mac
if meter.name != displayName {
meter.updateNameFromStore(displayName)
didUpdateAnyMeter = true
}
let previousTemperaturePreference = meter.tc66TemperatureUnitPreference
meter.reloadTemperatureUnitPreference()
if meter.tc66TemperatureUnitPreference != previousTemperaturePreference {
didUpdateAnyMeter = true
}
}
if didUpdateAnyMeter {
self.scheduleObjectWillChange()
}
}
}
}