From 148f8cea4f558f2d364eeb8758f244ad6ccf6d07 Mon Sep 17 00:00:00 2001 From: pelgraine <140762863+pelgraine@users.noreply.github.com> Date: Tue, 17 Mar 2026 17:42:10 +1100 Subject: [PATCH] tdpro lock screen stage 2 - auto lock settings preferences implemented --- examples/companion_radio/NodePrefs.h | 1 + .../companion_radio/ui-new/Settingsscreen.h | 54 +++++++++++++++++++ examples/companion_radio/ui-new/UITask.cpp | 35 ++++++++++++ examples/companion_radio/ui-new/UITask.h | 2 + 4 files changed, 92 insertions(+) diff --git a/examples/companion_radio/NodePrefs.h b/examples/companion_radio/NodePrefs.h index adf93e2a..b4e339f3 100644 --- a/examples/companion_radio/NodePrefs.h +++ b/examples/companion_radio/NodePrefs.h @@ -37,4 +37,5 @@ struct NodePrefs { // persisted to file uint8_t interference_threshold; // Interference threshold in dB (0=disabled, 14+=enabled) uint8_t dark_mode; // 0=off (white bg), 1=on (black bg) uint8_t portrait_mode; // 0=landscape, 1=portrait — T5S3 only + uint8_t auto_lock_minutes; // 0=disabled, 2/5/10/15/30=auto-lock after idle }; \ No newline at end of file diff --git a/examples/companion_radio/ui-new/Settingsscreen.h b/examples/companion_radio/ui-new/Settingsscreen.h index 8b6af1a8..c86f7f18 100644 --- a/examples/companion_radio/ui-new/Settingsscreen.h +++ b/examples/companion_radio/ui-new/Settingsscreen.h @@ -70,6 +70,24 @@ static inline int findGpsBaudIndex(uint32_t baud) { return 0; } +// Auto-lock timeout options (minutes, 0=disabled) +static const uint8_t AUTO_LOCK_OPTIONS[] = { 0, 2, 5, 10, 15, 30 }; +#define AUTO_LOCK_OPTION_COUNT 6 + +static inline const char* autoLockLabel(uint8_t minutes) { + if (minutes == 0) return "None"; + static char buf[8]; + snprintf(buf, sizeof(buf), "%d min", minutes); + return buf; +} + +static inline int findAutoLockIndex(uint8_t minutes) { + for (int i = 0; i < AUTO_LOCK_OPTION_COUNT; i++) { + if (AUTO_LOCK_OPTIONS[i] == minutes) return i; + } + return 0; +} + // --------------------------------------------------------------------------- // Settings row types // --------------------------------------------------------------------------- @@ -86,6 +104,9 @@ enum SettingsRowType : uint8_t { ROW_DARK_MODE, // Dark mode toggle (inverted display) #if defined(LilyGo_T5S3_EPaper_Pro) ROW_PORTRAIT_MODE, // Portrait orientation toggle +#endif +#if defined(LilyGo_T5S3_EPaper_Pro) || defined(LilyGo_TDeck_Pro) + ROW_AUTO_LOCK, // Auto-lock timeout picker (None/2/5/10/15/30 min) #endif ROW_GPS_BAUD, // GPS baud rate picker (requires reboot) ROW_PATH_HASH_SIZE, // Path hash size (1, 2, or 3 bytes per hop) @@ -312,6 +333,9 @@ private: addRow(ROW_DARK_MODE); #if defined(LilyGo_T5S3_EPaper_Pro) addRow(ROW_PORTRAIT_MODE); +#endif +#if defined(LilyGo_T5S3_EPaper_Pro) || defined(LilyGo_TDeck_Pro) + addRow(ROW_AUTO_LOCK); #endif #ifdef MECK_WIFI_COMPANION addRow(ROW_WIFI_SETUP); @@ -873,6 +897,19 @@ public: break; #endif +#if defined(LilyGo_T5S3_EPaper_Pro) || defined(LilyGo_TDeck_Pro) + case ROW_AUTO_LOCK: + if (editing && _editMode == EDIT_PICKER) { + snprintf(tmp, sizeof(tmp), "< Auto Lock: %s >", + autoLockLabel(AUTO_LOCK_OPTIONS[_editPickerIdx])); + } else { + snprintf(tmp, sizeof(tmp), "Auto Lock: %s", + autoLockLabel(_prefs->auto_lock_minutes)); + } + display.print(tmp); + break; +#endif + #ifdef MECK_WIFI_COMPANION case ROW_WIFI_SETUP: if (WiFi.status() == WL_CONNECTED) { @@ -1493,6 +1530,9 @@ public: } else if (type == ROW_GPS_BAUD) { _editPickerIdx--; if (_editPickerIdx < 0) _editPickerIdx = GPS_BAUD_OPTION_COUNT - 1; + } else if (type == ROW_AUTO_LOCK) { + _editPickerIdx--; + if (_editPickerIdx < 0) _editPickerIdx = AUTO_LOCK_OPTION_COUNT - 1; } else { // Radio preset _editPickerIdx--; @@ -1507,6 +1547,9 @@ public: } else if (type == ROW_GPS_BAUD) { _editPickerIdx++; if (_editPickerIdx >= GPS_BAUD_OPTION_COUNT) _editPickerIdx = 0; + } else if (type == ROW_AUTO_LOCK) { + _editPickerIdx++; + if (_editPickerIdx >= AUTO_LOCK_OPTION_COUNT) _editPickerIdx = 0; } else { // Radio preset _editPickerIdx++; @@ -1524,6 +1567,12 @@ public: _editMode = EDIT_NONE; Serial.printf("Settings: GPS baud set to %lu (reboot to apply)\n", (unsigned long)_prefs->gps_baudrate); + } else if (type == ROW_AUTO_LOCK) { + _prefs->auto_lock_minutes = AUTO_LOCK_OPTIONS[_editPickerIdx]; + the_mesh.savePrefs(); + _editMode = EDIT_NONE; + Serial.printf("Settings: Auto lock = %s\n", + autoLockLabel(_prefs->auto_lock_minutes)); } else { // Apply radio preset if (_editPickerIdx >= 0 && _editPickerIdx < (int)NUM_RADIO_PRESETS) { @@ -1719,6 +1768,11 @@ public: Serial.printf("Settings: Portrait mode = %s\n", _prefs->portrait_mode ? "ON" : "OFF"); break; +#endif +#if defined(LilyGo_T5S3_EPaper_Pro) || defined(LilyGo_TDeck_Pro) + case ROW_AUTO_LOCK: + startEditPicker(findAutoLockIndex(_prefs->auto_lock_minutes)); + break; #endif #ifdef MECK_WIFI_COMPANION case ROW_WIFI_SETUP: { diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index a529271d..b0e4f6df 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -1164,6 +1164,9 @@ void UITask::begin(DisplayDriver* display, SensorManager* sensors, NodePrefs* no ui_started_at = millis(); _alert_expiry = 0; +#if defined(LilyGo_T5S3_EPaper_Pro) || defined(LilyGo_TDeck_Pro) + _lastInputMillis = millis(); +#endif splash = new SplashScreen(this); home = new HomeScreen(this, &rtc_clock, sensors, node_prefs); @@ -1494,6 +1497,9 @@ void UITask::loop() { curr->handleInput(c); _auto_off = millis() + AUTO_OFF_MILLIS; // extend auto-off timer _next_refresh = 100; // trigger refresh +#if defined(LilyGo_T5S3_EPaper_Pro) || defined(LilyGo_TDeck_Pro) + _lastInputMillis = millis(); // Reset auto-lock idle timer +#endif } userLedHandler(); @@ -1642,6 +1648,20 @@ if (curr) curr->poll(); #endif } + // Auto-lock idle timer — runs regardless of display on/off state +#if defined(LilyGo_T5S3_EPaper_Pro) || defined(LilyGo_TDeck_Pro) + if (_node_prefs && _node_prefs->auto_lock_minutes > 0 && !_locked) { + uint8_t alm = _node_prefs->auto_lock_minutes; + // Only act on valid option values (guards against garbage from uninitialised prefs) + if (alm == 2 || alm == 5 || alm == 10 || alm == 15 || alm == 30) { + unsigned long lock_timeout = (unsigned long)alm * 60000UL; + if (millis() - _lastInputMillis >= lock_timeout) { + lockScreen(); + } + } + } +#endif + #ifdef PIN_VIBRATION vibration.loop(); #endif @@ -1683,6 +1703,9 @@ char UITask::checkDisplayOn(char c) { } _auto_off = millis() + AUTO_OFF_MILLIS; // extend auto-off timer _next_refresh = 0; // trigger refresh +#if defined(LilyGo_T5S3_EPaper_Pro) || defined(LilyGo_TDeck_Pro) + _lastInputMillis = millis(); // Reset auto-lock idle timer +#endif } return c; } @@ -1755,6 +1778,10 @@ void UITask::lockScreen() { _locked = true; _screenBeforeLock = curr; setCurrScreen(lock_screen); + // Ensure display is on so lock screen renders (auto-off may have turned it off) + if (_display != NULL && !_display->isOn()) { + _display->turnOn(); + } #if defined(LilyGo_T5S3_EPaper_Pro) board.setBacklight(false); // Save power (T5S3 backlight) #endif @@ -1772,7 +1799,12 @@ void UITask::unlockScreen() { gotoHomeScreen(); } _screenBeforeLock = nullptr; + // Ensure display is on so unlocked screen renders + if (_display != NULL && !_display->isOn()) { + _display->turnOn(); + } _auto_off = millis() + AUTO_OFF_MILLIS; + _lastInputMillis = millis(); // Reset auto-lock idle timer _next_refresh = 0; Serial.println("[UI] Screen unlocked"); } @@ -2007,6 +2039,9 @@ void UITask::injectKey(char c) { } curr->handleInput(c); _auto_off = millis() + AUTO_OFF_MILLIS; // extend auto-off timer +#if defined(LilyGo_T5S3_EPaper_Pro) || defined(LilyGo_TDeck_Pro) + _lastInputMillis = millis(); // Reset auto-lock idle timer +#endif // Debounce refresh when editing UTC offset - e-ink takes 644ms per refresh // so don't queue another render until the current one could have finished if (isEditingHomeScreen()) { diff --git a/examples/companion_radio/ui-new/UITask.h b/examples/companion_radio/ui-new/UITask.h index e549f49a..c1a49a3d 100644 --- a/examples/companion_radio/ui-new/UITask.h +++ b/examples/companion_radio/ui-new/UITask.h @@ -95,6 +95,7 @@ class UITask : public AbstractUITask { UIScreen* lock_screen; // Lock screen (big clock + battery + unread) UIScreen* _screenBeforeLock = nullptr; bool _locked = false; + unsigned long _lastInputMillis = 0; // Auto-lock idle tracking VirtualKeyboard _vkb; bool _vkbActive = false; @@ -104,6 +105,7 @@ class UITask : public AbstractUITask { UIScreen* lock_screen; // Lock screen (big clock + battery + unread) UIScreen* _screenBeforeLock = nullptr; bool _locked = false; + unsigned long _lastInputMillis = 0; // Auto-lock idle tracking #endif void userLedHandler();