diff --git a/BackgroundService.md b/BackgroundService.md
index 9e74556..a2dbdbe 100644
--- a/BackgroundService.md
+++ b/BackgroundService.md
@@ -8,6 +8,8 @@ The background service can report the following statuses from your device to you
- Location and location accuracy.
- Activity information, but only if your device supports API level 3.2.0. If your device does not support this API level, this information is simply omitted. How do you know? Easiest way is to see if the data is reported.
+If your device does not support the background service, the application will clear this setting after you have enabled it. This tells you that you are unable to take advantage of the background reporting service for the functions below.
+
## Limits
The values are merely samples of your device's current status. They are sent by a single background service at the repetition frequency you chose in the settings. The samples are sent at that one rate only, they _do not vary_ for example on in activity, on charge, time of day. You get one refresh interval and that is it. If you want to change the refresh interval, you change your settings. We do appreciate that may not be what you would ideally like to trigger actions on Home Assistant. Messing with the repeat interval of the background service requires more code, more settings and more complexity. That means older devices using widgets would have to be taken out of support to achieve it.
diff --git a/HISTORY.md b/HISTORY.md
index 5acadbe..d7f4c62 100644
--- a/HISTORY.md
+++ b/HISTORY.md
@@ -18,4 +18,5 @@
| 2.3 | Fix for battery level updates where previously the function only worked for administrator accounts. The new solution is based on Webhooks and is simpler to implement on Home Assistant. Language support fix where an automatic translation produced an inappropriate word, possibly in more than one language. |
| 2.4 | Sensor status reporting via Home Assistant 'templates'. This provides a generalised way of viewing the status of any entity as long as the result can be rendered as text, e.g. 'uncovered', 'open', '76%', '21 °C'. Removal of the menu style option. The original style was kept after the introduction of the icon style solely to keep the code for a possible re-use for sensor statuses. This version delivers that new feature, hence the style option has been removed. The new JSON configuration file format allows for the old style to be replicated if you are desperate! Added a feature to provide parameters to actions (`tap` or `template`). Added a feature to confirm `toggle` menu items. |
| 2.5 | A small memory efficiency of about 1kB by removing `RezStrings.mc`. This will aid widgets on old watches that only have 60kB available to an application and are using about 45kB before the menu is fetched, hence 1kB is more significant to those devices. |
-| 2.6 | Added more information reporting to the background service, in addition to the device battery level and charging status, we now include location, location accuracy, and (if supported by your device) the activity information. Note the updates are sent periodically and are not event driven. |
\ No newline at end of file
+| 2.6 | Added more information reporting to the background service, in addition to the device battery level and charging status, we now include location, location accuracy, and (if supported by your device) the activity information. Note the updates are sent periodically and are not event driven. |
+| 2.7 | Bug fix release: 1. Handling a settings change, 2. Avoid caching a bad menu, 3. Managing bad JSON in menu definitions. |
diff --git a/README.md b/README.md
index eb92704..a4a8f85 100644
--- a/README.md
+++ b/README.md
@@ -193,6 +193,8 @@ Paste in your JSON (and change the file type to JSON if not saving), it will the
A failure to get the file format right tends to mean that the response to the application errors with `INVALID_HTTP_BODY_IN_NETWORK_RESPONSE` (code of -400). This means the response did not contain JSON, it was probably an error message in plain text that could not be parsed by the Connect IQ API call. See [Toybox.Communications](https://developer.garmin.com/connect-iq/api-docs/Toybox/Communications.html) for the list of error code you might be presented with on your device.
+There are some cases where the file format may be valid JSON, but invalid against the schema, and the failure to catch this error could cause the application to crash. Whilst we have taken care to manage many issues, there may still be cases that are uncaught.
+
Make sure you can browse to the URL of your JSON file in a standard web browser to make sure it is accessible.
## API Key Creation
@@ -278,15 +280,17 @@ The `id` attribute values are taken from the same names used in [`strings.xml`](
## Known Issues
-1. On some (old) devices (e.g. Vivoactive 3, Fenix 5s & Edge 520+), the menu does not update correctly to reflect changes in state effected by an external Home Assistant control. E.g. when the phone application changes the toggle status of a switch, the Garmin application does not reflect that change until the menu is touched or scrolled a little. This is a [known issue](https://forums.garmin.com/developer/connect-iq/i/bug-reports/menu2-doesn-t-allow-live-updates) already reported without a suggested software fix.
+1. There are some cases where the file format may be valid JSON, but invalid against the schema, and the failure to catch this error could cause the application to crash. Whilst we have taken care to manage many issues, there may still be cases that are uncaught. Please verify your JSON schema, see the [trouble shooting guide](TroubleShooting.md).
-2. Widgets have less memory than applications. With the new template based sensor display, widgets are more likely to run out of memory. E.g. a Vivoactive 3 device has a memory limit of 60 kB runtime memory for widgets (compared with 124 kB for applications) and memory is likely to be ~90% used. This makes it very likely that a larger menu will crash the application. We cannot predict what will take the application "over the edge", but we can provide this feedback to users to raise awareness, hence the widget displays menu usage as a reminder. If the widget is crashing but the application variant is not, then your menu configuration is too big for the widget. **Please don't give the application a poor review for crashing on your excessive menu definition!**
+2. On some (old) devices (e.g. Vivoactive 3, Fenix 5s & Edge 520+), the menu does not update correctly to reflect changes in state effected by an external Home Assistant control. E.g. when the phone application changes the toggle status of a switch, the Garmin application does not reflect that change until the menu is touched or scrolled a little. This is a [known issue](https://forums.garmin.com/developer/connect-iq/i/bug-reports/menu2-doesn-t-allow-live-updates) already reported without a suggested software fix.
+
+3. Widgets have less memory than applications. With the new template based sensor display, widgets are more likely to run out of memory. E.g. a Vivoactive 3 device has a memory limit of 60 kB runtime memory for widgets (compared with 124 kB for applications) and memory is likely to be ~90% used. This makes it very likely that a larger menu will crash the application. We cannot predict what will take the application "over the edge", but we can provide this feedback to users to raise awareness, hence the widget displays menu usage as a reminder. If the widget is crashing but the application variant is not, then your menu configuration is too big for the widget. **Please don't give the application a poor review for crashing on your excessive menu definition!**
-3. Templates can require significant definition for highly customised text. Just remember, you have the ability to crash the application by creating an excessively long menu definition. Don't be silly.
+4. Templates can require significant definition for highly customised text. Just remember, you have the ability to crash the application by creating an excessively long menu definition. Don't be silly.
-4. Parameters to tap menu items cannot have their parameter usage verified. If you get this wrong and crash the application, that's your fault not the application's. In this case, start by removing the parameters for the menu item causing the crash, and add them back one at a time until you find your fault. **Please don't give the application a poor review for your bad parameter definition!**
+5. Parameters to tap menu items cannot have their parameter usage verified. If you get this wrong and crash the application, that's your fault not the application's. In this case, start by removing the parameters for the menu item causing the crash, and add them back one at a time until you find your fault. **Please don't give the application a poor review for your bad parameter definition!**
-5. We are unable to support Edge 540 and Edge 840 devices at this time. The simulation of both these devices has two unexpected errors when toggling or executing taps. We get both `Communications.NETWORK_RESPONSE_OUT_OF_MEMORY` and `Communications.BLE_QUEUE_FULL` even though the memory usage is about 6% of the available RAM. We would appreciate any leads others may have on how to solve this issue.
+6. We are unable to support Edge 540 and Edge 840 devices at this time. The simulation of both these devices has two unexpected errors when toggling or executing taps. We get both `Communications.NETWORK_RESPONSE_OUT_OF_MEMORY` and `Communications.BLE_QUEUE_FULL` even though the memory usage is about 6% of the available RAM. We would appreciate any leads others may have on how to solve this issue.
diff --git a/TroubleShooting.md b/TroubleShooting.md
index abf1f43..69944bb 100644
--- a/TroubleShooting.md
+++ b/TroubleShooting.md
@@ -13,6 +13,8 @@ Before [raising an issue](https://github.com/house-of-abbey/GarminHomeAssistant/
A failure to get the file format right tends to mean that the response to the application errors with `INVALID_HTTP_BODY_IN_NETWORK_RESPONSE` (code of -400). This means the response did not contain JSON, it was probably an error message in plain text that could not be parsed by the Connect IQ API call. See [Toybox.Communications](https://developer.garmin.com/connect-iq/api-docs/Toybox/Communications.html) for the list of error code you might be presented with on your device.
+There are some cases where the file format may be valid JSON, but invalid against the schema, and the failure to catch this error could cause the application to crash. Whilst we have taken care to manage many issues, there may still be cases that are uncaught.
+
Make sure you can browse to the URL of your JSON file in a standard web browser to make sure it is accessible.
## Watch Menu and API
diff --git a/source/HomeAssistantApp.mc b/source/HomeAssistantApp.mc
index fd75ee3..4c22c69 100644
--- a/source/HomeAssistantApp.mc
+++ b/source/HomeAssistantApp.mc
@@ -173,16 +173,24 @@ class HomeAssistantApp extends Application.AppBase {
break;
case 200:
- if (Settings.getCacheConfig()) {
- Storage.setValue("menu", data as Lang.Dictionary);
- mMenuStatus = WatchUi.loadResource($.Rez.Strings.Cached) as Lang.String;
+ if (data == null) {
+ mMenuStatus = WatchUi.loadResource($.Rez.Strings.Unavailable) as Lang.String;
} else {
- mMenuStatus = WatchUi.loadResource($.Rez.Strings.Available) as Lang.String;
+ if (Settings.getCacheConfig()) {
+ Storage.setValue("menu", data as Lang.Dictionary);
+ mMenuStatus = WatchUi.loadResource($.Rez.Strings.Cached) as Lang.String;
+ } else {
+ mMenuStatus = WatchUi.loadResource($.Rez.Strings.Available) as Lang.String;
+ }
}
if (!mIsGlance) {
- buildMenu(data);
- if (!WidgetApp.isWidget) {
- WatchUi.switchToView(mHaMenu, new HomeAssistantViewDelegate(false), WatchUi.SLIDE_IMMEDIATE);
+ if (data == null) {
+ ErrorView.show(WatchUi.loadResource($.Rez.Strings.NoJson) as Lang.String);
+ } else {
+ buildMenu(data);
+ if (!WidgetApp.isWidget) {
+ WatchUi.switchToView(mHaMenu, new HomeAssistantViewDelegate(false), WatchUi.SLIDE_IMMEDIATE);
+ }
}
}
break;
@@ -401,14 +409,11 @@ class HomeAssistantApp extends Application.AppBase {
// (-101) error. This function is called by a timer every Globals.menuItemUpdateInterval ms.
function updateNextMenuItem() as Void {
var itu = mItemsToUpdate as Lang.Array;
- if (itu == null) {
- // System.println("HomeAssistantApp updateNextMenuItem(): No menu items to update");
- if (!mIsGlance) {
- ErrorView.show(WatchUi.loadResource($.Rez.Strings.ConfigUrlNotFound) as Lang.String);
- }
- } else {
+ if (itu != null) {
itu[mNextItemToUpdate].getState();
mNextItemToUpdate = (mNextItemToUpdate + 1) % itu.size();
+ // } else {
+ // System.println("HomeAssistantApp updateNextMenuItem(): No menu items to update");
}
}
@@ -417,8 +422,7 @@ class HomeAssistantApp extends Application.AppBase {
}
function getGlanceView() as Lang.Array or Null {
- mIsGlance = true;
- // RezStrings.update_glance();
+ mIsGlance = true;
mApiStatus = WatchUi.loadResource($.Rez.Strings.Checking) as Lang.String;
mMenuStatus = WatchUi.loadResource($.Rez.Strings.Checking) as Lang.String;
updateStatus();
diff --git a/source/HomeAssistantView.mc b/source/HomeAssistantView.mc
index a518a29..ac93663 100644
--- a/source/HomeAssistantView.mc
+++ b/source/HomeAssistantView.mc
@@ -41,38 +41,40 @@ class HomeAssistantView extends WatchUi.Menu2 {
}
WatchUi.Menu2.initialize(options);
- var items = definition.get("items") as Lang.Dictionary;
- for(var i = 0; i < items.size(); i++) {
- var type = items[i].get("type") as Lang.String or Null;
- var name = items[i].get("name") as Lang.String or Null;
- var content = items[i].get("content") as Lang.String or Null;
- var entity = items[i].get("entity") as Lang.String or Null;
- var tap_action = items[i].get("tap_action") as Lang.Dictionary or Null;
- var service = items[i].get("service") as Lang.String or Null; // Deprecated schema
- var confirm = false as Lang.Boolean or Null;
- var data = null as Lang.Dictionary or Null;
- if (tap_action != null) {
- service = tap_action.get("service");
- confirm = tap_action.get("confirm"); // Optional
- data = tap_action.get("data"); // Optional
- if (confirm == null) {
- confirm = false;
- }
- }
- if (type != null && name != null) {
- if (type.equals("toggle") && entity != null) {
- addItem(HomeAssistantMenuItemFactory.create().toggle(name, entity, confirm));
- } else if (type.equals("template") && content != null) {
- if (service == null) {
- addItem(HomeAssistantMenuItemFactory.create().template_notap(name, content));
- } else {
- addItem(HomeAssistantMenuItemFactory.create().template_tap(name, entity, content, service, confirm, data));
+ var items = definition.get("items") as Lang.Array;
+ for (var i = 0; i < items.size(); i++) {
+ if (items[i] instanceof(Lang.Dictionary)) {
+ var type = items[i].get("type") as Lang.String or Null;
+ var name = items[i].get("name") as Lang.String or Null;
+ var content = items[i].get("content") as Lang.String or Null;
+ var entity = items[i].get("entity") as Lang.String or Null;
+ var tap_action = items[i].get("tap_action") as Lang.Dictionary or Null;
+ var service = items[i].get("service") as Lang.String or Null; // Deprecated schema
+ var confirm = false as Lang.Boolean or Null;
+ var data = null as Lang.Dictionary or Null;
+ if (tap_action != null) {
+ service = tap_action.get("service");
+ confirm = tap_action.get("confirm"); // Optional
+ data = tap_action.get("data"); // Optional
+ if (confirm == null) {
+ confirm = false;
}
+ }
+ if (type != null && name != null) {
+ if (type.equals("toggle") && entity != null) {
+ addItem(HomeAssistantMenuItemFactory.create().toggle(name, entity, confirm));
+ } else if (type.equals("template") && content != null) {
+ if (service == null) {
+ addItem(HomeAssistantMenuItemFactory.create().template_notap(name, content));
+ } else {
+ addItem(HomeAssistantMenuItemFactory.create().template_tap(name, entity, content, service, confirm, data));
+ }
- } else if (type.equals("tap") && service != null) {
- addItem(HomeAssistantMenuItemFactory.create().tap(name, entity, service, confirm, data));
- } else if (type.equals("group")) {
- addItem(HomeAssistantMenuItemFactory.create().group(items[i]));
+ } else if (type.equals("tap") && service != null) {
+ addItem(HomeAssistantMenuItemFactory.create().tap(name, entity, service, confirm, data));
+ } else if (type.equals("group")) {
+ addItem(HomeAssistantMenuItemFactory.create().group(items[i]));
+ }
}
}
}
diff --git a/source/Settings.mc b/source/Settings.mc
index 7f98f0f..890cf0b 100644
--- a/source/Settings.mc
+++ b/source/Settings.mc
@@ -67,22 +67,19 @@ class Settings {
// Manage this inside the application or widget only (not a glance or background service process)
if (mIsApp) {
- if (mIsBatteryLevelEnabled) {
+ if (mIsBatteryLevelEnabled and mHasService) {
if (getWebhookId().equals("")) {
mWebhookManager = new WebhookManager();
mWebhookManager.requestWebhookId();
- } else if (
- mHasService and
- ((Background.getTemporalEventRegisteredTime() == null) or
- (Background.getTemporalEventRegisteredTime() != (mBatteryRefreshRate * 60)))
- ) {
+ }
+ if ((Background.getTemporalEventRegisteredTime() == null) or
+ (Background.getTemporalEventRegisteredTime() != (mBatteryRefreshRate * 60))) {
Background.registerForTemporalEvent(new Time.Duration(mBatteryRefreshRate * 60)); // Convert to seconds
}
} else {
// Explicitly disable the background event which persists when the application closes.
- if (mHasService and (Background.getTemporalEventRegisteredTime() != null)) {
- Background.deleteTemporalEvent();
- }
+ // If !mHasService disable the Settings option as user feedback
+ unsetIsBatteryLevelEnabled();
unsetWebhookId();
}
}