diff --git a/source/BackgroundServiceDelegate.mc b/source/BackgroundServiceDelegate.mc index b76e38a..98537cf 100644 --- a/source/BackgroundServiceDelegate.mc +++ b/source/BackgroundServiceDelegate.mc @@ -23,6 +23,7 @@ using Toybox.Lang; using Toybox.Application.Properties; using Toybox.Background; using Toybox.System; +using Toybox.Activity; (:background) class BackgroundServiceDelegate extends System.ServiceDelegate { @@ -37,99 +38,77 @@ class BackgroundServiceDelegate extends System.ServiceDelegate { Background.exit(null); } + function onActivityCompleted(activity as { :sport as Activity.Sport, :subSport as Activity.SubSport }) as Void { + if (!System.getDeviceSettings().phoneConnected) { + // System.println("BackgroundServiceDelegate onActivityCompleted(): No Phone connection, skipping API call."); + } else if (!System.getDeviceSettings().connectionAvailable) { + // System.println("BackgroundServiceDelegate onActivityCompleted(): No Internet connection, skipping API call."); + } else { + // Ensure we're logging completion, i.e. ignore 'activity' parameter + // System.println("BackgroundServiceDelegate onActivityCompleted(): Event triggered"); + doUpdate(-1, -1); + } + } + function onTemporalEvent() as Void { if (!System.getDeviceSettings().phoneConnected) { // System.println("BackgroundServiceDelegate onTemporalEvent(): No Phone connection, skipping API call."); } else if (!System.getDeviceSettings().connectionAvailable) { // System.println("BackgroundServiceDelegate onTemporalEvent(): No Internet connection, skipping API call."); } else { - // System.println("BackgroundServiceDelegate onTemporalEvent(): Making API call."); - var position = Position.getInfo(); - // System.println("BackgroundServiceDelegate onTemporalEvent(): gps: " + position.position.toDegrees()); - // System.println("BackgroundServiceDelegate onTemporalEvent(): speed: " + position.speed); - // System.println("BackgroundServiceDelegate onTemporalEvent(): course: " + position.heading + "rad (" + (position.heading * 180 / Math.PI) + "°)"); - // System.println("BackgroundServiceDelegate onTemporalEvent(): altitude: " + position.altitude); - // System.println("BackgroundServiceDelegate onTemporalEvent(): battery: " + System.getSystemStats().battery); - // System.println("BackgroundServiceDelegate onTemporalEvent(): charging: " + System.getSystemStats().charging); - // System.println("BackgroundServiceDelegate onTemporalEvent(): activity: " + Activity.getProfileInfo().name); - - // Don't use Settings.* here as the object lasts < 30 secs and is recreated each time the background service is run - - if (position.accuracy != Position.QUALITY_NOT_AVAILABLE && position.accuracy != Position.QUALITY_LAST_KNOWN) { - var accuracy = 0; - switch (position.accuracy) { - case Position.QUALITY_POOR: - accuracy = 500; - break; - case Position.QUALITY_USABLE: - accuracy = 100; - break; - case Position.QUALITY_GOOD: - accuracy = 10; - break; - } - Communications.makeWebRequest( - (Properties.getValue("api_url") as Lang.String) + "/webhook/" + (Properties.getValue("webhook_id") as Lang.String), - { - "type" => "update_location", - "data" => { - "gps" => position.position.toDegrees(), - "gps_accuracy" => accuracy, - "speed" => Math.round(position.speed), - "course" => Math.round(position.heading * 180 / Math.PI), - "altitude" => Math.round(position.altitude), - } - }, - { - :method => Communications.HTTP_REQUEST_METHOD_POST, - :headers => { - "Content-Type" => Communications.REQUEST_CONTENT_TYPE_JSON - }, - :responseType => Communications.HTTP_RESPONSE_CONTENT_TYPE_JSON - }, - method(:onReturnBatteryUpdate) - ); + var activity = Activity.getProfileInfo().sport; + var sub_activity = Activity.getProfileInfo().subSport; + // We need to check if we are actually tracking any activity as the enumerated type does not include "No Sport". + if ((Activity.getActivityInfo() != null) and + ((Activity.getActivityInfo().elapsedTime == null) or + (Activity.getActivityInfo().elapsedTime == 0))) { + // Indicate no activity with -1, not part of Garmin's activity codes. + // https://developer.garmin.com/connect-iq/api-docs/Toybox/Activity.html#Sport-module + activity = -1; + sub_activity = -1; } - var data = [ - { - "state" => System.getSystemStats().battery, - "type" => "sensor", - "unique_id" => "battery_level" - }, - { - "state" => System.getSystemStats().charging, - "type" => "binary_sensor", - "unique_id" => "battery_is_charging" - } - ]; - if ((Activity has :getActivityInfo) and (Activity has :getProfileInfo)) { - var activity = Activity.getProfileInfo().sport; - var sub_activity = Activity.getProfileInfo().subSport; - // We need to check if we are actually tracking any activity as the enumerated type does not include "No Sport". - if ((Activity.getActivityInfo() != null) and - ((Activity.getActivityInfo().elapsedTime == null) or - (Activity.getActivityInfo().elapsedTime == 0))) { - // Indicate no activity with -1, not part of Garmin's activity codes. - // https://developer.garmin.com/connect-iq/api-docs/Toybox/Activity.html#Sport-module - activity = -1; - sub_activity = -1; - } - data.add({ - "state" => activity, - "type" => "sensor", - "unique_id" => "activity" - }); - data.add({ - "state" => sub_activity, - "type" => "sensor", - "unique_id" => "sub_activity" - }); + // System.println("BackgroundServiceDelegate onTemporalEvent(): Event triggered, activity = " + activity + " sub_activity = " + sub_activity); + doUpdate(activity, sub_activity); + } + } + + private function doUpdate(activity as Lang.Number, sub_activity as Lang.Number) { + // System.println("BackgroundServiceDelegate onTemporalEvent(): Making API call."); + var position = Position.getInfo(); + // System.println("BackgroundServiceDelegate onTemporalEvent(): gps: " + position.position.toDegrees()); + // System.println("BackgroundServiceDelegate onTemporalEvent(): speed: " + position.speed); + // System.println("BackgroundServiceDelegate onTemporalEvent(): course: " + position.heading + "rad (" + (position.heading * 180 / Math.PI) + "°)"); + // System.println("BackgroundServiceDelegate onTemporalEvent(): altitude: " + position.altitude); + // System.println("BackgroundServiceDelegate onTemporalEvent(): battery: " + System.getSystemStats().battery); + // System.println("BackgroundServiceDelegate onTemporalEvent(): charging: " + System.getSystemStats().charging); + // System.println("BackgroundServiceDelegate onTemporalEvent(): activity: " + Activity.getProfileInfo().name); + + // Don't use Settings.* here as the object lasts < 30 secs and is recreated each time the background service is run + + if (position.accuracy != Position.QUALITY_NOT_AVAILABLE && position.accuracy != Position.QUALITY_LAST_KNOWN) { + var accuracy = 0; + switch (position.accuracy) { + case Position.QUALITY_POOR: + accuracy = 500; + break; + case Position.QUALITY_USABLE: + accuracy = 100; + break; + case Position.QUALITY_GOOD: + accuracy = 10; + break; } Communications.makeWebRequest( (Properties.getValue("api_url") as Lang.String) + "/webhook/" + (Properties.getValue("webhook_id") as Lang.String), { - "type" => "update_sensor_states", - "data" => data + "type" => "update_location", + "data" => { + "gps" => position.position.toDegrees(), + "gps_accuracy" => accuracy, + "speed" => Math.round(position.speed), + "course" => Math.round(position.heading * 180 / Math.PI), + "altitude" => Math.round(position.altitude), + } }, { :method => Communications.HTTP_REQUEST_METHOD_POST, @@ -141,6 +120,45 @@ class BackgroundServiceDelegate extends System.ServiceDelegate { method(:onReturnBatteryUpdate) ); } + var data = [ + { + "state" => System.getSystemStats().battery, + "type" => "sensor", + "unique_id" => "battery_level" + }, + { + "state" => System.getSystemStats().charging, + "type" => "binary_sensor", + "unique_id" => "battery_is_charging" + } + ]; + if ((Activity has :getActivityInfo) and (Activity has :getProfileInfo)) { + data.add({ + "state" => activity, + "type" => "sensor", + "unique_id" => "activity" + }); + data.add({ + "state" => sub_activity, + "type" => "sensor", + "unique_id" => "sub_activity" + }); + } + Communications.makeWebRequest( + (Properties.getValue("api_url") as Lang.String) + "/webhook/" + (Properties.getValue("webhook_id") as Lang.String), + { + "type" => "update_sensor_states", + "data" => data + }, + { + :method => Communications.HTTP_REQUEST_METHOD_POST, + :headers => { + "Content-Type" => Communications.REQUEST_CONTENT_TYPE_JSON + }, + :responseType => Communications.HTTP_RESPONSE_CONTENT_TYPE_JSON + }, + method(:onReturnBatteryUpdate) + ); } } diff --git a/source/Settings.mc b/source/Settings.mc index 067e8bf..111cc34 100644 --- a/source/Settings.mc +++ b/source/Settings.mc @@ -95,9 +95,11 @@ class Settings { if ((Background.getTemporalEventRegisteredTime() == null) or (Background.getTemporalEventRegisteredTime() != (mBatteryRefreshRate * 60))) { Background.registerForTemporalEvent(new Time.Duration(mBatteryRefreshRate * 60)); // Convert to seconds + Background.registerForActivityCompletedEvent(); } } else if (Background.getTemporalEventRegisteredTime() != null) { Background.deleteTemporalEvent(); + Background.deleteActivityCompletedEvent(); } } else { // Explicitly disable the background event which persists when the application closes. @@ -182,6 +184,7 @@ class Settings { Properties.setValue("enable_battery_level", mIsSensorsLevelEnabled); if (mHasService and (Background.getTemporalEventRegisteredTime() != null)) { Background.deleteTemporalEvent(); + Background.deleteActivityCompletedEvent(); } }