-
-
Notifications
You must be signed in to change notification settings - Fork 60
Enhance RealtimeIrl by adding the missing metrics #189
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,6 +5,49 @@ class RealtimeIrl { | |
| private let pushUrl: URL | ||
| private let stopUrl: URL | ||
| private var updateCount = 0 | ||
| private var pedometerStepsWatch: (value: Int, date: Date)? | ||
| private var pedometerStepsDevice: (value: Int, date: Date)? | ||
| private var heartRateWatch: (value: Int, date: Date)? | ||
| private var heartRateDevice: (value: Int, date: Date)? | ||
| private var cyclingPowerWatch: (value: Int, date: Date)? | ||
| private var cyclingPowerDevice: (value: Int, date: Date)? | ||
| private var cyclingCrankWatch: (value: Int, date: Date)? | ||
| private var cyclingCrankDevice: (value: Int, date: Date)? | ||
| private var cyclingWheelWatch: (value: Int, date: Date)? | ||
| private var cyclingWheelDevice: (value: Int, date: Date)? | ||
|
|
||
| private struct Payload: Encodable { | ||
| let latitude: Double | ||
| let longitude: Double | ||
| let speed: Double | ||
| let altitude: Double | ||
| let heading: CLLocationDirection? | ||
| let timestamp: TimeInterval | ||
| let pedometerSteps: Int? | ||
| let heartRate: Int? | ||
| let cyclingPower: Int? | ||
| let cyclingCrank: Int? | ||
| let cyclingWheel: Int? | ||
| } | ||
|
|
||
| private func watchFirst( | ||
| watch: (value: Int, date: Date)?, | ||
| device: (value: Int, date: Date)? | ||
| ) -> Int? { | ||
| if let watch { | ||
| return watch.value | ||
| } | ||
| if let device { | ||
| return device.value | ||
| } | ||
| return nil | ||
| } | ||
|
|
||
| private var pedometerSteps: Int? { watchFirst(watch: pedometerStepsWatch, device: pedometerStepsDevice) } | ||
| private var heartRate: Int? { watchFirst(watch: heartRateWatch, device: heartRateDevice) } | ||
| private var cyclingPower: Int? { watchFirst(watch: cyclingPowerWatch, device: cyclingPowerDevice) } | ||
| private var cyclingCrank: Int? { watchFirst(watch: cyclingCrankWatch, device: cyclingCrankDevice) } | ||
| private var cyclingWheel: Int? { watchFirst(watch: cyclingWheelWatch, device: cyclingWheelDevice) } | ||
|
|
||
| init?(baseUrl: String, pushKey: String) { | ||
| guard let url = URL(string: "\(baseUrl)/push?key=\(pushKey)") else { | ||
|
|
@@ -29,23 +72,85 @@ class RealtimeIrl { | |
| updateCount += 1 | ||
| var request = URLRequest(url: pushUrl) | ||
| request.httpMethod = "POST" | ||
| request.httpBody = """ | ||
| { | ||
| \"latitude\":\(location.coordinate.latitude), | ||
| \"longitude\":\(location.coordinate.longitude), | ||
| \"speed\":\(location.speed), | ||
| \"altitude\":\(location.altitude), | ||
| \"timestamp\":\(location.timestamp.timeIntervalSince1970) | ||
| } | ||
| """.utf8Data | ||
| request.httpBody = try? JSONEncoder().encode(Payload( | ||
| latitude: location.coordinate.latitude, | ||
| longitude: location.coordinate.longitude, | ||
| speed: location.speed, | ||
| altitude: location.altitude, | ||
| heading: location.course, | ||
| pedometerSteps: pedometerSteps, | ||
| heartRate: heartRate, | ||
| cyclingPower: cyclingPower, | ||
| cyclingCrank: cyclingCrank, | ||
| cyclingWheel: cyclingWheel, | ||
| timestamp: location.timestamp.timeIntervalSince1970 | ||
| )) | ||
| request.setContentType("application/json") | ||
| URLSession.shared.dataTask(with: request) { _, _, _ in | ||
| } | ||
| .resume() | ||
| } | ||
|
|
||
| func updatePedometerSteps(_ steps: Int, fromWatch: Bool = false) { | ||
| if fromWatch { | ||
| pedometerStepsWatch = (steps, Date()) | ||
| } else { | ||
| pedometerStepsDevice = (steps, Date()) | ||
| } | ||
| } | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why does this class have to know about the source (watch or device) of the measurement? Not needed imo.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because we should prefer using watch as it should be more precise I think
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if we are lucky the watch work at all =) I probably didn't officially added to to text widget because it didn't work, but maybe.. either way, logic like this should not be part of this class, but some other class that can be used by the text widget as well. |
||
|
|
||
| func updateHeartRate(_ heartRate: Int, fromWatch: Bool = false) { | ||
| if fromWatch { | ||
| heartRateWatch = (heartRate, Date()) | ||
| } else { | ||
| heartRateDevice = (heartRate, Date()) | ||
| } | ||
| } | ||
|
|
||
| func updateCyclingPower(_ power: Int, fromWatch: Bool = false) { | ||
| if fromWatch { | ||
| cyclingPowerWatch = (power, Date()) | ||
| } else { | ||
| cyclingPowerDevice = (power, Date()) | ||
| } | ||
| } | ||
|
|
||
| func updateCyclingCrank(_ cadence: Int, fromWatch: Bool = false) { | ||
| if fromWatch { | ||
| cyclingCrankWatch = (cadence, Date()) | ||
| } else { | ||
| cyclingCrankDevice = (cadence, Date()) | ||
| } | ||
| } | ||
|
|
||
| func updateCyclingWheel(_ rpm: Int?, fromWatch: Bool = false) { | ||
| if fromWatch { | ||
| if let rpm { | ||
| cyclingWheelWatch = (rpm, Date()) | ||
| } else { | ||
| cyclingWheelWatch = nil | ||
| } | ||
| } else { | ||
| if let rpm { | ||
| cyclingWheelDevice = (rpm, Date()) | ||
| } else { | ||
| cyclingWheelDevice = nil | ||
| } | ||
| } | ||
| } | ||
|
|
||
| func stop() { | ||
| updateCount = 0 | ||
| pedometerStepsWatch = nil | ||
| pedometerStepsDevice = nil | ||
| heartRateWatch = nil | ||
| heartRateDevice = nil | ||
| cyclingPowerWatch = nil | ||
| cyclingPowerDevice = nil | ||
| cyclingCrankWatch = nil | ||
| cyclingCrankDevice = nil | ||
| cyclingWheelWatch = nil | ||
| cyclingWheelDevice = nil | ||
| var request = URLRequest(url: stopUrl) | ||
| request.httpMethod = "POST" | ||
| URLSession.shared.dataTask(with: request) { _, _, _ in | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| import CoreMotion | ||
|
|
||
| extension Model { | ||
| func startRealtimeIrlPedometer() { | ||
| guard isLive else { | ||
| return | ||
| } | ||
| guard isRealtimeIrlConfigured(), CMPedometer.isStepCountingAvailable() else { | ||
| return | ||
| } | ||
| guard !pedometerUpdatesActive else { | ||
| return | ||
| } | ||
|
|
||
| pedometerUpdatesActive = true | ||
| pedometer.startUpdates(from: Date()) { [weak self] data, error in | ||
| guard let self else { | ||
| return | ||
| } | ||
| guard error == nil, let steps = data?.numberOfSteps.intValue else { | ||
| return | ||
| } | ||
| DispatchQueue.main.async { | ||
| self.realtimeIrl?.updatePedometerSteps(steps) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| func stopRealtimeIrlPedometer() { | ||
| guard pedometerUpdatesActive else { | ||
| return | ||
| } | ||
| pedometer.stopUpdates() | ||
| pedometerUpdatesActive = false | ||
| } | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.