diff --git a/Makefile b/Makefile index 1916662d..3dbdf4b8 100644 --- a/Makefile +++ b/Makefile @@ -30,6 +30,7 @@ INCLUDES += \ -I./ \ -I./tinyusb/src \ -I./littlefs \ + -I./utz \ -I./filesystem \ -I./shell \ -I./watch-library/shared/watch \ @@ -42,6 +43,8 @@ SRCS += \ ./littlefs/lfs.c \ ./littlefs/lfs_util.c \ ./filesystem/filesystem.c \ + ./utz/utz.c \ + ./utz/zones.c \ ./shell/shell.c \ ./shell/shell_cmd_list.c \ ./watch-library/shared/watch/watch_common_buzzer.c \ diff --git a/movement.c b/movement.c index df93d24e..9a512fcb 100644 --- a/movement.c +++ b/movement.c @@ -38,6 +38,7 @@ #include "movement.h" #include "filesystem.h" #include "shell.h" +#include "utz.h" /// FIXME: #SecondMovement needs to bring back the following include (and remove the default signal_tune) // #include "movement_custom_signal_tunes.h" @@ -62,50 +63,6 @@ const int32_t movement_le_inactivity_deadlines[8] = {INT_MAX, 600, 3600, 7200, 2 const int16_t movement_timeout_inactivity_deadlines[4] = {60, 120, 300, 1800}; movement_event_t event; -const int16_t movement_timezone_offsets[] = { - 0, // 0 : 0:00:00 (UTC) - 60, // 1 : 1:00:00 (Central European Time) - 120, // 2 : 2:00:00 (South African Standard Time) - 180, // 3 : 3:00:00 (Arabia Standard Time) - 210, // 4 : 3:30:00 (Iran Standard Time) - 240, // 5 : 4:00:00 (Georgia Standard Time) - 270, // 6 : 4:30:00 (Afghanistan Time) - 300, // 7 : 5:00:00 (Pakistan Standard Time) - 330, // 8 : 5:30:00 (Indian Standard Time) - 345, // 9 : 5:45:00 (Nepal Time) - 360, // 10 : 6:00:00 (Kyrgyzstan time) - 390, // 11 : 6:30:00 (Myanmar Time) - 420, // 12 : 7:00:00 (Thailand Standard Time) - 480, // 13 : 8:00:00 (China Standard Time, Australian Western Standard Time) - 525, // 14 : 8:45:00 (Australian Central Western Standard Time) - 540, // 15 : 9:00:00 (Japan Standard Time, Korea Standard Time) - 570, // 16 : 9:30:00 (Australian Central Standard Time) - 600, // 17 : 10:00:00 (Australian Eastern Standard Time) - 630, // 18 : 10:30:00 (Lord Howe Standard Time) - 660, // 19 : 11:00:00 (Solomon Islands Time) - 720, // 20 : 12:00:00 (New Zealand Standard Time) - 765, // 21 : 12:45:00 (Chatham Standard Time) - 780, // 22 : 13:00:00 (Tonga Time) - 825, // 23 : 13:45:00 (Chatham Daylight Time) - 840, // 24 : 14:00:00 (Line Islands Time) - -720, // 25 : -12:00:00 (Baker Island Time) - -660, // 26 : -11:00:00 (Niue Time) - -600, // 27 : -10:00:00 (Hawaii-Aleutian Standard Time) - -570, // 28 : -9:30:00 (Marquesas Islands Time) - -540, // 29 : -9:00:00 (Alaska Standard Time) - -480, // 30 : -8:00:00 (Pacific Standard Time) - -420, // 31 : -7:00:00 (Mountain Standard Time) - -360, // 32 : -6:00:00 (Central Standard Time) - -300, // 33 : -5:00:00 (Eastern Standard Time) - -270, // 34 : -4:30:00 (Venezuelan Standard Time) - -240, // 35 : -4:00:00 (Atlantic Standard Time) - -210, // 36 : -3:30:00 (Newfoundland Standard Time) - -180, // 37 : -3:00:00 (Brasilia Time) - -150, // 38 : -2:30:00 (Newfoundland Daylight Time) - -120, // 39 : -2:00:00 (Fernando de Noronha Time) - -60, // 40 : -1:00:00 (Azores Standard Time) -}; - const char movement_valid_position_0_chars[] = " AaBbCcDdEeFGgHhIiJKLMNnOoPQrSTtUuWXYZ-='+\\/0123456789"; const char movement_valid_position_1_chars[] = " ABCDEFHlJLNORTtUX-='01378"; @@ -348,6 +305,30 @@ uint8_t movement_claim_backup_register(void) { return movement_state.next_available_backup_register++; } +int32_t movement_get_current_timezone_offset_for_zone(uint8_t zone_index) { + watch_date_time date_time = watch_rtc_get_date_time(); + uzone_t time_zone; + uoffset_t offset; + udatetime_t udate_time = { + .date.dayofmonth = date_time.unit.day, + .date.dayofweek = dayofweek(date_time.unit.year + WATCH_RTC_REFERENCE_YEAR - UYEAR_OFFSET, date_time.unit.month, date_time.unit.day), + .date.month = date_time.unit.month, + .date.year = date_time.unit.year + WATCH_RTC_REFERENCE_YEAR - UYEAR_OFFSET, + .time.hour = date_time.unit.hour, + .time.minute = date_time.unit.minute, + .time.second = date_time.unit.second + }; + + unpack_zone(&zone_defns[zone_index], "", &time_zone); + get_current_offset(&time_zone, &udate_time, &offset); + + return offset.hours * 3600 + offset.minutes * 60; +} + +int32_t movement_get_current_timezone_offset(void) { + return movement_get_current_timezone_offset_for_zone(movement_state.settings.bit.time_zone); +} + void app_init(void) { _watch_init(); @@ -381,6 +362,7 @@ void app_init(void) { movement_state.settings.bit.clock_mode_24h = true; #else movement_state.settings.bit.clock_mode_24h = MOVEMENT_DEFAULT_24H_MODE; + movement_state.settings.bit.time_zone = 15; #endif movement_state.settings.bit.led_red_color = MOVEMENT_DEFAULT_RED_COLOR; movement_state.settings.bit.led_green_color = MOVEMENT_DEFAULT_GREEN_COLOR; diff --git a/movement.h b/movement.h index fd323ea6..7eecd2be 100644 --- a/movement.h +++ b/movement.h @@ -27,6 +27,7 @@ #include #include #include "watch.h" +#include "utz.h" // Movement Preferences // These four 32-bit structs store information about the wearer and their preferences. Tentatively, the plan is @@ -314,3 +315,6 @@ void movement_play_alarm(void); void movement_play_alarm_beeps(uint8_t rounds, watch_buzzer_note_t alarm_note); uint8_t movement_claim_backup_register(void); + +int32_t movement_get_current_timezone_offset_for_zone(uint8_t zone_index); +int32_t movement_get_current_timezone_offset(void); diff --git a/movement_config.h b/movement_config.h index e90efd4c..902e72a3 100644 --- a/movement_config.h +++ b/movement_config.h @@ -29,6 +29,7 @@ const watch_face_t watch_faces[] = { simple_clock_face, + world_clock_face, preferences_face, set_time_face, }; diff --git a/movement_faces.h b/movement_faces.h index b7791f65..f4ca6917 100644 --- a/movement_faces.h +++ b/movement_faces.h @@ -25,6 +25,7 @@ #pragma once #include "simple_clock_face.h" +#include "world_clock_face.h" #include "set_time_face.h" #include "preferences_face.h" // New includes go above this line. diff --git a/watch-faces.mk b/watch-faces.mk index 338c3b99..c84a17e5 100644 --- a/watch-faces.mk +++ b/watch-faces.mk @@ -1,4 +1,5 @@ SRCS += \ ./watch-faces/clock/simple_clock_face.c \ + ./watch-faces/clock/world_clock_face.c \ ./watch-faces/settings/set_time_face.c \ ./watch-faces/settings/preferences_face.c \ diff --git a/watch-faces/clock/world_clock_face.c b/watch-faces/clock/world_clock_face.c index 8f530133..924651b2 100644 --- a/watch-faces/clock/world_clock_face.c +++ b/watch-faces/clock/world_clock_face.c @@ -28,6 +28,12 @@ #include "watch.h" #include "watch_utility.h" #include "watch_common_display.h" +#include "zones.h" + +void _update_timezone_offset(world_clock_state_t *state); +void _update_timezone_offset(world_clock_state_t *state) { + state->current_offset = movement_get_current_timezone_offset_for_zone(state->settings.bit.timezone_index); +} void world_clock_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) { (void) settings; @@ -35,9 +41,10 @@ void world_clock_face_setup(movement_settings_t *settings, uint8_t watch_face_in if (*context_ptr == NULL) { *context_ptr = malloc(sizeof(world_clock_state_t)); memset(*context_ptr, 0, sizeof(world_clock_state_t)); + world_clock_state_t *state = (world_clock_state_t *)*context_ptr; + state->settings.bit.timezone_index = 15; uint8_t backup_register = movement_claim_backup_register(); if (backup_register) { - world_clock_state_t *state = (world_clock_state_t *)*context_ptr; state->settings.reg = watch_get_backup_data(backup_register); state->backup_register = backup_register; } @@ -47,7 +54,9 @@ void world_clock_face_setup(movement_settings_t *settings, uint8_t watch_face_in void world_clock_face_activate(movement_settings_t *settings, void *context) { (void) settings; world_clock_state_t *state = (world_clock_state_t *)context; + state->current_screen = 0; + _update_timezone_offset(state); if (watch_tick_animation_is_running()) watch_stop_tick_animation(); } @@ -55,7 +64,6 @@ void world_clock_face_activate(movement_settings_t *settings, void *context) { static bool world_clock_face_do_display_mode(movement_event_t event, movement_settings_t *settings, world_clock_state_t *state) { char buf[11]; - uint32_t timestamp; uint32_t previous_date_time; watch_date_time date_time; switch (event.event_type) { @@ -67,8 +75,7 @@ static bool world_clock_face_do_display_mode(movement_event_t event, movement_se case EVENT_TICK: case EVENT_LOW_ENERGY_UPDATE: date_time = watch_rtc_get_date_time(); - timestamp = watch_utility_date_time_to_unix_time(date_time, movement_timezone_offsets[settings->bit.time_zone] * 60); - date_time = watch_utility_date_time_from_unix_time(timestamp, movement_timezone_offsets[state->settings.bit.timezone_index] * 60); + date_time = watch_utility_date_time_convert_zone(date_time, movement_get_current_timezone_offset(), state->current_offset); previous_date_time = state->previous_date_time; state->previous_date_time = date_time.reg; if ((date_time.reg >> 6) == (previous_date_time >> 6) && event.event_type != EVENT_LOW_ENERGY_UPDATE) { @@ -81,6 +88,9 @@ static bool world_clock_face_do_display_mode(movement_event_t event, movement_se sprintf(buf, "%02d%02d", date_time.unit.minute, date_time.unit.second); watch_display_text(WATCH_POSITION_MINUTES, buf); watch_display_text(WATCH_POSITION_SECONDS, buf + 2); + if (date_time.unit.minute % 15 == 0) { + _update_timezone_offset(state); + } } else { // other stuff changed; let's do it all. if (!settings->bit.clock_mode_24h) { @@ -127,6 +137,7 @@ static bool _world_clock_face_do_settings_mode(movement_event_t event, movement_ state->current_screen++; if (state->current_screen > 3) { movement_request_tick_frequency(1); + _update_timezone_offset(state); state->current_screen = 0; if (state->backup_register) watch_store_backup_data(state->settings.reg, state->backup_register); event.event_type = EVENT_ACTIVATE; @@ -149,7 +160,7 @@ static bool _world_clock_face_do_settings_mode(movement_event_t event, movement_ break; case 3: state->settings.bit.timezone_index++; - if (state->settings.bit.timezone_index > 40) state->settings.bit.timezone_index = 0; + if (state->settings.bit.timezone_index >= NUM_ZONE_NAMES) state->settings.bit.timezone_index = 0; break; } break; @@ -161,12 +172,12 @@ static bool _world_clock_face_do_settings_mode(movement_event_t event, movement_ } char buf[13]; - sprintf(buf, "%c%c %3d%02d ", + + watch_clear_colon(); + sprintf(buf, "%c%c %s", movement_valid_position_0_chars[state->settings.bit.char_0], movement_valid_position_1_chars[state->settings.bit.char_1], - (int8_t) (movement_timezone_offsets[state->settings.bit.timezone_index] / 60), - (int8_t) (movement_timezone_offsets[state->settings.bit.timezone_index] % 60) * (movement_timezone_offsets[state->settings.bit.timezone_index] < 0 ? -1 : 1)); - watch_set_colon(); + (char *) (3 + zone_names + 11 * state->settings.bit.timezone_index)); watch_clear_indicator(WATCH_INDICATOR_PM); // blink up the parameter we're setting @@ -177,7 +188,6 @@ static bool _world_clock_face_do_settings_mode(movement_event_t event, movement_ buf[state->current_screen - 1] = '_'; break; case 3: - watch_clear_colon(); sprintf(buf + 3, " "); break; } diff --git a/watch-faces/clock/world_clock_face.h b/watch-faces/clock/world_clock_face.h index 92e91a6f..bca82cd9 100644 --- a/watch-faces/clock/world_clock_face.h +++ b/watch-faces/clock/world_clock_face.h @@ -62,6 +62,7 @@ typedef struct { uint8_t backup_register; uint8_t current_screen; uint32_t previous_date_time; + int32_t current_offset; } world_clock_state_t; void world_clock_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr); diff --git a/watch-faces/settings/set_time_face.c b/watch-faces/settings/set_time_face.c index 549ad8b7..c1c92577 100644 --- a/watch-faces/settings/set_time_face.c +++ b/watch-faces/settings/set_time_face.c @@ -27,9 +27,10 @@ #include "set_time_face.h" #include "watch.h" #include "watch_utility.h" +#include "zones.h" #define SET_TIME_FACE_NUM_SETTINGS (7) -const char set_time_face_titles[SET_TIME_FACE_NUM_SETTINGS][3] = {"HR", "M1", "SE", "YR", "MO", "DA", "ZO"}; +const char set_time_face_titles[SET_TIME_FACE_NUM_SETTINGS][3] = {"HR", "M1", "SE", "YR", "MO", "DA", " "}; static bool _quick_ticks_running; @@ -58,7 +59,7 @@ static void _handle_alarm_button(movement_settings_t *settings, watch_date_time } case 6: // time zone settings->bit.time_zone++; - if (settings->bit.time_zone > 40) settings->bit.time_zone = 0; + if (settings->bit.time_zone >= NUM_ZONE_NAMES) settings->bit.time_zone = 0; break; } if (date_time.unit.day > days_in_month(date_time.unit.month, date_time.unit.year + WATCH_RTC_REFERENCE_YEAR)) @@ -145,13 +146,12 @@ bool set_time_face_loop(movement_event_t event, movement_settings_t *settings, v watch_clear_indicator(WATCH_INDICATOR_PM); sprintf(buf, "%2d%02d%02d", date_time.unit.year + 20, date_time.unit.month, date_time.unit.day); } else { + watch_display_text(WATCH_POSITION_TOP_RIGHT, " Z"); if (event.subsecond % 2) { - watch_clear_colon(); memset(buf, ' ', sizeof(buf)); } else { - watch_set_colon(); - if (movement_timezone_offsets[settings->bit.time_zone] < 0) watch_display_text(WATCH_POSITION_TOP_RIGHT, " -"); - sprintf(buf, "%2d%02d ", (int8_t) abs(movement_timezone_offsets[settings->bit.time_zone] / 60), (int8_t) (movement_timezone_offsets[settings->bit.time_zone] % 60) * (movement_timezone_offsets[settings->bit.time_zone] < 0 ? -1 : 1)); + watch_display_text(WATCH_POSITION_TOP_LEFT, (char *) (zone_names + 11 * settings->bit.time_zone)); + sprintf(buf, "%s", (char *) (3 + zone_names + 11 * settings->bit.time_zone)); } } diff --git a/watch-library/shared/watch/watch_utility.c b/watch-library/shared/watch/watch_utility.c index 51b8538c..95869776 100644 --- a/watch-library/shared/watch/watch_utility.c +++ b/watch-library/shared/watch/watch_utility.c @@ -315,11 +315,3 @@ uint32_t watch_utility_offset_timestamp(uint32_t now, int8_t hours, int8_t minut new += seconds; return new; } - -uint8_t days_in_month(uint8_t month, uint16_t year) { - static const uint8_t days_in_month[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; - uint8_t days = days_in_month[month - 1]; - if (month == 2 && is_leap(year)) - days += 1; - return days; -}