]> git.earman.xyz Git - sensor-watch.git/commitdiff
Merge PR #470 - implement automatic DST toggling
authorMatheus Afonso Martins Moreira <matheus@matheusmoreira.com>
Sun, 8 Sep 2024 16:41:52 +0000 (13:41 -0300)
committerMatheus Afonso Martins Moreira <matheus@matheusmoreira.com>
Sun, 8 Sep 2024 16:41:52 +0000 (13:41 -0300)
Implements logic to automatically offset daylight saving time settings
when calculating timezone offsets. This should make the DST functions
work automatically with no need for user input in most cases.

Reviewed-by: Matheus Afonso Martins Moreira <matheus@matheusmoreira.com>
GitHub-Pull-Request: https://github.com/joeycastillo/Sensor-Watch/pull/470

16 files changed:
1  2 
movement/movement.c
movement/movement.h
movement/movement_config.h
movement/watch_faces/clock/world_clock2_face.c
movement/watch_faces/clock/world_clock_face.c
movement/watch_faces/complication/countdown_face.c
movement/watch_faces/complication/planetary_hours_face.c
movement/watch_faces/complication/planetary_time_face.c
movement/watch_faces/complication/sunrise_sunset_face.c
movement/watch_faces/complication/sunrise_sunset_face.h
movement/watch_faces/complication/totp_face.c
movement/watch_faces/complication/totp_face_lfs.c
movement/watch_faces/settings/set_time_face.c
movement/watch_faces/settings/set_time_hackwatch_face.c
watch-library/shared/watch/watch_utility.c
watch-library/shared/watch/watch_utility.h

index 81e1b3ce4b57c6b20d6125fb0693efb521b1edc5,60e91067394424f27cb9f010794de32ffd47a6bb..ba21a4e61adb3b0a0c7a7650bac2a16b6c906267
@@@ -105,31 -96,11 +106,36 @@@ of debounce time
  #define MOVEMENT_DEFAULT_LED_DURATION 1
  #endif
  
 +// Default to no set location latitude
 +#ifndef MOVEMENT_DEFAULT_LATITUDE
 +#define MOVEMENT_DEFAULT_LATITUDE 0
 +#endif
 +
 +// Default to no set location longitude
 +#ifndef MOVEMENT_DEFAULT_LONGITUDE
 +#define MOVEMENT_DEFAULT_LONGITUDE 0
 +#endif
 +
 +// Default to no set birthdate year
 +#ifndef MOVEMENT_DEFAULT_BIRTHDATE_YEAR
 +#define MOVEMENT_DEFAULT_BIRTHDATE_YEAR 0
 +#endif
 +
 +// Default to no set birthdate month
 +#ifndef MOVEMENT_DEFAULT_BIRTHDATE_MONTH
 +#define MOVEMENT_DEFAULT_BIRTHDATE_MONTH 0
 +#endif
 +
 +// Default to no set birthdate day
 +#ifndef MOVEMENT_DEFAULT_BIRTHDATE_DAY
 +#define MOVEMENT_DEFAULT_BIRTHDATE_DAY 0
 +#endif
 +
+ // Default to having DST get set
+ #ifndef MOVEMENT_DEFAULT_DST_ACTIVE
+ #define MOVEMENT_DEFAULT_DST_ACTIVE true
+ #endif
  #if __EMSCRIPTEN__
  #include <emscripten.h>
  #endif
@@@ -323,6 -247,31 +286,31 @@@ static inline void _movement_disable_fa
      }
  }
  
 -    if (date_time.unit.minute > 1 && date_time.unit.minute < 59) return false; 
