Added option to turn off automatic menu update checking

This is because it uses additional memory than can cause some older devices to crash unless they have smaller menus.
This commit is contained in:
Philip Abbey
2025-09-13 11:06:29 +01:00
parent e284cd2d4a
commit 7ba0d76bf6
45 changed files with 180 additions and 31 deletions

View File

@@ -31,8 +31,9 @@ class Globals {
//! an ErrorView.
static const scApiResumeMs = 200; // ms
//! Warn the user after fetching the menu if their watch is low on memory before the device crashes.
static const scLowMem = 0.90; // percent as a fraction.
//! Threshold of memory usage (guessed) to consider a device unable to automatically check
//! for a more recent menu due to insufficient memory.
static const scLowMem = 0.85; // Fraction of total memory used.
//! Constant for PIN confirmation dialog.<br>
//! Maximum number of failed PIN confirmation attempts allowed in `scPinMaxFailureMinutes`.

View File

@@ -302,9 +302,8 @@ class HomeAssistantApp extends Application.AppBase {
)
) {
// System.println("HomeAssistantApp fetchMenuConfigBasic(): Fetching JSON menu.");
var phoneConnected = System.getDeviceSettings().phoneConnected;
var internetAvailable = System.getDeviceSettings().connectionAvailable;
if (! phoneConnected or ! internetAvailable) {
if (! System.getDeviceSettings().phoneConnected or ! internetAvailable) {
// System.println("HomeAssistantApp fetchMenuConfigBasic(): No Phone connection, skipping API call.");
var errorRez = $.Rez.Strings.NoPhone;
if (Settings.getWifiLteExecutionEnabled()) {
@@ -447,6 +446,28 @@ class HomeAssistantApp extends Application.AppBase {
return true;
}
//! Takes the actions required to disable the check for an updated menu and notify the user.
//
function disableMenuCheck() as Void {
// As we're out of memory, pretend we've checked and don't try again.
mIsCacheChecked = true;
// Prevent the menu check happening in future.
Settings.unsetMenuCheck();
// Tell the user
var toast = WatchUi.loadResource($.Rez.Strings.MenuCheckDisabled) as Lang.String;
if (mHasToast) {
WatchUi.showToast(toast, null);
} else {
new Alert({
:timeout => Globals.scAlertTimeoutMs,
:font => Graphics.FONT_MEDIUM,
:text => toast,
:fgcolor => Graphics.COLOR_WHITE,
:bgcolor => Graphics.COLOR_BLACK
}).pushView(WatchUi.SLIDE_IMMEDIATE);
}
}
//! Callback function for the menu check GET request.
//!
//! @param responseCode Response code.
@@ -482,11 +503,9 @@ class HomeAssistantApp extends Application.AppBase {
break;
case Communications.NETWORK_RESPONSE_OUT_OF_MEMORY:
// As we're out of memory, pretend we've checked and don't try again.
mIsCacheChecked = true;
// System.println("HomeAssistantApp onReturnCheckMenuConfig() Response Code: NETWORK_RESPONSE_OUT_OF_MEMORY, are we going too fast?");
disableMenuCheck();
var myTimer = new Timer.Timer();
// Now this feels very "closely coupled" to the application, but it is the most reliable method instead of using a timer.
myTimer.start(method(:updateMenuItems), Globals.scApiBackoffMs, false);
break;
@@ -578,7 +597,6 @@ class HomeAssistantApp extends Application.AppBase {
case Communications.NETWORK_RESPONSE_OUT_OF_MEMORY:
// System.println("HomeAssistantApp onReturnUpdateMenuItems() Response Code: NETWORK_RESPONSE_OUT_OF_MEMORY, are we going too fast?");
var myTimer = new Timer.Timer();
// Now this feels very "closely coupled" to the application, but it is the most reliable method instead of using a timer.
myTimer.start(method(:updateMenuItems), Globals.scApiBackoffMs, false);
// Revert status
status = getApiStatus();
@@ -612,9 +630,19 @@ class HomeAssistantApp extends Application.AppBase {
(item as HomeAssistantToggleMenuItem).updateToggleState(data[i.toString() + "t"]);
}
}
if (Settings.getCacheConfig() && !mIsCacheChecked) {
if (Settings.getMenuCheck() && Settings.getCacheConfig() && !mIsCacheChecked) {
// We are caching the menu configuration, so let's fetch it and check if its been updated.
fetchMenuConfigBasic(method(:onReturnCheckMenuConfig));
var stats = System.getSystemStats(); // stats.* values in bytes
// https://developer.garmin.com/connect-iq/core-topics/debugging/, see "Basic Debugging"
// Create a file on the device called /GARMIN/APPS/LOGS/HOMEASSISTANT.TXT in order to log the values here.
System.println("Memory: total=" + stats.totalMemory + ", used=" + stats.usedMemory + ", free=" + stats.freeMemory);
if (stats.usedMemory > (Globals.scLowMem * stats.totalMemory)) {
// Assume insufficient memory
disableMenuCheck();
} else {
// Assume sufficient memory, but the response code might still turn the automatic check off.
fetchMenuConfigBasic(method(:onReturnCheckMenuConfig));
}
} else {
var delay = Settings.getPollDelay();
if (delay > 0) {
@@ -640,9 +668,9 @@ class HomeAssistantApp extends Application.AppBase {
if (mUpdating) {
var phoneConnected = System.getDeviceSettings().phoneConnected;
var connectionAvailable = System.getDeviceSettings().connectionAvailable;
// In Wi-Fi/LTE execution mode, we should not show an error page but use a toast instead.
if (Settings.getWifiLteExecutionEnabled() && (! phoneConnected || ! connectionAvailable)) {
if (Settings.getWifiLteExecutionEnabled() &&
(! phoneConnected || ! connectionAvailable)) {
// Notify only once per disconnection cycle
if (!mNotifiedNoBle) {
var toast = WatchUi.loadResource($.Rez.Strings.NoPhone);
@@ -662,12 +690,10 @@ class HomeAssistantApp extends Application.AppBase {
}).pushView(WatchUi.SLIDE_IMMEDIATE);
}
}
mNotifiedNoBle = true;
mUpdating = false;
setApiStatus(WatchUi.loadResource($.Rez.Strings.Unavailable) as Lang.String);
mUpdateTimer.start(method(:startUpdates), Globals.wifiPollResumeDelayMs, false);
mUpdating = false;
return;
}
@@ -681,7 +707,6 @@ class HomeAssistantApp extends Application.AppBase {
setApiStatus(WatchUi.loadResource($.Rez.Strings.Unavailable) as Lang.String);
} else {
mNotifiedNoBle = false;
if (mItemsToUpdate == null or mTemplates == null) {
mItemsToUpdate = mHaMenu.getItemsToUpdate();
mTemplates = {};
@@ -801,7 +826,7 @@ class HomeAssistantApp extends Application.AppBase {
mApiStatus = WatchUi.loadResource($.Rez.Strings.Unconfigured) as Lang.String;
WatchUi.requestUpdate();
} else {
if ( mIsApp && Settings.getWifiLteExecutionEnabled() && (! phoneConnected || ! connectionAvailable)) {
if (mIsApp && Settings.getWifiLteExecutionEnabled() && (! phoneConnected || ! connectionAvailable)) {
// System.println("HomeAssistantApp fetchApiStatus(): In-app Wifi mode (No Phone and Internet connection), early return.");
return;
} else if (! phoneConnected) {

View File

@@ -92,9 +92,9 @@ class HomeAssistantTapMenuItem extends HomeAssistantMenuItem {
);
}
} else if (mConfirm) {
var phoneConnected = System.getDeviceSettings().phoneConnected;
var internetAvailable = System.getDeviceSettings().connectionAvailable;
if ((! phoneConnected || ! internetAvailable) && Settings.getWifiLteExecutionEnabled()) {
if ((! System.getDeviceSettings().phoneConnected ||
! System.getDeviceSettings().connectionAvailable) &&
Settings.getWifiLteExecutionEnabled()) {
var dialogMsg = WatchUi.loadResource($.Rez.Strings.WifiLtePrompt) as Lang.String;
var dialog = new WatchUi.Confirmation(dialogMsg);
WatchUi.pushView(

View File

@@ -319,10 +319,9 @@ class HomeAssistantToggleMenuItem extends WatchUi.ToggleMenuItem {
} else if (mConfirm) {
// Undo the toggle
setEnabled(!isEnabled());
var phoneConnected = System.getDeviceSettings().phoneConnected;
var internetAvailable = System.getDeviceSettings().connectionAvailable;
if ((! phoneConnected || ! internetAvailable) && Settings.getWifiLteExecutionEnabled()) {
if ((! System.getDeviceSettings().phoneConnected ||
! System.getDeviceSettings().connectionAvailable) &&
Settings.getWifiLteExecutionEnabled()) {
wifiPrompt(b);
} else {
var confirmationView = new HomeAssistantConfirmation();

View File

@@ -35,6 +35,7 @@ class Settings {
private static var mConfigUrl as Lang.String = "";
private static var mCacheConfig as Lang.Boolean = false;
private static var mClearCache as Lang.Boolean = false;
private static var mMenuCheck as Lang.Boolean = false;
private static var mVibrate as Lang.Boolean = false;
private static var mWifiLteExecution as Lang.Boolean = false;
//! seconds
@@ -68,6 +69,7 @@ class Settings {
mConfigUrl = Properties.getValue("config_url");
mCacheConfig = Properties.getValue("cache_config");
mClearCache = Properties.getValue("clear_cache");
mMenuCheck = Properties.getValue("enable_menu_update_check");
mWifiLteExecution = Properties.getValue("wifi_lte_execution");
mVibrate = Properties.getValue("enable_vibration");
mAppTimeout = Properties.getValue("app_timeout");
@@ -80,6 +82,16 @@ class Settings {
mUserHeaderName = Properties.getValue("user_http_header_name");
mUserHeaderValue = Properties.getValue("user_http_header_value");
mClearWebhookId = Properties.getValue("clear_webhook_id");
if (mIsApp && mMenuCheck && !mCacheConfig) {
unsetMenuCheck();
// Tell the user
if (WatchUi has :showToast) {
WatchUi.showToast(WatchUi.loadResource($.Rez.Strings.MenuCheckDisabled) as Lang.String, null);
//} else {
// NB. Cannot show an Alert() here.
}
}
}
//! A webhook is required for non-privileged API calls.
@@ -225,6 +237,24 @@ class Settings {
Properties.setValue("clear_cache", mClearCache);
}
//! Get the menu check Boolean option supplied as part of the Settings.
//!
//! @return Boolean for whether the menu should be checked for updates when
//! the application is started, and the cache updated ready for the
//! next time the application is restarted.
//
static function getMenuCheck() as Lang.Boolean {
return mMenuCheck;
}
//! Unset the menu check Boolean option supplied as part of the Settings. This
//! option should only be set when the menu definition is cached too.
//
static function unsetMenuCheck() {
mMenuCheck = false;
Properties.setValue("enable_menu_update_check", mMenuCheck);
}
//! Get the value of the Wi-Fi/LTE toggle in settings.
//!
//! @return The state of the toggle.