// // FirstViewController.swift // LoopFollow // // Created by Jon Fawcett on 6/1/20. // Copyright © 2020 Jon Fawcett. All rights reserved. // import UIKit import Charts class MainViewController: UIViewController, UITableViewDataSource { @IBOutlet weak var BGText: UILabel! @IBOutlet weak var DeltaText: UILabel! @IBOutlet weak var DirectionText: UILabel! @IBOutlet weak var BGChart: LineChartView! @IBOutlet weak var BGChartFull: LineChartView! @IBOutlet weak var MinAgoText: UILabel! @IBOutlet weak var infoTable: UITableView! //NS BG Struct struct sgvData: Codable { var sgv: Int var date: TimeInterval var direction: String? } // Data Table Struct struct infoData { var name: String var value: String } // Variables for BG Charts public var numPoints: Int = 13 public var linePlotData: [Double] = [] public var linePlotDataTime: [Double] = [] var firstStart: Bool = true // Vars for NS Pull var graphHours:Int=24 var mmol = false as Bool var urlUser = UserDefaultsRepository.url.value as String var token = "" as String var defaults : UserDefaults? let consoleLogging = true var timeofLastBGUpdate = 0 as TimeInterval var backgroundTask = BackgroundTask() // Refresh NS Data var timer = Timer() // check every 30 Seconds whether new bgvalues should be retrieved let timeInterval: TimeInterval = 30.0 // Info Table Setup var tableData = [ infoData(name: "IOB", value: ""), //0 infoData(name: "COB", value: ""), //1 infoData(name: "Basal", value: ""), //2 infoData(name: "Override", value: ""), //3 infoData(name: "Battery", value: ""), //4 infoData(name: "Pump", value: ""), //5 infoData(name: "Loop", value: ""), //6 infoData(name: "SAGE", value: ""), //7 infoData(name: "CAGE", value: "") //8 ] override func viewDidLoad() { super.viewDidLoad() UNUserNotificationCenter.current().requestAuthorization(options: .badge) { (granted, error) in if error != nil { // success! } } // ToDo: Should continue running in background // stop timer when app enters in background, start is again when becomes active let notificationCenter = NotificationCenter.default notificationCenter.addObserver(self, selector: #selector(appMovedToBackground), name: UIApplication.didEnterBackgroundNotification, object: nil) notificationCenter.addObserver(self, selector: #selector(appCameToForeground), name: UIApplication.willEnterForegroundNotification, object: nil) //Bind info data infoTable.rowHeight = 25 infoTable.dataSource = self } override func viewWillAppear(_ animated: Bool) { Reload() } // Info Table Functions func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return tableData.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "LabelCell", for: indexPath) let values = tableData[indexPath.row] cell.textLabel?.text = values.name cell.detailTextLabel?.text = values.value return cell } // Timer fileprivate func startTimer() { timer = Timer.scheduledTimer(timeInterval: timeInterval, target: self, selector: #selector(MainViewController.timerDidEnd(_:)), userInfo: nil, repeats: true) } @objc func appMovedToBackground() { // timer.invalidate() backgroundTask.startBackgroundTask() } @objc func appCameToForeground() { backgroundTask.stopBackgroundTask() //timer.invalidate() startTimer() loadBGData(urlUser: urlUser) loadDeviceStatus(urlUser: urlUser) } // check whether new Values should be retrieved @objc func timerDidEnd(_ timer:Timer) { print("timer ended") Reload() } func Reload() { loadBGData(urlUser: urlUser) clearLastInfoData() loadDeviceStatus(urlUser: urlUser) loadTempBasals(urlUser: urlUser) } // Main NS Data Pull func loadBGData(urlUser: String) { let points = String(self.graphHours * 12 + 1) var urlBGDataPath: String = urlUser + "/api/v1/entries/sgv.json?" if token == "" { urlBGDataPath = urlBGDataPath + "count=" + points } else { urlBGDataPath = urlBGDataPath + "token=" + token + "&count=" + points } guard let urlBGData = URL(string: urlBGDataPath) else { return } var request = URLRequest(url: urlBGData) request.cachePolicy = URLRequest.CachePolicy.reloadIgnoringLocalCacheData let getBGTask = URLSession.shared.dataTask(with: request) { data, response, error in if self.consoleLogging == true {print("start bg url")} guard error == nil else { return } guard let data = data else { return } let decoder = JSONDecoder() let entriesResponse = try? decoder.decode([sgvData].self, from: data) if let entriesResponse = entriesResponse { DispatchQueue.main.async { if self.backgroundTask.player.isPlaying { self.updateBadge(entries: entriesResponse) } else { self.updateBG(entries: entriesResponse) self.createGraph(entries: entriesResponse) } } } else { return } } getBGTask.resume() } //Clear the info data before next pull func clearLastInfoData(){ for i in 0.. String { let interval = Int(interval) let minutes = (interval / 60) % 60 let hours = (interval / 3600) return String(format: "%02d:%02d", hours, minutes) } func createGraph(entries: [sgvData]){ var bgChartEntry = [ChartDataEntry]() var colors = [NSUIColor]() for i in 0.. 180 { colors.append(NSUIColor.yellow) } else if Double(entries[entries.count - 1 - i].sgv) < 70 { colors.append(NSUIColor.red) } else { colors.append(NSUIColor.green) } } let line1 = LineChartDataSet(entries:bgChartEntry, label: "") line1.circleRadius = 3 line1.circleColors = [NSUIColor.green] line1.drawCircleHoleEnabled = false if UserDefaultsRepository.showLines.value { line1.lineWidth = 2 } else { line1.lineWidth = 0 } if UserDefaultsRepository.showDots.value { line1.drawCirclesEnabled = true } else { line1.drawCirclesEnabled = false } line1.setDrawHighlightIndicators(false) line1.valueFont.withSize(50) for i in 1.. 0 { let latestBG = entries[0].sgv UIApplication.shared.applicationIconBadgeNumber = latestBG } print("updated badge") } func updateBG (entries: [sgvData]) { if consoleLogging == true {print("in update BG")} UIApplication.shared.applicationIconBadgeNumber = 0 if entries.count > 0 { let latestBG = entries[0].sgv let priorBG = entries[1].sgv let deltaBG = latestBG - priorBG as Int let lastBGTime = entries[0].date / 1000 //NS has different units let deltaTime = (TimeInterval(Date().timeIntervalSince1970)-lastBGTime) / 60 var userUnit = " mg/dL" if mmol { userUnit = " mmol/L" } UIApplication.shared.applicationIconBadgeNumber = latestBG BGText.text = bgOutputFormat(bg: Double(latestBG), mmol: mmol) MinAgoText.text = String(Int(deltaTime)) + " min ago" print(String(Int(deltaTime)) + " min ago") if let directionBG = entries[0].direction { DirectionText.text = bgDirectionGraphic(directionBG) } else { DirectionText.text = "" } if deltaBG < 0 { self.DeltaText.text = String(deltaBG) } else { self.DeltaText.text = "+" + String(deltaBG) } } else { return } if consoleLogging == true {print("end update bg")} } func bgOutputFormat(bg: Double, mmol: Bool) -> String { if !mmol { return String(format:"%.0f", bg) } else { return String(format:"%.1f", bg / 18.0) } } func bgDirectionGraphic(_ value:String)->String { let graphics:[String:String]=["Flat":"\u{2192}","DoubleUp":"\u{21C8}","SingleUp":"\u{2191}","FortyFiveUp":"\u{2197}\u{FE0E}","FortyFiveDown":"\u{2198}\u{FE0E}","SingleDown":"\u{2193}","DoubleDown":"\u{21CA}","None":"-","NOT COMPUTABLE":"-","RATE OUT OF RANGE":"-"] return graphics[value]! } }