From a18486b56497e0f0c34ff91e178b9a19c827dfd4 Mon Sep 17 00:00:00 2001 From: Priyonto M Rahman Date: Sun, 6 Oct 2024 21:15:31 +0200 Subject: [PATCH] task: Support C5 Firmware The commit contains necessary changes support for C5 firmware and background scanning RuuviTag without pairing it. --- .../Analytics/RuuviAnalyticsImpl.swift | 5 + .../Dashboard/Cards/View/CardsViewModel.swift | 18 ++- .../Cards/View/UI/CardsLargeImageCell.swift | 2 +- .../Cards/View/UI/CardsViewController.swift | 6 +- .../View/UI/TagChartsViewController.swift | 2 +- .../Home/Presenter/DashboardPresenter.swift | 3 +- .../Home/View/DashboardImageCell.swift | 6 +- .../Home/View/DashboardPlainCell.swift | 5 +- .../Presenter/TagSettingsPresenter.swift | 19 +++- .../View/TagSettingsViewModel.swift | 1 + .../View/UI/TagSettingsViewController.swift | 20 +++- .../Sources/Model/Model+Extension.swift | 1 + Modules/RuuviDiscover/Package.swift | 2 +- Packages/RuuviCloud/Package.swift | 2 +- .../Response/RuuviCloudApiUserResponse.swift | 4 + .../RuuviCloudPure/RuuviCloudPure.swift | 3 + .../SQLiteContextGRDB.swift | 32 ++++++ Packages/RuuviDaemon/Package.swift | 2 +- .../RuuviTagHeartbeatDaemonBTKit.swift | 107 ++++++++++++++++-- .../RuuviTagPropertiesDaemonBTKit.swift | 1 + .../RuuviNotificationLocalImpl.swift | 2 + Packages/RuuviOntology/Package.swift | 2 +- .../Mappers/RuuviTag+RuuviTagSensor.swift | 1 + ...viTagEnvLogFull+RuuviTagSensorRecord.swift | 1 + .../RuuviOntology/Sensor/CloudSensor.swift | 9 ++ .../Sensor/CloudSensorDense.swift | 8 ++ .../Sensor/RuuviTag/RuuviTagSensor.swift | 29 ++++- .../RuuviTag/RuuviTagSensorRecord.swift | 15 +++ .../Sources/RuuviOntology/Sensor/Sensor.swift | 7 +- .../RuuviTagDataSQLite.swift | 7 ++ .../RuuviTagLatestDataSQLite.swift | 7 ++ .../RuuviOntologySQLite/RuuviTagSQLite.swift | 8 ++ ...viTagSensorRecord+RuuviTagDataSQLite.swift | 2 + .../RuuviPersistenceSQLite.swift | 3 + Packages/RuuviService/Package.swift | 2 +- .../RuuviServiceCloudSyncImpl.swift | 6 + project.yml | 2 +- 37 files changed, 310 insertions(+), 42 deletions(-) diff --git a/Apps/RuuviStation/Sources/Classes/Analytics/RuuviAnalyticsImpl.swift b/Apps/RuuviStation/Sources/Classes/Analytics/RuuviAnalyticsImpl.swift index 4e742ea8f..1929f45b8 100644 --- a/Apps/RuuviStation/Sources/Classes/Analytics/RuuviAnalyticsImpl.swift +++ b/Apps/RuuviStation/Sources/Classes/Analytics/RuuviAnalyticsImpl.swift @@ -25,6 +25,8 @@ public final class RuuviAnalyticsImpl: RuuviAnalytics { case df4_tags(Int) // Quantity of tags using data format 5 case df5_tags(Int) + // Quantity of tags using data format C5 + case dfC5_tags(Int) // Background scan enabled or not (true/false) case backgroundScanEnabled(Bool) // Background scan interval (seconds) @@ -80,6 +82,8 @@ public final class RuuviAnalyticsImpl: RuuviAnalytics { "use_df4" case .df5_tags: "use_df5" + case .dfC5_tags: + "use_dfC5" case .backgroundScanEnabled: "background_scan_enabled" case .backgroundScanInterval: @@ -201,6 +205,7 @@ public final class RuuviAnalyticsImpl: RuuviAnalytics { let .df3_tags(count), let .df4_tags(count), let .df5_tags(count), + let .dfC5_tags(count), let .alertTemperature(count), let .alertHumidity(count), let .alertPressure(count), diff --git a/Apps/RuuviStation/Sources/Classes/Presentation/Modules/Dashboard/Cards/View/CardsViewModel.swift b/Apps/RuuviStation/Sources/Classes/Presentation/Modules/Dashboard/Cards/View/CardsViewModel.swift index d538ae8b4..e571289a1 100644 --- a/Apps/RuuviStation/Sources/Classes/Presentation/Modules/Dashboard/Cards/View/CardsViewModel.swift +++ b/Apps/RuuviStation/Sources/Classes/Presentation/Modules/Dashboard/Cards/View/CardsViewModel.swift @@ -15,6 +15,7 @@ class CardsViewModel: NSObject { var id: Observable = .init() var luid: Observable = .init() var mac: Observable = .init() + var serviceUUID: Observable = .init() var name: Observable = .init() var source: Observable = .init() var temperature: Observable = .init() @@ -82,11 +83,12 @@ class CardsViewModel: NSObject { if let macId = ruuviTag.macId?.any { mac.value = macId } + serviceUUID.value = ruuviTag.serviceUUID name.value = ruuviTag.name version.value = ruuviTag.version isConnectable.value = ruuviTag.isConnectable - isChartAvailable.value = ruuviTag.isConnectable || ruuviTag.isCloud - isAlertAvailable.value = ruuviTag.isCloud || isConnected.value ?? false + isChartAvailable.value = ruuviTag.isConnectable || ruuviTag.isCloud || ruuviTag.serviceUUID != nil + isAlertAvailable.value = ruuviTag.isCloud || isConnected.value ?? false || ruuviTag.serviceUUID != nil isCloud.value = ruuviTag.isCloud isOwner.value = ruuviTag.isOwner canShareTag.value = @@ -111,7 +113,7 @@ class CardsViewModel: NSObject { temperature: record.temperature, voltage: record.voltage ) - isAlertAvailable.value = isCloud.value ?? false || isConnected.value ?? false + isAlertAvailable.value = isCloud.value ?? false || isConnected.value ?? false || serviceUUID.value != nil } func update(with ruuviTag: RuuviTag) { @@ -122,8 +124,15 @@ class CardsViewModel: NSObject { ruuviTag.isConnectable { isChartAvailable.value = true } + } else { + if let isChart = isChartAvailable.value, + !isChart, + ruuviTag.serviceUUID != nil { + isChartAvailable.value = true + } } - isAlertAvailable.value = isCloud.value ?? false || ruuviTag.isConnected + isAlertAvailable.value = isCloud.value ?? false || + ruuviTag.isConnected || ruuviTag.serviceUUID != nil temperature.value = ruuviTag.temperature humidity.value = ruuviTag.humidity pressure.value = ruuviTag.pressure @@ -132,6 +141,7 @@ class CardsViewModel: NSObject { if let macId = ruuviTag.mac?.mac.any { mac.value = macId } + serviceUUID.value = ruuviTag.serviceUUID date.value = Date() movementCounter.value = ruuviTag.movementCounter source.value = ruuviTag.source diff --git a/Apps/RuuviStation/Sources/Classes/Presentation/Modules/Dashboard/Cards/View/UI/CardsLargeImageCell.swift b/Apps/RuuviStation/Sources/Classes/Presentation/Modules/Dashboard/Cards/View/UI/CardsLargeImageCell.swift index 0052b3059..ccf4a48b1 100644 --- a/Apps/RuuviStation/Sources/Classes/Presentation/Modules/Dashboard/Cards/View/UI/CardsLargeImageCell.swift +++ b/Apps/RuuviStation/Sources/Classes/Presentation/Modules/Dashboard/Cards/View/UI/CardsLargeImageCell.swift @@ -334,7 +334,7 @@ extension CardsLargeImageCell { switch source { case .unknown: dataSourceIconView.image = nil - case .advertisement: + case .advertisement, .bgAdvertisement: dataSourceIconView.image = RuuviAsset.iconBluetooth.image case .heartbeat, .log: dataSourceIconView.image = RuuviAsset.iconBluetoothConnected.image diff --git a/Apps/RuuviStation/Sources/Classes/Presentation/Modules/Dashboard/Cards/View/UI/CardsViewController.swift b/Apps/RuuviStation/Sources/Classes/Presentation/Modules/Dashboard/Cards/View/UI/CardsViewController.swift index f73d9d16a..18935591e 100644 --- a/Apps/RuuviStation/Sources/Classes/Presentation/Modules/Dashboard/Cards/View/UI/CardsViewController.swift +++ b/Apps/RuuviStation/Sources/Classes/Presentation/Modules/Dashboard/Cards/View/UI/CardsViewController.swift @@ -853,8 +853,10 @@ extension CardsViewController { alertButton.isHidden = !isAlertAvailable alertButtonHidden.isUserInteractionEnabled = isAlertAvailable } else { - alertButton.isHidden = !viewModel.isConnected.value.bound - alertButtonHidden.isUserInteractionEnabled = viewModel.isConnected.value.bound + alertButton.isHidden = + !viewModel.isConnected.value.bound || viewModel.serviceUUID.value == nil + alertButtonHidden.isUserInteractionEnabled = + viewModel.isConnected.value.bound || viewModel.serviceUUID.value != nil } } } diff --git a/Apps/RuuviStation/Sources/Classes/Presentation/Modules/Dashboard/Charts/View/UI/TagChartsViewController.swift b/Apps/RuuviStation/Sources/Classes/Presentation/Modules/Dashboard/Charts/View/UI/TagChartsViewController.swift index cc729daea..045c43f40 100644 --- a/Apps/RuuviStation/Sources/Classes/Presentation/Modules/Dashboard/Charts/View/UI/TagChartsViewController.swift +++ b/Apps/RuuviStation/Sources/Classes/Presentation/Modules/Dashboard/Charts/View/UI/TagChartsViewController.swift @@ -854,7 +854,7 @@ extension TagChartsViewController: TagChartsViewInput { switch record.source { case .unknown: dataSourceIconView.image = nil - case .advertisement: + case .advertisement, .bgAdvertisement: dataSourceIconView.image = RuuviAsset.iconBluetooth.image case .heartbeat, .log: dataSourceIconView.image = RuuviAsset.iconBluetoothConnected.image diff --git a/Apps/RuuviStation/Sources/Classes/Presentation/Modules/Dashboard/Home/Presenter/DashboardPresenter.swift b/Apps/RuuviStation/Sources/Classes/Presentation/Modules/Dashboard/Home/Presenter/DashboardPresenter.swift index 6694ac394..9e4dfa159 100644 --- a/Apps/RuuviStation/Sources/Classes/Presentation/Modules/Dashboard/Home/Presenter/DashboardPresenter.swift +++ b/Apps/RuuviStation/Sources/Classes/Presentation/Modules/Dashboard/Home/Presenter/DashboardPresenter.swift @@ -511,7 +511,8 @@ extension DashboardPresenter: RuuviNotifierObserver { .filter { $0.luid.value?.value == uuid || $0.mac.value?.value == uuid } .forEach { viewModel in let isFireable = viewModel.isCloud.value ?? false || - viewModel.isConnected.value ?? false + viewModel.isConnected.value ?? false || + viewModel.serviceUUID.value != nil switch alertType { case .temperature: let isTriggered = isTriggered && isFireable diff --git a/Apps/RuuviStation/Sources/Classes/Presentation/Modules/Dashboard/Home/View/DashboardImageCell.swift b/Apps/RuuviStation/Sources/Classes/Presentation/Modules/Dashboard/Home/View/DashboardImageCell.swift index 0a9e51c2a..0ea6389a2 100644 --- a/Apps/RuuviStation/Sources/Classes/Presentation/Modules/Dashboard/Home/View/DashboardImageCell.swift +++ b/Apps/RuuviStation/Sources/Classes/Presentation/Modules/Dashboard/Home/View/DashboardImageCell.swift @@ -222,7 +222,7 @@ class DashboardImageCell: DashboardCell { switch source { case .unknown: dataSourceIconView.image = nil - case .advertisement: + case .advertisement, .bgAdvertisement: dataSourceIconView.image = RuuviAsset.iconBluetooth.image case .heartbeat, .log: dataSourceIconView.image = RuuviAsset.iconBluetoothConnected.image @@ -256,7 +256,9 @@ class DashboardImageCell: DashboardCell { // swiftlint:disable:next function_body_length cyclomatic_complexity override func restartAlertAnimation(for viewModel: CardsViewModel) { // Alert - let alertVisible = viewModel.isCloud.value ?? false || viewModel.isConnected.value ?? false + let alertVisible = viewModel.isCloud.value ?? false || + viewModel.isConnected.value ?? false || + viewModel.serviceUUID.value != nil let mutedTills = [ viewModel.temperatureAlertMutedTill.value, diff --git a/Apps/RuuviStation/Sources/Classes/Presentation/Modules/Dashboard/Home/View/DashboardPlainCell.swift b/Apps/RuuviStation/Sources/Classes/Presentation/Modules/Dashboard/Home/View/DashboardPlainCell.swift index 14d814544..2612c752f 100644 --- a/Apps/RuuviStation/Sources/Classes/Presentation/Modules/Dashboard/Home/View/DashboardPlainCell.swift +++ b/Apps/RuuviStation/Sources/Classes/Presentation/Modules/Dashboard/Home/View/DashboardPlainCell.swift @@ -192,7 +192,7 @@ class DashboardPlainCell: DashboardCell { switch source { case .unknown: dataSourceIconView.image = nil - case .advertisement: + case .advertisement, .bgAdvertisement: dataSourceIconView.image = RuuviAsset.iconBluetooth.image case .heartbeat, .log: dataSourceIconView.image = RuuviAsset.iconBluetoothConnected.image @@ -227,7 +227,8 @@ class DashboardPlainCell: DashboardCell { override func restartAlertAnimation(for viewModel: CardsViewModel) { // Alert let alertVisible = viewModel.isCloud.value ?? false || - viewModel.isConnected.value ?? false + viewModel.isConnected.value ?? false || + viewModel.serviceUUID.value != nil let mutedTills = [ viewModel.temperatureAlertMutedTill.value, diff --git a/Apps/RuuviStation/Sources/Classes/Presentation/Modules/TagSettings/Presenter/TagSettingsPresenter.swift b/Apps/RuuviStation/Sources/Classes/Presentation/Modules/TagSettings/Presenter/TagSettingsPresenter.swift index 437f183e6..ea79395c4 100644 --- a/Apps/RuuviStation/Sources/Classes/Presentation/Modules/TagSettings/Presenter/TagSettingsPresenter.swift +++ b/Apps/RuuviStation/Sources/Classes/Presentation/Modules/TagSettings/Presenter/TagSettingsPresenter.swift @@ -512,20 +512,23 @@ extension TagSettingsPresenter { bind(viewModel.isConnected) { [weak isCloudAlertsAvailable] observer, isConnected in let isCl = isCloudAlertsAvailable?.value ?? false let isCo = isConnected ?? false - observer.viewModel.isAlertsEnabled.value = isCl || isCo + observer.viewModel.isAlertsEnabled.value = isCl || isCo || observer.ruuviTag.serviceUUID != nil } let isConnected = viewModel.isConnected bind(viewModel.isCloudAlertsAvailable) { [weak isConnected] observer, isCloudAlertsAvailable in let isCl = isCloudAlertsAvailable ?? false let isCo = isConnected?.value ?? false - observer.viewModel.isAlertsEnabled.value = isCl || isCo + observer.viewModel.isAlertsEnabled.value = isCl || isCo || observer.ruuviTag.serviceUUID != nil self.processAlerts() } } - /// Sets the view model properties related to the associated RuuviTag + // Sets the view model properties related to the associated RuuviTag + // swiftlint:disable:next function_body_length private func syncTag() { + viewModel.serviceUUID.value = ruuviTag.serviceUUID + ruuviSensorPropertiesService.getImage(for: ruuviTag) .on(success: { [weak self] image in self?.viewModel.background.value = image @@ -895,13 +898,13 @@ extension TagSettingsPresenter { return } advertisementToken = foreground.observe(self, uuid: luid.value, closure: { [weak self] _, device in - if let tag = device.ruuvi?.tag { + if let tag = device.ruuvi?.tag, tag.luid?.value == luid.value { self?.handleMeasurementPoint(tag: tag, luid: luid, source: .advertisement) } }) heartbeatToken = background.observe(self, uuid: luid.value, closure: { [weak self] _, device in - if let tag = device.ruuvi?.tag { + if let tag = device.ruuvi?.tag, tag.luid?.value == luid.value { self?.handleMeasurementPoint(tag: tag, luid: luid, source: .heartbeat) } }) @@ -933,6 +936,7 @@ extension TagSettingsPresenter { } } + // swiftlint:disable:next function_body_length private func sync( device: RuuviTag, luid _: LocalIdentifier, @@ -944,6 +948,7 @@ extension TagSettingsPresenter { source: source, macId: device.mac?.mac, rssi: device.rssi, + version: device.version, temperature: device.temperature, humidity: device.humidity, pressure: device.pressure, @@ -1332,7 +1337,9 @@ extension TagSettingsPresenter: RuuviNotifierObserver { for uuid: String ) { if ruuviTag.luid?.value == uuid || ruuviTag.macId?.value == uuid { - let isFireable = ruuviTag.isCloud || viewModel.isConnected.value ?? false + let isFireable = ruuviTag.isCloud || + viewModel.isConnected.value ?? false || + viewModel.serviceUUID.value != nil switch alertType { case .temperature: let isTriggered = isTriggered && isFireable && (viewModel.isAlertsEnabled.value ?? false) diff --git a/Apps/RuuviStation/Sources/Classes/Presentation/Modules/TagSettings/View/TagSettingsViewModel.swift b/Apps/RuuviStation/Sources/Classes/Presentation/Modules/TagSettings/View/TagSettingsViewModel.swift index 86c0ceffe..d17ab4675 100644 --- a/Apps/RuuviStation/Sources/Classes/Presentation/Modules/TagSettings/View/TagSettingsViewModel.swift +++ b/Apps/RuuviStation/Sources/Classes/Presentation/Modules/TagSettings/View/TagSettingsViewModel.swift @@ -6,6 +6,7 @@ struct TagSettingsViewModel { let name: Observable = .init() let uuid: Observable = .init() let mac: Observable = .init() + var serviceUUID: Observable = .init() let rssi: Observable = .init() let humidity: Observable = .init() let temperature: Observable = .init() diff --git a/Apps/RuuviStation/Sources/Classes/Presentation/Modules/TagSettings/View/UI/TagSettingsViewController.swift b/Apps/RuuviStation/Sources/Classes/Presentation/Modules/TagSettings/View/UI/TagSettingsViewController.swift index 4caa98ae7..ae24ca667 100644 --- a/Apps/RuuviStation/Sources/Classes/Presentation/Modules/TagSettings/View/UI/TagSettingsViewController.swift +++ b/Apps/RuuviStation/Sources/Classes/Presentation/Modules/TagSettings/View/UI/TagSettingsViewController.swift @@ -1902,7 +1902,8 @@ extension TagSettingsViewController { private func alertsAvailable() -> Bool { (viewModel?.isCloudAlertsAvailable.value ?? false || - viewModel?.isConnected.value ?? false) + viewModel?.isConnected.value ?? false || + viewModel?.serviceUUID.value != nil) } private func reloadAlertSectionHeaders() { @@ -3038,8 +3039,9 @@ extension TagSettingsViewController { // Data format if let moreInfoDataFormatCell { - moreInfoDataFormatCell.bind(viewModel.version) { cell, version in - cell.configure(value: version.stringValue) + moreInfoDataFormatCell.bind(viewModel.version) { [weak self] cell, version in + guard let sSelf = self else { return } + cell.configure(value: sSelf.formattedVersion(value: version)) } } @@ -3170,7 +3172,7 @@ extension TagSettingsViewController { createdCell: { [weak self] in self?.moreInfoDataFormatCell?.configure( title: RuuviLocalization.TagSettings.DataFormatTitleLabel.text, - value: self?.viewModel?.version.value?.stringValue + value: self?.formattedVersion(value: self?.viewModel?.version.value) ) self?.moreInfoDataFormatCell?.selectionStyle = .none return self?.moreInfoDataFormatCell ?? UITableViewCell() @@ -3319,7 +3321,7 @@ extension TagSettingsViewController { if let source { var sourceString = emptyString switch source { - case .advertisement: + case .advertisement, .bgAdvertisement: sourceString = RuuviLocalization.TagSettings.DataSource.Advertisement.title case .heartbeat: sourceString = RuuviLocalization.TagSettings.DataSource.Heartbeat.title @@ -3370,6 +3372,14 @@ extension TagSettingsViewController { RuuviLocalization.na } } + + private func formattedVersion(value: Int?) -> String { + if value == 197 { + return "C5" + } else { + return value.stringValue + } + } } // MARK: - FIRMWARE SECTION diff --git a/Apps/RuuviStation/Widgets/Sources/Model/Model+Extension.swift b/Apps/RuuviStation/Widgets/Sources/Model/Model+Extension.swift index 8b1306b13..2ebbd794c 100644 --- a/Apps/RuuviStation/Widgets/Sources/Model/Model+Extension.swift +++ b/Apps/RuuviStation/Widgets/Sources/Model/Model+Extension.swift @@ -16,6 +16,7 @@ extension RuuviTagSensorRecordStruct { source: .ruuviNetwork, macId: nil, rssi: nil, + version: 5, temperature: Temperature(69.50), humidity: nil, pressure: nil, diff --git a/Modules/RuuviDiscover/Package.swift b/Modules/RuuviDiscover/Package.swift index 0848745a7..771174132 100644 --- a/Modules/RuuviDiscover/Package.swift +++ b/Modules/RuuviDiscover/Package.swift @@ -21,7 +21,7 @@ let package = Package( .package(path: "../../Packages/RuuviService"), .package(path: "../../Common/RuuviPresenters"), .package(path: "../../Common/RuuviLocalization"), - .package(url: "https://github.com/ruuvi/BTKit", .upToNextMinor(from: "0.4.3")), + .package(url: "https://github.com/ruuvi/BTKit", branch: "master"), ], targets: [ .target( diff --git a/Packages/RuuviCloud/Package.swift b/Packages/RuuviCloud/Package.swift index 44ed50d20..cd4ba34fa 100644 --- a/Packages/RuuviCloud/Package.swift +++ b/Packages/RuuviCloud/Package.swift @@ -22,7 +22,7 @@ let package = Package( ], dependencies: [ .package(url: "https://github.com/kean/Future", .exact("1.3.0")), - .package(url: "https://github.com/ruuvi/BTKit", .upToNextMinor(from: "0.4.3")), + .package(url: "https://github.com/ruuvi/BTKit", branch: "master"), .package(path: "../RuuviOntology"), .package(path: "../RuuviUser"), .package(path: "../RuuviPool"), diff --git a/Packages/RuuviCloud/Sources/RuuviCloudApi/URLSession/Models/Response/RuuviCloudApiUserResponse.swift b/Packages/RuuviCloud/Sources/RuuviCloudApi/URLSession/Models/Response/RuuviCloudApiUserResponse.swift index f3704274d..6ddf902d0 100644 --- a/Packages/RuuviCloud/Sources/RuuviCloudApi/URLSession/Models/Response/RuuviCloudApiUserResponse.swift +++ b/Packages/RuuviCloud/Sources/RuuviCloudApi/URLSession/Models/Response/RuuviCloudApiUserResponse.swift @@ -92,4 +92,8 @@ extension RuuviCloudApiSensor: CloudSensor { public var id: String { sensorId } + + public var serviceUUID: String? { + nil + } } diff --git a/Packages/RuuviCloud/Sources/RuuviCloudPure/RuuviCloudPure.swift b/Packages/RuuviCloud/Sources/RuuviCloudPure/RuuviCloudPure.swift index bc8274f22..74815c9fc 100644 --- a/Packages/RuuviCloud/Sources/RuuviCloudPure/RuuviCloudPure.swift +++ b/Packages/RuuviCloud/Sources/RuuviCloudPure/RuuviCloudPure.swift @@ -807,6 +807,7 @@ public final class RuuviCloudPure: RuuviCloud { RuuviCloudSensorDense( sensor: CloudSensorStruct( id: sensor.sensor, + serviceUUID: nil, name: sensor.name, isClaimed: true, isOwner: sensor.owner == self?.user.email, @@ -1326,6 +1327,7 @@ public final class RuuviCloudPure: RuuviCloud { source: .ruuviNetwork, macId: macId, rssi: rssi, + version: tag.version, temperature: tag.temperature, humidity: tag.humidity, pressure: tag.pressure, @@ -1365,6 +1367,7 @@ public final class RuuviCloudPure: RuuviCloud { source: .ruuviNetwork, macId: macId, rssi: record.rssi, + version: tag.version, temperature: tag.temperature, humidity: tag.humidity, pressure: tag.pressure, diff --git a/Packages/RuuviContext/Sources/RuuviContextSQLite/SQLiteContextGRDB.swift b/Packages/RuuviContext/Sources/RuuviContextSQLite/SQLiteContextGRDB.swift index 2fe6823e4..216e71307 100644 --- a/Packages/RuuviContext/Sources/RuuviContextSQLite/SQLiteContextGRDB.swift +++ b/Packages/RuuviContext/Sources/RuuviContextSQLite/SQLiteContextGRDB.swift @@ -222,6 +222,38 @@ extension SQLiteGRDBDatabase { migrator.registerMigration("Create RuuviCloudSensorSubscription table") { db in try RuuviCloudSensorSubscriptionSQLite.createTable(in: db) } + // v14 + migrator.registerMigration("Create RuuviTagSQLite serviceUUID column") { db in + guard try db.columns(in: RuuviTagSQLite.databaseTableName) + .contains(where: { $0.name == RuuviTagSQLite.serviceUUIDColumn.name }) == false + else { + return + } + try db.alter(table: RuuviTagSQLite.databaseTableName, body: { t in + t.add(column: RuuviTagSQLite.serviceUUIDColumn.name, .text) + }) + } + // v15 + migrator.registerMigration("Create RuuviTagDataSQLite version column") { db in + guard try db.columns(in: RuuviTagDataSQLite.databaseTableName) + .contains(where: { $0.name == RuuviTagDataSQLite.versionColumn.name }) == false + else { + return + } + try db.alter(table: RuuviTagDataSQLite.databaseTableName, body: { t in + t.add(column: RuuviTagDataSQLite.versionColumn.name, .integer) + }) + } + migrator.registerMigration("Create RuuviTagLatestDataSQLite version column") { db in + guard try db.columns(in: RuuviTagLatestDataSQLite.databaseTableName) + .contains(where: { $0.name == RuuviTagLatestDataSQLite.versionColumn.name }) == false + else { + return + } + try db.alter(table: RuuviTagLatestDataSQLite.databaseTableName, body: { t in + t.add(column: RuuviTagLatestDataSQLite.versionColumn.name, .integer) + }) + } try migrator.migrate(dbPool) } diff --git a/Packages/RuuviDaemon/Package.swift b/Packages/RuuviDaemon/Package.swift index 9ed7f0b45..ffe534ba6 100644 --- a/Packages/RuuviDaemon/Package.swift +++ b/Packages/RuuviDaemon/Package.swift @@ -38,7 +38,7 @@ let package = Package( .package(path: "../RuuviPersistence"), .package(path: "../RuuviNotifier"), .package(path: "../RuuviNotification"), - .package(url: "https://github.com/ruuvi/BTKit", .upToNextMinor(from: "0.4.3")), + .package(url: "https://github.com/ruuvi/BTKit", branch: "master"), ], targets: [ .target( diff --git a/Packages/RuuviDaemon/Sources/RuuviDaemonRuuviTag/Heartbeat/RuuviTagHeartbeatDaemonBTKit.swift b/Packages/RuuviDaemon/Sources/RuuviDaemonRuuviTag/Heartbeat/RuuviTagHeartbeatDaemonBTKit.swift index 403efcfbe..834d69edb 100644 --- a/Packages/RuuviDaemon/Sources/RuuviDaemonRuuviTag/Heartbeat/RuuviTagHeartbeatDaemonBTKit.swift +++ b/Packages/RuuviDaemon/Sources/RuuviDaemonRuuviTag/Heartbeat/RuuviTagHeartbeatDaemonBTKit.swift @@ -24,6 +24,7 @@ public final class RuuviTagHeartbeatDaemonBTKit: RuuviDaemonWorker, RuuviTagHear private var ruuviTags = [AnyRuuviTagSensor]() private var sensorSettingsList = [SensorSettings]() + private var observeTokens = [ObservationToken]() private var connectTokens = [String: ObservationToken]() private var disconnectTokens = [String: ObservationToken]() private var connectionAddedToken: NSObjectProtocol? @@ -180,6 +181,7 @@ public final class RuuviTagHeartbeatDaemonBTKit: RuuviDaemonWorker, RuuviTagHear ruuviStorage.readAll().on(success: { [weak self] sensors in self?.ruuviTags = sensors self?.handleRuuviTagsChange() + self?.restartObserving() }) } @@ -247,14 +249,16 @@ extension RuuviTagHeartbeatDaemonBTKit { self.createRecords( observer: observer, ruuviTag: ruuviTag, - uuid: uuid + uuid: uuid, + source: .heartbeat ) } } else { self.createRecords( observer: observer, ruuviTag: ruuviTag, - uuid: uuid + uuid: uuid, + source: .heartbeat ) } } @@ -286,9 +290,10 @@ extension RuuviTagHeartbeatDaemonBTKit { private func createRecords( observer: RuuviTagHeartbeatDaemonBTKit, ruuviTag: RuuviTag, - uuid: String + uuid: String, + source: RuuviTagSensorRecordSource ) { - createRecord(observer: observer, ruuviTag: ruuviTag, uuid: uuid) + createRecord(observer: observer, ruuviTag: ruuviTag, uuid: uuid, source: source) heartbeatQueue.async { [weak observer] in observer?.savedDate[uuid] = Date() } @@ -297,24 +302,32 @@ extension RuuviTagHeartbeatDaemonBTKit { private func createRecord( observer: RuuviTagHeartbeatDaemonBTKit, ruuviTag: RuuviTag, - uuid: String + uuid: String, + source: RuuviTagSensorRecordSource ) { observer.ruuviPool.create( ruuviTag - .with(source: .heartbeat) - ).on(success: { [weak self] _ in - self?.createLastRecord(observer: observer, ruuviTag: ruuviTag, uuid: uuid) + .with(source: source) + ).on( + success: { [weak self] _ in + self?.createLastRecord( + observer: observer, + ruuviTag: ruuviTag, + uuid: uuid, + source: source + ) }) } private func createLastRecord( observer: RuuviTagHeartbeatDaemonBTKit, ruuviTag: RuuviTag, - uuid: String + uuid: String, + source: RuuviTagSensorRecordSource ) { if let tag = ruuviTags.first(where: { $0.luid?.value == uuid }) { ruuviStorage.readLatest(tag).on(success: { localRecord in - let record = ruuviTag.with(source: .heartbeat) + let record = ruuviTag.with(source: source) if let localRecord, record.macId?.value == localRecord.macId?.value { observer.ruuviPool.updateLast(record) @@ -354,6 +367,7 @@ extension RuuviTagHeartbeatDaemonBTKit { } } restartSensorSettingsObservers() + restartObserving() } @objc private func connect(uuid: String) { @@ -387,6 +401,8 @@ extension RuuviTagHeartbeatDaemonBTKit { private func invalidateTokens() { autoreleasepool { ruuviTagsToken?.invalidate() + observeTokens.forEach { $0.invalidate() } + observeTokens.removeAll() connectTokens.values.forEach { $0.invalidate() } connectTokens.removeAll() disconnectTokens.values.forEach { $0.invalidate() } @@ -432,6 +448,77 @@ extension RuuviTagHeartbeatDaemonBTKit { // MARK: - Sensor Settings extension RuuviTagHeartbeatDaemonBTKit { + + // swiftlint:disable:next function_body_length + private func restartObserving() { + observeTokens.forEach { $0.invalidate() } + observeTokens.removeAll() + + for ruuviTag in ruuviTags { + + let shouldAvoidObserving = + (settings.cloudModeEnabled && ruuviTag.isCloud) || settings.appIsOnForeground + if shouldAvoidObserving { + continue + } + guard let serviceUUID = ruuviTag.serviceUUID else { continue } + observeTokens.append( + background.observe( + self, + uuid: serviceUUID, + options: [.callbackQueue(.untouch)] + ) { + [weak self] _, device in + guard let sSelf = self else { return } + + if let ruuviTag = device.ruuvi?.tag, ruuviTag.vC5?.serviceUUID != nil { + var sensorSettings: SensorSettings? + if let ruuviTagSensor = sSelf.ruuviTags + .first(where: { + ($0.macId?.value != nil && $0.macId?.value == ruuviTag.mac) + || ($0.luid?.any != nil && $0.luid?.any == ruuviTag.luid?.any) + }), + let settings = sSelf.sensorSettingsList + .first(where: { + ($0.luid?.any != nil && $0.luid?.any == ruuviTagSensor.luid?.any) + || ($0.macId?.any != nil && $0.macId?.any == ruuviTagSensor.macId?.any) + }) { + sensorSettings = settings + } + sSelf.alertHandler.process( + record: ruuviTag + .with(sensorSettings: sensorSettings) + .with(source: .bgAdvertisement), + trigger: true + ) + if sSelf.settings.saveHeartbeats { + let uuid = ruuviTag.uuid + guard ruuviTag.luid != nil, ruuviTag.vC5?.serviceUUID != nil else { return } + let interval = sSelf.settings.saveHeartbeatsForegroundIntervalSeconds + if let date = sSelf.savedDate[uuid] { + if Date().timeIntervalSince(date) > TimeInterval(interval) { + sSelf.createRecords( + observer: sSelf, + ruuviTag: ruuviTag, + uuid: uuid, + source: .bgAdvertisement + ) + } + } else { + sSelf.createRecords( + observer: sSelf, + ruuviTag: ruuviTag, + uuid: uuid, + source: .bgAdvertisement + ) + } + } + } + } + ) + } + } + private func restartSensorSettingsObservers() { sensorSettingsTokens.forEach { $0.value.invalidate() } sensorSettingsTokens.removeAll() diff --git a/Packages/RuuviDaemon/Sources/RuuviDaemonRuuviTag/Properties/RuuviTagPropertiesDaemonBTKit.swift b/Packages/RuuviDaemon/Sources/RuuviDaemonRuuviTag/Properties/RuuviTagPropertiesDaemonBTKit.swift index 1beaa3823..0c2fdf48a 100644 --- a/Packages/RuuviDaemon/Sources/RuuviDaemonRuuviTag/Properties/RuuviTagPropertiesDaemonBTKit.swift +++ b/Packages/RuuviDaemon/Sources/RuuviDaemonRuuviTag/Properties/RuuviTagPropertiesDaemonBTKit.swift @@ -216,6 +216,7 @@ public final class RuuviTagPropertiesDaemonBTKit: RuuviDaemonWorker, RuuviTagPro firmwareVersion: nil, luid: device.uuid.luid, macId: mac, + serviceUUID: nil, isConnectable: device.isConnectable, name: ruuviTag.name, isClaimed: ruuviTag.isClaimed, diff --git a/Packages/RuuviNotification/Sources/RuuviNotificationLocal/RuuviNotificationLocalImpl.swift b/Packages/RuuviNotification/Sources/RuuviNotificationLocal/RuuviNotificationLocalImpl.swift index 7b09b13f1..f80bc4456 100644 --- a/Packages/RuuviNotification/Sources/RuuviNotificationLocal/RuuviNotificationLocalImpl.swift +++ b/Packages/RuuviNotification/Sources/RuuviNotificationLocal/RuuviNotificationLocalImpl.swift @@ -428,6 +428,7 @@ extension RuuviNotificationLocalImpl: UNUserNotificationCenterDelegate { firmwareVersion: nil, luid: uuid.luid, macId: uuid.mac, + serviceUUID: nil, isConnectable: true, name: "", isClaimed: false, @@ -456,6 +457,7 @@ extension RuuviNotificationLocalImpl: UNUserNotificationCenterDelegate { firmwareVersion: nil, luid: uuid.luid, macId: uuid.mac, + serviceUUID: nil, isConnectable: true, name: "", isClaimed: false, diff --git a/Packages/RuuviOntology/Package.swift b/Packages/RuuviOntology/Package.swift index 1ddbc7802..69b4e33b4 100644 --- a/Packages/RuuviOntology/Package.swift +++ b/Packages/RuuviOntology/Package.swift @@ -18,7 +18,7 @@ let package = Package( ], dependencies: [ .package(url: "https://github.com/rinat-enikeev/Humidity", from: "0.1.5"), - .package(url: "https://github.com/ruuvi/BTKit", .upToNextMinor(from: "0.4.3")), + .package(url: "https://github.com/ruuvi/BTKit", branch: "master"), .package(name: "GRDB", url: "https://github.com/groue/GRDB.swift", .upToNextMajor(from: "4.14.0")), ], targets: [ diff --git a/Packages/RuuviOntology/Sources/RuuviOntology/Mappers/RuuviTag+RuuviTagSensor.swift b/Packages/RuuviOntology/Sources/RuuviOntology/Mappers/RuuviTag+RuuviTagSensor.swift index ab7c4bf6f..213b6ae95 100644 --- a/Packages/RuuviOntology/Sources/RuuviOntology/Mappers/RuuviTag+RuuviTagSensor.swift +++ b/Packages/RuuviOntology/Sources/RuuviOntology/Mappers/RuuviTag+RuuviTagSensor.swift @@ -8,6 +8,7 @@ public extension RuuviTag { firmwareVersion: nil, luid: uuid.luid, macId: mac?.mac, + serviceUUID: serviceUUID, isConnectable: isConnectable, name: name, isClaimed: false, diff --git a/Packages/RuuviOntology/Sources/RuuviOntology/Mappers/RuuviTagEnvLogFull+RuuviTagSensorRecord.swift b/Packages/RuuviOntology/Sources/RuuviOntology/Mappers/RuuviTagEnvLogFull+RuuviTagSensorRecord.swift index ba3790aa6..51878c583 100644 --- a/Packages/RuuviOntology/Sources/RuuviOntology/Mappers/RuuviTagEnvLogFull+RuuviTagSensorRecord.swift +++ b/Packages/RuuviOntology/Sources/RuuviOntology/Mappers/RuuviTagEnvLogFull+RuuviTagSensorRecord.swift @@ -34,6 +34,7 @@ extension RuuviTagEnvLogFull { source: .log, macId: mac?.mac, rssi: nil, + version: 0, temperature: unitTemperature, humidity: unitHumidity, pressure: unitPressure, diff --git a/Packages/RuuviOntology/Sources/RuuviOntology/Sensor/CloudSensor.swift b/Packages/RuuviOntology/Sources/RuuviOntology/Sensor/CloudSensor.swift index 5b60e45c5..fae82f62a 100644 --- a/Packages/RuuviOntology/Sources/RuuviOntology/Sensor/CloudSensor.swift +++ b/Packages/RuuviOntology/Sources/RuuviOntology/Sensor/CloudSensor.swift @@ -7,6 +7,7 @@ public extension CloudSensor { firmwareVersion: nil, luid: nil, macId: id.mac, + serviceUUID: serviceUUID, isConnectable: true, name: name.isEmpty ? id : name, isClaimed: isOwner, @@ -23,6 +24,7 @@ public extension CloudSensor { func with(email: String) -> CloudSensor { CloudSensorStruct( id: id, + serviceUUID: serviceUUID, name: name, isClaimed: email.lowercased() == owner?.lowercased(), isOwner: email.lowercased() == owner?.lowercased(), @@ -42,6 +44,7 @@ public extension CloudSensor { public struct CloudSensorStruct: CloudSensor { public var id: String + public var serviceUUID: String? public var name: String public var isClaimed: Bool public var isOwner: Bool @@ -58,6 +61,7 @@ public struct CloudSensorStruct: CloudSensor { public init( id: String, + serviceUUID: String?, name: String, isClaimed: Bool, isOwner: Bool, @@ -73,6 +77,7 @@ public struct CloudSensorStruct: CloudSensor { maxHistoryDays: Int? ) { self.id = id + self.serviceUUID = serviceUUID self.name = name self.isClaimed = isClaimed self.isOwner = isOwner @@ -169,4 +174,8 @@ public struct AnyCloudSensor: CloudSensor, Equatable, Hashable, Reorderable { public var orderElement: String { id } + + public var serviceUUID: String? { + object.serviceUUID + } } diff --git a/Packages/RuuviOntology/Sources/RuuviOntology/Sensor/CloudSensorDense.swift b/Packages/RuuviOntology/Sources/RuuviOntology/Sensor/CloudSensorDense.swift index 56bc6cde2..4f0c5bbea 100644 --- a/Packages/RuuviOntology/Sources/RuuviOntology/Sensor/CloudSensorDense.swift +++ b/Packages/RuuviOntology/Sources/RuuviOntology/Sensor/CloudSensorDense.swift @@ -38,6 +38,10 @@ public struct AnyCloudSensorDense: CloudSensor, Equatable, Hashable, Reorderable sensor.id } + public var serviceUUID: String? { + sensor.serviceUUID + } + public var name: String { sensor.name } @@ -167,4 +171,8 @@ extension AnyCloudSensorDense: RuuviTagSensorRecord { public var pressureOffset: Double { record.pressureOffset } + + public var version: Int { + record.version + } } diff --git a/Packages/RuuviOntology/Sources/RuuviOntology/Sensor/RuuviTag/RuuviTagSensor.swift b/Packages/RuuviOntology/Sources/RuuviOntology/Sensor/RuuviTag/RuuviTagSensor.swift index aa2359fee..992640e69 100644 --- a/Packages/RuuviOntology/Sources/RuuviOntology/Sensor/RuuviTag/RuuviTagSensor.swift +++ b/Packages/RuuviOntology/Sources/RuuviOntology/Sensor/RuuviTag/RuuviTagSensor.swift @@ -7,7 +7,8 @@ public protocol RuuviTagSensor: PhysicalSensor, Connectable, Nameable, Shareable, - HistoryFetchable {} + HistoryFetchable, + BackgroundScanable {} public enum SensorOwnership { case claimedByMe @@ -51,6 +52,7 @@ public extension RuuviTagSensor { firmwareVersion: firmwareVersion, luid: luid, macId: macId, + serviceUUID: serviceUUID, isConnectable: isConnectable, name: name, isClaimed: isClaimed, @@ -70,6 +72,7 @@ public extension RuuviTagSensor { firmwareVersion: firmwareVersion, luid: luid, macId: macId, + serviceUUID: serviceUUID, isConnectable: isConnectable, name: name, isClaimed: isClaimed, @@ -89,6 +92,7 @@ public extension RuuviTagSensor { firmwareVersion: firmwareVersion, luid: luid, macId: macId, + serviceUUID: serviceUUID, isConnectable: isConnectable, name: name, isClaimed: isClaimed, @@ -108,6 +112,7 @@ public extension RuuviTagSensor { firmwareVersion: firmwareVersion, luid: luid, macId: macId, + serviceUUID: serviceUUID, isConnectable: isConnectable, name: name, isClaimed: isClaimed, @@ -127,6 +132,7 @@ public extension RuuviTagSensor { firmwareVersion: firmwareVersion, luid: luid, macId: macId, + serviceUUID: serviceUUID, isConnectable: isConnectable, name: name, isClaimed: isClaimed, @@ -146,6 +152,7 @@ public extension RuuviTagSensor { firmwareVersion: firmwareVersion, luid: luid, macId: macId, + serviceUUID: serviceUUID, isConnectable: isConnectable, name: name, isClaimed: isClaimed, @@ -165,6 +172,7 @@ public extension RuuviTagSensor { firmwareVersion: firmwareVersion, luid: luid, macId: macId, + serviceUUID: serviceUUID, isConnectable: isConnectable, name: name, isClaimed: isClaimed, @@ -184,6 +192,7 @@ public extension RuuviTagSensor { firmwareVersion: firmwareVersion, luid: luid, macId: macId, + serviceUUID: serviceUUID, isConnectable: isConnectable, name: name, isClaimed: isClaimed, @@ -203,6 +212,7 @@ public extension RuuviTagSensor { firmwareVersion: firmwareVersion, luid: luid, macId: macId, + serviceUUID: serviceUUID, isConnectable: isConnectable, name: name, isClaimed: isClaimed, @@ -222,6 +232,7 @@ public extension RuuviTagSensor { firmwareVersion: firmwareVersion, luid: luid, macId: macId, + serviceUUID: serviceUUID, isConnectable: isConnectable, name: name, isClaimed: isClaimed, @@ -241,6 +252,7 @@ public extension RuuviTagSensor { firmwareVersion: firmwareVersion, luid: luid, macId: nil, + serviceUUID: serviceUUID, isConnectable: isConnectable, name: name, isClaimed: isClaimed, @@ -260,6 +272,7 @@ public extension RuuviTagSensor { firmwareVersion: firmwareVersion, luid: luid, macId: macId, + serviceUUID: serviceUUID, isConnectable: isConnectable, name: name, isClaimed: isClaimed, @@ -279,6 +292,7 @@ public extension RuuviTagSensor { firmwareVersion: firmwareVersion, luid: luid, macId: macId, + serviceUUID: serviceUUID, isConnectable: isConnectable, name: name, isClaimed: isClaimed, @@ -298,6 +312,7 @@ public extension RuuviTagSensor { firmwareVersion: firmwareVersion, luid: luid, macId: macId, + serviceUUID: serviceUUID, isConnectable: isConnectable, name: cloudSensor.name.isEmpty ? cloudSensor.id : cloudSensor.name, isClaimed: cloudSensor.isOwner, @@ -318,6 +333,7 @@ public extension RuuviTagSensor { firmwareVersion: firmwareVersion, luid: luid, macId: macId, + serviceUUID: serviceUUID, isConnectable: isConnectable, name: name, isClaimed: isClaimed, @@ -337,6 +353,7 @@ public extension RuuviTagSensor { firmwareVersion: firmwareVersion, luid: luid, macId: macId, + serviceUUID: serviceUUID, isConnectable: isConnectable, name: name, isClaimed: false, @@ -356,6 +373,7 @@ public extension RuuviTagSensor { firmwareVersion: firmwareVersion, luid: luid, macId: macId, + serviceUUID: serviceUUID, isConnectable: isConnectable, name: name, isClaimed: isClaimed, @@ -375,6 +393,7 @@ public extension RuuviTagSensor { firmwareVersion: firmwareVersion, luid: luid, macId: macId, + serviceUUID: serviceUUID, isConnectable: isConnectable, name: name, isClaimed: isClaimed, @@ -394,6 +413,7 @@ public extension RuuviTagSensor { firmwareVersion: firmwareVersion, luid: luid, macId: macId, + serviceUUID: serviceUUID, isConnectable: isConnectable, name: name, isClaimed: isClaimed, @@ -419,6 +439,7 @@ public struct RuuviTagSensorStruct: RuuviTagSensor { public var firmwareVersion: String? public var luid: LocalIdentifier? // local unqiue id public var macId: MACIdentifier? + public var serviceUUID: String? public var isConnectable: Bool public var name: String public var isClaimed: Bool @@ -435,6 +456,7 @@ public struct RuuviTagSensorStruct: RuuviTagSensor { firmwareVersion: String?, luid: LocalIdentifier?, macId: MACIdentifier?, + serviceUUID: String?, isConnectable: Bool, name: String, isClaimed: Bool, @@ -450,6 +472,7 @@ public struct RuuviTagSensorStruct: RuuviTagSensor { self.firmwareVersion = firmwareVersion self.luid = luid self.macId = macId + self.serviceUUID = serviceUUID self.isConnectable = isConnectable self.name = name self.isClaimed = isClaimed @@ -490,6 +513,10 @@ public struct AnyRuuviTagSensor: RuuviTagSensor, Equatable, Hashable, Reorderabl object.macId } + public var serviceUUID: String? { + object.serviceUUID + } + public var isConnectable: Bool { object.isConnectable } diff --git a/Packages/RuuviOntology/Sources/RuuviOntology/Sensor/RuuviTag/RuuviTagSensorRecord.swift b/Packages/RuuviOntology/Sources/RuuviOntology/Sensor/RuuviTag/RuuviTagSensorRecord.swift index e4fd10da5..81bab97fa 100644 --- a/Packages/RuuviOntology/Sources/RuuviOntology/Sensor/RuuviTag/RuuviTagSensorRecord.swift +++ b/Packages/RuuviOntology/Sources/RuuviOntology/Sensor/RuuviTag/RuuviTagSensorRecord.swift @@ -4,6 +4,7 @@ import Humidity public enum RuuviTagSensorRecordSource: String { case unknown case advertisement + case bgAdvertisement case log case heartbeat case ruuviNetwork @@ -30,6 +31,9 @@ public protocol RuuviTagSensorRecord: PhysicalSensor { var temperatureOffset: Double { get } var humidityOffset: Double { get } var pressureOffset: Double { get } + + // Firmware version + var version: Int { get } } public extension RuuviTagSensorRecord { @@ -66,6 +70,7 @@ public extension RuuviTagSensorRecord { source: source, macId: macId, rssi: rssi, + version: version, temperature: temperature, humidity: humidity, pressure: pressure, @@ -87,6 +92,7 @@ public extension RuuviTagSensorRecord { source: source, macId: macId, rssi: rssi, + version: version, temperature: temperature, humidity: humidity, pressure: pressure, @@ -110,6 +116,7 @@ public extension RuuviTagSensorRecord { source: source, macId: macId, rssi: rssi, + version: version, temperature: temperature, humidity: humidity, pressure: pressure, @@ -131,6 +138,7 @@ public extension RuuviTagSensorRecord { source: source, macId: macId, rssi: rssi, + version: version, temperature: temperature? .minus(value: temperatureOffset)? .plus(sensorSettings: sensorSettings), @@ -158,6 +166,7 @@ public struct RuuviTagSensorRecordStruct: RuuviTagSensorRecord { public var source: RuuviTagSensorRecordSource public var macId: MACIdentifier? public var rssi: Int? + public var version: Int public var temperature: Temperature? public var humidity: Humidity? public var pressure: Pressure? @@ -180,6 +189,7 @@ public struct RuuviTagSensorRecordStruct: RuuviTagSensorRecord { source: RuuviTagSensorRecordSource, macId: MACIdentifier?, rssi: Int?, + version: Int, temperature: Temperature?, humidity: Humidity?, pressure: Pressure?, @@ -197,6 +207,7 @@ public struct RuuviTagSensorRecordStruct: RuuviTagSensorRecord { self.source = source self.macId = macId self.rssi = rssi + self.version = version self.temperature = temperature self.humidity = humidity self.pressure = pressure @@ -238,6 +249,10 @@ public struct AnyRuuviTagSensorRecord: RuuviTagSensorRecord, Equatable, Hashable object.rssi } + public var version: Int { + object.version + } + public var temperature: Temperature? { object.temperature } diff --git a/Packages/RuuviOntology/Sources/RuuviOntology/Sensor/Sensor.swift b/Packages/RuuviOntology/Sources/RuuviOntology/Sensor/Sensor.swift index 4e33280e3..bf9497cd5 100644 --- a/Packages/RuuviOntology/Sources/RuuviOntology/Sensor/Sensor.swift +++ b/Packages/RuuviOntology/Sources/RuuviOntology/Sensor/Sensor.swift @@ -4,6 +4,10 @@ public protocol StringIdentifieable { var id: String { get } } +public protocol BackgroundScanable { + var serviceUUID: String? { get } +} + public protocol Connectable { var isConnectable: Bool { get } } @@ -47,7 +51,8 @@ public protocol CloudSensor: Sensor, HasRemotePicture, Calibratable, Shareable, - HistoryFetchable {} + HistoryFetchable, + BackgroundScanable {} public protocol Shareable { var canShare: Bool { get } diff --git a/Packages/RuuviOntology/Sources/RuuviOntologySQLite/RuuviTagDataSQLite.swift b/Packages/RuuviOntology/Sources/RuuviOntologySQLite/RuuviTagDataSQLite.swift index b7c4c181a..041c51e28 100644 --- a/Packages/RuuviOntology/Sources/RuuviOntologySQLite/RuuviTagDataSQLite.swift +++ b/Packages/RuuviOntology/Sources/RuuviOntologySQLite/RuuviTagDataSQLite.swift @@ -8,6 +8,7 @@ public struct RuuviTagDataSQLite: RuuviTagSensorRecord { public var source: RuuviTagSensorRecordSource public var macId: MACIdentifier? public var rssi: Int? + public var version: Int public var temperature: Temperature? public var humidity: Humidity? public var pressure: Pressure? @@ -26,6 +27,7 @@ public struct RuuviTagDataSQLite: RuuviTagSensorRecord { source: RuuviTagSensorRecordSource, macId: MACIdentifier?, rssi: Int?, + version: Int, temperature: Temperature?, humidity: Humidity?, pressure: Pressure?, @@ -43,6 +45,7 @@ public struct RuuviTagDataSQLite: RuuviTagSensorRecord { self.source = source self.macId = macId self.rssi = rssi + self.version = version self.temperature = temperature self.humidity = humidity self.pressure = pressure @@ -65,6 +68,7 @@ public extension RuuviTagDataSQLite { static let sourceColumn = Column("source") static let macColumn = Column("mac") static let rssiColumn = Column("rssi") + static let versionColumn = Column("version") static let celsiusColumn = Column("celsius") static let relativeHumidityInPercentColumn = Column("relativeHumidityInPercent") static let hectopascalsColumn = Column("hectopascals") @@ -103,6 +107,7 @@ extension RuuviTagDataSQLite: FetchableRecord { macId = MACIdentifierStruct(value: macIdValue) } rssi = row[RuuviTagDataSQLite.rssiColumn] + version = row[RuuviTagDataSQLite.versionColumn] ?? 5 if let celsius = Double.fromDatabaseValue(row[RuuviTagDataSQLite.celsiusColumn]) { temperature = Temperature(value: celsius, unit: .celsius) if let relativeHumidity @@ -151,6 +156,7 @@ extension RuuviTagDataSQLite: PersistableRecord { container[RuuviTagDataSQLite.dateColumn] = date container[RuuviTagDataSQLite.sourceColumn] = source.rawValue container[RuuviTagDataSQLite.rssiColumn] = rssi + container[RuuviTagDataSQLite.versionColumn] = version container[RuuviTagDataSQLite.celsiusColumn] = temperature?.converted(to: .celsius).value container[RuuviTagDataSQLite.relativeHumidityInPercentColumn] = humidity?.value container[RuuviTagDataSQLite.hectopascalsColumn] = pressure?.converted(to: .hectopascals).value @@ -177,6 +183,7 @@ public extension RuuviTagDataSQLite { table.column(RuuviTagDataSQLite.sourceColumn.name, .text).notNull() table.column(RuuviTagDataSQLite.macColumn.name, .text) table.column(RuuviTagDataSQLite.rssiColumn.name, .integer) + table.column(RuuviTagDataSQLite.versionColumn.name, .integer).notNull() table.column(RuuviTagDataSQLite.celsiusColumn.name, .double) table.column(RuuviTagDataSQLite.relativeHumidityInPercentColumn.name, .double) table.column(RuuviTagDataSQLite.hectopascalsColumn.name, .double) diff --git a/Packages/RuuviOntology/Sources/RuuviOntologySQLite/RuuviTagLatestDataSQLite.swift b/Packages/RuuviOntology/Sources/RuuviOntologySQLite/RuuviTagLatestDataSQLite.swift index 6e5b89f42..999cf3d1a 100644 --- a/Packages/RuuviOntology/Sources/RuuviOntologySQLite/RuuviTagLatestDataSQLite.swift +++ b/Packages/RuuviOntology/Sources/RuuviOntologySQLite/RuuviTagLatestDataSQLite.swift @@ -20,6 +20,7 @@ public struct RuuviTagLatestDataSQLite: RuuviTagSensorRecord { public var temperatureOffset: Double public var humidityOffset: Double public var pressureOffset: Double + public var version: Int public init( id: String, @@ -28,6 +29,7 @@ public struct RuuviTagLatestDataSQLite: RuuviTagSensorRecord { source: RuuviTagSensorRecordSource, macId: MACIdentifier?, rssi: Int?, + version: Int, temperature: Temperature?, humidity: Humidity?, pressure: Pressure?, @@ -46,6 +48,7 @@ public struct RuuviTagLatestDataSQLite: RuuviTagSensorRecord { self.source = source self.macId = macId self.rssi = rssi + self.version = version self.temperature = temperature self.humidity = humidity self.pressure = pressure @@ -68,6 +71,7 @@ public extension RuuviTagLatestDataSQLite { static let sourceColumn = Column("source") static let macColumn = Column("mac") static let rssiColumn = Column("rssi") + static let versionColumn = Column("version") static let celsiusColumn = Column("celsius") static let relativeHumidityInPercentColumn = Column("relativeHumidityInPercent") static let hectopascalsColumn = Column("hectopascals") @@ -107,6 +111,7 @@ extension RuuviTagLatestDataSQLite: FetchableRecord { macId = MACIdentifierStruct(value: macIdValue) } rssi = row[RuuviTagLatestDataSQLite.rssiColumn] + version = row[RuuviTagLatestDataSQLite.versionColumn] ?? 5 if let celsius = Double.fromDatabaseValue(row[RuuviTagLatestDataSQLite.celsiusColumn]) { temperature = Temperature(value: celsius, unit: .celsius) if let relativeHumidity @@ -155,6 +160,7 @@ extension RuuviTagLatestDataSQLite: PersistableRecord { container[RuuviTagLatestDataSQLite.dateColumn] = date container[RuuviTagLatestDataSQLite.sourceColumn] = source.rawValue container[RuuviTagLatestDataSQLite.rssiColumn] = rssi + container[RuuviTagLatestDataSQLite.versionColumn] = version container[RuuviTagLatestDataSQLite.celsiusColumn] = temperature?.converted(to: .celsius).value container[RuuviTagLatestDataSQLite.relativeHumidityInPercentColumn] = humidity?.value container[RuuviTagLatestDataSQLite.hectopascalsColumn] = pressure?.converted(to: .hectopascals).value @@ -184,6 +190,7 @@ public extension RuuviTagLatestDataSQLite { table.column(RuuviTagLatestDataSQLite.sourceColumn.name, .text).notNull() table.column(RuuviTagLatestDataSQLite.macColumn.name, .text) table.column(RuuviTagLatestDataSQLite.rssiColumn.name, .integer) + table.column(RuuviTagLatestDataSQLite.versionColumn.name, .integer).notNull() table.column(RuuviTagLatestDataSQLite.celsiusColumn.name, .double) table.column(RuuviTagLatestDataSQLite.relativeHumidityInPercentColumn.name, .double) table.column(RuuviTagLatestDataSQLite.hectopascalsColumn.name, .double) diff --git a/Packages/RuuviOntology/Sources/RuuviOntologySQLite/RuuviTagSQLite.swift b/Packages/RuuviOntology/Sources/RuuviOntologySQLite/RuuviTagSQLite.swift index 00eb99f1b..6e44301c8 100644 --- a/Packages/RuuviOntology/Sources/RuuviOntologySQLite/RuuviTagSQLite.swift +++ b/Packages/RuuviOntology/Sources/RuuviOntologySQLite/RuuviTagSQLite.swift @@ -5,6 +5,7 @@ public struct RuuviTagSQLite: RuuviTagSensor, Equatable { public var id: String public var macId: MACIdentifier? public var luid: LocalIdentifier? + public var serviceUUID: String? public var name: String public var version: Int public var firmwareVersion: String? @@ -22,6 +23,7 @@ public struct RuuviTagSQLite: RuuviTagSensor, Equatable { id: String, macId: MACIdentifier?, luid: LocalIdentifier?, + serviceUUID: String?, name: String, version: Int, firmwareVersion: String?, @@ -38,6 +40,7 @@ public struct RuuviTagSQLite: RuuviTagSensor, Equatable { self.id = id self.macId = macId self.luid = luid + self.serviceUUID = serviceUUID self.name = name self.version = version self.firmwareVersion = firmwareVersion @@ -56,6 +59,7 @@ public struct RuuviTagSQLite: RuuviTagSensor, Equatable { return lhs.id == rhs.id && lhs.macId?.any == rhs.macId?.any && lhs.luid?.any == rhs.luid?.any + && lhs.serviceUUID == rhs.serviceUUID && lhs.name == rhs.name && lhs.version == rhs.version && lhs.firmwareVersion == rhs.firmwareVersion @@ -75,6 +79,7 @@ public extension RuuviTagSQLite { static let idColumn = Column("id") static let macColumn = Column("mac") static let luidColumn = Column("luid") + static let serviceUUIDColumn = Column("serviceUUID") static let nameColumn = Column("name") static let versionColumn = Column("version") static let firmwareVersionColumn = Column("firmwareVersion") @@ -99,6 +104,7 @@ extension RuuviTagSQLite: FetchableRecord { if let luidColumn = row[RuuviTagSQLite.luidColumn] as? String { luid = LocalIdentifierStruct(value: luidColumn) } + serviceUUID = row[RuuviTagSQLite.serviceUUIDColumn] name = row[RuuviTagSQLite.nameColumn] version = row[RuuviTagSQLite.versionColumn] firmwareVersion = row[RuuviTagSQLite.firmwareVersionColumn] @@ -127,6 +133,7 @@ extension RuuviTagSQLite: PersistableRecord { container[RuuviTagSQLite.idColumn] = id container[RuuviTagSQLite.macColumn] = macId?.value container[RuuviTagSQLite.luidColumn] = luid?.value + container[RuuviTagSQLite.serviceUUIDColumn] = serviceUUID container[RuuviTagSQLite.nameColumn] = name container[RuuviTagSQLite.versionColumn] = version container[RuuviTagSQLite.firmwareVersionColumn] = firmwareVersion @@ -148,6 +155,7 @@ public extension RuuviTagSQLite { table.column(RuuviTagSQLite.idColumn.name, .text).notNull().primaryKey(onConflict: .replace) table.column(RuuviTagSQLite.macColumn.name, .text) table.column(RuuviTagSQLite.luidColumn.name, .text) + table.column(RuuviTagSQLite.serviceUUIDColumn.name, .text) table.column(RuuviTagSQLite.nameColumn.name, .text).notNull() table.column(RuuviTagSQLite.versionColumn.name, .integer).notNull() table.column(RuuviTagSQLite.firmwareVersionColumn.name, .text) diff --git a/Packages/RuuviOntology/Sources/RuuviOntologySQLite/RuuviTagSensorRecord+RuuviTagDataSQLite.swift b/Packages/RuuviOntology/Sources/RuuviOntologySQLite/RuuviTagSensorRecord+RuuviTagDataSQLite.swift index 17ca93aaa..be8fb7e8f 100644 --- a/Packages/RuuviOntology/Sources/RuuviOntologySQLite/RuuviTagSensorRecord+RuuviTagDataSQLite.swift +++ b/Packages/RuuviOntology/Sources/RuuviOntologySQLite/RuuviTagSensorRecord+RuuviTagDataSQLite.swift @@ -8,6 +8,7 @@ public extension RuuviTagSensorRecord { source: source, macId: macId, rssi: rssi, + version: version, temperature: temperature, humidity: humidity, pressure: pressure, @@ -30,6 +31,7 @@ public extension RuuviTagSensorRecord { source: source, macId: macId, rssi: rssi, + version: version, temperature: temperature, humidity: humidity, pressure: pressure, diff --git a/Packages/RuuviPersistence/Sources/RuuviPersistenceSQLite/RuuviPersistenceSQLite.swift b/Packages/RuuviPersistence/Sources/RuuviPersistenceSQLite/RuuviPersistenceSQLite.swift index 61ea2cc79..f8b36a5bd 100644 --- a/Packages/RuuviPersistence/Sources/RuuviPersistenceSQLite/RuuviPersistenceSQLite.swift +++ b/Packages/RuuviPersistence/Sources/RuuviPersistenceSQLite/RuuviPersistenceSQLite.swift @@ -36,6 +36,7 @@ public class RuuviPersistenceSQLite: RuuviPersistence, DatabaseService { id: ruuviTag.id, macId: ruuviTag.macId, luid: ruuviTag.luid, + serviceUUID: ruuviTag.serviceUUID, name: ruuviTag.name, version: ruuviTag.version, firmwareVersion: ruuviTag.firmwareVersion, @@ -394,6 +395,7 @@ public class RuuviPersistenceSQLite: RuuviPersistence, DatabaseService { id: ruuviTag.id, macId: ruuviTag.macId, luid: ruuviTag.luid, + serviceUUID: ruuviTag.serviceUUID, name: ruuviTag.name, version: ruuviTag.version, firmwareVersion: ruuviTag.firmwareVersion, @@ -426,6 +428,7 @@ public class RuuviPersistenceSQLite: RuuviPersistence, DatabaseService { id: ruuviTag.id, macId: ruuviTag.macId, luid: ruuviTag.luid, + serviceUUID: ruuviTag.serviceUUID, name: ruuviTag.name, version: ruuviTag.version, firmwareVersion: ruuviTag.firmwareVersion, diff --git a/Packages/RuuviService/Package.swift b/Packages/RuuviService/Package.swift index 693538860..6c3604282 100644 --- a/Packages/RuuviService/Package.swift +++ b/Packages/RuuviService/Package.swift @@ -67,7 +67,7 @@ let package = Package( dependencies: [ .package(url: "https://github.com/kean/Future", .exact("1.3.0")), .package(url: "https://github.com/rinat-enikeev/Humidity", from: "0.1.5"), - .package(url: "https://github.com/ruuvi/BTKit", .upToNextMinor(from: "0.4.3")), + .package(url: "https://github.com/ruuvi/BTKit", branch: "master"), .package(url: "https://github.com/ruuvi/xlsxwriter.swift", branch: "SPM"), .package(path: "../RuuviOntology"), .package(path: "../RuuviStorage"), diff --git a/Packages/RuuviService/Sources/RuuviServiceCloudSync/RuuviServiceCloudSyncImpl.swift b/Packages/RuuviService/Sources/RuuviServiceCloudSync/RuuviServiceCloudSyncImpl.swift index 032797eb7..530630b72 100644 --- a/Packages/RuuviService/Sources/RuuviServiceCloudSync/RuuviServiceCloudSyncImpl.swift +++ b/Packages/RuuviService/Sources/RuuviServiceCloudSync/RuuviServiceCloudSyncImpl.swift @@ -650,6 +650,12 @@ public final class RuuviServiceCloudSyncImpl: RuuviServiceCloudSync { return promise.future } + // First update the version number of the tag if there is a difference between + // cloud data and local data. + if cloudRecord.version > 0 && cloudRecord.version != ruuviTag.version { + ruuviPool.update(ruuviTag.with(version: cloudRecord.version)) + } + ruuviStorage.readLatest(ruuviTag).on(success: { [weak self] record in guard let sSelf = self else { return } // If the latest table already have a data point for the mac update that record diff --git a/project.yml b/project.yml index d850296c6..f96bbcdee 100644 --- a/project.yml +++ b/project.yml @@ -85,7 +85,7 @@ targetTemplates: packages: BTKit: url: https://github.com/ruuvi/BTKit - from: 0.5.2 + branch: "master" DGCharts: url: https://github.com/ruuvi/Charts from: 5.1.1