Fix to prevent out of memory error in a glance

This commit also includes a minor code tidy for constants, to make their format consistent.
This commit is contained in:
Philip Abbey
2026-01-25 16:39:59 +00:00
parent 34d13865e9
commit a3ffabae27
3 changed files with 37 additions and 25 deletions

View File

@@ -27,6 +27,8 @@ using Toybox.Timer;
// //
(:glance, :background) (:glance, :background)
class HomeAssistantApp extends Application.AppBase { class HomeAssistantApp extends Application.AppBase {
static const scStorageKeyMenu as Lang.String = "menu";
private var mHasToast as Lang.Boolean = false; private var mHasToast as Lang.Boolean = false;
private var mApiStatus as Lang.String?; private var mApiStatus as Lang.String?;
private var mMenuStatus as Lang.String?; private var mMenuStatus as Lang.String?;
@@ -207,7 +209,15 @@ class HomeAssistantApp extends Application.AppBase {
mMenuStatus = WatchUi.loadResource($.Rez.Strings.Unavailable) as Lang.String; mMenuStatus = WatchUi.loadResource($.Rez.Strings.Unavailable) as Lang.String;
} else { } else {
if (Settings.getCacheConfig()) { if (Settings.getCacheConfig()) {
Storage.setValue("menu", data as Lang.Dictionary); // var stats = System.getSystemStats(); // stats.* values in bytes
// System.println("HomeAssistantApp onReturnFetchMenuConfig() Memory: total=" + stats.totalMemory + ", used=" + stats.usedMemory + ", free=" + stats.freeMemory);
if (mIsApp) {
// This call can crash a glance with "Error: Out Of Memory Error"
// https://forums.garmin.com/developer/connect-iq/i/bug-reports/storage-setvalue-should-handle-memory-limits-gracefully
// "Keys and values are limited to 8 KB each, and a total of 128 KB of storage is available."
// "Storage.setValue() fails with an uncatchable out-of-memory error."
Storage.setValue(scStorageKeyMenu, data as Lang.Dictionary);
}
mMenuStatus = WatchUi.loadResource($.Rez.Strings.Cached) as Lang.String; mMenuStatus = WatchUi.loadResource($.Rez.Strings.Cached) as Lang.String;
} else { } else {
mMenuStatus = WatchUi.loadResource($.Rez.Strings.Available) as Lang.String; mMenuStatus = WatchUi.loadResource($.Rez.Strings.Available) as Lang.String;
@@ -246,7 +256,7 @@ class HomeAssistantApp extends Application.AppBase {
if (Settings.getClearCache() || !Settings.getCacheConfig()) { if (Settings.getClearCache() || !Settings.getCacheConfig()) {
return false; return false;
} }
return (Storage.getValue("menu") as Lang.Dictionary) != null; return (Storage.getValue(scStorageKeyMenu) as Lang.Dictionary) != null;
} }
//! Fetch the menu configuration over HTTPS, which might be locally cached. //! Fetch the menu configuration over HTTPS, which might be locally cached.
@@ -260,10 +270,10 @@ class HomeAssistantApp extends Application.AppBase {
mMenuStatus = WatchUi.loadResource($.Rez.Strings.Unconfigured) as Lang.String; mMenuStatus = WatchUi.loadResource($.Rez.Strings.Unconfigured) as Lang.String;
WatchUi.requestUpdate(); WatchUi.requestUpdate();
} else { } else {
var menu = Storage.getValue("menu") as Lang.Dictionary; var menu = Storage.getValue(scStorageKeyMenu) as Lang.Dictionary;
if (menu != null and (Settings.getClearCache() || !Settings.getCacheConfig())) { if (menu != null and (Settings.getClearCache() || !Settings.getCacheConfig())) {
// System.println("HomeAssistantApp fetchMenuConfig(): Clearing cached menu on user request."); // System.println("HomeAssistantApp fetchMenuConfig(): Clearing cached menu on user request.");
Storage.deleteValue("menu"); Storage.deleteValue(scStorageKeyMenu);
menu = null; menu = null;
Settings.unsetClearCache(); Settings.unsetClearCache();
} }
@@ -524,10 +534,10 @@ class HomeAssistantApp extends Application.AppBase {
case 200: case 200:
if (data != null) { if (data != null) {
// 'menu' will be null if caching has just been enabled, but not yet cached locally. // 'menu' will be null if caching has just been enabled, but not yet cached locally.
var menu = Storage.getValue("menu") as Lang.Dictionary; var menu = Storage.getValue(scStorageKeyMenu) as Lang.Dictionary;
if (menu == null || !structuralEquals(data, menu)) { if (menu == null || !structuralEquals(data, menu)) {
// System.println("HomeAssistantApp onReturnCheckMenuConfig() New menu found."); // System.println("HomeAssistantApp onReturnCheckMenuConfig() New menu found.");
Storage.setValue("menu", data as Lang.Dictionary); Storage.setValue(scStorageKeyMenu, data as Lang.Dictionary);
if (menu != null) { if (menu != null) {
// Notify the the user we have just got a newer menu file // Notify the the user we have just got a newer menu file
var toast = WatchUi.loadResource($.Rez.Strings.MenuUpdated) as Lang.String; var toast = WatchUi.loadResource($.Rez.Strings.MenuUpdated) as Lang.String;

View File

@@ -338,8 +338,8 @@ class HomeAssistantPinConfirmationDelegate extends WatchUi.BehaviorDelegate {
// //
class PinFailures { class PinFailures {
const STORAGE_KEY_FAILURES as Lang.String = "pin_failures"; static const scStorageKeyFailures as Lang.String = "pin_failures";
const STORAGE_KEY_LOCKED as Lang.String = "pin_locked"; static const scStorageKeyLocked as Lang.String = "pin_locked";
private var mFailures as Lang.Array<Lang.Number>; private var mFailures as Lang.Array<Lang.Number>;
private var mLockedUntil as Lang.Number?; private var mLockedUntil as Lang.Number?;
@@ -348,9 +348,9 @@ class PinFailures {
// //
function initialize() { function initialize() {
// System.println("PinFailures initialize() Initializing PIN failures from storage"); // System.println("PinFailures initialize() Initializing PIN failures from storage");
var failures = Application.Storage.getValue(PinFailures.STORAGE_KEY_FAILURES); var failures = Application.Storage.getValue(scStorageKeyFailures);
mFailures = (failures == null) ? [] : failures; mFailures = (failures == null) ? [] : failures;
mLockedUntil = Application.Storage.getValue(PinFailures.STORAGE_KEY_LOCKED); mLockedUntil = Application.Storage.getValue(scStorageKeyLocked);
} }
//! Record a PIN entry failure. If too many have occurred lock the application. //! Record a PIN entry failure. If too many have occurred lock the application.
@@ -368,11 +368,11 @@ class PinFailures {
} else { } else {
mFailures = []; mFailures = [];
mLockedUntil = Time.now().add(new Time.Duration(Globals.scPinLockTimeMinutes * Time.Gregorian.SECONDS_PER_MINUTE)).value(); mLockedUntil = Time.now().add(new Time.Duration(Globals.scPinLockTimeMinutes * Time.Gregorian.SECONDS_PER_MINUTE)).value();
Application.Storage.setValue(STORAGE_KEY_LOCKED, mLockedUntil); Application.Storage.setValue(scStorageKeyLocked, mLockedUntil);
// System.println("PinFailures addFailure() Locked until " + mLockedUntil); // System.println("PinFailures addFailure() Locked until " + mLockedUntil);
} }
} }
Application.Storage.setValue(STORAGE_KEY_FAILURES, mFailures); Application.Storage.setValue(scStorageKeyFailures, mFailures);
} }
//! Clear the record of previous PIN entry failures, e.g. because the correct PIN has now been entered //! Clear the record of previous PIN entry failures, e.g. because the correct PIN has now been entered
@@ -382,8 +382,8 @@ class PinFailures {
// System.println("PinFailures reset() Resetting failures"); // System.println("PinFailures reset() Resetting failures");
mFailures = []; mFailures = [];
mLockedUntil = null; mLockedUntil = null;
Application.Storage.deleteValue(STORAGE_KEY_FAILURES); Application.Storage.deleteValue(scStorageKeyFailures);
Application.Storage.deleteValue(STORAGE_KEY_LOCKED); Application.Storage.deleteValue(scStorageKeyLocked);
} }
//! Retrieve the remaining time the application must be locked out for. //! Retrieve the remaining time the application must be locked out for.
@@ -404,7 +404,7 @@ class PinFailures {
var isLocked = new Time.Moment(Time.now().value()).lessThan(new Time.Moment(mLockedUntil)); var isLocked = new Time.Moment(Time.now().value()).lessThan(new Time.Moment(mLockedUntil));
if (!isLocked) { if (!isLocked) {
mLockedUntil = null; mLockedUntil = null;
Application.Storage.deleteValue(STORAGE_KEY_LOCKED); Application.Storage.deleteValue(scStorageKeyLocked);
} }
return isLocked; return isLocked;
} }

View File

@@ -29,6 +29,8 @@ using Toybox.Time;
// //
(:glance, :background) (:glance, :background)
class Settings { class Settings {
static const scStorageKeySensorsEn as Lang.String = "sensors_enabled";
private static var mApiKey as Lang.String? = ""; private static var mApiKey as Lang.String? = "";
private static var mWebhookId as Lang.String? = ""; private static var mWebhookId as Lang.String? = "";
private static var mApiUrl as Lang.String? = ""; private static var mApiUrl as Lang.String? = "";
@@ -115,9 +117,9 @@ class Settings {
} else { } else {
// System.println("Settings update(): Doing just sensor creation."); // System.println("Settings update(): Doing just sensor creation.");
// We already have a Webhook ID, so just enable or disable the sensor in Home Assistant. // We already have a Webhook ID, so just enable or disable the sensor in Home Assistant.
// Storage.getValue("sensors_enabled") returns true, false, or null // Storage.getValue(scStorageKeySensorsEn) returns true, false, or null
if (mIsSensorsEnabled != Storage.getValue("sensors_enabled")) { if (mIsSensorsEnabled != Storage.getValue(scStorageKeySensorsEn)) {
Storage.setValue("sensors_enabled", mIsSensorsEnabled); Storage.setValue(scStorageKeySensorsEn, mIsSensorsEnabled);
mWebhookManager.registerWebhookSensors(); mWebhookManager.registerWebhookSensors();
} }
} }