mirror of
https://github.com/house-of-abbey/GarminHomeAssistant.git
synced 2025-06-16 19:38:34 +00:00
Compare commits
19 Commits
Author | SHA1 | Date | |
---|---|---|---|
08829dac1f | |||
ca6e0733c5 | |||
01f073e67b | |||
64a9c5f274 | |||
f2fb7f65a0 | |||
1e103069bc | |||
520309729d | |||
d2aec16811 | |||
a424e35784 | |||
5558e25bda | |||
769731bff2 | |||
2c56155593 | |||
c38f91f456 | |||
94d806c4d3 | |||
51081ee2e6 | |||
7c7130367f | |||
42d1a7233c | |||
43378bfe8c | |||
700e7ca822 |
@ -24,7 +24,10 @@
|
||||
| 2.9 | Added an option to enable confirmation vibration so it can be turned off by request of a user. Removed a redundant setting for the alternative Widget version that was not removed previously, and fixed a bug with dereferencing Null. |
|
||||
| 2.10 | Added a user requested feature to slow down the rate of API calls in order to reduce battery wear for a situation where the application is kept open permanently on the device for convenience. Added 4 new devices. |
|
||||
| 2.11 | Bug fix release for menu caching being turned off and language corrections (Czech & Slovenian). |
|
||||
| 2.12 | Re-enabled Edge 540 and Edge 840 devices which we are unable to support due to simulator issues, but the Edge 840 device has been confirmed as working by a @Petucky. |
|
||||
| 2.12 | Re-enabled Edge 540 and Edge 840 devices which we are unable to support due to simulator issues, but the Edge 840 device has been confirmed as working by a [Petucky](https://github.com/Petucky). |
|
||||
| 2.13 | Moved the template status queries to Webhooks in order to fix the situation where an account is a non-privileged user. Added telemetry update on activity completion to make automations more timely at the end of an activity. When using a polling delay, there is no longer a startup delay for status updates and an action will trigger an immediate round of updates. |
|
||||
| 2.14 | Cautionary bug fix for the background service code where refactorisation spoilt some API level guard clauses. |
|
||||
| 2.15 | Better support for templates by isolating erroneous returns and marking the menu item. |
|
||||
| 2.16 | Bug fix for lack of phone connection when starting the application. Includes new activity reporting features from [KPWhiver](https://github.com/KPWhiver) covering steps, heart rate, floors climbed and descended, and respiration rate. |
|
||||
| 2.17 | Bug fix for reporting activity metrics that are not found on some devices. |
|
||||
| 2.18 | Bug fix for reporting activity metrics that might be `null` sometimes. This is unsimulatable situation, so this version is a change based on an informed guess. |
|
||||
|
10
README.md
10
README.md
@ -25,6 +25,16 @@ As of version 2.0, there are now two installable versions. For older devices bef
|
||||
| Application (original) | For newer devices that allow glance views in their applications, e.g. Venu 2, the GarminHomeAssistant application can be started either from a glance or from the list of applications and activities. Head over to the [GarminHomeAssistant](https://apps.garmin.com/en-US/apps/61c91d28-ec5e-438d-9f83-39e9f45b199d) application page on the [Connect IQ application store](https://apps.garmin.com/en-US/) to download the application. The application can be started two different ways, either from the glance in the carousel, or as an application from the list of applications & activities. With the latter, it is worth marking the application as a favourite.<br/><img src="images/Venu2_app_start.png" width="200" title="Venu 2" style="margin:5px"/><img src="images/Vivoactive3_app_start.jpg" width="200" title="Venu 2" style="margin:5px"/><br/>If you place the application on your list of favourites, and rearrange it to appear near the top, then the item is just one button press away from the watch face. This second picture here shows the application menu on a Vivoactive 3 watch.<br/><img src="images/Venu2_glance_start.png" width="200" title="Venu 2" style="margin:5px"/><br/>On newer watches, you can also start the application from the glance carousel. The glance view here typically displays some trackable status, so ours provides some early indication of availability. Older watches will still allow you to start this application from the list of applications and activities. |
|
||||
| Widget | **"Maintenance only mode"** so no new features will be added to this version.<br>For older devices that use widgets, e.g. Venu (1) as opposed to applications with "glances", the GarminHomeAssistant application can instead be started from the widget carousel. This is a separate item in the Connect IQ AppStore and with this installation, the application will no longer appear in the list of applications and activities. Head over to the [GarminHomeAssistant](https://apps.garmin.com/en-US/apps/) widget page on the [Connect IQ application store](https://apps.garmin.com/en-US/) to download the widget.<br/><img src="images/Venu_Widget_sim.png" width="200" title="Venu 2" style="margin:5px"/><br/>Typically the widget view implements something similar to the glance view, e.g. status, and exists in a widget carousel to allow you to select an application to launch.<br>**Please note that memory in widgets is more limited than applications. This means a large menu definition can crash the widget without the code catching the error.**<br> This version was born out of the application version and from Ver 2.0 shared the same source code repository until Ver 2.8 when they were [separated](https://github.com/house-of-abbey/GarminHomeAssistantWidget) to allow the application version to take advantage of its increase memory availability. |
|
||||
|
||||
### Features
|
||||
|
||||
The following table lists the differences in functionality between the two. The Widget version is more limited due to memory constraints. As such new features are only being added to the Application.
|
||||
|
||||
| Feature | Application | Widget |
|
||||
|---------|-------------|--------|
|
||||
| Vibration | Optional setting | Always on |
|
||||
| "Always on" support | Slow refresh option to reduce batter demand | No available |
|
||||
| Metric reporting | Fuller, includes: activity, sub-activity, battery, charging, steps, heart rate, floors ascended and descended, respiration rate | Basic, includes: activity, sub-activity, battery only. |
|
||||
|
||||
### Source Code Repositories
|
||||
|
||||
* [Application](https://github.com/house-of-abbey/GarminHomeAssistant)
|
||||
|
@ -69,6 +69,8 @@ rem -x,--excludes <arg> Add annotations to the exclude list (deprecated)
|
||||
rem -y,--private-key <arg> Private key to sign builds with
|
||||
rem -z,--rez <arg> Resource files (deprecated)
|
||||
|
||||
title Exporting Garmin Home Assistant Application
|
||||
|
||||
rem Batch file's directory where the source code is
|
||||
set SRC=%~dp0
|
||||
rem drop last character '\'
|
||||
|
@ -79,13 +79,13 @@ class BackgroundServiceDelegate extends System.ServiceDelegate {
|
||||
private function doUpdate(activity as Lang.Number or Null, sub_activity as Lang.Number or Null) {
|
||||
// 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);
|
||||
// System.println("BackgroundServiceDelegate onTemporalEvent(): GPS : " + position.position.toDegrees());
|
||||
// System.println("BackgroundServiceDelegate onTemporalEvent(): Speed : " + position.speed);
|
||||
// System.println("BackgroundServiceDelegate onTemporalEvent(): Course : " + position.heading + " radians (" + (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
|
||||
|
||||
@ -102,17 +102,28 @@ class BackgroundServiceDelegate extends System.ServiceDelegate {
|
||||
accuracy = 10;
|
||||
break;
|
||||
}
|
||||
|
||||
var data = { "gps_accuracy" => accuracy };
|
||||
// Only add the non-null fields as all the values are optional in Home Assistant, and it avoid submitting fake values.
|
||||
if (position.position != null) {
|
||||
data.put("gps", position.position.toDegrees());
|
||||
}
|
||||
if (position.speed != null) {
|
||||
data.put("speed", Math.round(position.speed));
|
||||
}
|
||||
if (position.heading != null) {
|
||||
data.put("course", Math.round(position.heading * 180 / Math.PI));
|
||||
}
|
||||
if (position.altitude != null) {
|
||||
data.put("altitude", Math.round(position.altitude));
|
||||
}
|
||||
// System.println("BackgroundServiceDelegate onTemporalEvent(): data = " + data.toString());
|
||||
|
||||
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),
|
||||
}
|
||||
"data" => data,
|
||||
},
|
||||
{
|
||||
:method => Communications.HTTP_REQUEST_METHOD_POST,
|
||||
@ -124,18 +135,62 @@ class BackgroundServiceDelegate extends System.ServiceDelegate {
|
||||
method(:onReturnBatteryUpdate)
|
||||
);
|
||||
}
|
||||
var activityInfo = ActivityMonitor.getInfo();
|
||||
var heartRate = Activity.getActivityInfo().currentHeartRate;
|
||||
var data = [
|
||||
{
|
||||
"state" => System.getSystemStats().battery,
|
||||
"type" => "sensor",
|
||||
"unique_id" => "battery_level"
|
||||
"unique_id" => "battery_level",
|
||||
"icon" => "mdi:battery"
|
||||
},
|
||||
{
|
||||
"state" => System.getSystemStats().charging,
|
||||
"type" => "binary_sensor",
|
||||
"unique_id" => "battery_is_charging"
|
||||
"unique_id" => "battery_is_charging",
|
||||
"icon" => System.getSystemStats().charging ? "mdi:battery-plus" : "mdi:battery-minus"
|
||||
},
|
||||
{
|
||||
"state" => activityInfo.steps == null ? "unknown" : activityInfo.steps,
|
||||
"type" => "sensor",
|
||||
"unique_id" => "steps_today",
|
||||
"icon" => "mdi:walk"
|
||||
},
|
||||
{
|
||||
"state" => heartRate == null ? "unknown" : heartRate,
|
||||
"type" => "sensor",
|
||||
"unique_id" => "heart_rate",
|
||||
"icon" => "mdi:heart-pulse"
|
||||
}
|
||||
];
|
||||
|
||||
if (ActivityMonitor.Info has :floorsClimbed) {
|
||||
data.add({
|
||||
"state" => activityInfo.floorsClimbed == null ? "unknown" : activityInfo.floorsClimbed,
|
||||
"type" => "sensor",
|
||||
"unique_id" => "floors_climbed_today",
|
||||
"icon" => "mdi:stairs-up"
|
||||
});
|
||||
}
|
||||
|
||||
if (ActivityMonitor.Info has :floorsDescended) {
|
||||
data.add({
|
||||
"state" => activityInfo.floorsDescended == null ? "unknown" : activityInfo.floorsDescended,
|
||||
"type" => "sensor",
|
||||
"unique_id" => "floors_descended_today",
|
||||
"icon" => "mdi:stairs-down"
|
||||
});
|
||||
}
|
||||
|
||||
if (ActivityMonitor.Info has :respirationRate) {
|
||||
data.add({
|
||||
"state" => activityInfo.respirationRate == null ? "unknown" : activityInfo.respirationRate,
|
||||
"type" => "sensor",
|
||||
"unique_id" => "respiration_rate",
|
||||
"icon" => "mdi:lungs"
|
||||
});
|
||||
}
|
||||
|
||||
if (activity != null) {
|
||||
data.add({
|
||||
"state" => activity,
|
||||
|
@ -70,36 +70,29 @@ class Settings {
|
||||
// Manage this inside the application or widget only (not a glance or background service process)
|
||||
if (mIsApp) {
|
||||
if (mHasService) {
|
||||
mWebhookManager = new WebhookManager();
|
||||
if (getWebhookId().equals("")) {
|
||||
// System.println("Settings update(): Doing full webhook & sensor creation.");
|
||||
mWebhookManager.requestWebhookId();
|
||||
} else {
|
||||
// System.println("Settings update(): Doing just sensor creation.");
|
||||
// We already have a Webhook ID, so just enable or disable the sensor in Home Assistant.
|
||||
// Its a multiple step process, hence starting at step 0.
|
||||
mWebhookManager.registerWebhookSensor({
|
||||
"device_class" => "battery",
|
||||
"name" => "Battery Level",
|
||||
"state" => System.getSystemStats().battery,
|
||||
"type" => "sensor",
|
||||
"unique_id" => "battery_level",
|
||||
"unit_of_measurement" => "%",
|
||||
"state_class" => "measurement",
|
||||
"entity_category" => "diagnostic",
|
||||
"disabled" => !Settings.isSensorsLevelEnabled()
|
||||
}, 0);
|
||||
}
|
||||
if (mIsSensorsLevelEnabled) {
|
||||
// Create the timed activity
|
||||
if ((Background.getTemporalEventRegisteredTime() == null) or
|
||||
(Background.getTemporalEventRegisteredTime() != (mBatteryRefreshRate * 60))) {
|
||||
Background.registerForTemporalEvent(new Time.Duration(mBatteryRefreshRate * 60)); // Convert to seconds
|
||||
Background.registerForActivityCompletedEvent();
|
||||
if (System.getDeviceSettings().phoneConnected) {
|
||||
mWebhookManager = new WebhookManager();
|
||||
if (getWebhookId().equals("")) {
|
||||
// System.println("Settings update(): Doing full webhook & sensor creation.");
|
||||
mWebhookManager.requestWebhookId();
|
||||
} else {
|
||||
// System.println("Settings update(): Doing just sensor creation.");
|
||||
// We already have a Webhook ID, so just enable or disable the sensor in Home Assistant.
|
||||
mWebhookManager.registerWebhookSensors();
|
||||
}
|
||||
} else if (Background.getTemporalEventRegisteredTime() != null) {
|
||||
Background.deleteTemporalEvent();
|
||||
Background.deleteActivityCompletedEvent();
|
||||
if (mIsSensorsLevelEnabled) {
|
||||
// Create the timed activity
|
||||
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 {
|
||||
ErrorView.show(WatchUi.loadResource($.Rez.Strings.NoPhone) as Lang.String);
|
||||
}
|
||||
} else {
|
||||
// Explicitly disable the background event which persists when the application closes.
|
||||
|
@ -69,17 +69,7 @@ class WebhookManager {
|
||||
if (id != null) {
|
||||
Settings.setWebhookId(id);
|
||||
// System.println("WebhookManager onReturnRegisterWebhookSensor(): Registering first sensor: Battery Level");
|
||||
registerWebhookSensor({
|
||||
"device_class" => "battery",
|
||||
"name" => "Battery Level",
|
||||
"state" => System.getSystemStats().battery,
|
||||
"type" => "sensor",
|
||||
"unique_id" => "battery_level",
|
||||
"unit_of_measurement" => "%",
|
||||
"state_class" => "measurement",
|
||||
"entity_category" => "diagnostic",
|
||||
"disabled" => !Settings.isSensorsLevelEnabled()
|
||||
}, 0);
|
||||
registerWebhookSensors();
|
||||
} else {
|
||||
// System.println("WebhookManager onReturnRequestWebhookId(): No webhook id in response data.");
|
||||
Settings.unsetIsSensorsLevelEnabled();
|
||||
@ -125,7 +115,7 @@ class WebhookManager {
|
||||
);
|
||||
}
|
||||
|
||||
function onReturnRegisterWebhookSensor(responseCode as Lang.Number, data as Null or Lang.Dictionary or Lang.String, step as Lang.Number) as Void {
|
||||
function onReturnRegisterWebhookSensor(responseCode as Lang.Number, data as Null or Lang.Dictionary or Lang.String, sensors as Lang.Array<Lang.Object>) as Void {
|
||||
switch (responseCode) {
|
||||
case Communications.BLE_HOST_TIMEOUT:
|
||||
case Communications.BLE_CONNECTION_UNAVAILABLE:
|
||||
@ -172,63 +162,10 @@ class WebhookManager {
|
||||
case 201:
|
||||
var d = data as Lang.Dictionary;
|
||||
if ((d.get("success") as Lang.Boolean or Null) != false) {
|
||||
// System.println("WebhookManager onReturnRegisterWebhookSensor(): Success");
|
||||
switch (step) {
|
||||
case 0:
|
||||
// System.println("WebhookManager onReturnRegisterWebhookSensor(): Registering next sensor: Battery is Charging");
|
||||
registerWebhookSensor({
|
||||
"device_class" => "battery_charging",
|
||||
"name" => "Battery is Charging",
|
||||
"state" => System.getSystemStats().charging,
|
||||
"type" => "binary_sensor",
|
||||
"unique_id" => "battery_is_charging",
|
||||
"entity_category" => "diagnostic",
|
||||
"disabled" => !Settings.isSensorsLevelEnabled()
|
||||
}, 1);
|
||||
break;
|
||||
case 1:
|
||||
// System.println("WebhookManager onReturnRegisterWebhookSensor(): Registering next sensor: Activity");
|
||||
if (Activity has :getProfileInfo) {
|
||||
var activity = Activity.getProfileInfo().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;
|
||||
}
|
||||
registerWebhookSensor({
|
||||
"name" => "Activity",
|
||||
"state" => activity,
|
||||
"type" => "sensor",
|
||||
"unique_id" => "activity",
|
||||
"disabled" => !Settings.isSensorsLevelEnabled()
|
||||
}, 2);
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
// System.println("WebhookManager onReturnRegisterWebhookSensor(): Registering next sensor: Sub-Activity");
|
||||
if (Activity has :getProfileInfo) {
|
||||
var sub_activity = Activity.getProfileInfo().subSport;
|
||||
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
|
||||
sub_activity = -1;
|
||||
}
|
||||
registerWebhookSensor({
|
||||
"name" => "Sub-activity",
|
||||
"state" => sub_activity,
|
||||
"type" => "sensor",
|
||||
"unique_id" => "sub_activity",
|
||||
"disabled" => !Settings.isSensorsLevelEnabled()
|
||||
}, 3);
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
getApp().startUpdates();
|
||||
default:
|
||||
if (sensors.size() == 0) {
|
||||
getApp().startUpdates();
|
||||
} else {
|
||||
registerWebhookSensor(sensors);
|
||||
}
|
||||
} else {
|
||||
// System.println("WebhookManager onReturnRegisterWebhookSensor(): Failure");
|
||||
@ -246,7 +183,7 @@ class WebhookManager {
|
||||
}
|
||||
}
|
||||
|
||||
function registerWebhookSensor(sensor as Lang.Object, step as Lang.Number) {
|
||||
function registerWebhookSensor(sensors as Lang.Array<Lang.Object>) {
|
||||
var url = Settings.getApiUrl() + "/webhook/" + Settings.getWebhookId();
|
||||
// System.println("WebhookManager registerWebhookSensor(): Registering webhook sensor: " + sensor.toString());
|
||||
// System.println("WebhookManager registerWebhookSensor(): URL=" + url);
|
||||
@ -255,7 +192,7 @@ class WebhookManager {
|
||||
url,
|
||||
{
|
||||
"type" => "register_sensor",
|
||||
"data" => sensor
|
||||
"data" => sensors[0]
|
||||
},
|
||||
{
|
||||
:method => Communications.HTTP_REQUEST_METHOD_POST,
|
||||
@ -263,10 +200,126 @@ class WebhookManager {
|
||||
"Content-Type" => Communications.REQUEST_CONTENT_TYPE_JSON
|
||||
},
|
||||
:responseType => Communications.HTTP_RESPONSE_CONTENT_TYPE_JSON,
|
||||
:context => step
|
||||
:context => sensors.slice(1, null)
|
||||
},
|
||||
method(:onReturnRegisterWebhookSensor)
|
||||
);
|
||||
}
|
||||
|
||||
function registerWebhookSensors() {
|
||||
var activityInfo = ActivityMonitor.getInfo();
|
||||
var heartRate = Activity.getActivityInfo().currentHeartRate;
|
||||
|
||||
var sensors = [
|
||||
{
|
||||
"device_class" => "battery",
|
||||
"name" => "Battery Level",
|
||||
"state" => System.getSystemStats().battery,
|
||||
"type" => "sensor",
|
||||
"unique_id" => "battery_level",
|
||||
"icon" => "mdi:battery",
|
||||
"unit_of_measurement" => "%",
|
||||
"state_class" => "measurement",
|
||||
"entity_category" => "diagnostic",
|
||||
"disabled" => !Settings.isSensorsLevelEnabled()
|
||||
},
|
||||
{
|
||||
"device_class" => "battery_charging",
|
||||
"name" => "Battery is Charging",
|
||||
"state" => System.getSystemStats().charging,
|
||||
"type" => "binary_sensor",
|
||||
"unique_id" => "battery_is_charging",
|
||||
"icon" => System.getSystemStats().charging ? "mdi:battery-plus" : "mdi:battery-minus",
|
||||
"entity_category" => "diagnostic",
|
||||
"disabled" => !Settings.isSensorsLevelEnabled()
|
||||
},
|
||||
{
|
||||
"name" => "Steps today",
|
||||
"state" => activityInfo.steps == null ? "unknown" : activityInfo.steps,
|
||||
"type" => "sensor",
|
||||
"unique_id" => "steps_today",
|
||||
"icon" => "mdi:walk",
|
||||
"state_class" => "total",
|
||||
"disabled" => !Settings.isSensorsLevelEnabled()
|
||||
},
|
||||
{
|
||||
"name" => "Heart rate",
|
||||
"state" => heartRate == null ? "unknown" : heartRate,
|
||||
"type" => "sensor",
|
||||
"unique_id" => "heart_rate",
|
||||
"icon" => "mdi:heart-pulse",
|
||||
"unit_of_measurement" => "bpm",
|
||||
"state_class" => "measurement",
|
||||
"disabled" => !Settings.isSensorsLevelEnabled()
|
||||
}
|
||||
];
|
||||
|
||||
if (ActivityMonitor.Info has :floorsClimbed) {
|
||||
sensors.add({
|
||||
"name" => "Floors climbed today",
|
||||
"state" => activityInfo.floorsClimbed == null ? "unknown" : activityInfo.floorsClimbed,
|
||||
"type" => "sensor",
|
||||
"unique_id" => "floors_climbed_today",
|
||||
"icon" => "mdi:stairs-up",
|
||||
"state_class" => "total",
|
||||
"disabled" => !Settings.isSensorsLevelEnabled()
|
||||
});
|
||||
}
|
||||
|
||||
if (ActivityMonitor.Info has :floorsDescended) {
|
||||
sensors.add({
|
||||
"name" => "Floors descended today",
|
||||
"state" => activityInfo.floorsDescended == null ? "unknown" : activityInfo.floorsDescended,
|
||||
"type" => "sensor",
|
||||
"unique_id" => "floors_descended_today",
|
||||
"icon" => "mdi:stairs-down",
|
||||
"state_class" => "total",
|
||||
"disabled" => !Settings.isSensorsLevelEnabled()
|
||||
});
|
||||
}
|
||||
|
||||
if (ActivityMonitor.Info has :respirationRate) {
|
||||
sensors.add({
|
||||
"name" => "Respiration rate",
|
||||
"state" => activityInfo.respirationRate == null ? "unknown" : activityInfo.respirationRate,
|
||||
"type" => "sensor",
|
||||
"unique_id" => "respiration_rate",
|
||||
"icon" => "mdi:lungs",
|
||||
"unit_of_measurement" => "bpm",
|
||||
"state_class" => "measurement",
|
||||
"disabled" => !Settings.isSensorsLevelEnabled()
|
||||
});
|
||||
}
|
||||
|
||||
if (Activity has :getProfileInfo) {
|
||||
var activity = Activity.getProfileInfo().sport;
|
||||
var sub_activity = Activity.getProfileInfo().subSport;
|
||||
|
||||
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;
|
||||
}
|
||||
sensors.add({
|
||||
"name" => "Activity",
|
||||
"state" => activity,
|
||||
"type" => "sensor",
|
||||
"unique_id" => "activity",
|
||||
"disabled" => !Settings.isSensorsLevelEnabled()
|
||||
});
|
||||
sensors.add({
|
||||
"name" => "Sub-activity",
|
||||
"state" => sub_activity,
|
||||
"type" => "sensor",
|
||||
"unique_id" => "sub_activity",
|
||||
"disabled" => !Settings.isSensorsLevelEnabled()
|
||||
});
|
||||
}
|
||||
|
||||
registerWebhookSensor(sensors);
|
||||
}
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user