+ static bool _check_and_act_on_daylight_savings(void) {
+     if (!movement_state.settings.bit.dst_active) return false;
+     watch_date_time date_time = watch_rtc_get_date_time();
+     // No need for all of the unix time calculations for times not at the beginning or end of the hour
 -        watch_rtc_set_date_time(date_time);  
++    if (date_time.unit.minute > 1 && date_time.unit.minute < 59) return false;
+     uint8_t dst_result = get_dst_status(date_time);
+     bool dst_skip_rolling_back = get_dst_skip_rolling_back();
+     if (dst_skip_rolling_back && (dst_result == DST_ENDED)) {
+         clear_dst_skip_rolling_back();
+     }
+     else if (dst_result == DST_ENDING && !dst_skip_rolling_back) {
+         date_time.unit.hour = (date_time.unit.hour + 24 - 1) % 24;
+         watch_rtc_set_date_time(date_time);
+         set_dst_skip_rolling_back();
+         return true;
+     }
+     else if (dst_result == DST_STARTING) {
+         date_time.unit.hour = (date_time.unit.hour + 1) % 24;
++        watch_rtc_set_date_time(date_time);
+         return true;
+     }
+     return false;
+ }
  static void _movement_handle_background_tasks(void) {
      for(uint8_t i = 0; i < MOVEMENT_NUM_FACES; i++) {
          // For each face, if the watch face wants a background task...
@@@ -546,11 -480,19 +541,25 @@@ void app_init(void) 
      movement_state.settings.bit.to_interval = MOVEMENT_DEFAULT_TIMEOUT_INTERVAL;
      movement_state.settings.bit.le_interval = MOVEMENT_DEFAULT_LOW_ENERGY_INTERVAL;
      movement_state.settings.bit.led_duration = MOVEMENT_DEFAULT_LED_DURATION;
 +    movement_state.location.bit.latitude = MOVEMENT_DEFAULT_LATITUDE;
 +    movement_state.location.bit.longitude = MOVEMENT_DEFAULT_LONGITUDE;
 +    movement_state.birthdate.bit.year = MOVEMENT_DEFAULT_BIRTHDATE_YEAR;
 +    movement_state.birthdate.bit.month = MOVEMENT_DEFAULT_BIRTHDATE_MONTH;
 +    movement_state.birthdate.bit.day = MOVEMENT_DEFAULT_BIRTHDATE_DAY;
+     movement_state.settings.bit.dst_active = MOVEMENT_DEFAULT_DST_ACTIVE;
+ #ifdef MAKEFILE_TIMEZONE
+     timezone_offsets = dst_occurring(watch_rtc_get_date_time()) ? movement_timezone_dst_offsets : movement_timezone_offsets;
+     for (int i = 0; i < NUM_TIME_ZONES; i++) {
+         if (timezone_offsets[i] == MAKEFILE_TIMEZONE) {
+             movement_state.settings.bit.time_zone = i;
+             break;
+         }
+     }
+ #else
+     movement_state.settings.bit.time_zone = 35;  // Atlantic Time as default
+ #endif
++
      movement_state.light_ticks = -1;
      movement_state.alarm_ticks = -1;
      movement_state.next_available_backup_register = 4;
Simple merge
index abceacf1c07c3419dde2ae2863e71c6cdd9a4e5f,46e1d2e4ef00946c56ebb2fe754afe34f3f8811c..566ea3e7185b6896e366c45f893659773c1fe812
@@@ -95,20 -95,11 +95,27 @@@ const watch_face_t watch_faces[] = 
   */
  #define MOVEMENT_DEFAULT_LED_DURATION 1
  
 +/* The latitude and longitude used for the wearers location
 + * Set signed values in 1/100ths of a degree
 + */
 +#define MOVEMENT_DEFAULT_LATITUDE 0
 +#define MOVEMENT_DEFAULT_LONGITUDE 0
 +
 +/* The wearers birthdate
 + * Valid values:
 + * Year: 1 - 4095
 + * Month: 1 - 12
 + * Day: 1 - 31
 + */
 +#define MOVEMENT_DEFAULT_BIRTHDATE_YEAR 0
 +#define MOVEMENT_DEFAULT_BIRTHDATE_MONTH 0
 +#define MOVEMENT_DEFAULT_BIRTHDATE_DAY 0
 +
+ /* Set if using DST
+  * Valid values are:
+  * false: Don't allow the watch to use DST
+  * true: Allow the watch to use DST
+  */
+ #define MOVEMENT_DEFAULT_DST_ACTIVE true
  #endif // MOVEMENT_CONFIG_H_
index f585ebc39c94f4ce15d22f0fe04ca8aa3ab3ed43,04dc5922d51f9eb8160260a61286fb909605d58c..825595e88dd12a9fbb85d52eebe488d257f091e2
@@@ -69,30 -68,18 +69,32 @@@ static inline void button_beep(movement
          watch_buzzer_play_note(BUZZER_NOTE_C7, 50);
  }
  
 -static void start(countdown_state_t *state, movement_settings_t *settings) {
 +static void schedule_countdown(countdown_state_t *state, movement_settings_t *settings) {
+     watch_date_time now = watch_rtc_get_date_time();
+     int16_t tz = get_tz_offset(settings, now);
  
-     // Calculate the new state->now_ts but don't update it until we've updated the target - 
 -    state->mode = cd_running;
 -    state->now_ts = watch_utility_date_time_to_unix_time(now, tz);
 -    state->target_ts = watch_utility_offset_timestamp(state->now_ts, state->hours, state->minutes, state->seconds);
++    // Calculate the new state->now_ts but don't update it until we've updated the target -
 +    // avoid possible race where the old target is compared to the new time and immediately triggers
-     uint32_t new_now = watch_utility_date_time_to_unix_time(watch_rtc_get_date_time(), get_tz_offset(settings));
++    uint32_t new_now = watch_utility_date_time_to_unix_time(now, tz);
 +    state->target_ts = watch_utility_offset_timestamp(new_now, state->hours, state->minutes, state->seconds);
 +    state->now_ts = new_now;
-     watch_date_time target_dt = watch_utility_date_time_from_unix_time(state->target_ts, get_tz_offset(settings));
+     watch_date_time target_dt = watch_utility_date_time_from_unix_time(state->target_ts, tz);
 -    movement_schedule_background_task(target_dt);
 -    watch_set_indicator(WATCH_INDICATOR_BELL);
 +    movement_schedule_background_task_for_face(state->watch_face_index, target_dt);
 +}
 +
 +static void auto_repeat(countdown_state_t *state, movement_settings_t *settings) {
 +    movement_play_alarm();
 +    load_countdown(state);
 +    schedule_countdown(state, settings);
  }
  
 +static void start(countdown_state_t *state, movement_settings_t *settings) {
 +    state->mode = cd_running;
 +    schedule_countdown(state, settings);
 +}
 +
 +
 +
  static void draw(countdown_state_t *state, uint8_t subsecond) {
      char buf[16];
  
@@@ -203,12 -177,10 +205,12 @@@ void countdown_face_activate(movement_s
      countdown_state_t *state = (countdown_state_t *)context;
      if(state->mode == cd_running) {
          watch_date_time now = watch_rtc_get_date_time();
-         state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings));
+         state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings, now));
 -        watch_set_indicator(WATCH_INDICATOR_BELL);
 +        watch_set_indicator(WATCH_INDICATOR_SIGNAL);
      }
      watch_set_colon();
 +    if(state->repeat)
 +        watch_set_indicator(WATCH_INDICATOR_BELL);
  
      movement_request_tick_frequency(1);
      quick_ticks_running = false;
index 7e7ac6c7db1faede660dcf53b9b6ebf330addc91,836ada69c639be0311e16245c8e7b2d879539176..1245cc6162261bb4d9f8d8601dd60c2aa86c922c
@@@ -37,7 -37,7 +37,7 @@@
  // STATIC FUNCTIONS AND CONSTANTS /////////////////////////////////////////////
  
  /** @brief Planetary rulers in the Chaldean order from slowest to fastest
-- *  @details Planetary rulers in the Chaldean order from slowest to fastest: 
++ *  @details Planetary rulers in the Chaldean order from slowest to fastest:
   *  Jupiter, Mars, Sun, Venus, Mercury, Moon
   */
  static const char planets[7][3] = {"Sa", "Ju", "Ma", "So", "Ve", "Me", "Lu"}; // Latin
@@@ -150,7 -151,7 +151,7 @@@ static void _planetary_solar_phase(move
  
      // calculate sunrise and sunset of current day in decimal hours after midnight
      sun_rise_set(scratch_time.unit.year + WATCH_RTC_REFERENCE_YEAR, scratch_time.unit.month, scratch_time.unit.day, lon, lat, &sunrise, &sunset);
--    
++
      // calculate sunrise and sunset UNIX timestamps
      sunrise_epoch = midnight_epoch + sunrise * 3600;
      sunset_epoch = midnight_epoch + sunset * 3600;
          state->phase_end = sunrise_epoch;
      }
  
--    // calculate the duration of a planetary second during this solar phase 
++    // calculate the duration of a planetary second during this solar phase
      // and convert to Hertz so we can call a faster tick rate
      state->freq = (1 / ((double)( state->phase_end - state->phase_start ) / 43200));
  }
