From a6aed8d30efe60ab6868af1b734b868b8976db2c Mon Sep 17 00:00:00 2001 From: jpirnay Date: Mon, 20 Apr 2026 20:40:38 +0200 Subject: [PATCH 1/4] Add additional timezones --- lib/hal/HalClock.cpp | 3 ++ src/CrossPointSettings.h | 3 ++ .../settings/ClockSettingsActivity.cpp | 39 +++++++++++++++---- .../settings/DetectTimezoneActivity.cpp | 9 +++++ src/activities/settings/SettingInfo.cpp | 6 ++- src/activities/settings/SettingInfo.h | 1 + src/network/CrossPointWebServer.cpp | 10 ++++- 7 files changed, 60 insertions(+), 11 deletions(-) diff --git a/lib/hal/HalClock.cpp b/lib/hal/HalClock.cpp index 0264556b..c6f7ce0a 100644 --- a/lib/hal/HalClock.cpp +++ b/lib/hal/HalClock.cpp @@ -111,6 +111,9 @@ static constexpr TimeZoneEntry TIMEZONES[] = { {"CST6CDT,M3.2.0/2,M11.1.0/2"}, {"MST7MDT,M3.2.0/2,M11.1.0/2"}, {"PST8PDT,M3.2.0/2,M11.1.0/2"}, + {"AST4ADT,M3.2.0/2,M11.1.0/2"}, + {"ACST-9:30ACDT,M10.1.0/2,M4.1.0/3"}, + {"AKST9AKDT,M3.2.0/2,M11.1.0/2"}, }; // ---- NVS helpers ---------------------------------------------------------- diff --git a/src/CrossPointSettings.h b/src/CrossPointSettings.h index 7334ca71..e19eaf29 100644 --- a/src/CrossPointSettings.h +++ b/src/CrossPointSettings.h @@ -184,6 +184,9 @@ class CrossPointSettings { TZ_CST = 13, TZ_MST = 14, TZ_PST = 15, + TZ_AST_ADT = 16, + TZ_ACST_ACDT = 17, + TZ_AKST_AKDT = 18, TIMEZONE_COUNT }; diff --git a/src/activities/settings/ClockSettingsActivity.cpp b/src/activities/settings/ClockSettingsActivity.cpp index ca9c2aeb..06026083 100644 --- a/src/activities/settings/ClockSettingsActivity.cpp +++ b/src/activities/settings/ClockSettingsActivity.cpp @@ -15,7 +15,8 @@ const StrId timeZoneNames[CrossPointSettings::TIMEZONE_COUNT] = { StrId::STR_TZ_UTC, StrId::STR_TZ_CET, StrId::STR_TZ_EET, StrId::STR_TZ_MSK, StrId::STR_TZ_UTC_PLUS4, StrId::STR_TZ_IST, StrId::STR_TZ_UTC_PLUS7, StrId::STR_TZ_UTC_PLUS8, StrId::STR_TZ_UTC_PLUS9, StrId::STR_TZ_AEST, StrId::STR_TZ_NZST, StrId::STR_TZ_UTC_MINUS3, - StrId::STR_TZ_EST, StrId::STR_TZ_CST, StrId::STR_TZ_MST, StrId::STR_TZ_PST}; + StrId::STR_TZ_EST, StrId::STR_TZ_CST, StrId::STR_TZ_MST, StrId::STR_TZ_PST, + StrId::STR_TZ_UTC, StrId::STR_TZ_UTC, StrId::STR_TZ_UTC}; } // namespace void ClockSettingsActivity::buildMenuItems() { @@ -25,13 +26,35 @@ void ClockSettingsActivity::buildMenuItems() { SettingInfo::Toggle(StrId::STR_USE_CLOCK, &CrossPointSettings::useClock, "useClock", StrId::STR_CAT_SYSTEM)); menuItems.push_back(SettingInfo::Enum(StrId::STR_CLOCK_FORMAT, &CrossPointSettings::clockFormat12h, {StrId::STR_24H, StrId::STR_12H}, "clockFormat12h", StrId::STR_CAT_SYSTEM)); - menuItems.push_back( - SettingInfo::Enum(StrId::STR_TIMEZONE, &CrossPointSettings::timeZone, - {StrId::STR_TZ_UTC, StrId::STR_TZ_CET, StrId::STR_TZ_EET, StrId::STR_TZ_MSK, - StrId::STR_TZ_UTC_PLUS4, StrId::STR_TZ_IST, StrId::STR_TZ_UTC_PLUS7, StrId::STR_TZ_UTC_PLUS8, - StrId::STR_TZ_UTC_PLUS9, StrId::STR_TZ_AEST, StrId::STR_TZ_NZST, StrId::STR_TZ_UTC_MINUS3, - StrId::STR_TZ_EST, StrId::STR_TZ_CST, StrId::STR_TZ_MST, StrId::STR_TZ_PST}, - "timeZone", StrId::STR_CAT_SYSTEM)); + { + auto tzSetting = SettingInfo::Enum( + StrId::STR_TIMEZONE, &CrossPointSettings::timeZone, + {StrId::STR_TZ_UTC, StrId::STR_TZ_CET, StrId::STR_TZ_EET, StrId::STR_TZ_MSK, StrId::STR_TZ_UTC_PLUS4, + StrId::STR_TZ_IST, StrId::STR_TZ_UTC_PLUS7, StrId::STR_TZ_UTC_PLUS8, StrId::STR_TZ_UTC_PLUS9, + StrId::STR_TZ_AEST, StrId::STR_TZ_NZST, StrId::STR_TZ_UTC_MINUS3, StrId::STR_TZ_EST, StrId::STR_TZ_CST, + StrId::STR_TZ_MST, StrId::STR_TZ_PST, StrId::STR_TZ_UTC, StrId::STR_TZ_UTC, StrId::STR_TZ_UTC}, + "timeZone", StrId::STR_CAT_SYSTEM); + tzSetting.enumLabels = {"UTC (GMT/BST)", + "Central European (CET/CEST)", + "Eastern European (EET/EEST)", + "Moscow (MSK)", + "UTC+4", + "India (IST)", + "UTC+7", + "UTC+8", + "UTC+9", + "Australia Eastern (AEST/AEDT)", + "New Zealand (NZST/NZDT)", + "UTC-3", + "Eastern US/Canada (EST/EDT)", + "Central US/Canada (CST/CDT)", + "Mountain US/Canada (MST/MDT)", + "Pacific US/Canada (PST/PDT)", + "Atlantic Canada (AST/ADT)", + "Australia Central (ACST/ACDT)", + "Alaska (AKST/AKDT)"}; + menuItems.push_back(std::move(tzSetting)); + } menuItems.push_back(SettingInfo::Action(StrId::STR_DETECT_TIMEZONE, SettingAction::DetectTimezone) .withSubcategory(StrId::STR_READER_TOOLS)); diff --git a/src/activities/settings/DetectTimezoneActivity.cpp b/src/activities/settings/DetectTimezoneActivity.cpp index e5ec6a9d..ed2110cf 100644 --- a/src/activities/settings/DetectTimezoneActivity.cpp +++ b/src/activities/settings/DetectTimezoneActivity.cpp @@ -56,6 +56,15 @@ bool mapIanaTimezone(const std::string& tz, uint8_t& outSetting) { outSetting = TZ::TZ_PST; return true; } + if (tz == "America/Halifax" || tz == "America/Glace_Bay" || tz == "America/Moncton" || tz == "America/Thule") { + outSetting = TZ::TZ_AST_ADT; + return true; + } + if (tz == "America/Anchorage" || tz == "America/Juneau" || tz == "America/Nome" || tz == "America/Sitka" || + tz == "America/Yakutat" || tz == "America/Metlakatla") { + outSetting = TZ::TZ_AKST_AKDT; + return true; + } if (tz == "America/Sao_Paulo" || tz == "America/Argentina/Buenos_Aires" || tz == "America/Montevideo") { outSetting = TZ::TZ_UTC_MINUS3; return true; diff --git a/src/activities/settings/SettingInfo.cpp b/src/activities/settings/SettingInfo.cpp index f7f40825..912f8556 100644 --- a/src/activities/settings/SettingInfo.cpp +++ b/src/activities/settings/SettingInfo.cpp @@ -32,6 +32,10 @@ std::string SettingInfo::getDisplayValue() const { value = callValueGetter(); else return {}; + if (!enumLabels.empty()) { + if (value < enumLabels.size()) return enumLabels[value]; + return {}; + } if (value < enumValues.size()) return std::string(I18N.get(enumValues[value])); return {}; } @@ -61,7 +65,7 @@ void SettingInfo::toggleValue() const { break; case SettingType::ENUM: { - const auto count = static_cast(enumValues.size()); + const auto count = static_cast(enumLabels.empty() ? enumValues.size() : enumLabels.size()); if (count == 0) break; if (valuePtr) { SETTINGS.*(valuePtr) = (SETTINGS.*(valuePtr) + 1) % count; diff --git a/src/activities/settings/SettingInfo.h b/src/activities/settings/SettingInfo.h index 4a46d2ed..9baaecdc 100644 --- a/src/activities/settings/SettingInfo.h +++ b/src/activities/settings/SettingInfo.h @@ -32,6 +32,7 @@ struct SettingInfo { SettingType type; uint8_t CrossPointSettings::* valuePtr = nullptr; std::vector enumValues; + std::vector enumLabels; SettingAction action = SettingAction::None; struct ValueRange { diff --git a/src/network/CrossPointWebServer.cpp b/src/network/CrossPointWebServer.cpp index 1d922ce8..ad04dc13 100644 --- a/src/network/CrossPointWebServer.cpp +++ b/src/network/CrossPointWebServer.cpp @@ -1238,8 +1238,14 @@ void CrossPointWebServer::handleGetSettings() const { doc["value"] = static_cast(s.callValueGetter()); } JsonArray options = doc["options"].to(); - for (const auto& opt : s.enumValues) { - options.add(I18N.get(opt)); + if (!s.enumLabels.empty()) { + for (const auto& opt : s.enumLabels) { + options.add(opt); + } + } else { + for (const auto& opt : s.enumValues) { + options.add(I18N.get(opt)); + } } break; } From a00712a085974f7592e2196f38b1697c84c20082 Mon Sep 17 00:00:00 2001 From: jpirnay Date: Mon, 20 Apr 2026 20:50:33 +0200 Subject: [PATCH 2/4] Adjust warnings for x3 --- lib/I18n/translations/english.yaml | 3 ++- src/activities/settings/ClockSettingsActivity.cpp | 15 ++++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/lib/I18n/translations/english.yaml b/lib/I18n/translations/english.yaml index c30e04b0..492c76ac 100644 --- a/lib/I18n/translations/english.yaml +++ b/lib/I18n/translations/english.yaml @@ -112,7 +112,8 @@ STR_SHOW_FILE_EXTENSIONS: "Show File Extensions" STR_USE_CLOCK: "Use Clock" STR_USE_WEATHER: "Use Weather" STR_CLOCK_SETTINGS: "Clock Settings" -STR_CLOCK_SETTINGS_WARNING: "Uses more battery; clock may drift" +STR_CLOCK_SETTINGS_WARNING_BATTERY: "Uses more battery" +STR_CLOCK_SETTINGS_WARNING_DRIFT: "Clock may drift" STR_CLOCK: "Clock" STR_CLOCK_FORMAT: "Clock Format" STR_TIMEZONE: "Timezone" diff --git a/src/activities/settings/ClockSettingsActivity.cpp b/src/activities/settings/ClockSettingsActivity.cpp index 06026083..ae3e42f1 100644 --- a/src/activities/settings/ClockSettingsActivity.cpp +++ b/src/activities/settings/ClockSettingsActivity.cpp @@ -2,14 +2,18 @@ #include #include +#include #include +#include + #include "CrossPointSettings.h" #include "DetectTimezoneActivity.h" #include "SyncTimeActivity.h" #include "components/UITheme.h" #include "fontIds.h" + namespace { const StrId timeZoneNames[CrossPointSettings::TIMEZONE_COUNT] = { StrId::STR_TZ_UTC, StrId::STR_TZ_CET, StrId::STR_TZ_EET, StrId::STR_TZ_MSK, @@ -96,10 +100,19 @@ void ClockSettingsActivity::render(RenderLock&&) { GUI.drawHeader(renderer, Rect(contentRect.x, contentRect.y + metrics.topPadding, contentRect.width, metrics.headerHeight), tr(STR_CLOCK_SETTINGS)); + + const char* batteryWarning = tr(STR_CLOCK_SETTINGS_WARNING_BATTERY); + const char* driftWarning = tr(STR_CLOCK_SETTINGS_WARNING_DRIFT); + + std::string warning = driftWarning; + if (!gpio.deviceIsX3()) { + warning = std::string(batteryWarning) + "; " + driftWarning; + } + GUI.drawSubHeader(renderer, Rect(contentRect.x, contentRect.y + metrics.topPadding + metrics.headerHeight, contentRect.width, metrics.tabBarHeight), - tr(STR_CLOCK_SETTINGS_WARNING)); + warning.c_str()); const int contentTop = contentRect.y + metrics.topPadding + metrics.headerHeight + metrics.tabBarHeight + metrics.verticalSpacing; From 3a4923c8d54abf1464362d5b848c3dcf6a4b1085 Mon Sep 17 00:00:00 2001 From: jpirnay Date: Mon, 20 Apr 2026 21:00:20 +0200 Subject: [PATCH 3/4] yaclf --- src/activities/settings/ClockSettingsActivity.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/activities/settings/ClockSettingsActivity.cpp b/src/activities/settings/ClockSettingsActivity.cpp index ae3e42f1..8ec04d42 100644 --- a/src/activities/settings/ClockSettingsActivity.cpp +++ b/src/activities/settings/ClockSettingsActivity.cpp @@ -13,7 +13,6 @@ #include "components/UITheme.h" #include "fontIds.h" - namespace { const StrId timeZoneNames[CrossPointSettings::TIMEZONE_COUNT] = { StrId::STR_TZ_UTC, StrId::STR_TZ_CET, StrId::STR_TZ_EET, StrId::STR_TZ_MSK, From 425c1db13281d9fb6ac5aa1e3120cc60565f1d95 Mon Sep 17 00:00:00 2001 From: jpirnay Date: Mon, 20 Apr 2026 21:16:31 +0200 Subject: [PATCH 4/4] Review comments --- lib/I18n/translations/english.yaml | 3 ++ .../settings/ClockSettingsActivity.cpp | 30 +------------------ .../settings/DetectTimezoneActivity.cpp | 4 +++ src/activities/settings/SettingInfo.h | 6 ++++ src/network/CrossPointWebServer.cpp | 3 +- 5 files changed, 16 insertions(+), 30 deletions(-) diff --git a/lib/I18n/translations/english.yaml b/lib/I18n/translations/english.yaml index 492c76ac..b7836f90 100644 --- a/lib/I18n/translations/english.yaml +++ b/lib/I18n/translations/english.yaml @@ -126,6 +126,9 @@ STR_TZ_EST: "US Eastern (EST/EDT)" STR_TZ_CST: "US Central (CST/CDT)" STR_TZ_MST: "US Mountain (MST/MDT)" STR_TZ_PST: "US Pacific (PST/PDT)" +STR_TZ_AST_ADT: "Atlantic Canada (AST/ADT)" +STR_TZ_ACST_ACDT: "Australia Central (ACST/ACDT)" +STR_TZ_AKST_AKDT: "Alaska (AKST/AKDT)" STR_TZ_AEST: "Australia Eastern (AEST/AEDT)" STR_TZ_NZST: "New Zealand (NZST/NZDT)" STR_TZ_MSK: "Russia (MSK)" diff --git a/src/activities/settings/ClockSettingsActivity.cpp b/src/activities/settings/ClockSettingsActivity.cpp index 8ec04d42..f0d64a1f 100644 --- a/src/activities/settings/ClockSettingsActivity.cpp +++ b/src/activities/settings/ClockSettingsActivity.cpp @@ -13,15 +13,6 @@ #include "components/UITheme.h" #include "fontIds.h" -namespace { -const StrId timeZoneNames[CrossPointSettings::TIMEZONE_COUNT] = { - StrId::STR_TZ_UTC, StrId::STR_TZ_CET, StrId::STR_TZ_EET, StrId::STR_TZ_MSK, - StrId::STR_TZ_UTC_PLUS4, StrId::STR_TZ_IST, StrId::STR_TZ_UTC_PLUS7, StrId::STR_TZ_UTC_PLUS8, - StrId::STR_TZ_UTC_PLUS9, StrId::STR_TZ_AEST, StrId::STR_TZ_NZST, StrId::STR_TZ_UTC_MINUS3, - StrId::STR_TZ_EST, StrId::STR_TZ_CST, StrId::STR_TZ_MST, StrId::STR_TZ_PST, - StrId::STR_TZ_UTC, StrId::STR_TZ_UTC, StrId::STR_TZ_UTC}; -} // namespace - void ClockSettingsActivity::buildMenuItems() { menuItems.reserve(6); menuItems.push_back(SettingInfo::Separator(StrId::STR_SETTINGS_TITLE)); @@ -35,27 +26,8 @@ void ClockSettingsActivity::buildMenuItems() { {StrId::STR_TZ_UTC, StrId::STR_TZ_CET, StrId::STR_TZ_EET, StrId::STR_TZ_MSK, StrId::STR_TZ_UTC_PLUS4, StrId::STR_TZ_IST, StrId::STR_TZ_UTC_PLUS7, StrId::STR_TZ_UTC_PLUS8, StrId::STR_TZ_UTC_PLUS9, StrId::STR_TZ_AEST, StrId::STR_TZ_NZST, StrId::STR_TZ_UTC_MINUS3, StrId::STR_TZ_EST, StrId::STR_TZ_CST, - StrId::STR_TZ_MST, StrId::STR_TZ_PST, StrId::STR_TZ_UTC, StrId::STR_TZ_UTC, StrId::STR_TZ_UTC}, + StrId::STR_TZ_MST, StrId::STR_TZ_PST, StrId::STR_TZ_AST_ADT, StrId::STR_TZ_ACST_ACDT, StrId::STR_TZ_AKST_AKDT}, "timeZone", StrId::STR_CAT_SYSTEM); - tzSetting.enumLabels = {"UTC (GMT/BST)", - "Central European (CET/CEST)", - "Eastern European (EET/EEST)", - "Moscow (MSK)", - "UTC+4", - "India (IST)", - "UTC+7", - "UTC+8", - "UTC+9", - "Australia Eastern (AEST/AEDT)", - "New Zealand (NZST/NZDT)", - "UTC-3", - "Eastern US/Canada (EST/EDT)", - "Central US/Canada (CST/CDT)", - "Mountain US/Canada (MST/MDT)", - "Pacific US/Canada (PST/PDT)", - "Atlantic Canada (AST/ADT)", - "Australia Central (ACST/ACDT)", - "Alaska (AKST/AKDT)"}; menuItems.push_back(std::move(tzSetting)); } diff --git a/src/activities/settings/DetectTimezoneActivity.cpp b/src/activities/settings/DetectTimezoneActivity.cpp index ed2110cf..2f1560ee 100644 --- a/src/activities/settings/DetectTimezoneActivity.cpp +++ b/src/activities/settings/DetectTimezoneActivity.cpp @@ -65,6 +65,10 @@ bool mapIanaTimezone(const std::string& tz, uint8_t& outSetting) { outSetting = TZ::TZ_AKST_AKDT; return true; } + if (tz == "Australia/Adelaide" || tz == "Australia/Broken_Hill") { + outSetting = TZ::TZ_ACST_ACDT; + return true; + } if (tz == "America/Sao_Paulo" || tz == "America/Argentina/Buenos_Aires" || tz == "America/Montevideo") { outSetting = TZ::TZ_UTC_MINUS3; return true; diff --git a/src/activities/settings/SettingInfo.h b/src/activities/settings/SettingInfo.h index 9baaecdc..d0e684c0 100644 --- a/src/activities/settings/SettingInfo.h +++ b/src/activities/settings/SettingInfo.h @@ -31,6 +31,12 @@ struct SettingInfo { StrId nameId; SettingType type; uint8_t CrossPointSettings::* valuePtr = nullptr; + // Enum values are used as StrId references for localization via I18N.get(). + // If enumLabels is populated, it is authoritative for display and index bounds. + // In that case it must have the same length as enumValues, because consumers + // such as getDisplayValue(), toggleValue(), and CrossPointWebServer::handleGetSettings() + // prefer enumLabels when present. Code paths which validate posted enum values + // should also use enumLabels.size() when enumLabels is non-empty. std::vector enumValues; std::vector enumLabels; SettingAction action = SettingAction::None; diff --git a/src/network/CrossPointWebServer.cpp b/src/network/CrossPointWebServer.cpp index ad04dc13..80261482 100644 --- a/src/network/CrossPointWebServer.cpp +++ b/src/network/CrossPointWebServer.cpp @@ -1325,7 +1325,8 @@ void CrossPointWebServer::handlePostSettings() { } case SettingType::ENUM: { const int val = doc[s.key].as(); - if (val >= 0 && val < static_cast(s.enumValues.size())) { + const auto count = static_cast(s.enumLabels.empty() ? s.enumValues.size() : s.enumLabels.size()); + if (val >= 0 && val < count) { if (s.valuePtr) { SETTINGS.*(s.valuePtr) = static_cast(val); } else if (s.valueSetter) {