react-native-mytatva-rn-sdk 1.2.37 → 1.2.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ios/MyReactNativeBridge.m +294 -0
- package/ios/TableViewCell/VIdeoTVC/VideoTVC.swift +31 -0
- package/ios/ViewControllers/ConnectToSensorViewController.swift +415 -0
- package/ios/ViewControllers/StartConnectionViewController.swift +219 -0
- package/ios/ViewModel/FinalViewModel.swift +648 -0
- package/package.json +1 -1
|
@@ -0,0 +1,648 @@
|
|
|
1
|
+
//
|
|
2
|
+
// FinalViewModel.swift
|
|
3
|
+
// MyTatvaCare
|
|
4
|
+
//
|
|
5
|
+
// Created by Nirav Ramani on 31/05/25.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
import Foundation
|
|
9
|
+
@objc public class FinalViewModelManager: NSObject {
|
|
10
|
+
@objc public static let shared = FinalViewModelManager()
|
|
11
|
+
@objc let viewModel = FinalViewModel()
|
|
12
|
+
|
|
13
|
+
private override init() {
|
|
14
|
+
super.init()
|
|
15
|
+
viewModel.initialize()
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// @objc public func callForObserveTransmitterUnbindStatus() {
|
|
19
|
+
// let sensorID = UserDefaults.standard.string(forKey: "sensorId") ?? ""
|
|
20
|
+
// if !sensorID.isEmpty {
|
|
21
|
+
// viewModel.getStatus()
|
|
22
|
+
// }
|
|
23
|
+
// }
|
|
24
|
+
|
|
25
|
+
@objc public func callForObserveTransmitterUnbindStatusWithCompletion(
|
|
26
|
+
_ completion: @escaping (_ finalResponse: [String: Any]?, _ error: Error?) -> Void
|
|
27
|
+
) {
|
|
28
|
+
// Load JSON data from UserDefaults
|
|
29
|
+
guard let data = UserDefaults.standard.data(forKey: "CGMStatusItem") else {
|
|
30
|
+
let error = NSError(
|
|
31
|
+
domain: "SensorData",
|
|
32
|
+
code: 404,
|
|
33
|
+
userInfo: [NSLocalizedDescriptionKey: "No cached response found"]
|
|
34
|
+
)
|
|
35
|
+
completion(nil, error)
|
|
36
|
+
return
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
do {
|
|
40
|
+
let json = try JSONSerialization.jsonObject(with: data, options: [])
|
|
41
|
+
// Forward to actual handler, which includes a safety check
|
|
42
|
+
self.callForObserveTransmitterUnbindStatusWithResponse(json, completion: completion)
|
|
43
|
+
} catch {
|
|
44
|
+
completion(nil, NSError(domain: "SensorData", code: 405, userInfo: [NSLocalizedDescriptionKey: "Failed to parse cached JSON"]))
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@objc public func callForObserveTransmitterUnbindStatusWithResponse(
|
|
50
|
+
_ response: Any, // Changed from [String: Any] to Any to add type safety
|
|
51
|
+
completion: @escaping (_ finalResponse: [String: Any]?, _ error: Error?) -> Void
|
|
52
|
+
) {
|
|
53
|
+
// Ensure response is a dictionary
|
|
54
|
+
guard let responseDict = response as? [String: Any] else {
|
|
55
|
+
let error = NSError(
|
|
56
|
+
domain: "SensorData",
|
|
57
|
+
code: 406,
|
|
58
|
+
userInfo: [NSLocalizedDescriptionKey: "Response is not a dictionary"]
|
|
59
|
+
)
|
|
60
|
+
completion(nil, error)
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
guard let status = responseDict["status"] as? String, status == "success",
|
|
65
|
+
let dataArray = responseDict["data"] as? [[String: Any]],
|
|
66
|
+
let data = dataArray.first else {
|
|
67
|
+
let error = NSError(
|
|
68
|
+
domain: "SensorData",
|
|
69
|
+
code: 400,
|
|
70
|
+
userInfo: [NSLocalizedDescriptionKey: "Invalid status or missing data"]
|
|
71
|
+
)
|
|
72
|
+
completion(nil, error)
|
|
73
|
+
return
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
guard let sensorId = data["sensorId"] as? String,
|
|
77
|
+
let startDate = data["startDate"] as? String,
|
|
78
|
+
let endDate = data["endDate"] as? String,
|
|
79
|
+
let statusValue = data["status"] as? String,
|
|
80
|
+
let daysToExpire = data["daysToExpire"] as? Int else {
|
|
81
|
+
let error = NSError(
|
|
82
|
+
domain: "SensorData",
|
|
83
|
+
code: 401,
|
|
84
|
+
userInfo: [NSLocalizedDescriptionKey: "Missing or invalid sensor data in 'data'"]
|
|
85
|
+
)
|
|
86
|
+
completion(nil, error)
|
|
87
|
+
return
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
let baseResponse: [String: Any] = [
|
|
91
|
+
"sensorId": sensorId,
|
|
92
|
+
"startDate": startDate,
|
|
93
|
+
"endDate": endDate,
|
|
94
|
+
"status": statusValue,
|
|
95
|
+
"daysToExpire": daysToExpire
|
|
96
|
+
]
|
|
97
|
+
|
|
98
|
+
let finalResponse = self.evaluateUnbindStatus(response: baseResponse, startDate: startDate, endDate: endDate)
|
|
99
|
+
completion(finalResponse, nil)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// MARK: - Main Logic
|
|
103
|
+
@objc public func evaluateUnbindStatus(
|
|
104
|
+
response: [String: Any],
|
|
105
|
+
startDate: String,
|
|
106
|
+
endDate: String
|
|
107
|
+
) -> [String: Any] {
|
|
108
|
+
let updatedResponse = response
|
|
109
|
+
|
|
110
|
+
if viewModel.isCurrentDateInRange(startDate: startDate, endDate: endDate) {
|
|
111
|
+
print("Current date is in range")
|
|
112
|
+
if ((viewModel.manager.connectedPeripheral == nil) && !KLTLocalSettingManager.shareInstance().canConnectOtherDevice) {
|
|
113
|
+
viewModel.manager.startScan()
|
|
114
|
+
viewModel.debouncer.update(with: .disconnected)
|
|
115
|
+
} else {
|
|
116
|
+
print("here")
|
|
117
|
+
//debouncer.update(with: .connected)
|
|
118
|
+
//API.shared.sendStatus(status: .connected) 计算 算法入参
|
|
119
|
+
if let last = viewModel.manager.currentDevice { }
|
|
120
|
+
else {
|
|
121
|
+
//self.manager.closeBleSensor()
|
|
122
|
+
//manager.startScan()
|
|
123
|
+
//self.debouncer.update(with: .disconnected)
|
|
124
|
+
//debouncer.update(with: .transmitterDisconnectBox)
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
// if KLTBluetoothManager.shared().currentDevice.initialEndDate != nil {
|
|
128
|
+
//
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return updatedResponse
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
class FinalViewModel: NSObject {
|
|
136
|
+
var initialPeriod = 0
|
|
137
|
+
var totalSeconds : TimeInterval = 60 * 60
|
|
138
|
+
let manager: KLTBluetoothManager!
|
|
139
|
+
var countdownTimer: Timer?
|
|
140
|
+
var startScanTimer: Timer?
|
|
141
|
+
let debouncer = EnumDebouncer<CGMConnectionStatus>()
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
@objc public override init() {
|
|
145
|
+
self.manager = KLTBluetoothManager.shared()
|
|
146
|
+
super.init()
|
|
147
|
+
debouncer.onDebounceSuccess = { value in
|
|
148
|
+
API.shared.sendStatus(status: value)
|
|
149
|
+
print("Same type held for an hour!")
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
@objc public func initialize() {
|
|
154
|
+
//getStatus()
|
|
155
|
+
startCountDown()
|
|
156
|
+
|
|
157
|
+
if ((manager.connectedPeripheral == nil) && !KLTLocalSettingManager.shareInstance().canConnectOtherDevice) {
|
|
158
|
+
manager.startScan()
|
|
159
|
+
debouncer.update(with: .disconnected)
|
|
160
|
+
} else {
|
|
161
|
+
print("here")
|
|
162
|
+
//debouncer.update(with: .connected)
|
|
163
|
+
//API.shared.sendStatus(status: .connected) 计算 算法入参
|
|
164
|
+
if let last = manager.currentDevice { }
|
|
165
|
+
else {
|
|
166
|
+
//self.manager.closeBleSensor()
|
|
167
|
+
//manager.startScan()
|
|
168
|
+
debouncer.update(with: .disconnected)
|
|
169
|
+
//debouncer.update(with: .transmitterDisconnectBox)
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
func getStatus(encryptionKey: String = PROD_ENC_KEY,
|
|
175
|
+
encryptionIv: String = PROD_ENC_IV) {
|
|
176
|
+
print("ios token:", TOKEN)
|
|
177
|
+
let url = URL(string: "https://api-feature2.mytatva.in/api/v8/cgm/status")!
|
|
178
|
+
var request = URLRequest(url: url)
|
|
179
|
+
request.httpMethod = "GET"
|
|
180
|
+
|
|
181
|
+
// Set headers
|
|
182
|
+
//request.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
183
|
+
request.addValue(PROD_API_KEY, forHTTPHeaderField: "api-key")
|
|
184
|
+
request.addValue(TOKEN, forHTTPHeaderField: "token")
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
// Set request body
|
|
188
|
+
|
|
189
|
+
// Perform request
|
|
190
|
+
let task = URLSession.shared.dataTask(with: request) { data, response, error in
|
|
191
|
+
if let error = error {
|
|
192
|
+
print("Error: \(error)")
|
|
193
|
+
return
|
|
194
|
+
}
|
|
195
|
+
guard let data = data else {
|
|
196
|
+
print("No data received")
|
|
197
|
+
return
|
|
198
|
+
}
|
|
199
|
+
if let responseString = String(data: data, encoding: .utf8) {
|
|
200
|
+
print("===>Server Response: \(responseString)")
|
|
201
|
+
if let decrypted = Crypto.shared.getDecryptedData(cipherText: responseString, encryptionKey: encryptionKey, encryptionIv: encryptionIv) {
|
|
202
|
+
print("===>Decrypted response (for verification): \(decrypted)")
|
|
203
|
+
|
|
204
|
+
if let decrptedData = decrypted.data(using: .utf8) {
|
|
205
|
+
let decoded = try? JSONDecoder().decode(CGMStatusResponse.self, from: decrptedData)
|
|
206
|
+
|
|
207
|
+
guard let sensor = decoded?.data.first,
|
|
208
|
+
!sensor.startDate.isEmpty,
|
|
209
|
+
!sensor.endDate.isEmpty else {
|
|
210
|
+
print("No valid sensor data")
|
|
211
|
+
return
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
let startDateStr = sensor.startDate
|
|
215
|
+
let endDateStr = sensor.endDate
|
|
216
|
+
|
|
217
|
+
print("Start Date: \(startDateStr)")
|
|
218
|
+
print("End Date: \(endDateStr)")
|
|
219
|
+
UserDefaults.standard.set(sensor.sensorId, forKey: "sensorId")
|
|
220
|
+
if self.isCurrentDateInRange(startDate: startDateStr, endDate: endDateStr) {
|
|
221
|
+
print("Current date is in range")
|
|
222
|
+
if ((self.manager.connectedPeripheral == nil) && !KLTLocalSettingManager.shareInstance().canConnectOtherDevice) {
|
|
223
|
+
self.manager.startScan()
|
|
224
|
+
self.debouncer.update(with: .disconnected)
|
|
225
|
+
} else {
|
|
226
|
+
print("here")
|
|
227
|
+
//debouncer.update(with: .connected)
|
|
228
|
+
//API.shared.sendStatus(status: .connected) 计算 算法入参
|
|
229
|
+
if let last = self.manager.currentDevice { }
|
|
230
|
+
else {
|
|
231
|
+
//self.manager.closeBleSensor()
|
|
232
|
+
//manager.startScan()
|
|
233
|
+
//self.debouncer.update(with: .disconnected)
|
|
234
|
+
//debouncer.update(with: .transmitterDisconnectBox)
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
// if KLTBluetoothManager.shared().currentDevice.initialEndDate != nil {
|
|
238
|
+
// self.sendStatus(status: .transmitterDisconnect)
|
|
239
|
+
// }
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
task.resume()
|
|
246
|
+
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
func isCurrentDateInRange(startDate: String, endDate: String) -> Bool {
|
|
250
|
+
let formatter = DateFormatter()
|
|
251
|
+
formatter.dateFormat = "yyyy-MM-dd"
|
|
252
|
+
guard let start = formatter.date(from: startDate),
|
|
253
|
+
let end = formatter.date(from: endDate) else {
|
|
254
|
+
return false
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
let now = Date()
|
|
258
|
+
return (now >= start) && (now <= end)
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
func startCountDown() {
|
|
262
|
+
|
|
263
|
+
// Most recently connected device
|
|
264
|
+
if let last = manager.currentDevice {
|
|
265
|
+
let eDevice = EDevice.getEnumDevice(last.advertise?.localName ?? "")
|
|
266
|
+
initialPeriod = Int(eDevice.initNumber * 3 * 60)
|
|
267
|
+
// Calculate initialization countdown time
|
|
268
|
+
if let beginDate = KLTDateFormatter.shared.getDateFromWholeString(last.initialBeginDate ?? "") {
|
|
269
|
+
let time = Date().timeIntervalSince(beginDate)
|
|
270
|
+
if Int(time) > self.initialPeriod {
|
|
271
|
+
// Initialization completed, normal use
|
|
272
|
+
|
|
273
|
+
} else {
|
|
274
|
+
|
|
275
|
+
// During initialization, notify the initialization page to restart the countdown
|
|
276
|
+
totalSeconds = Double(initialPeriod) - TimeInterval(time)
|
|
277
|
+
|
|
278
|
+
//for countdownlabel
|
|
279
|
+
print(String.getTimeString(with: Int(totalSeconds)))
|
|
280
|
+
|
|
281
|
+
if countdownTimer != nil {
|
|
282
|
+
countdownTimer?.invalidate()
|
|
283
|
+
countdownTimer = nil
|
|
284
|
+
}
|
|
285
|
+
countdownTimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(countdown), userInfo: nil, repeats: true)
|
|
286
|
+
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
} else {
|
|
290
|
+
// no device is connected
|
|
291
|
+
print("no device is connected")
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
addAllObservers()
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
func addAllObservers() {
|
|
298
|
+
manager.addObserver(self, forKeyPath: "status", options: .new, context: nil)
|
|
299
|
+
|
|
300
|
+
NotificationCenter.default.addObserver(self, selector: #selector(bluetoothEnable(_:)), name: NSNotification.Name("BluetoothEnable"), object: nil)
|
|
301
|
+
|
|
302
|
+
NotificationCenter.default.addObserver(self, selector: #selector(errorStatusFromTransmitter(_:)), name: NSNotification.Name("ErrorStatusFromTransmitter"), object: nil)
|
|
303
|
+
|
|
304
|
+
NotificationCenter.default.addObserver(self, selector: #selector(handleLatestReceiveData(_:)), name: NSNotification.Name(KLTAlertCurrentInInitialNotify), object: nil)
|
|
305
|
+
|
|
306
|
+
NotificationCenter.default.addObserver(self, selector: #selector(updateData(_:)), name: NSNotification.Name(KLTUpdateDataNotify), object: nil)
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
func removeAllObservers() {
|
|
310
|
+
manager.removeObserver(self, forKeyPath: "status")
|
|
311
|
+
|
|
312
|
+
NotificationCenter.default.removeObserver(self, name: NSNotification.Name("BluetoothEnable"), object: nil)
|
|
313
|
+
|
|
314
|
+
NotificationCenter.default.removeObserver(self, name: NSNotification.Name("ErrorStatusFromTransmitter"), object: nil)
|
|
315
|
+
|
|
316
|
+
NotificationCenter.default.removeObserver(self, name: NSNotification.Name(KLTAlertCurrentInInitialNotify), object: nil)
|
|
317
|
+
|
|
318
|
+
NotificationCenter.default.removeObserver(self, name: NSNotification.Name(KLTUpdateDataNotify), object: nil)
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
func uploadData(data: [ReceiveData]) {
|
|
323
|
+
let batchSize = 40
|
|
324
|
+
let batches = stride(from: 0, to: data.count, by: batchSize).map {
|
|
325
|
+
Array(data[$0..<min($0 + batchSize, data.count)])
|
|
326
|
+
}
|
|
327
|
+
uploadBatch(batches: batches, index: 0)
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
private func uploadBatch(batches: [[ReceiveData]], index: Int) {
|
|
331
|
+
guard index < batches.count else {
|
|
332
|
+
print("✅ All batches uploaded")
|
|
333
|
+
return
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
let batch = batches[index]
|
|
337
|
+
let cgmLogs = batch.map { ReceiveDataToLog(data: $0) }
|
|
338
|
+
let payload = Payload(logs: cgmLogs)
|
|
339
|
+
|
|
340
|
+
API.shared.postCGMData(environment: .stage, data: payload) {
|
|
341
|
+
print("✅ Batch \(index + 1) uploaded successfully")
|
|
342
|
+
KLTDatabaseHandler.shared().deleteReceiveDataArray(batch)
|
|
343
|
+
self.uploadBatch(batches: batches, index: index + 1)
|
|
344
|
+
} onFailure: { error in
|
|
345
|
+
print("❌ Failed to upload batch \(index + 1): \(String(describing: error?.localizedDescription))")
|
|
346
|
+
// Optionally retry or stop here
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
@objc func updateData(_ notification: Notification) {
|
|
351
|
+
let data = KLTDatabaseHandler.shared().queryAllReceiveData() as! [ReceiveData]
|
|
352
|
+
print(data)
|
|
353
|
+
print("===> all data count: ", data.count)
|
|
354
|
+
KLTDatabaseHandler.shared().reloadQueryHistoryData()
|
|
355
|
+
if let device = KLTBluetoothManager.shared().currentDevice {
|
|
356
|
+
let arrayFromInitial = KLTDatabaseHandler.shared().queryReceiveData(with: device, needUserBG: true) as! [ReceiveData]
|
|
357
|
+
print(arrayFromInitial)
|
|
358
|
+
print("===> arrayFromInitial count: ", arrayFromInitial.count)
|
|
359
|
+
self.uploadData(data: arrayFromInitial)
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
@objc func errorStatusFromTransmitter(_ notification: Notification) {
|
|
364
|
+
guard let receiveData = notification.object as? ReceiveData else { return }
|
|
365
|
+
|
|
366
|
+
let errorCode = ErrorCode(rawValue: Int32(receiveData.error?.intValue ?? 0))
|
|
367
|
+
if errorCode == ErrorCode.ERROR_CODE_FLOODING_WATER {
|
|
368
|
+
debouncer.update(with: CGMConnectionStatus.moistureDetect)
|
|
369
|
+
//API.shared.sendStatus(status: .moistureDetect)
|
|
370
|
+
} else if errorCode == ErrorCode.ERROR_CODE_CURRENT_SMALL || errorCode == ErrorCode.ERROR_CODE_NOISE || errorCode == ErrorCode.ERROR_CODE_SENSITIVITY_ATTENUATION {
|
|
371
|
+
debouncer.update(with: CGMConnectionStatus.weakSignal)
|
|
372
|
+
//API.shared.sendStatus(status: .weakSignal)
|
|
373
|
+
} else if errorCode != ErrorCode.ERROR_CODE_NONE {
|
|
374
|
+
debouncer.update(with: CGMConnectionStatus.errorCommon)
|
|
375
|
+
//API.shared.sendStatus(status: .errorCommon)
|
|
376
|
+
}
|
|
377
|
+
if receiveData.countdownDays == 0 && receiveData.countdownHours == 0 && receiveData.countdownMinutes == 0 {
|
|
378
|
+
debouncer.update(with: CGMConnectionStatus.expired)
|
|
379
|
+
//API.shared.sendStatus(status: .expired)
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
@objc func handleLatestReceiveData(_ notification: Notification) {
|
|
384
|
+
print("====> handleLatestReceiveData")
|
|
385
|
+
guard let receiveData = notification.object as? ReceiveData else { return }
|
|
386
|
+
|
|
387
|
+
let errorCode = ErrorCode(rawValue: Int32(receiveData.error?.intValue ?? 0))
|
|
388
|
+
|
|
389
|
+
let hasExceptionCurrent: Bool = (errorCode == ErrorCode.ERROR_CODE_CURRENT_SMALL || errorCode == ErrorCode.ERROR_CODE_NOISE ||
|
|
390
|
+
errorCode == ErrorCode.ERROR_CODE_SENSITIVITY_ATTENUATION || errorCode == ErrorCode.ERROR_CODE_TAKE_OFF ||
|
|
391
|
+
errorCode == ErrorCode.ERROR_CODE_BREAKAGE || errorCode == ErrorCode.ERROR_CODE_TOUCH ||
|
|
392
|
+
errorCode == ErrorCode.ERROR_CODE_FLOODING_WATER)
|
|
393
|
+
|
|
394
|
+
if hasExceptionCurrent {
|
|
395
|
+
guard let startDate = KLTFormat.getDate(from: manager.currentDevice.initialBeginDate ?? "") else { return }
|
|
396
|
+
|
|
397
|
+
// Get current UTC date from local date string
|
|
398
|
+
let now = Date()
|
|
399
|
+
let nowString = KLTDateFormatter.shared.getWholeString(from: now)
|
|
400
|
+
let utcDateString = KLTDateFormatter.shared.getUTCFormatDate(from: nowString) ?? ""
|
|
401
|
+
guard let currentDate = KLTDateFormatter.shared.getDateFromWholeString(utcDateString) else { return }
|
|
402
|
+
|
|
403
|
+
let seconds = Calendar(identifier: .gregorian)
|
|
404
|
+
.dateComponents([.second], from: startDate, to: currentDate).second ?? 0
|
|
405
|
+
|
|
406
|
+
let minutes = seconds / 60
|
|
407
|
+
if minutes > 17 {
|
|
408
|
+
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
@objc func bluetoothEnable(_ notification: Notification) {
|
|
414
|
+
if let isBluetoothEnabled = notification.object as? Bool {
|
|
415
|
+
if !isBluetoothEnabled {
|
|
416
|
+
debouncer.update(with: CGMConnectionStatus.bluetoothOff)
|
|
417
|
+
//API.shared.sendStatus(status: .bluetoothOff)
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
if ((manager.connectedPeripheral == nil) && !KLTLocalSettingManager.shareInstance().canConnectOtherDevice) {
|
|
421
|
+
manager.startScan()
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
@objc func countdown() {
|
|
426
|
+
if let last = KLTDatabaseHandler.shared().isLatestDeviceDisconnected() {
|
|
427
|
+
if let beginDate = KLTDateFormatter.shared.getDateFromWholeString(last.initialBeginDate ?? "") {
|
|
428
|
+
let time = Date().timeIntervalSince(beginDate)
|
|
429
|
+
totalSeconds = TimeInterval(3600 - Int(time))
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
if totalSeconds <= 0 {
|
|
434
|
+
countdownTimer?.invalidate()
|
|
435
|
+
countdownTimer = nil
|
|
436
|
+
|
|
437
|
+
startScanTimer?.invalidate()
|
|
438
|
+
startScanTimer = nil
|
|
439
|
+
|
|
440
|
+
//removeAllObservers()
|
|
441
|
+
|
|
442
|
+
//KLTLaunchConfiguration.sharedManager.resetRootViewController(type: .normalUsed)
|
|
443
|
+
return
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
totalSeconds -= 1
|
|
447
|
+
print(String.getTimeString(with: Int(totalSeconds)))
|
|
448
|
+
//countDownValueLabel.text =
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
override func observeValue(
|
|
452
|
+
forKeyPath keyPath: String?,
|
|
453
|
+
of object: Any?,
|
|
454
|
+
change: [NSKeyValueChangeKey : Any]?,
|
|
455
|
+
context: UnsafeMutableRawPointer?
|
|
456
|
+
) {
|
|
457
|
+
guard keyPath == "status",
|
|
458
|
+
let newValue = change?[.newKey] as? Int,
|
|
459
|
+
let status = BluetoothManagerStatus(rawValue: newValue) else {
|
|
460
|
+
return
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
print("===>device status is -> \(status)")
|
|
464
|
+
|
|
465
|
+
switch status {
|
|
466
|
+
|
|
467
|
+
case .disconnected:
|
|
468
|
+
if KLTLocalSettingManager.shareInstance().canConnectOtherDevice {
|
|
469
|
+
// Unbind then disconnect
|
|
470
|
+
endBleSensorCycle()
|
|
471
|
+
} else {
|
|
472
|
+
if UserDefaults.standard.integer(forKey: "bgmode") != 1 {
|
|
473
|
+
manager.startScan()
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
//API.shared.sendStatus(status: .disconnected)
|
|
477
|
+
print("===>device is disconnected")
|
|
478
|
+
debouncer.update(with: CGMConnectionStatus.disconnected)
|
|
479
|
+
case .tryToConnect:
|
|
480
|
+
// if UserDefaults.standard.integer(forKey: "bgmode") != 1 {
|
|
481
|
+
// DispatchQueue.main.asyncAfter(deadline: .now() + 20) {
|
|
482
|
+
// self.manager.startScan()
|
|
483
|
+
// }
|
|
484
|
+
// }
|
|
485
|
+
break
|
|
486
|
+
case .connected:
|
|
487
|
+
if let timer = startScanTimer {
|
|
488
|
+
timer.invalidate()
|
|
489
|
+
startScanTimer = nil
|
|
490
|
+
}
|
|
491
|
+
//API.shared.sendStatus(status: .connected)
|
|
492
|
+
print("===>device is connected")
|
|
493
|
+
debouncer.update(with: CGMConnectionStatus.connected)
|
|
494
|
+
case .timeOut:
|
|
495
|
+
// if !KLTLocalSettingManager.shareInstance().canConnectOtherDevice {
|
|
496
|
+
// startScanTimer = Timer.scheduledTimer(timeInterval: 20,
|
|
497
|
+
// target: self,
|
|
498
|
+
// selector: #selector(startScan),
|
|
499
|
+
// userInfo: nil,
|
|
500
|
+
// repeats: false)
|
|
501
|
+
// }
|
|
502
|
+
print("===>time out")
|
|
503
|
+
break
|
|
504
|
+
case .sensorLostPower:
|
|
505
|
+
sensorLostPower()
|
|
506
|
+
print("===>sensor lost power")
|
|
507
|
+
case .updateBindWatchSuccess:
|
|
508
|
+
//KLTLaunchConfiguration.sharedManager.bindSuccessRootViewController()
|
|
509
|
+
startCountDown()
|
|
510
|
+
print("===>Success done")
|
|
511
|
+
case .closed:
|
|
512
|
+
debouncer.update(with: CGMConnectionStatus.transmitterDisconnectBox)
|
|
513
|
+
//API.shared.sendStatus(status: .transmitterDisconnectBox)
|
|
514
|
+
print("===>closed")
|
|
515
|
+
default:
|
|
516
|
+
break
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
func sensorLostPower() {
|
|
521
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
|
|
522
|
+
// Will trigger `BluetoothManagerStatusDisconnected` due to forced disconnection
|
|
523
|
+
self.manager.forceEndBleSensor()
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
@objc func startScan() {
|
|
528
|
+
manager.startScan()
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
func endBleSensorCycle() {
|
|
532
|
+
if let timer = countdownTimer {
|
|
533
|
+
timer.invalidate()
|
|
534
|
+
countdownTimer = nil
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
removeAllObservers()
|
|
538
|
+
|
|
539
|
+
print("initial State all end")
|
|
540
|
+
// KLTLaunchConfiguration.sharedManager.resetRootViewController(.beforeUse)
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
|
|
545
|
+
struct KLTDateFormatter {
|
|
546
|
+
static let shared = KLTDateFormatter()
|
|
547
|
+
|
|
548
|
+
private let calendar = Calendar(identifier: .gregorian)
|
|
549
|
+
private let locale = Locale(identifier: "en_GB")
|
|
550
|
+
|
|
551
|
+
private func configuredFormatter(withFormat format: String, timeZone: TimeZone? = nil) -> DateFormatter {
|
|
552
|
+
let formatter = DateFormatter()
|
|
553
|
+
formatter.calendar = calendar
|
|
554
|
+
formatter.locale = locale
|
|
555
|
+
formatter.dateFormat = format
|
|
556
|
+
if let tz = timeZone {
|
|
557
|
+
formatter.timeZone = tz
|
|
558
|
+
}
|
|
559
|
+
return formatter
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
func getWholeString(from date: Date) -> String {
|
|
563
|
+
return configuredFormatter(withFormat: "yyyy-MM-dd HH:mm:ss").string(from: date)
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
func getDayString(from date: Date) -> String {
|
|
567
|
+
return configuredFormatter(withFormat: "MM-dd").string(from: date)
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
func getHourString(from date: Date) -> String {
|
|
571
|
+
let hour = configuredFormatter(withFormat: "HH").string(from: date)
|
|
572
|
+
return "\(hour):00"
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
func getMinuteString(from date: Date) -> String {
|
|
576
|
+
return configuredFormatter(withFormat: "HH:mm").string(from: date)
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
func getMinuteDate(from string: String) -> Date? {
|
|
580
|
+
return configuredFormatter(withFormat: "HH:mm").date(from: string)
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
func getHourDate(from string: String) -> Date? {
|
|
584
|
+
return configuredFormatter(withFormat: "HH:00").date(from: string)
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
func getDayDate(from string: String) -> Date? {
|
|
588
|
+
return configuredFormatter(withFormat: "MM-dd").date(from: string)
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
func getDateTime(from string: String) -> Date? {
|
|
592
|
+
return configuredFormatter(withFormat: "yyyy-MM-dd HH:mm").date(from: string)
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
func getDateFromWholeString(_ string: String) -> Date? {
|
|
596
|
+
return configuredFormatter(withFormat: "yyyy-MM-dd HH:mm:ss").date(from: string)
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
func getUTCFormatDate(from localDate: String) -> String? {
|
|
600
|
+
guard let date = getDateFromWholeString(localDate) else { return nil }
|
|
601
|
+
return configuredFormatter(withFormat: "yyyy-MM-dd HH:mm:ss", timeZone: TimeZone(abbreviation: "UTC")).string(from: date)
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
func getLocalFromUTC(_ utc: String) -> String? {
|
|
605
|
+
let formatter = configuredFormatter(withFormat: "yyyy-MM-dd HH:mm:ss", timeZone: TimeZone.current)
|
|
606
|
+
guard let date = formatter.date(from: utc) else { return nil }
|
|
607
|
+
return getWholeString(from: date)
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
extension String {
|
|
611
|
+
func klt_contains(_ other: String) -> Bool {
|
|
612
|
+
return self.range(of: other) != nil
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
func klt_hasValidTransmit() -> Bool {
|
|
616
|
+
guard self.count >= 2 else { return false }
|
|
617
|
+
let lastHex = String(self.suffix(2))
|
|
618
|
+
return lastHex == "00"
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
func reverseToString(groupSize: Int) -> String {
|
|
622
|
+
guard groupSize > 0 && self.count > groupSize else { return self }
|
|
623
|
+
|
|
624
|
+
var result = ""
|
|
625
|
+
var index = self.count
|
|
626
|
+
|
|
627
|
+
while index >= groupSize {
|
|
628
|
+
let start = self.index(self.startIndex, offsetBy: index - groupSize)
|
|
629
|
+
let end = self.index(self.startIndex, offsetBy: index)
|
|
630
|
+
result += self[start..<end]
|
|
631
|
+
index -= groupSize
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
if index > 0 {
|
|
635
|
+
let leftover = self.prefix(index)
|
|
636
|
+
result += leftover
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
return result
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
static func getTimeString(with count: Int) -> String {
|
|
643
|
+
let hours = String(format: "%02d", count / 3600)
|
|
644
|
+
let minutes = String(format: "%02d", (count % 3600) / 60)
|
|
645
|
+
let seconds = String(format: "%02d", count % 60)
|
|
646
|
+
return "\(hours):\(minutes):\(seconds)"
|
|
647
|
+
}
|
|
648
|
+
}
|