Adding timezone support
This commit is contained in:
@@ -85,12 +85,36 @@ STR_SHOW_HIDDEN_FILES: "Show Hidden Files"
|
|||||||
STR_KEEP_CLOCK_ALIVE: "Keep Clock Alive"
|
STR_KEEP_CLOCK_ALIVE: "Keep Clock Alive"
|
||||||
STR_CLOCK: "Clock"
|
STR_CLOCK: "Clock"
|
||||||
STR_CLOCK_FORMAT: "Clock Format"
|
STR_CLOCK_FORMAT: "Clock Format"
|
||||||
|
STR_TIMEZONE: "Timezone"
|
||||||
STR_24H: "24h"
|
STR_24H: "24h"
|
||||||
STR_12H: "12h"
|
STR_12H: "12h"
|
||||||
|
STR_TZ_UTC: "UTC (GMT/BST)"
|
||||||
|
STR_TZ_CET: "Central Europe (CET/CEST)"
|
||||||
|
STR_TZ_EET: "Eastern Europe (EET/EEST)"
|
||||||
|
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_AEST: "Australia Eastern (AEST/AEDT)"
|
||||||
|
STR_TZ_NZST: "New Zealand (NZST/NZDT)"
|
||||||
|
STR_TZ_MSK: "Russia (MSK)"
|
||||||
|
STR_TZ_UTC_MINUS3: "South America (UTC-3)"
|
||||||
|
STR_TZ_UTC_PLUS4: "Gulf (UTC+4)"
|
||||||
|
STR_TZ_IST: "India (UTC+5:30)"
|
||||||
|
STR_TZ_UTC_PLUS7: "SE Asia (UTC+7)"
|
||||||
|
STR_TZ_UTC_PLUS8: "China/SE Asia (UTC+8)"
|
||||||
|
STR_TZ_UTC_PLUS9: "Japan/Korea (UTC+9)"
|
||||||
STR_SYNC_TIME: "Sync Time"
|
STR_SYNC_TIME: "Sync Time"
|
||||||
|
STR_DETECT_TIMEZONE: "Detect Timezone"
|
||||||
STR_SYNCING_CLOCK: "Syncing clock..."
|
STR_SYNCING_CLOCK: "Syncing clock..."
|
||||||
|
STR_DETECTING_TIMEZONE: "Detecting timezone..."
|
||||||
STR_TIME_SYNCED: "Time synced"
|
STR_TIME_SYNCED: "Time synced"
|
||||||
STR_TIME_SYNC_FAILED: "Time sync failed"
|
STR_TIME_SYNC_FAILED: "Time sync failed"
|
||||||
|
STR_TIMEZONE_DETECTED: "Timezone detected"
|
||||||
|
STR_TIMEZONE_DETECT_FAILED: "Timezone detect failed"
|
||||||
|
STR_DST_ACTIVE: "DST: active"
|
||||||
|
STR_DST_INACTIVE: "DST: inactive"
|
||||||
|
STR_DST_UNKNOWN: "DST: unknown"
|
||||||
STR_REFRESH_FREQ: "Refresh Frequency"
|
STR_REFRESH_FREQ: "Refresh Frequency"
|
||||||
STR_KOREADER_SYNC: "KOReader Sync"
|
STR_KOREADER_SYNC: "KOReader Sync"
|
||||||
STR_CHECK_UPDATES: "Check for updates"
|
STR_CHECK_UPDATES: "Check for updates"
|
||||||
|
|||||||
@@ -6,6 +6,9 @@
|
|||||||
#include <esp_private/esp_clk.h>
|
#include <esp_private/esp_clk.h>
|
||||||
#include <esp_sntp.h>
|
#include <esp_sntp.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
// ---- RTC-memory state (survives deep sleep, not cold boot) ----------------
|
// ---- RTC-memory state (survives deep sleep, not cold boot) ----------------
|
||||||
|
|
||||||
@@ -19,6 +22,29 @@ RTC_NOINIT_ATTR static uint64_t rtcLpTimeUs; // esp_clk_rtc_time() at capture
|
|||||||
|
|
||||||
static bool clockApproximate = true;
|
static bool clockApproximate = true;
|
||||||
|
|
||||||
|
struct TimeZoneEntry {
|
||||||
|
const char* tz;
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr TimeZoneEntry TIMEZONES[] = {
|
||||||
|
{"GMT0BST,M3.5.0/1,M10.5.0/2"},
|
||||||
|
{"CET-1CEST,M3.5.0/2,M10.5.0/3"},
|
||||||
|
{"EET-2EEST,M3.5.0/3,M10.5.0/4"},
|
||||||
|
{"MSK-3"},
|
||||||
|
{"UTC-4"},
|
||||||
|
{"UTC-5:30"},
|
||||||
|
{"UTC-7"},
|
||||||
|
{"UTC-8"},
|
||||||
|
{"UTC-9"},
|
||||||
|
{"AEST-10AEDT,M10.1.0/2,M4.1.0/3"},
|
||||||
|
{"NZST-12NZDT,M9.5.0/2,M4.1.0/3"},
|
||||||
|
{"UTC+3"},
|
||||||
|
{"EST5EDT,M3.2.0/2,M11.1.0/2"},
|
||||||
|
{"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"},
|
||||||
|
};
|
||||||
|
|
||||||
// ---- NVS helpers ----------------------------------------------------------
|
// ---- NVS helpers ----------------------------------------------------------
|
||||||
|
|
||||||
static constexpr char NVS_NAMESPACE[] = "halclock";
|
static constexpr char NVS_NAMESPACE[] = "halclock";
|
||||||
@@ -65,6 +91,13 @@ static void capture(bool lpValid) {
|
|||||||
|
|
||||||
namespace HalClock {
|
namespace HalClock {
|
||||||
|
|
||||||
|
void applyTimezone(uint8_t timeZoneSetting) {
|
||||||
|
const size_t index = timeZoneSetting < (sizeof(TIMEZONES) / sizeof(TIMEZONES[0])) ? timeZoneSetting : 0;
|
||||||
|
setenv("TZ", TIMEZONES[index].tz, 1);
|
||||||
|
tzset();
|
||||||
|
LOG_DBG("CLK", "Timezone applied: %s", TIMEZONES[index].tz);
|
||||||
|
}
|
||||||
|
|
||||||
bool syncNtp() {
|
bool syncNtp() {
|
||||||
if (esp_sntp_enabled()) {
|
if (esp_sntp_enabled()) {
|
||||||
esp_sntp_stop();
|
esp_sntp_stop();
|
||||||
|
|||||||
@@ -30,6 +30,9 @@ namespace HalClock {
|
|||||||
/// Returns true if the sync succeeded.
|
/// Returns true if the sync succeeded.
|
||||||
bool syncNtp();
|
bool syncNtp();
|
||||||
|
|
||||||
|
/// Apply timezone/DST rules via the POSIX TZ string for the given setting.
|
||||||
|
void applyTimezone(uint8_t timeZoneSetting);
|
||||||
|
|
||||||
/// Call just before deep sleep. Snapshots the current system time to RTC
|
/// Call just before deep sleep. Snapshots the current system time to RTC
|
||||||
/// memory and NVS so it can be restored on wake / cold boot. Pass true when
|
/// memory and NVS so it can be restored on wake / cold boot. Pass true when
|
||||||
/// the LP timer is kept alive during sleep.
|
/// the LP timer is kept alive during sleep.
|
||||||
|
|||||||
@@ -137,6 +137,27 @@ class CrossPointSettings {
|
|||||||
// Image rendering in EPUB reader
|
// Image rendering in EPUB reader
|
||||||
enum IMAGE_RENDERING { IMAGES_DISPLAY = 0, IMAGES_PLACEHOLDER = 1, IMAGES_SUPPRESS = 2, IMAGE_RENDERING_COUNT };
|
enum IMAGE_RENDERING { IMAGES_DISPLAY = 0, IMAGES_PLACEHOLDER = 1, IMAGES_SUPPRESS = 2, IMAGE_RENDERING_COUNT };
|
||||||
|
|
||||||
|
// Timezone options (POSIX TZ rules for DST support)
|
||||||
|
enum TIMEZONE {
|
||||||
|
TZ_UTC = 0,
|
||||||
|
TZ_CET = 1,
|
||||||
|
TZ_EET = 2,
|
||||||
|
TZ_MSK = 3,
|
||||||
|
TZ_UTC_PLUS4 = 4,
|
||||||
|
TZ_IST = 5,
|
||||||
|
TZ_UTC_PLUS7 = 6,
|
||||||
|
TZ_UTC_PLUS8 = 7,
|
||||||
|
TZ_UTC_PLUS9 = 8,
|
||||||
|
TZ_AEST = 9,
|
||||||
|
TZ_NZST = 10,
|
||||||
|
TZ_UTC_MINUS3 = 11,
|
||||||
|
TZ_EST = 12,
|
||||||
|
TZ_CST = 13,
|
||||||
|
TZ_MST = 14,
|
||||||
|
TZ_PST = 15,
|
||||||
|
TIMEZONE_COUNT
|
||||||
|
};
|
||||||
|
|
||||||
// Sleep screen settings
|
// Sleep screen settings
|
||||||
uint8_t sleepScreen = DARK;
|
uint8_t sleepScreen = DARK;
|
||||||
// Sleep screen cover mode settings
|
// Sleep screen cover mode settings
|
||||||
@@ -203,6 +224,8 @@ class CrossPointSettings {
|
|||||||
uint8_t statusBarClock = 0;
|
uint8_t statusBarClock = 0;
|
||||||
// Clock format: 0 = 24h (14:00), 1 = 12h (2:00pm)
|
// Clock format: 0 = 24h (14:00), 1 = 12h (2:00pm)
|
||||||
uint8_t clockFormat12h = 0;
|
uint8_t clockFormat12h = 0;
|
||||||
|
// Timezone selection (applies POSIX TZ rules for DST)
|
||||||
|
uint8_t timeZone = TZ_UTC;
|
||||||
// Keep the LP timer running during deep sleep (GPIO13 HIGH) so the clock
|
// Keep the LP timer running during deep sleep (GPIO13 HIGH) so the clock
|
||||||
// can be accurately restored on wake. Increases sleep current by ~3-4 mA.
|
// can be accurately restored on wake. Increases sleep current by ~3-4 mA.
|
||||||
uint8_t keepClockAlive = 0;
|
uint8_t keepClockAlive = 0;
|
||||||
|
|||||||
@@ -82,6 +82,12 @@ inline const std::vector<SettingInfo>& getSettingsList() {
|
|||||||
StrId::STR_CAT_SYSTEM),
|
StrId::STR_CAT_SYSTEM),
|
||||||
SettingInfo::Enum(StrId::STR_CLOCK_FORMAT, &CrossPointSettings::clockFormat12h, {StrId::STR_24H, StrId::STR_12H},
|
SettingInfo::Enum(StrId::STR_CLOCK_FORMAT, &CrossPointSettings::clockFormat12h, {StrId::STR_24H, StrId::STR_12H},
|
||||||
"clockFormat12h", StrId::STR_CAT_SYSTEM),
|
"clockFormat12h", StrId::STR_CAT_SYSTEM),
|
||||||
|
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),
|
||||||
SettingInfo::Toggle(StrId::STR_KEEP_CLOCK_ALIVE, &CrossPointSettings::keepClockAlive, "keepClockAlive",
|
SettingInfo::Toggle(StrId::STR_KEEP_CLOCK_ALIVE, &CrossPointSettings::keepClockAlive, "keepClockAlive",
|
||||||
StrId::STR_CAT_SYSTEM),
|
StrId::STR_CAT_SYSTEM),
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "ActivityManager.h"
|
#include "ActivityManager.h"
|
||||||
|
|
||||||
|
#include <HalClock.h>
|
||||||
#include <HalPowerManager.h>
|
#include <HalPowerManager.h>
|
||||||
|
|
||||||
#include "boot_sleep/BootActivity.h"
|
#include "boot_sleep/BootActivity.h"
|
||||||
@@ -56,6 +57,18 @@ void ActivityManager::loop() {
|
|||||||
currentActivity->loop();
|
currentActivity->loop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (SETTINGS.statusBarClock && HalClock::isSynced()) {
|
||||||
|
static time_t lastClockMinute = 0;
|
||||||
|
time_t now = HalClock::now();
|
||||||
|
if (now > 0) {
|
||||||
|
time_t minute = now / 60;
|
||||||
|
if (minute != lastClockMinute) {
|
||||||
|
lastClockMinute = minute;
|
||||||
|
requestUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
while (pendingAction != PendingAction::None) {
|
while (pendingAction != PendingAction::None) {
|
||||||
if (pendingAction == PendingAction::Pop) {
|
if (pendingAction == PendingAction::Pop) {
|
||||||
RenderLock lock;
|
RenderLock lock;
|
||||||
|
|||||||
@@ -0,0 +1,315 @@
|
|||||||
|
#include "DetectTimezoneActivity.h"
|
||||||
|
|
||||||
|
#include <ArduinoJson.h>
|
||||||
|
#include <GfxRenderer.h>
|
||||||
|
#include <HalClock.h>
|
||||||
|
#include <I18n.h>
|
||||||
|
#include <Logging.h>
|
||||||
|
#include <WiFi.h>
|
||||||
|
#include <esp_sntp.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "CrossPointSettings.h"
|
||||||
|
#include "MappedInputManager.h"
|
||||||
|
#include "activities/network/WifiSelectionActivity.h"
|
||||||
|
#include "components/UITheme.h"
|
||||||
|
#include "fontIds.h"
|
||||||
|
#include "network/HttpDownloader.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
bool mapIanaTimezone(const std::string& tz, uint8_t& outSetting) {
|
||||||
|
using TZ = CrossPointSettings::TIMEZONE;
|
||||||
|
if (tz == "UTC" || tz == "Etc/UTC") {
|
||||||
|
outSetting = TZ::TZ_UTC;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (tz == "Europe/London" || tz == "Europe/Guernsey" || tz == "Europe/Isle_of_Man" ||
|
||||||
|
tz == "Europe/Jersey") {
|
||||||
|
outSetting = TZ::TZ_UTC;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (tz == "Europe/Athens" || tz == "Europe/Bucharest" || tz == "Europe/Helsinki" || tz == "Europe/Kiev" ||
|
||||||
|
tz == "Europe/Vilnius" || tz == "Europe/Riga" || tz == "Europe/Tallinn") {
|
||||||
|
outSetting = TZ::TZ_EET;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (tz.rfind("Europe/", 0) == 0) {
|
||||||
|
outSetting = TZ::TZ_CET;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (tz == "Europe/Moscow") {
|
||||||
|
outSetting = TZ::TZ_MSK;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (tz == "America/New_York" || tz == "America/Toronto") {
|
||||||
|
outSetting = TZ::TZ_EST;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (tz == "America/Chicago") {
|
||||||
|
outSetting = TZ::TZ_CST;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (tz == "America/Denver") {
|
||||||
|
outSetting = TZ::TZ_MST;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (tz == "America/Los_Angeles" || tz == "America/Vancouver") {
|
||||||
|
outSetting = TZ::TZ_PST;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (tz == "America/Sao_Paulo" || tz == "America/Argentina/Buenos_Aires" || tz == "America/Montevideo") {
|
||||||
|
outSetting = TZ::TZ_UTC_MINUS3;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (tz == "Asia/Dubai" || tz == "Asia/Muscat") {
|
||||||
|
outSetting = TZ::TZ_UTC_PLUS4;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (tz == "Asia/Kolkata") {
|
||||||
|
outSetting = TZ::TZ_IST;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (tz == "Asia/Bangkok" || tz == "Asia/Ho_Chi_Minh" || tz == "Asia/Jakarta" || tz == "Asia/Phnom_Penh" ||
|
||||||
|
tz == "Asia/Vientiane") {
|
||||||
|
outSetting = TZ::TZ_UTC_PLUS7;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (tz == "Asia/Shanghai" || tz == "Asia/Hong_Kong" || tz == "Asia/Singapore" || tz == "Asia/Taipei" ||
|
||||||
|
tz == "Asia/Kuala_Lumpur" || tz == "Asia/Manila") {
|
||||||
|
outSetting = TZ::TZ_UTC_PLUS8;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (tz == "Asia/Tokyo" || tz == "Asia/Seoul") {
|
||||||
|
outSetting = TZ::TZ_UTC_PLUS9;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (tz == "Australia/Sydney" || tz == "Australia/Melbourne" || tz == "Australia/Hobart") {
|
||||||
|
outSetting = TZ::TZ_AEST;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (tz == "Pacific/Auckland") {
|
||||||
|
outSetting = TZ::TZ_NZST;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool fetchTimezonePayload(const char* url, std::string& payload) {
|
||||||
|
// Give DNS a moment after WiFi connect.
|
||||||
|
delay(500);
|
||||||
|
for (int attempt = 0; attempt < 2; ++attempt) {
|
||||||
|
if (HttpDownloader::fetchUrl(url, payload)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
delay(300);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool fetchPublicIp(std::string& ip) {
|
||||||
|
std::string payload;
|
||||||
|
if (!fetchTimezonePayload("https://api.ipify.org", payload)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Payload is plain text: IP address
|
||||||
|
ip = payload;
|
||||||
|
// Trim any whitespace
|
||||||
|
while (!ip.empty() && (ip.back() == '\n' || ip.back() == '\r' || ip.back() == ' ' || ip.back() == '\t')) {
|
||||||
|
ip.pop_back();
|
||||||
|
}
|
||||||
|
if (!ip.empty()) {
|
||||||
|
LOG_DBG("CLK", "Public IP: %s", ip.c_str());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool detectTimezoneSetting(uint8_t& outSetting, std::string& outIana, bool& outDstKnown, bool& outDstActive) {
|
||||||
|
std::string payload;
|
||||||
|
std::string publicIp;
|
||||||
|
if (fetchPublicIp(publicIp)) {
|
||||||
|
std::string timeUrl = std::string("https://timeapi.io/api/Time/current/ip?ipAddress=") + publicIp;
|
||||||
|
LOG_DBG("CLK", "Timezone detect via TimeAPI: %s", timeUrl.c_str());
|
||||||
|
if (fetchTimezonePayload(timeUrl.c_str(), payload)) {
|
||||||
|
// Continue to parse TimeAPI payload below.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (payload.empty() && !fetchTimezonePayload("http://ip-api.com/json/?fields=timezone,dst", payload)) {
|
||||||
|
LOG_ERR("CLK", "Timezone detect failed: fetch error");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonDocument doc;
|
||||||
|
const auto err = deserializeJson(doc, payload);
|
||||||
|
if (err) {
|
||||||
|
LOG_ERR("CLK", "Timezone detect failed: %s", err.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* tz = doc["timezone"] | "";
|
||||||
|
if (!tz || tz[0] == '\0') {
|
||||||
|
tz = doc["timeZone"] | "";
|
||||||
|
}
|
||||||
|
if (!tz || tz[0] == '\0') {
|
||||||
|
tz = doc["time_zone"] | "";
|
||||||
|
}
|
||||||
|
if (!tz || tz[0] == '\0') {
|
||||||
|
LOG_ERR("CLK", "Timezone detect failed: missing timezone (payload: %s)", payload.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
outIana = tz;
|
||||||
|
if (!doc["dst_active"].isNull()) {
|
||||||
|
outDstKnown = true;
|
||||||
|
outDstActive = doc["dst_active"].as<bool>();
|
||||||
|
} else if (!doc["dstActive"].isNull()) {
|
||||||
|
outDstKnown = true;
|
||||||
|
outDstActive = doc["dstActive"].as<bool>();
|
||||||
|
} else if (!doc["dst"].isNull()) {
|
||||||
|
outDstKnown = true;
|
||||||
|
outDstActive = doc["dst"].as<bool>();
|
||||||
|
} else {
|
||||||
|
outDstKnown = false;
|
||||||
|
outDstActive = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mapIanaTimezone(tz, outSetting)) {
|
||||||
|
LOG_ERR("CLK", "Timezone detect unsupported: %s", tz);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outDstKnown) {
|
||||||
|
LOG_DBG("CLK", "Timezone detected: %s (dst=%d)", tz, outDstActive ? 1 : 0);
|
||||||
|
} else {
|
||||||
|
LOG_DBG("CLK", "Timezone detected: %s (dst=unknown)", tz);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void wifiOff() {
|
||||||
|
if (esp_sntp_enabled()) {
|
||||||
|
esp_sntp_stop();
|
||||||
|
}
|
||||||
|
WiFi.disconnect(false);
|
||||||
|
delay(100);
|
||||||
|
WiFi.mode(WIFI_OFF);
|
||||||
|
delay(100);
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void DetectTimezoneActivity::onEnter() {
|
||||||
|
Activity::onEnter();
|
||||||
|
|
||||||
|
if (WiFi.status() == WL_CONNECTED) {
|
||||||
|
onWifiSelectionComplete(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
startActivityForResult(std::make_unique<WifiSelectionActivity>(renderer, mappedInput),
|
||||||
|
[this](const ActivityResult& result) {
|
||||||
|
if (result.isCancelled) {
|
||||||
|
onWifiSelectionCancelled();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onWifiSelectionComplete(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void DetectTimezoneActivity::onExit() {
|
||||||
|
Activity::onExit();
|
||||||
|
wifiOff();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DetectTimezoneActivity::onWifiSelectionComplete(bool success) {
|
||||||
|
if (!success) {
|
||||||
|
state = FAILED;
|
||||||
|
requestUpdate();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
RenderLock lock(*this);
|
||||||
|
state = DETECTING;
|
||||||
|
}
|
||||||
|
requestUpdateAndWait();
|
||||||
|
|
||||||
|
performDetect();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DetectTimezoneActivity::onWifiSelectionCancelled() { finish(); }
|
||||||
|
|
||||||
|
void DetectTimezoneActivity::performDetect() {
|
||||||
|
uint8_t detected = SETTINGS.timeZone;
|
||||||
|
detectedTimezone.clear();
|
||||||
|
dstKnown = false;
|
||||||
|
dstActive = false;
|
||||||
|
if (detectTimezoneSetting(detected, detectedTimezone, dstKnown, dstActive)) {
|
||||||
|
SETTINGS.timeZone = detected;
|
||||||
|
HalClock::applyTimezone(SETTINGS.timeZone);
|
||||||
|
SETTINGS.saveToFile();
|
||||||
|
state = SUCCESS;
|
||||||
|
} else {
|
||||||
|
state = FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
wifiOff();
|
||||||
|
requestUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DetectTimezoneActivity::render(RenderLock&&) {
|
||||||
|
const auto& metrics = UITheme::getInstance().getMetrics();
|
||||||
|
const auto pageWidth = renderer.getScreenWidth();
|
||||||
|
const auto pageHeight = renderer.getScreenHeight();
|
||||||
|
|
||||||
|
renderer.clearScreen();
|
||||||
|
GUI.drawHeader(renderer, Rect{0, metrics.topPadding, pageWidth, metrics.headerHeight}, tr(STR_DETECT_TIMEZONE));
|
||||||
|
|
||||||
|
if (state == DETECTING) {
|
||||||
|
renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2, tr(STR_DETECTING_TIMEZONE), true, EpdFontFamily::BOLD);
|
||||||
|
renderer.displayBuffer();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state == SUCCESS) {
|
||||||
|
renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2 - 20, tr(STR_TIMEZONE_DETECTED), true,
|
||||||
|
EpdFontFamily::BOLD);
|
||||||
|
|
||||||
|
if (!detectedTimezone.empty()) {
|
||||||
|
renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2 + 5, detectedTimezone.c_str());
|
||||||
|
renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2 + 25, dstStatusLabel());
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto labels = mappedInput.mapLabels(tr(STR_BACK), "", "", "");
|
||||||
|
GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
||||||
|
renderer.displayBuffer();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state == FAILED) {
|
||||||
|
renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2, tr(STR_TIMEZONE_DETECT_FAILED), true,
|
||||||
|
EpdFontFamily::BOLD);
|
||||||
|
|
||||||
|
const auto labels = mappedInput.mapLabels(tr(STR_BACK), "", "", "");
|
||||||
|
GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
||||||
|
renderer.displayBuffer();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* DetectTimezoneActivity::dstStatusLabel() const {
|
||||||
|
if (!dstKnown) {
|
||||||
|
return tr(STR_DST_UNKNOWN);
|
||||||
|
}
|
||||||
|
return dstActive ? tr(STR_DST_ACTIVE) : tr(STR_DST_INACTIVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DetectTimezoneActivity::loop() {
|
||||||
|
if (state == SUCCESS || state == FAILED) {
|
||||||
|
if (mappedInput.wasPressed(MappedInputManager::Button::Back)) {
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "activities/Activity.h"
|
||||||
|
|
||||||
|
class DetectTimezoneActivity final : public Activity {
|
||||||
|
public:
|
||||||
|
explicit DetectTimezoneActivity(GfxRenderer& renderer, MappedInputManager& mappedInput)
|
||||||
|
: Activity("DetectTimezone", renderer, mappedInput) {}
|
||||||
|
|
||||||
|
void onEnter() override;
|
||||||
|
void onExit() override;
|
||||||
|
void loop() override;
|
||||||
|
void render(RenderLock&&) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum State { CONNECTING, DETECTING, SUCCESS, FAILED };
|
||||||
|
State state = CONNECTING;
|
||||||
|
std::string detectedTimezone;
|
||||||
|
bool dstKnown = false;
|
||||||
|
bool dstActive = false;
|
||||||
|
|
||||||
|
void onWifiSelectionComplete(bool success);
|
||||||
|
void onWifiSelectionCancelled();
|
||||||
|
void performDetect();
|
||||||
|
const char* dstStatusLabel() const;
|
||||||
|
};
|
||||||
@@ -1,12 +1,14 @@
|
|||||||
#include "SettingsActivity.h"
|
#include "SettingsActivity.h"
|
||||||
|
|
||||||
#include <GfxRenderer.h>
|
#include <GfxRenderer.h>
|
||||||
|
#include <HalClock.h>
|
||||||
#include <Logging.h>
|
#include <Logging.h>
|
||||||
|
|
||||||
#include "ButtonRemapActivity.h"
|
#include "ButtonRemapActivity.h"
|
||||||
#include "CalibreSettingsActivity.h"
|
#include "CalibreSettingsActivity.h"
|
||||||
#include "ClearCacheActivity.h"
|
#include "ClearCacheActivity.h"
|
||||||
#include "CrossPointSettings.h"
|
#include "CrossPointSettings.h"
|
||||||
|
#include "DetectTimezoneActivity.h"
|
||||||
#include "KOReaderSettingsActivity.h"
|
#include "KOReaderSettingsActivity.h"
|
||||||
#include "LanguageSelectActivity.h"
|
#include "LanguageSelectActivity.h"
|
||||||
#include "MappedInputManager.h"
|
#include "MappedInputManager.h"
|
||||||
@@ -48,6 +50,7 @@ void SettingsActivity::onEnter() {
|
|||||||
controlsSettings.insert(controlsSettings.begin(),
|
controlsSettings.insert(controlsSettings.begin(),
|
||||||
SettingInfo::Action(StrId::STR_REMAP_FRONT_BUTTONS, SettingAction::RemapFrontButtons));
|
SettingInfo::Action(StrId::STR_REMAP_FRONT_BUTTONS, SettingAction::RemapFrontButtons));
|
||||||
systemSettings.push_back(SettingInfo::Action(StrId::STR_SYNC_TIME, SettingAction::SyncTime));
|
systemSettings.push_back(SettingInfo::Action(StrId::STR_SYNC_TIME, SettingAction::SyncTime));
|
||||||
|
systemSettings.push_back(SettingInfo::Action(StrId::STR_DETECT_TIMEZONE, SettingAction::DetectTimezone));
|
||||||
systemSettings.push_back(SettingInfo::Action(StrId::STR_WIFI_NETWORKS, SettingAction::Network));
|
systemSettings.push_back(SettingInfo::Action(StrId::STR_WIFI_NETWORKS, SettingAction::Network));
|
||||||
systemSettings.push_back(SettingInfo::Action(StrId::STR_KOREADER_SYNC, SettingAction::KOReaderSync));
|
systemSettings.push_back(SettingInfo::Action(StrId::STR_KOREADER_SYNC, SettingAction::KOReaderSync));
|
||||||
systemSettings.push_back(SettingInfo::Action(StrId::STR_OPDS_BROWSER, SettingAction::OPDSBrowser));
|
systemSettings.push_back(SettingInfo::Action(StrId::STR_OPDS_BROWSER, SettingAction::OPDSBrowser));
|
||||||
@@ -197,6 +200,9 @@ void SettingsActivity::toggleCurrentSetting() {
|
|||||||
case SettingAction::SyncTime:
|
case SettingAction::SyncTime:
|
||||||
startActivityForResult(std::make_unique<SyncTimeActivity>(renderer, mappedInput), resultHandler);
|
startActivityForResult(std::make_unique<SyncTimeActivity>(renderer, mappedInput), resultHandler);
|
||||||
break;
|
break;
|
||||||
|
case SettingAction::DetectTimezone:
|
||||||
|
startActivityForResult(std::make_unique<DetectTimezoneActivity>(renderer, mappedInput), resultHandler);
|
||||||
|
break;
|
||||||
case SettingAction::None:
|
case SettingAction::None:
|
||||||
// Do nothing
|
// Do nothing
|
||||||
break;
|
break;
|
||||||
@@ -206,6 +212,10 @@ void SettingsActivity::toggleCurrentSetting() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (setting.nameId == StrId::STR_TIMEZONE) {
|
||||||
|
HalClock::applyTimezone(SETTINGS.timeZone);
|
||||||
|
}
|
||||||
|
|
||||||
SETTINGS.saveToFile();
|
SETTINGS.saveToFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ enum class SettingAction {
|
|||||||
ClearCache,
|
ClearCache,
|
||||||
CheckForUpdates,
|
CheckForUpdates,
|
||||||
Language,
|
Language,
|
||||||
|
DetectTimezone,
|
||||||
SyncTime,
|
SyncTime,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -260,6 +260,7 @@ void setup() {
|
|||||||
HalSystem::clearPanic(); // TODO: move this to an activity when we have one to display the panic info
|
HalSystem::clearPanic(); // TODO: move this to an activity when we have one to display the panic info
|
||||||
|
|
||||||
SETTINGS.loadFromFile();
|
SETTINGS.loadFromFile();
|
||||||
|
HalClock::applyTimezone(SETTINGS.timeZone);
|
||||||
I18N.loadSettings();
|
I18N.loadSettings();
|
||||||
KOREADER_STORE.loadFromFile();
|
KOREADER_STORE.loadFromFile();
|
||||||
UITheme::getInstance().reload();
|
UITheme::getInstance().reload();
|
||||||
|
|||||||
Reference in New Issue
Block a user