From a3ffabae272eca767006c78b943826d8607c7250 Mon Sep 17 00:00:00 2001 From: Philip Abbey Date: Sun, 25 Jan 2026 16:39:59 +0000 Subject: [PATCH 1/4] 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. --- source/HomeAssistantApp.mc | 22 +++++++++++++----- source/HomeAssistantPinConfirmation.mc | 32 +++++++++++++------------- source/Settings.mc | 8 ++++--- 3 files changed, 37 insertions(+), 25 deletions(-) diff --git a/source/HomeAssistantApp.mc b/source/HomeAssistantApp.mc index 87aef09..799b25b 100644 --- a/source/HomeAssistantApp.mc +++ b/source/HomeAssistantApp.mc @@ -27,6 +27,8 @@ using Toybox.Timer; // (:glance, :background) class HomeAssistantApp extends Application.AppBase { + static const scStorageKeyMenu as Lang.String = "menu"; + private var mHasToast as Lang.Boolean = false; private var mApiStatus 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; } else { 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; } else { mMenuStatus = WatchUi.loadResource($.Rez.Strings.Available) as Lang.String; @@ -246,7 +256,7 @@ class HomeAssistantApp extends Application.AppBase { if (Settings.getClearCache() || !Settings.getCacheConfig()) { 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. @@ -260,10 +270,10 @@ class HomeAssistantApp extends Application.AppBase { mMenuStatus = WatchUi.loadResource($.Rez.Strings.Unconfigured) as Lang.String; WatchUi.requestUpdate(); } 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())) { // System.println("HomeAssistantApp fetchMenuConfig(): Clearing cached menu on user request."); - Storage.deleteValue("menu"); + Storage.deleteValue(scStorageKeyMenu); menu = null; Settings.unsetClearCache(); } @@ -524,10 +534,10 @@ class HomeAssistantApp extends Application.AppBase { case 200: if (data != null) { // '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)) { // System.println("HomeAssistantApp onReturnCheckMenuConfig() New menu found."); - Storage.setValue("menu", data as Lang.Dictionary); + Storage.setValue(scStorageKeyMenu, data as Lang.Dictionary); if (menu != null) { // Notify the the user we have just got a newer menu file var toast = WatchUi.loadResource($.Rez.Strings.MenuUpdated) as Lang.String; diff --git a/source/HomeAssistantPinConfirmation.mc b/source/HomeAssistantPinConfirmation.mc index bccf5b9..9ed1b4b 100644 --- a/source/HomeAssistantPinConfirmation.mc +++ b/source/HomeAssistantPinConfirmation.mc @@ -34,7 +34,7 @@ class PinDigit extends WatchUi.Selectable { // function initialize(digit as Lang.Number, stepX as Lang.Number, stepY as Lang.Number) { var marginX = stepX * 0.05; // 5% margin on all sides - var marginY = stepY * 0.05; + var marginY = stepY * 0.05; var x = (digit == 0) ? stepX : stepX * ((digit+2) % 3); // layout '0' in 2nd col, others ltr in 3 columns x += marginX + HomeAssistantPinConfirmationView.MARGIN_X; var y = (digit == 0) ? stepY * 4 : (digit <= 3) ? stepY : (digit <=6) ? stepY * 2 : stepY * 3; // layout '0' in bottom row (5), others top to bottom in 3 rows (2-4) (row 1 is reserved for masked pin) @@ -216,7 +216,7 @@ class HomeAssistantPinConfirmationDelegate extends WatchUi.BehaviorDelegate { mFailures = new PinFailures(); if (mFailures.isLocked()) { var msg = WatchUi.loadResource($.Rez.Strings.PinInputLocked) + " " + - mFailures.getLockedUntilSeconds() + " " + + mFailures.getLockedUntilSeconds() + " " + WatchUi.loadResource($.Rez.Strings.Seconds); WatchUi.showToast(msg, {}); } @@ -298,7 +298,7 @@ class HomeAssistantPinConfirmationDelegate extends WatchUi.BehaviorDelegate { if (mTimer != null) { mTimer.stop(); } - + WatchUi.popView(WatchUi.SLIDE_RIGHT); } @@ -310,7 +310,7 @@ class HomeAssistantPinConfirmationDelegate extends WatchUi.BehaviorDelegate { if (Attention has :vibrate && Settings.getVibrate()) { Attention.vibrate([ new Attention.VibeProfile(100, 100), - new Attention.VibeProfile( 0, 200), + new Attention.VibeProfile( 0, 200), new Attention.VibeProfile( 75, 100), new Attention.VibeProfile( 0, 200), new Attention.VibeProfile( 50, 100), @@ -337,10 +337,10 @@ class HomeAssistantPinConfirmationDelegate extends WatchUi.BehaviorDelegate { //! Manage PIN entry failures to try and prevent brute force exhaustion by inserting delays in retries. // class PinFailures { - - const STORAGE_KEY_FAILURES as Lang.String = "pin_failures"; - const STORAGE_KEY_LOCKED as Lang.String = "pin_locked"; - + + static const scStorageKeyFailures as Lang.String = "pin_failures"; + static const scStorageKeyLocked as Lang.String = "pin_locked"; + private var mFailures as Lang.Array; private var mLockedUntil as Lang.Number?; @@ -348,9 +348,9 @@ class PinFailures { // function initialize() { // 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; - 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. @@ -368,11 +368,11 @@ class PinFailures { } else { mFailures = []; 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); } } - 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 @@ -382,8 +382,8 @@ class PinFailures { // System.println("PinFailures reset() Resetting failures"); mFailures = []; mLockedUntil = null; - Application.Storage.deleteValue(STORAGE_KEY_FAILURES); - Application.Storage.deleteValue(STORAGE_KEY_LOCKED); + Application.Storage.deleteValue(scStorageKeyFailures); + Application.Storage.deleteValue(scStorageKeyLocked); } //! Retrieve the remaining time the application must be locked out for. @@ -399,12 +399,12 @@ class PinFailures { // function isLocked() as Lang.Boolean { if (mLockedUntil == null) { - return false; + return false; } var isLocked = new Time.Moment(Time.now().value()).lessThan(new Time.Moment(mLockedUntil)); if (!isLocked) { mLockedUntil = null; - Application.Storage.deleteValue(STORAGE_KEY_LOCKED); + Application.Storage.deleteValue(scStorageKeyLocked); } return isLocked; } diff --git a/source/Settings.mc b/source/Settings.mc index 4dc435e..0e7e007 100644 --- a/source/Settings.mc +++ b/source/Settings.mc @@ -29,6 +29,8 @@ using Toybox.Time; // (:glance, :background) class Settings { + static const scStorageKeySensorsEn as Lang.String = "sensors_enabled"; + private static var mApiKey as Lang.String? = ""; private static var mWebhookId as Lang.String? = ""; private static var mApiUrl as Lang.String? = ""; @@ -115,9 +117,9 @@ class Settings { } 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. - // Storage.getValue("sensors_enabled") returns true, false, or null - if (mIsSensorsEnabled != Storage.getValue("sensors_enabled")) { - Storage.setValue("sensors_enabled", mIsSensorsEnabled); + // Storage.getValue(scStorageKeySensorsEn) returns true, false, or null + if (mIsSensorsEnabled != Storage.getValue(scStorageKeySensorsEn)) { + Storage.setValue(scStorageKeySensorsEn, mIsSensorsEnabled); mWebhookManager.registerWebhookSensors(); } } From d8ddd19b5154e20e2a255e5fe968ef761060cd6f Mon Sep 17 00:00:00 2001 From: Philip Abbey Date: Sun, 25 Jan 2026 16:42:42 +0000 Subject: [PATCH 2/4] Update HISTORY.md Added v3.10 text. --- HISTORY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/HISTORY.md b/HISTORY.md index a1ed2b6..428ea6a 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -55,3 +55,4 @@ | 3.7 | Bug fix for `numeric` menu items not working over Wi-Fi & LTE. | | 3.8 | Added icon for `numeric` menu items and revised icons in general. | | 3.9 | Small update to warn users with empty menu definitions to read the instructions! | +| 3.10 | Bug fix for out of memory error in the glance when caching the menu to [`Storage`](https://developer.garmin.com/connect-iq/api-docs/Toybox/Application/Storage.html). This is now delayed until the main application is opened. | From a30f0c621821612258dc3aea4800036ce48ed717 Mon Sep 17 00:00:00 2001 From: Philip Abbey Date: Sun, 25 Jan 2026 17:46:09 +0000 Subject: [PATCH 3/4] Bulk amendment of header comments. Removed "." from the end of all URLs because Copilot complains it can't find the URLs and its too stupid to realise the "." should be omitted. E.g. "https://github.com/house-of-abbey/GarminHomeAssistant/blob/main/LICENSE." --- compile_sim.cmd | 4 ++-- export.cmd | 4 ++-- iconResize.cmd | 4 ++-- iconResize.py | 4 ++-- launcherIconResize.py | 4 ++-- manifest.xml | 4 ++-- monkey.jungle | 4 ++-- removeTranslations.py | 4 ++-- resources-ara/strings/strings.xml | 4 ++-- resources-bul/strings/strings.xml | 4 ++-- resources-ces/strings/strings.xml | 4 ++-- resources-dan/strings/strings.xml | 4 ++-- resources-deu/strings/corrections.xml | 4 ++-- resources-deu/strings/strings.xml | 4 ++-- resources-dut/strings/strings.xml | 4 ++-- resources-est/strings/strings.xml | 4 ++-- resources-fin/strings/strings.xml | 4 ++-- resources-fre/strings/corrections.xml | 4 ++-- resources-fre/strings/strings.xml | 4 ++-- resources-gre/strings/strings.xml | 4 ++-- resources-heb/strings/strings.xml | 4 ++-- resources-hrv/strings/strings.xml | 4 ++-- resources-hun/strings/strings.xml | 4 ++-- resources-icons-18-w/drawables.xml | 4 ++-- resources-icons-18/drawables.xml | 4 ++-- resources-icons-21-w/drawables.xml | 4 ++-- resources-icons-21/drawables.xml | 4 ++-- resources-icons-24/drawables.xml | 4 ++-- resources-icons-26/drawables.xml | 4 ++-- resources-icons-28/drawables.xml | 4 ++-- resources-icons-30/drawables.xml | 4 ++-- resources-icons-32/drawables.xml | 4 ++-- resources-icons-34/drawables.xml | 4 ++-- resources-icons-38/drawables.xml | 4 ++-- resources-icons-42/drawables.xml | 4 ++-- resources-icons-46/drawables.xml | 4 ++-- resources-icons-48/drawables.xml | 4 ++-- resources-icons-53/drawables.xml | 4 ++-- resources-icons-55/drawables.xml | 4 ++-- resources-ind/strings/strings.xml | 4 ++-- resources-ita/strings/strings.xml | 4 ++-- resources-jpn/strings/strings.xml | 4 ++-- resources-kor/strings/strings.xml | 4 ++-- resources-launcher-26-26/drawables.xml | 4 ++-- resources-launcher-30-30/drawables.xml | 4 ++-- resources-launcher-33-33/drawables.xml | 4 ++-- resources-launcher-35-35/drawables.xml | 4 ++-- resources-launcher-36-36/drawables.xml | 4 ++-- resources-launcher-38-38/drawables.xml | 4 ++-- resources-launcher-40-40/drawables.xml | 4 ++-- resources-launcher-52-52/drawables.xml | 4 ++-- resources-launcher-54-54/drawables.xml | 4 ++-- resources-launcher-56-56/drawables.xml | 4 ++-- resources-launcher-60-60/drawables.xml | 4 ++-- resources-launcher-61-61/drawables.xml | 4 ++-- resources-launcher-62-62/drawables.xml | 4 ++-- resources-launcher-65-65/drawables.xml | 4 ++-- resources-launcher-68-68/drawables.xml | 4 ++-- resources-launcher-70-70/drawables.xml | 4 ++-- resources-launcher-80-80/drawables.xml | 4 ++-- resources-lav/strings/strings.xml | 4 ++-- resources-lit/strings/strings.xml | 4 ++-- resources-nob/strings/strings.xml | 4 ++-- resources-pol/strings/strings.xml | 4 ++-- resources-por/strings/strings.xml | 4 ++-- resources-ron/strings/strings.xml | 4 ++-- resources-slo/strings/corrections.xml | 4 ++-- resources-slo/strings/strings.xml | 4 ++-- resources-slv/strings/strings.xml | 4 ++-- resources-spa/strings/strings.xml | 4 ++-- resources-swe/strings/strings.xml | 4 ++-- resources-tha/strings/strings.xml | 4 ++-- resources-tur/strings/strings.xml | 4 ++-- resources-ukr/strings/strings.xml | 4 ++-- resources-vie/strings/strings.xml | 4 ++-- resources-zhs/strings/strings.xml | 4 ++-- resources-zht/strings/strings.xml | 4 ++-- resources-zsm/strings/strings.xml | 4 ++-- resources/settings/properties.xml | 4 ++-- resources/settings/settings.xml | 4 ++-- resources/strings/strings.xml | 4 ++-- source/Alert.mc | 4 ++-- source/BackgroundServiceDelegate.mc | 4 ++-- source/ClientId.mc.unpopulated | 4 ++-- source/ErrorView.mc | 4 ++-- source/Globals.mc | 4 ++-- source/HomeAssistantApp.mc | 4 ++-- source/HomeAssistantConfirmation.mc | 4 ++-- source/HomeAssistantGlanceView.mc | 4 ++-- source/HomeAssistantGroupMenuItem.mc | 4 ++-- source/HomeAssistantMenuItem.mc | 4 ++-- source/HomeAssistantMenuItemFactory.mc | 4 ++-- source/HomeAssistantNumericFactory.mc | 4 ++-- source/HomeAssistantNumericMenuItem.mc | 4 ++-- source/HomeAssistantNumericPicker.mc | 4 ++-- source/HomeAssistantPinConfirmation.mc | 4 ++-- source/HomeAssistantService.mc | 4 ++-- source/HomeAssistantSyncDelegate.mc | 4 ++-- source/HomeAssistantTapMenuItem.mc | 4 ++-- source/HomeAssistantToggleMenuItem.mc | 4 ++-- source/HomeAssistantView.mc | 4 ++-- source/QuitTimer.mc | 4 ++-- source/ScalableView.mc | 4 ++-- source/Settings.mc | 4 ++-- source/WebLog.mc | 4 ++-- source/WebhookManager.mc | 4 ++-- source/WifiLteExecutionConfirmDelegate.mc | 4 ++-- translate.cmd | 4 ++-- translate.py | 4 ++-- 109 files changed, 218 insertions(+), 218 deletions(-) diff --git a/compile_sim.cmd b/compile_sim.cmd index 43c0a88..4f8d418 100644 --- a/compile_sim.cmd +++ b/compile_sim.cmd @@ -2,13 +2,13 @@ rem ----------------------------------------------------------------------------------- rem rem Distributed under MIT Licence -rem See https://github.com/house-of-abbey/GarminHomeAssistant/blob/main/LICENSE. +rem See https://github.com/house-of-abbey/GarminHomeAssistant/blob/main/LICENSE rem rem ----------------------------------------------------------------------------------- rem rem GarminHomeAssistant is a Garmin IQ application written in Monkey C and routinely rem tested on a Venu 2 device. The source code is provided at: -rem https://github.com/house-of-abbey/GarminHomeAssistant. +rem https://github.com/house-of-abbey/GarminHomeAssistant rem rem J D Abbey & P A Abbey, 28 December 2022 rem diff --git a/export.cmd b/export.cmd index c4b0b72..e7d1848 100644 --- a/export.cmd +++ b/export.cmd @@ -2,13 +2,13 @@ rem ----------------------------------------------------------------------------------- rem rem Distributed under MIT Licence -rem See https://github.com/house-of-abbey/GarminHomeAssistant/blob/main/LICENSE. +rem See https://github.com/house-of-abbey/GarminHomeAssistant/blob/main/LICENSE rem rem ----------------------------------------------------------------------------------- rem rem GarminHomeAssistant is a Garmin IQ application written in Monkey C and routinely rem tested on a Venu 2 device. The source code is provided at: -rem https://github.com/house-of-abbey/GarminHomeAssistant. +rem https://github.com/house-of-abbey/GarminHomeAssistant rem rem J D Abbey & P A Abbey, 28 December 2022 rem diff --git a/iconResize.cmd b/iconResize.cmd index 4ba803f..be8c983 100644 --- a/iconResize.cmd +++ b/iconResize.cmd @@ -2,13 +2,13 @@ rem ----------------------------------------------------------------------------------- rem rem Distributed under MIT Licence -rem See https://github.com/house-of-abbey/GarminHomeAssistant/blob/main/LICENSE. +rem See https://github.com/house-of-abbey/GarminHomeAssistant/blob/main/LICENSE rem rem ----------------------------------------------------------------------------------- rem rem GarminHomeAssistant is a Garmin IQ application written in Monkey C and routinely rem tested on a Venu 2 device. The source code is provided at: -rem https://github.com/house-of-abbey/GarminHomeAssistant. +rem https://github.com/house-of-abbey/GarminHomeAssistant rem rem J D Abbey & P A Abbey, 11 November 2025 rem diff --git a/iconResize.py b/iconResize.py index 41cdb11..db87733 100644 --- a/iconResize.py +++ b/iconResize.py @@ -1,13 +1,13 @@ #################################################################################### # # Distributed under MIT Licence -# See https://github.com/house-of-abbey/GarminHomeAssistant/blob/main/LICENSE. +# See https://github.com/house-of-abbey/GarminHomeAssistant/blob/main/LICENSE # #################################################################################### # # GarminHomeAssistant is a Garmin IQ application written in Monkey C and routinely # tested on a Venu 2 device. The source code is provided at: -# https://github.com/house-of-abbey/GarminHomeAssistant. +# https://github.com/house-of-abbey/GarminHomeAssistant # # J D Abbey & P A Abbey, 28 December 2022 # diff --git a/launcherIconResize.py b/launcherIconResize.py index 1d53941..402e109 100644 --- a/launcherIconResize.py +++ b/launcherIconResize.py @@ -1,13 +1,13 @@ #################################################################################### # # Distributed under MIT Licence -# See https://github.com/house-of-abbey/GarminHomeAssistant/blob/main/LICENSE. +# See https://github.com/house-of-abbey/GarminHomeAssistant/blob/main/LICENSE # #################################################################################### # # GarminHomeAssistant is a Garmin IQ application written in Monkey C and routinely # tested on a Venu 2 device. The source code is provided at: -# https://github.com/house-of-abbey/GarminHomeAssistant. +# https://github.com/house-of-abbey/GarminHomeAssistant # # J D Abbey & P A Abbey, 29 December 2023 # diff --git a/manifest.xml b/manifest.xml index dbdd144..3feee19 100644 --- a/manifest.xml +++ b/manifest.xml @@ -2,12 +2,12 @@