@@@ -206,12 -207,12 +207,13 @@@ static void _planetary_time(movement_ev
      double night_hour_count = 0.0;
      uint8_t weekday, planet, planetary_hour;
      double hour_duration, current_hour, current_minute, current_second;
 +    bool set_leading_zero = false;
+     watch_date_time date_time = watch_rtc_get_date_time();
  
          watch_set_colon();
  
      // get current time and convert to UTC
-     state->scratch = watch_utility_date_time_convert_zone(watch_rtc_get_date_time(), movement_timezone_offsets[settings->bit.time_zone] * 60, 0); 
 -    state->scratch = watch_utility_date_time_convert_zone(date_time, get_timezone_offset(settings->bit.time_zone, date_time) * 60, 0); 
++    state->scratch = watch_utility_date_time_convert_zone(date_time, get_timezone_offset(settings->bit.time_zone, date_time) * 60, 0);
  
      // when current phase ends calculate the next phase
      if ( watch_utility_date_time_to_unix_time(state->scratch, 0) >= state->phase_end ) {
      if ( state->ruler == 0 ) strncpy(ruler, planets[planet], 3);
      if ( state->ruler == 1 ) strncpy(ruler, planetes[planet], 3);
      if ( state->ruler == 2 ) strncpy(ruler, "  ", 3);
--    
++
      // display planetary time with ruler of the hour or ruler of the day
      if ( state->day_ruler ) sprintf(buf, "%s d%2d%02d%02d", ruler, state->scratch.unit.hour, state->scratch.unit.minute, state->scratch.unit.second);
      else sprintf(buf, "%s h%2d%02d%02d", ruler, state->scratch.unit.hour, state->scratch.unit.minute, state->scratch.unit.second);
--    
++
      watch_display_string(buf, 0);
 +    if (set_leading_zero)
 +        watch_display_string("0", 4);
  
      if ( state->ruler == 2 ) _planetary_icon(planet);
  
@@@ -301,7 -297,7 +303,7 @@@ void planetary_time_face_activate(movem
  #endif
  
      planetary_time_state_t *state = (planetary_time_state_t *)context;
--    
++
      // calculate phase
      _planetary_solar_phase(settings, state);
  }
index d50d24be01c11da0671cc41493dee77baecbb713,1b19df8d1c3242794be560ef66eb8763940c0ad9..0b7057557fc232b2cbab9ec9d8abdc77451c4cf1
@@@ -135,8 -134,7 +128,9 @@@ bool set_time_face_loop(movement_event_
              return movement_default_loop_handler(event, settings);
      }
  
-     char buf[11];
+     char buf[13];
 +    bool set_leading_zero = false;
++
      if (current_page < 3) {
          watch_set_colon();
          if (settings->bit.clock_mode_24h) {
index 75bc7bb46070b019f6871615309e7906548b2bf6,44ae04e296384fbe4d7c5ab54dc640dd21be37d7..6e67808d45b1cbe77889c262ac6c5a5a07b61b0a
@@@ -132,10 -134,11 +132,11 @@@ bool set_time_hackwatch_face_loop(movem
                      }
                      break;
              }
-             if (current_page != 2) // Do not set time when we are at seconds, it was already set previously
+             if (current_page != 2) // Do not set time when we are at seconds, it was already set previously
                  watch_rtc_set_date_time(date_time_settings);
+             }
              break;
 -        
 +
          case EVENT_ALARM_LONG_UP://Setting seconds on long release
              switch (current_page) {
                  case 2: // second
                      if (settings->bit.time_zone > 40) settings->bit.time_zone = 0;
                      break;
              }
-             if (current_page != 2) // Do not set time when we are at seconds, it was already set previously
++
 +            if (date_time_settings.unit.day > days_in_month(date_time_settings.unit.month, date_time_settings.unit.year + WATCH_RTC_REFERENCE_YEAR))
 +                date_time_settings.unit.day = 1;
++
+             if (current_page != 2) { // Do not set time when we are at seconds, it was already set previously
                  watch_rtc_set_date_time(date_time_settings);
+             }
++
              //TODO: Do not update whole RTC, just what we are changing
++
              break;
          case EVENT_TIMEOUT:
              movement_move_to_face(0);