diff --git a/src/c/constants.c b/src/c/constants.c new file mode 100644 index 0000000..c991aff --- /dev/null +++ b/src/c/constants.c @@ -0,0 +1,24 @@ +#include +#include "constants.h" + +const int DEFAULT_WEATHER_USE_GPS = 1; //true +const int DEFAULT_SHAKE_FOR_LOHI = 1; //True - shake to show Lo Hi Temps +const int DEFAULT_DISPLAY_LOHI_TIMER = 5000; //length time (milliseconds) to display Lo Hi Temps +const int DEFAULT_MIN_SINCE_WEATHER_UPDATE = 0; //weather just updated +const char DEFAULT_LAST_WEATHER_UPDATE[16] = "--:--"; //default last update "time" display +const char DEFAULT_ERROR_WEATHER_UPDATE[16] = "---"; //default text display upon weather update error +const int DEFAULT_CONDITION_CODE = -99; //Invalid condition code to force weather update +const int DEFAULT_DISPLAY_WEATHER = 1; //true +const int DEFAULT_WEATHER_FREQUENCY = 30; //minutes +const int DEFAULT_USE_CELSIUS = 0; //false +const int DEFAULT_BLINK_COLON = 0; //false +const int DEFAULT_INVERT = 0; //false +const int DEFAULT_VIBBRATE_BT_STATUS = 1; //true +const int DEFAULT_DISPLAY_SECONDS = 0; //false +const int DEFAULT_HIDE_BATTERY = 0; //false +const int DEFAULT_WEATHER_READABILITY = 1; //use original size +const int DEFAULT_WEATHER_Y_OFFSET_READABILITY = 4; //Y-axis adjustment for increased readability font options +const int DEFAULT_BACKGROUND_COLOR = 0x000000; //black +const int DEFAULT_WD_COLOR = 0xFFFFFF; //white + +const int DEFAULT_MAX_WEATHER_RETRY_COUNT = 5; //max (seconds) weather retry counts \ No newline at end of file diff --git a/src/c/constants.h b/src/c/constants.h new file mode 100644 index 0000000..f43eefa --- /dev/null +++ b/src/c/constants.h @@ -0,0 +1,53 @@ +#pragma once + + #define DEBUG 0 + + #define KEY_BLINK_COLON 1 + #define KEY_VIBBRATE_BT_STATUS 2 + #define KEY_HOURLY_VIBRATE 3 + #define KEY_HIDE_BATTERY 4 + #define KEY_DISPLAY_SECONDS 5 + #define KEY_TEMPERATURE_IN_C 6 + #define KEY_CONDITIONS 7 + #define KEY_BACKGROUND_COLOR 8 + #define KEY_TIME_COLOR 9 + #define KEY_WEATHER_FREQUENCY 10 + #define KEY_USE_CELSIUS 11 + #define KEY_DISPLAY_WEATHER 12 + #define KEY_GET_WEATHER 13 + #define KEY_TEMPERATURE 14 + #define KEY_WEATHER_READABILITY 15 + #define KEY_TEMPERATURE_LO 16 + #define KEY_TEMPERATURE_HI 17 + #define KEY_TEMPERATURE_IN_C_LO 18 + #define KEY_TEMPERATURE_IN_C_HI 19 + #define KEY_CONDITION_CODE 20 + #define KEY_SHAKE_FOR_LOHI 21 + #define KEY_WEATHER_USE_GPS 22 + #define KEY_WEATHER_LOCATION 23 + #define KEY_JS_READY 24 + #define KEY_WD_COLOR 25 + #define KEY_MIN_SINCE_WEATHER_UPDATE 26 + #define KEY_INVERT 27 + #define KEY_OPTIONS 99 + + extern const int DEFAULT_SHAKE_FOR_LOHI; + extern const int DEFAULT_DISPLAY_LOHI_TIMER; + extern const int DEFAULT_MIN_SINCE_WEATHER_UPDATE; + extern const char DEFAULT_LAST_WEATHER_UPDATE[16]; + extern const char DEFAULT_ERROR_WEATHER_UPDATE[16]; + extern const int DEFAULT_CONDITION_CODE; + extern const int DEFAULT_DISPLAY_WEATHER; + extern const int DEFAULT_WEATHER_FREQUENCY; + extern const int DEFAULT_WEATHER_USE_GPS; + extern const int DEFAULT_USE_CELSIUS; + extern const int DEFAULT_BLINK_COLON; + extern const int DEFAULT_INVERT; + extern const int DEFAULT_DISPLAY_SECONDS; + extern const int DEFAULT_BACKGROUND_COLOR; + extern const int DEFAULT_WEATHER_READABILITY; + extern const int DEFAULT_WEATHER_Y_OFFSET_READABILITY; + extern const int DEFAULT_VIBRATE_BT_STATUS; + extern const int DEFAULT_WD_COLOR; + + extern const int DEFAULT_MAX_WEATHER_RETRY_COUNT; \ No newline at end of file diff --git a/src/c/ninety_one_dub.c b/src/c/ninety_one_dub.c new file mode 100644 index 0000000..577d48c --- /dev/null +++ b/src/c/ninety_one_dub.c @@ -0,0 +1,1080 @@ +#include +#include "ninety_one_dub.h" +#include "constants.h" +#include "options.h" +#include "weather.h" +//#include "gbitmap_colour_manipulator.h" + +#define SCREEN_SHOT_HR 11 +#define SCREEN_SHOT_MIN 59 + +static Window *window; +static Layer *window_layer; + +static uint8_t batteryPercent; + +static AppSync sync; + +Options s_options; + +//stores weather retry counts (incremented in second tick handler). +//Stop retry count at DEFAULT_MAX_WEATHER_RETRY_COUNT +int g_weather_retry_count = 0; + +void send(int key, int value, int key2, int value2, int key3, char value3[64]); + +GTextAlignment row_alignment(int alignment) { + switch (alignment) { + case 0: return GTextAlignmentLeft; + case 1: return GTextAlignmentCenter; + case 2: return GTextAlignmentRight; + default: return GTextAlignmentLeft; + } +} + +void set_wd_layers_color(GColor textColor) { + text_layer_set_text_color(weather_layer1, textColor); +} + +void init_static_row(TextLayer *label, GFont font) { + //set row alignment + text_layer_set_text_alignment(label, row_alignment(1)); //center + + text_layer_set_text_color(label, GColorWhite); + //text_layer_set_text_color(label, GColorFromHEX(s_options.wd_color)); + + if (s_options.invert) { + text_layer_set_text_color(label, GColorBlack); + } + else{ + text_layer_set_text_color(label, GColorWhite); + } + + if (font) { + text_layer_set_font(label, font); + } +} + +static void bt_handler(bool isConnected) { + int current_bluetooth_state = 0; //default bluetooth disconnected + if (isConnected) + current_bluetooth_state = 1; //bluetooth connected + + //if bluetooth was previously connected and is now disconnected + if (s_options.bluetooth_state == 1 && + current_bluetooth_state == 0) { + + if (s_options.vibrate_bt_status) { + vibes_short_pulse(); //vibrate on bt disconnect, if option enabled + } + } + + //if bluetooth was previously disconnected and is now connected + if (s_options.bluetooth_state == 0 && + current_bluetooth_state == 1) { + + //clear weather conditions and reset weather retry count to force weather update + memset(s_options.conditions, 0,sizeof(s_options.conditions)); + s_options.condition_code = DEFAULT_CONDITION_CODE; + g_weather_retry_count = 0; + } + + //store bluetooth state + s_options.bluetooth_state = current_bluetooth_state; +} + +static void tap_handler(AccelAxisType axis, int32_t direction) { + if (s_options.shake_for_lohi == 1) { + //Display Lo Hi temp info; + display_lohi_weather_info(); + } +} + +//every five seconds +static void handle_5second_tick(struct tm *tick_time, TimeUnits units_changed) { + + //if conditions blank (no current weather info) + if(strlen(s_options.conditions) == 0 && + g_weather_retry_count < DEFAULT_MAX_WEATHER_RETRY_COUNT) { + + //add one to weather retry count - stop trying this app session at DEFAULT_MAX_WEATHER_RETRY_COUNT + ++g_weather_retry_count; + + //reset time since last forecast timer + s_options.min_since_last_forecast = 0; + + //get updated weather + send(KEY_GET_WEATHER, 1, KEY_WEATHER_USE_GPS, s_options.weather_use_GPS, KEY_WEATHER_LOCATION, s_options.weather_location); + + //update weather layer + update_weather_layer(); + } + //APP_LOG(APP_LOG_LEVEL_DEBUG, "heap_bytes_free:heap_bytes_used %zu: %zu", heap_bytes_free(), heap_bytes_used()); +} + +//every minute +static void handle_minute_tick(struct tm *tick_time, TimeUnits units_changed) { + + if (units_changed & DAY_UNIT) { + //vibes_double_pulse(); //hourly vibration + } + + //get minutes since last weather update + time_t t = time(NULL); + int min_since_last_update = (difftime(t, s_options.last_update) / 60) + 1; + + APP_LOG(APP_LOG_LEVEL_DEBUG, " (Last update %s) min_since_last_update:weather_frequency - %d:%d", s_options.last_weather_update24hr, min_since_last_update, s_options.weather_frequency); + //APP_LOG(APP_LOG_LEVEL_DEBUG, "handle_minute_tick strlen(s_options.conditions): %zu", strlen(s_options.conditions)); + //if min_since_last_forecast greater than or equal to weatherUpdateFrequency + if(min_since_last_update >= s_options.weather_frequency || + strlen(s_options.conditions) == 0) { + + s_options.min_since_last_forecast = 0; + + //get updated weather + send(KEY_GET_WEATHER, 1, KEY_WEATHER_USE_GPS, s_options.weather_use_GPS, KEY_WEATHER_LOCATION, s_options.weather_location); + + //update weather layer + update_weather_layer(); + } +} + +static const PropertyAnimationImplementation layer_hidden_implementation = { + .base = { + .update = (AnimationUpdateImplementation) property_animation_update_int16, + }, + .accessors = { + .setter = { .int16 = (Int16Setter) layer_set_hidden, }, + .getter = { .int16 = (Int16Getter) layer_get_hidden, }, + }, +}; + +static bool appStarted = false; + +enum { + BLINK_KEY = 0x0, + INVERT_KEY = 0x1, + BLUETOOTHVIBE_KEY = 0x2, + HOURLYVIBE_KEY = 0x3, + BRANDING_MASK_KEY = 0x4, + BATTERY_HIDE_KEY = 0x5, + SECONDS_KEY = 0x6 +}; + +enum { + BLINK_OFF = 0, + BLINK_ON = 1, + BLINK_DOUBLE_RATE = 2, +}; + +static GBitmap *branding_mask_image; +static BitmapLayer *branding_mask_layer; + +static GBitmap *background_image; +static BitmapLayer *background_layer; + +static GBitmap *separator_image; +static BitmapLayer *separator_layer; +static PropertyAnimation *separator_animation; + +static GBitmap *separator_med_image; +static BitmapLayer *separator_med_layer; +static PropertyAnimation *separator_med_animation; + +static GBitmap *meter_bar_image; +static BitmapLayer *meter_bar_layer; + +static GBitmap *bluetooth_image; +static BitmapLayer *bluetooth_layer; + +static GBitmap *battery_image; +static BitmapLayer *battery_image_layer; +static BitmapLayer *battery_layer; + +static GBitmap *time_format_image; +static BitmapLayer *time_format_layer; + +static GBitmap *day_name_image; +static BitmapLayer *day_name_layer; + +static Layer *big_time_layer; +static Layer *med_time_layer; + +const int DAY_NAME_IMAGE_RESOURCE_IDS[] = { + RESOURCE_ID_IMAGE_DAY_NAME_SUN, + RESOURCE_ID_IMAGE_DAY_NAME_MON, + RESOURCE_ID_IMAGE_DAY_NAME_TUE, + RESOURCE_ID_IMAGE_DAY_NAME_WED, + RESOURCE_ID_IMAGE_DAY_NAME_THU, + RESOURCE_ID_IMAGE_DAY_NAME_FRI, + RESOURCE_ID_IMAGE_DAY_NAME_SAT +}; + +#define TOTAL_DATE_DIGITS 2 +static GBitmap *date_digits_images[TOTAL_DATE_DIGITS]; +static BitmapLayer *date_digits_layers[TOTAL_DATE_DIGITS]; + +const int DATENUM_IMAGE_RESOURCE_IDS[] = { + RESOURCE_ID_IMAGE_DATENUM_0, + RESOURCE_ID_IMAGE_DATENUM_1, + RESOURCE_ID_IMAGE_DATENUM_2, + RESOURCE_ID_IMAGE_DATENUM_3, + RESOURCE_ID_IMAGE_DATENUM_4, + RESOURCE_ID_IMAGE_DATENUM_5, + RESOURCE_ID_IMAGE_DATENUM_6, + RESOURCE_ID_IMAGE_DATENUM_7, + RESOURCE_ID_IMAGE_DATENUM_8, + RESOURCE_ID_IMAGE_DATENUM_9 +}; + +#define TOTAL_TIME_DIGITS 4 +static GBitmap *time_digits_images[TOTAL_TIME_DIGITS]; +static BitmapLayer *time_digits_layers[TOTAL_TIME_DIGITS]; + +const int BIG_DIGIT_IMAGE_RESOURCE_IDS[] = { + RESOURCE_ID_IMAGE_NUM_0, + RESOURCE_ID_IMAGE_NUM_1, + RESOURCE_ID_IMAGE_NUM_2, + RESOURCE_ID_IMAGE_NUM_3, + RESOURCE_ID_IMAGE_NUM_4, + RESOURCE_ID_IMAGE_NUM_5, + RESOURCE_ID_IMAGE_NUM_6, + RESOURCE_ID_IMAGE_NUM_7, + RESOURCE_ID_IMAGE_NUM_8, + RESOURCE_ID_IMAGE_NUM_9 +}; + +#define TOTAL_TIME_MED_DIGITS 4 +static GBitmap *time_med_digits_images[TOTAL_TIME_MED_DIGITS]; +static BitmapLayer *time_med_digits_layers[TOTAL_TIME_MED_DIGITS]; + +const int MED_DIGIT_IMAGE_RESOURCE_IDS[] = { + RESOURCE_ID_IMAGE_MED_0, + RESOURCE_ID_IMAGE_MED_1, + RESOURCE_ID_IMAGE_MED_2, + RESOURCE_ID_IMAGE_MED_3, + RESOURCE_ID_IMAGE_MED_4, + RESOURCE_ID_IMAGE_MED_5, + RESOURCE_ID_IMAGE_MED_6, + RESOURCE_ID_IMAGE_MED_7, + RESOURCE_ID_IMAGE_MED_8, + RESOURCE_ID_IMAGE_MED_9 +}; + +#define TOTAL_TIME_SM_DIGITS 2 +static GBitmap *time_sm_digits_images[TOTAL_TIME_SM_DIGITS]; +static BitmapLayer *time_sm_digits_layers[TOTAL_TIME_SM_DIGITS]; + +const int SM_DIGIT_IMAGE_RESOURCE_IDS[] = { + RESOURCE_ID_IMAGE_SM_0, + RESOURCE_ID_IMAGE_SM_1, + RESOURCE_ID_IMAGE_SM_2, + RESOURCE_ID_IMAGE_SM_3, + RESOURCE_ID_IMAGE_SM_4, + RESOURCE_ID_IMAGE_SM_5, + RESOURCE_ID_IMAGE_SM_6, + RESOURCE_ID_IMAGE_SM_7, + RESOURCE_ID_IMAGE_SM_8, + RESOURCE_ID_IMAGE_SM_9 +}; + + +#define TOTAL_BATTERY_PERCENT_DIGITS 3 +static GBitmap *battery_percent_image[TOTAL_BATTERY_PERCENT_DIGITS]; +static BitmapLayer *battery_percent_layers[TOTAL_BATTERY_PERCENT_DIGITS]; + +const int TINY_IMAGE_RESOURCE_IDS[] = { + RESOURCE_ID_IMAGE_TINY_0, + RESOURCE_ID_IMAGE_TINY_1, + RESOURCE_ID_IMAGE_TINY_2, + RESOURCE_ID_IMAGE_TINY_3, + RESOURCE_ID_IMAGE_TINY_4, + RESOURCE_ID_IMAGE_TINY_5, + RESOURCE_ID_IMAGE_TINY_6, + RESOURCE_ID_IMAGE_TINY_7, + RESOURCE_ID_IMAGE_TINY_8, + RESOURCE_ID_IMAGE_TINY_9, + RESOURCE_ID_IMAGE_TINY_PERCENT +}; +#define palette_num 5 + +uint8_t my_palettes[2* palette_num]= { + GColorWhiteARGB8, GColorBlackARGB8 //black-white +}; + +enum {COLOUR_USER, COLOUR_BW, COLOUR_YELLOW, COLOUR_DukeBlue, COLOUR_CYEN }; + +GBitmap* gbitmap_create_with_palette(uint8_t palette, uint32_t resource){ + + GBitmap* bitmap = gbitmap_create_with_resource(resource); + GColor * colors= gbitmap_get_palette(bitmap); + if (palettekey) { + + case KEY_TEMPERATURE: + s_options.tempF = (int)t->value->int32; + break; + + case KEY_TEMPERATURE_LO: + s_options.tempFLo = (int)t->value->int32; + break; + + case KEY_TEMPERATURE_HI: + s_options.tempFHi = (int)t->value->int32; + break; + + case KEY_TEMPERATURE_IN_C: + s_options.tempC = (int)t->value->int32; + break; + + case KEY_TEMPERATURE_IN_C_LO: + s_options.tempCLo = (int)t->value->int32; + break; + + case KEY_TEMPERATURE_IN_C_HI: + s_options.tempCHi = (int)t->value->int32; + break; + + case KEY_CONDITIONS: + snprintf(s_options.conditions, sizeof(s_options.conditions), "%s", t->value->cstring); + + //check if successful weather retrieval + if (strlen(s_options.conditions) > 0) { + // Get a tm structure + time_t temp = time(NULL); + struct tm *tick_time = localtime(&temp); + strftime(s_options.last_weather_update24hr, sizeof("00:00"), "%H:%M", tick_time); + strftime(s_options.last_weather_update12hr, sizeof("00:00 pm"), "%l:%M %P", tick_time); + s_options.last_update = temp; + } + break; + + case KEY_CONDITION_CODE: + s_options.condition_code = (int)t->value->int32; + break; + + case KEY_SHAKE_FOR_LOHI: + s_options.shake_for_lohi = (int)t->value->int32; + break; + + case KEY_USE_CELSIUS: + s_options.use_celsius = (int)t->value->int32; + break; + + case KEY_WEATHER_USE_GPS: + //force weather update after each config page Save + memset(s_options.conditions, 0,sizeof(s_options.conditions)); + s_options.condition_code = DEFAULT_CONDITION_CODE; + + s_options.weather_use_GPS = (int)t->value->int32; + break; + + case KEY_WEATHER_LOCATION: + snprintf(s_options.weather_location, sizeof(s_options.weather_location), "%s", t->value->cstring); + break; + + case KEY_WEATHER_FREQUENCY: + s_options.weather_frequency = (int)t->value->int32; + break; + + case KEY_WD_COLOR: + s_options.wd_color = (int)t->value->int32; + set_wd_layers_color(GColorFromHEX(s_options.wd_color)); + break; + + case KEY_INVERT: + s_options.invert = (int)t->value->int32; + change_background(); + if (s_options.invert) { + text_layer_set_text_color(weather_layer1, GColorBlack); + text_layer_set_background_color(weather_layer1, GColorWhite); + } + else{ + text_layer_set_text_color(weather_layer1, GColorWhite); + text_layer_set_background_color(weather_layer1, GColorBlack); + } + break; + + case KEY_WEATHER_READABILITY: + s_options.weather_readability = (int)t->value->int32; + break; + + case KEY_HIDE_BATTERY: + s_options.hide_battery = (int)t->value->int32; + layer_set_hidden(bitmap_layer_get_layer(battery_layer), s_options.hide_battery); + layer_set_hidden(bitmap_layer_get_layer(battery_image_layer), s_options.hide_battery); + break; + + case KEY_BLINK_COLON: + s_options.blink_colon = (int)t->value->int32; + + if(s_options.blink_colon || s_options.display_seconds) { + //do nothing + } + else { + layer_set_hidden(bitmap_layer_get_layer(separator_layer), false); + layer_set_hidden(bitmap_layer_get_layer(separator_med_layer), false); + } + break; + + case KEY_DISPLAY_SECONDS: + s_options.display_seconds = (int)t->value->int32; + + if(s_options.display_seconds) { + layer_set_hidden(big_time_layer, true); + layer_set_hidden(med_time_layer, false); + } + else { + layer_set_hidden(big_time_layer, false); + layer_set_hidden(med_time_layer, true); + } + appStarted = false; + time_t now = time(NULL); + struct tm *tick_time = localtime(&now); + handle_tick(tick_time, HOUR_UNIT + MINUTE_UNIT + SECOND_UNIT); + appStarted = true; + break; + + case KEY_VIBBRATE_BT_STATUS: + s_options.vibrate_bt_status = (int)t->value->int32; + break; + + case KEY_HOURLY_VIBRATE: + s_options.hourly_vibe = (int)t->value->int32; + break; + + case KEY_JS_READY: + s_options.pebble_js_ready = (int)t->value->int32; + #if DEBUG + APP_LOG(APP_LOG_LEVEL_DEBUG, "inbox_received_callback: s_options.pebble_js_ready: %d", s_options.pebble_js_ready); + #endif + break; + } + // Look for next item + t = dict_read_next(iterator); + } + + //check update weather layers + update_weather_layer(); +} + +char *translate_error(AppMessageResult result) { + switch (result) { + case APP_MSG_OK: return "APP_MSG_OK"; + case APP_MSG_SEND_TIMEOUT: return "APP_MSG_SEND_TIMEOUT"; + case APP_MSG_SEND_REJECTED: return "APP_MSG_SEND_REJECTED"; + case APP_MSG_NOT_CONNECTED: return "APP_MSG_NOT_CONNECTED"; + case APP_MSG_APP_NOT_RUNNING: return "APP_MSG_APP_NOT_RUNNING"; + case APP_MSG_INVALID_ARGS: return "APP_MSG_INVALID_ARGS"; + case APP_MSG_BUSY: return "APP_MSG_BUSY"; + case APP_MSG_BUFFER_OVERFLOW: return "APP_MSG_BUFFER_OVERFLOW"; + case APP_MSG_ALREADY_RELEASED: return "APP_MSG_ALREADY_RELEASED"; + case APP_MSG_CALLBACK_ALREADY_REGISTERED: return "APP_MSG_CALLBACK_ALREADY_REGISTERED"; + case APP_MSG_CALLBACK_NOT_REGISTERED: return "APP_MSG_CALLBACK_NOT_REGISTERED"; + case APP_MSG_OUT_OF_MEMORY: return "APP_MSG_OUT_OF_MEMORY"; + case APP_MSG_CLOSED: return "APP_MSG_CLOSED"; + case APP_MSG_INTERNAL_ERROR: return "APP_MSG_INTERNAL_ERROR"; + default: return "UNKNOWN ERROR"; + } +} + +void message_dropped(AppMessageResult reason, void *context) { + APP_LOG(APP_LOG_LEVEL_ERROR, "Dropped message! Reason given: %s", translate_error(reason)); +} + +void message_out_success(DictionaryIterator *iter, void *context) { + APP_LOG(APP_LOG_LEVEL_DEBUG, "Message sent."); +} + +void message_out_failed(DictionaryIterator *iter, AppMessageResult reason, void *context) { + APP_LOG(APP_LOG_LEVEL_DEBUG, "Failed to send message. Reason = %s", translate_error(reason)); +} + +//***************************************************** Messaging *************************************************************************** + + +static void set_container_image(GBitmap **bmp_image, BitmapLayer *bmp_layer, const int resource_id, GPoint origin) { + GBitmap *old_image = *bmp_image; + *bmp_image = gbitmap_create_with_palette(COLOUR_USER, resource_id); + GRect frame = (GRect) { + .origin = origin, + .size = gbitmap_get_bounds(*bmp_image).size + }; + + bitmap_layer_set_bitmap(bmp_layer, *bmp_image); + layer_set_frame(bitmap_layer_get_layer(bmp_layer), frame); + + if (old_image != NULL) { + gbitmap_destroy(old_image); + old_image = NULL; + } +} + +static void update_battery(BatteryChargeState charge_state) { + + if(s_options.hide_battery) { + return; + } + + batteryPercent = charge_state.charge_percent; + + if(batteryPercent==100) { + change_battery_icon(false); + layer_set_hidden(bitmap_layer_get_layer(battery_layer), false); + for (int i = 0; i < TOTAL_BATTERY_PERCENT_DIGITS; ++i) { + layer_set_hidden(bitmap_layer_get_layer(battery_percent_layers[i]), true); + } + return; + } + + layer_set_hidden(bitmap_layer_get_layer(battery_layer), charge_state.is_charging); + change_battery_icon(charge_state.is_charging); + + for (int i = 0; i < TOTAL_BATTERY_PERCENT_DIGITS; ++i) { + layer_set_hidden(bitmap_layer_get_layer(battery_percent_layers[i]), false); + } + set_container_image(&battery_percent_image[0], battery_percent_layers[0], TINY_IMAGE_RESOURCE_IDS[charge_state.charge_percent/10], GPoint(89, 43)); + set_container_image(&battery_percent_image[1], battery_percent_layers[1], TINY_IMAGE_RESOURCE_IDS[charge_state.charge_percent%10], GPoint(95, 43)); + set_container_image(&battery_percent_image[2], battery_percent_layers[2], TINY_IMAGE_RESOURCE_IDS[10], GPoint(101, 43)); + +} + +static void toggle_bluetooth_icon(bool connected) { + if(appStarted && !connected && s_options.vibrate_bt_status) { + //vibe! + vibes_long_pulse(); + } + layer_set_hidden(bitmap_layer_get_layer(bluetooth_layer), !connected); +} + +void bluetooth_connection_callback(bool connected) { + toggle_bluetooth_icon(connected); + + bt_handler(connected); +} + +void battery_layer_update_callback(Layer *me, GContext* ctx) { + //draw the remaining battery percentage + graphics_context_set_stroke_color(ctx, COLOR_FALLBACK(GColorBlack, GColorBlack)); + graphics_context_set_fill_color(ctx, COLOR_FALLBACK(GColorBlack, GColorBlack)); + graphics_fill_rect(ctx, GRect(2, 2, ((batteryPercent/100.0)*11.0), 5), 0, GCornerNone); +} + +unsigned short get_display_hour(unsigned short hour) { + if (clock_is_24h_style()) { + return hour; + } + unsigned short display_hour = hour % 12; + // Converts "0" to "12" + return display_hour ? display_hour : 12; +} + +static void update_days(struct tm *tick_time) { + set_container_image(&day_name_image, day_name_layer, DAY_NAME_IMAGE_RESOURCE_IDS[tick_time->tm_wday], GPoint(69, 61)); + + set_container_image(&date_digits_images[0], date_digits_layers[0], DATENUM_IMAGE_RESOURCE_IDS[tick_time->tm_mday/10], GPoint(108, 61)); + set_container_image(&date_digits_images[1], date_digits_layers[1], DATENUM_IMAGE_RESOURCE_IDS[tick_time->tm_mday%10], GPoint(121, 61)); +} + +static void update_hours(struct tm *tick_time) { + + if(appStarted && s_options.hourly_vibe) { + //vibe! + vibes_short_pulse(); + } + + unsigned short display_hour = get_display_hour(tick_time->tm_hour); + + if(s_options.display_seconds) { + set_container_image(&time_med_digits_images[0], time_med_digits_layers[0], MED_DIGIT_IMAGE_RESOURCE_IDS[display_hour/10], GPoint(12, 88)); + set_container_image(&time_med_digits_images[1], time_med_digits_layers[1], MED_DIGIT_IMAGE_RESOURCE_IDS[display_hour%10], GPoint(35, 88)); + } + else { + set_container_image(&time_digits_images[0], time_digits_layers[0], BIG_DIGIT_IMAGE_RESOURCE_IDS[display_hour/10], GPoint(10, 84)); + set_container_image(&time_digits_images[1], time_digits_layers[1], BIG_DIGIT_IMAGE_RESOURCE_IDS[display_hour%10], GPoint(40, 84)); + } + + if (!clock_is_24h_style()) { + if (tick_time->tm_hour >= 12) { + set_container_image(&time_format_image, time_format_layer, RESOURCE_ID_IMAGE_PM_MODE, GPoint(17, 68)); + layer_set_hidden(bitmap_layer_get_layer(time_format_layer), false); + } + else { + //layer_set_hidden(bitmap_layer_get_layer(time_format_layer), true); + set_container_image(&time_format_image, time_format_layer, RESOURCE_ID_IMAGE_AM_MODE, GPoint(17, 68)); + layer_set_hidden(bitmap_layer_get_layer(time_format_layer), false); + } + if (display_hour/10 == 0) { + if(s_options.display_seconds) { + layer_set_hidden(bitmap_layer_get_layer(time_med_digits_layers[0]), true); + } + else { + layer_set_hidden(bitmap_layer_get_layer(time_digits_layers[0]), true); + } + } + else { + if(s_options.display_seconds) { + layer_set_hidden(bitmap_layer_get_layer(time_med_digits_layers[0]), false); + } + else { + layer_set_hidden(bitmap_layer_get_layer(time_digits_layers[0]), false); + } + } + + } +} +static void update_minutes(struct tm *tick_time) { + if(s_options.display_seconds) { + set_container_image(&time_med_digits_images[2], time_med_digits_layers[2], MED_DIGIT_IMAGE_RESOURCE_IDS[tick_time->tm_min/10], GPoint(65, 88)); + set_container_image(&time_med_digits_images[3], time_med_digits_layers[3], MED_DIGIT_IMAGE_RESOURCE_IDS[tick_time->tm_min%10], GPoint(87, 88)); + } + else { + set_container_image(&time_digits_images[2], time_digits_layers[2], BIG_DIGIT_IMAGE_RESOURCE_IDS[tick_time->tm_min/10], GPoint(77, 84)); + set_container_image(&time_digits_images[3], time_digits_layers[3], BIG_DIGIT_IMAGE_RESOURCE_IDS[tick_time->tm_min%10], GPoint(105, 84)); + } + +} +static void update_seconds(struct tm *tick_time) { + + if(s_options.blink_colon == BLINK_ON) { + animation_unschedule_all(); + if(s_options.display_seconds) { + layer_set_hidden(bitmap_layer_get_layer(separator_med_layer), tick_time->tm_sec%2); + } + else { + layer_set_hidden(bitmap_layer_get_layer(separator_layer), tick_time->tm_sec%2); + } + } + else if(s_options.blink_colon == BLINK_DOUBLE_RATE) { + if(s_options.display_seconds) { + layer_set_hidden(bitmap_layer_get_layer(separator_med_layer), false); + animation_schedule((Animation *)separator_med_animation); + } + else { + layer_set_hidden(bitmap_layer_get_layer(separator_layer), false); + animation_schedule((Animation *)separator_animation); + } + } + else if (s_options.blink_colon == BLINK_OFF) { + animation_unschedule_all(); + if(s_options.display_seconds) { + layer_set_hidden(bitmap_layer_get_layer(separator_med_layer), false); + } + else { + layer_set_hidden(bitmap_layer_get_layer(separator_layer), false); + } + } + if(s_options.display_seconds) { + set_container_image(&time_sm_digits_images[0], time_sm_digits_layers[0], SM_DIGIT_IMAGE_RESOURCE_IDS[tick_time->tm_sec/10], GPoint(109, 104)); + set_container_image(&time_sm_digits_images[1], time_sm_digits_layers[1], SM_DIGIT_IMAGE_RESOURCE_IDS[tick_time->tm_sec%10], GPoint(121, 104)); + } +} + +static void handle_tick(struct tm *tick_time, TimeUnits units_changed) { + if (units_changed & DAY_UNIT) { + update_days(tick_time); + } + if (units_changed & HOUR_UNIT) { + update_hours(tick_time); + } + if (units_changed & MINUTE_UNIT) { + handle_minute_tick(tick_time, units_changed); + update_minutes(tick_time); + } + if (units_changed & SECOND_UNIT) { + update_seconds(tick_time); + if (tick_time->tm_sec % 5 == 0) { + handle_5second_tick(tick_time, units_changed); + } + } +} + +static void init(void) { + memset(&time_digits_layers, 0, sizeof(time_digits_layers)); + memset(&time_digits_images, 0, sizeof(time_digits_images)); + memset(&time_med_digits_layers, 0, sizeof(time_med_digits_layers)); + memset(&time_med_digits_images, 0, sizeof(time_med_digits_images)); + memset(&time_sm_digits_layers, 0, sizeof(time_sm_digits_layers)); + memset(&time_sm_digits_images, 0, sizeof(time_sm_digits_images)); + + memset(&date_digits_layers, 0, sizeof(date_digits_layers)); + memset(&date_digits_images, 0, sizeof(date_digits_images)); + memset(&battery_percent_layers, 0, sizeof(battery_percent_layers)); + memset(&battery_percent_image, 0, sizeof(battery_percent_image)); + + window = window_create(); + if (window == NULL) { + //APP_LOG(APP_LOG_LEVEL_DEBUG, "OOM: couldn't allocate window"); + return; + } + window_stack_push(window, true /* Animated */); + window_layer = window_get_root_layer(window); + + //set-retrieve watchface options + init_options(); + + //background_image = gbitmap_create_with_palette(COLOUR_USER, RESOURCE_ID_IMAGE_BACKGROUND_WIDE); + if(s_options.invert) { + background_image = gbitmap_create_with_palette(COLOUR_USER, RESOURCE_ID_IMAGE_BACKGROUND_INVERT_WIDE); + } + else { + background_image = gbitmap_create_with_palette(COLOUR_USER, RESOURCE_ID_IMAGE_BACKGROUND_WIDE); + } + + background_layer = bitmap_layer_create(layer_get_frame(window_layer)); + bitmap_layer_set_bitmap(background_layer, background_image); + layer_add_child(window_layer, bitmap_layer_get_layer(background_layer)); + + big_time_layer = layer_create(layer_get_frame(window_layer)); + layer_add_child(window_layer, big_time_layer); + + med_time_layer = layer_create(layer_get_frame(window_layer)); + layer_add_child(window_layer, med_time_layer); + + separator_image = gbitmap_create_with_palette(COLOUR_USER, RESOURCE_ID_IMAGE_SEPARATOR); + GRect frame = (GRect) { + .origin = { .x = 69, .y = 91 }, + .size = gbitmap_get_bounds(separator_image).size + }; + separator_layer = bitmap_layer_create(frame); + + bitmap_layer_set_bitmap(separator_layer, separator_image); + layer_add_child(big_time_layer, bitmap_layer_get_layer(separator_layer)); + + separator_med_image = gbitmap_create_with_palette(COLOUR_USER, RESOURCE_ID_IMAGE_MED_SEPARATOR); + GRect frame_med = (GRect) { + .origin = { .x = 57, .y = 93 }, + .size = gbitmap_get_bounds(separator_med_image).size + }; + separator_med_layer = bitmap_layer_create(frame_med); + bitmap_layer_set_bitmap(separator_med_layer, separator_med_image); + layer_add_child(med_time_layer, bitmap_layer_get_layer(separator_med_layer)); + layer_set_hidden(med_time_layer, true); + + meter_bar_image = gbitmap_create_with_palette(COLOUR_USER, RESOURCE_ID_IMAGE_METER_BAR); + GRect frame2 = (GRect) { + .origin = { .x = 12, .y = 43 }, //x17 + .size = gbitmap_get_bounds(meter_bar_image).size + }; + meter_bar_layer = bitmap_layer_create(frame2); + bitmap_layer_set_bitmap(meter_bar_layer, meter_bar_image); + layer_add_child(window_layer, bitmap_layer_get_layer(meter_bar_layer)); + + bluetooth_image = gbitmap_create_with_palette(COLOUR_USER, RESOURCE_ID_IMAGE_BLUETOOTH); + GRect frame3 = (GRect) { + .origin = { .x = 28, .y = 43 }, //x33 + .size = gbitmap_get_bounds(bluetooth_image).size + }; + bluetooth_layer = bitmap_layer_create(frame3); + bitmap_layer_set_bitmap(bluetooth_layer, bluetooth_image); + layer_add_child(window_layer, bitmap_layer_get_layer(bluetooth_layer)); + + battery_image = gbitmap_create_with_palette(COLOUR_USER, RESOURCE_ID_IMAGE_BATTERY); + GRect frame4 = (GRect) { + .origin = { .x = 116, .y = 43 }, //x111 + .size = gbitmap_get_bounds(battery_image).size + }; + battery_layer = bitmap_layer_create(frame4); + battery_image_layer = bitmap_layer_create(frame4); + bitmap_layer_set_bitmap(battery_image_layer, battery_image); + layer_set_update_proc(bitmap_layer_get_layer(battery_layer), battery_layer_update_callback); + + layer_add_child(window_layer, bitmap_layer_get_layer(battery_image_layer)); + layer_add_child(window_layer, bitmap_layer_get_layer(battery_layer)); + + GRect frame5 = (GRect) { + .origin = { .x = 17, .y = 68 }, + .size = {.w = 19, .h = 8} + }; + time_format_layer = bitmap_layer_create(frame5); + if (clock_is_24h_style()) { + time_format_image = gbitmap_create_with_palette(COLOUR_USER, RESOURCE_ID_IMAGE_24_HOUR_MODE); + bitmap_layer_set_bitmap(time_format_layer, time_format_image); + } + layer_add_child(window_layer, bitmap_layer_get_layer(time_format_layer)); + + // Create time and date layers + GRect dummy_frame = { {0, 0}, {0, 0} }; + day_name_layer = bitmap_layer_create(dummy_frame); + layer_add_child(window_layer, bitmap_layer_get_layer(day_name_layer)); + + for (int i = 0; i < TOTAL_TIME_DIGITS; ++i) { + time_digits_layers[i] = bitmap_layer_create(dummy_frame); + layer_add_child(big_time_layer, bitmap_layer_get_layer(time_digits_layers[i])); + } + for (int i = 0; i < TOTAL_TIME_MED_DIGITS; ++i) { + time_med_digits_layers[i] = bitmap_layer_create(dummy_frame); + layer_add_child(med_time_layer, bitmap_layer_get_layer(time_med_digits_layers[i])); + } + for (int i = 0; i < TOTAL_TIME_SM_DIGITS; ++i) { + time_sm_digits_layers[i] = bitmap_layer_create(dummy_frame); + layer_add_child(med_time_layer, bitmap_layer_get_layer(time_sm_digits_layers[i])); + } + for (int i = 0; i < TOTAL_DATE_DIGITS; ++i) { + date_digits_layers[i] = bitmap_layer_create(dummy_frame); + layer_add_child(window_layer, bitmap_layer_get_layer(date_digits_layers[i])); + } + for (int i = 0; i < TOTAL_BATTERY_PERCENT_DIGITS; ++i) { + battery_percent_layers[i] = bitmap_layer_create(dummy_frame); + layer_add_child(window_layer, bitmap_layer_get_layer(battery_percent_layers[i])); + } + + //mask the pebble branding + GRect framemask = (GRect) { + .origin = { .x = 0, .y = 0 }, + .size = { .w = 144, .h = 19 } + }; + branding_mask_layer = bitmap_layer_create(framemask); + layer_add_child(window_layer, bitmap_layer_get_layer(branding_mask_layer)); + branding_mask_image = gbitmap_create_with_palette(COLOUR_USER, RESOURCE_ID_IMAGE_BRANDING_MASK); + bitmap_layer_set_bitmap(branding_mask_layer, branding_mask_image); + layer_set_hidden(bitmap_layer_get_layer(branding_mask_layer), false); + + //add weather layer + GRect layer_frame = layer_get_frame(window_layer); + const int16_t width = layer_frame.size.w; + add_weather_layers(window_layer, width); + + toggle_bluetooth_icon(bluetooth_connection_service_peek()); + update_battery(battery_state_service_peek()); + + bool _true = true; + bool _false = false; + separator_animation = property_animation_create(&layer_hidden_implementation, bitmap_layer_get_layer(separator_layer), &_false, &_true); + animation_set_duration((Animation *) separator_animation, 1); + animation_set_delay((Animation *) separator_animation, 500); + animation_set_curve((Animation *) separator_animation, AnimationCurveLinear); + + separator_med_animation = property_animation_create(&layer_hidden_implementation, bitmap_layer_get_layer(separator_med_layer), &_false, &_true); + animation_set_duration((Animation *) separator_med_animation, 1); + animation_set_delay((Animation *) separator_med_animation, 500); + animation_set_curve((Animation *) separator_med_animation, AnimationCurveLinear); + + // Avoids a blank screen on watch start. + time_t now = time(NULL); + struct tm *tick_time = localtime(&now); + #if DEBUG + tick_time->tm_hour = SCREEN_SHOT_HR; //for screen shots + tick_time->tm_min = SCREEN_SHOT_MIN; + #endif + handle_tick(tick_time, DAY_UNIT + HOUR_UNIT + MINUTE_UNIT + SECOND_UNIT); + + appStarted = true; + + app_message_register_inbox_received(inbox_received_callback); + app_message_register_inbox_dropped(message_dropped); + app_message_register_outbox_sent(message_out_success); + app_message_register_outbox_failed(message_out_failed); + + const int inbound_size = 175; //128 + const int outbound_size = 250; //128 + + app_message_open(inbound_size, outbound_size); + //app_message_open(app_message_inbox_size_maximum(), app_message_outbox_size_maximum()); + + tick_timer_service_subscribe(MINUTE_UNIT | SECOND_UNIT, handle_tick); + + accel_tap_service_subscribe(tap_handler); + bluetooth_connection_service_subscribe(bluetooth_connection_callback); + battery_state_service_subscribe(&update_battery); +} + +static void deinit(void) { + + //save watchface options + persist_write_data(KEY_OPTIONS, &s_options, sizeof(s_options)); + + app_sync_deinit(&sync); + + tick_timer_service_unsubscribe(); + bluetooth_connection_service_unsubscribe(); + battery_state_service_unsubscribe(); + + animation_unschedule_all(); + + property_animation_destroy(separator_med_animation); + separator_med_animation = NULL; + + property_animation_destroy(separator_animation); + separator_animation = NULL; + + layer_remove_from_parent(bitmap_layer_get_layer(background_layer)); + bitmap_layer_destroy(background_layer); + gbitmap_destroy(background_image); + background_image = NULL; + + layer_remove_from_parent(bitmap_layer_get_layer(branding_mask_layer)); + bitmap_layer_destroy(branding_mask_layer); + gbitmap_destroy(branding_mask_image); + branding_mask_image = NULL; + + layer_remove_from_parent(bitmap_layer_get_layer(separator_layer)); + bitmap_layer_destroy(separator_layer); + gbitmap_destroy(separator_image); + separator_image = NULL; + + layer_remove_from_parent(bitmap_layer_get_layer(separator_med_layer)); + bitmap_layer_destroy(separator_med_layer); + gbitmap_destroy(separator_med_image); + separator_med_image = NULL; + + layer_remove_from_parent(bitmap_layer_get_layer(meter_bar_layer)); + bitmap_layer_destroy(meter_bar_layer); + gbitmap_destroy(meter_bar_image); + background_image = NULL; + + layer_remove_from_parent(bitmap_layer_get_layer(bluetooth_layer)); + bitmap_layer_destroy(bluetooth_layer); + gbitmap_destroy(bluetooth_image); + bluetooth_image = NULL; + + layer_remove_from_parent(bitmap_layer_get_layer(battery_layer)); + bitmap_layer_destroy(battery_layer); + gbitmap_destroy(battery_image); + battery_image = NULL; + + layer_remove_from_parent(bitmap_layer_get_layer(battery_image_layer)); + bitmap_layer_destroy(battery_image_layer); + + layer_remove_from_parent(bitmap_layer_get_layer(time_format_layer)); + bitmap_layer_destroy(time_format_layer); + gbitmap_destroy(time_format_image); + time_format_image = NULL; + + layer_remove_from_parent(bitmap_layer_get_layer(day_name_layer)); + bitmap_layer_destroy(day_name_layer); + gbitmap_destroy(day_name_image); + day_name_image = NULL; + + for (int i = 0; i < TOTAL_DATE_DIGITS; i++) { + layer_remove_from_parent(bitmap_layer_get_layer(date_digits_layers[i])); + gbitmap_destroy(date_digits_images[i]); + date_digits_images[i] = NULL; + bitmap_layer_destroy(date_digits_layers[i]); + date_digits_layers[i] = NULL; + } + + for (int i = 0; i < TOTAL_TIME_DIGITS; i++) { + layer_remove_from_parent(bitmap_layer_get_layer(time_digits_layers[i])); + gbitmap_destroy(time_digits_images[i]); + time_digits_images[i] = NULL; + bitmap_layer_destroy(time_digits_layers[i]); + time_digits_layers[i] = NULL; + } + + for (int i = 0; i < TOTAL_TIME_MED_DIGITS; i++) { + layer_remove_from_parent(bitmap_layer_get_layer(time_med_digits_layers[i])); + gbitmap_destroy(time_med_digits_images[i]); + time_med_digits_images[i] = NULL; + bitmap_layer_destroy(time_med_digits_layers[i]); + time_med_digits_layers[i] = NULL; + } + + for (int i = 0; i < TOTAL_TIME_SM_DIGITS; i++) { + layer_remove_from_parent(bitmap_layer_get_layer(time_sm_digits_layers[i])); + gbitmap_destroy(time_sm_digits_images[i]); + time_sm_digits_images[i] = NULL; + bitmap_layer_destroy(time_sm_digits_layers[i]); + time_sm_digits_layers[i] = NULL; + } + + for (int i = 0; i < TOTAL_BATTERY_PERCENT_DIGITS; i++) { + layer_remove_from_parent(bitmap_layer_get_layer(battery_percent_layers[i])); + gbitmap_destroy(battery_percent_image[i]); + battery_percent_image[i] = NULL; + bitmap_layer_destroy(battery_percent_layers[i]); + battery_percent_layers[i] = NULL; + } + + layer_remove_from_parent(big_time_layer); + big_time_layer = NULL; + + layer_remove_from_parent(med_time_layer); + med_time_layer = NULL; + + appStarted = NULL; + + text_layer_destroy(weather_layer1); //************** + + layer_remove_from_parent(window_layer); + layer_destroy(window_layer); + +} + +int main(void) { + init(); + app_event_loop(); + deinit(); +} \ No newline at end of file diff --git a/src/c/ninety_one_dub.h b/src/c/ninety_one_dub.h new file mode 100644 index 0000000..f10c843 --- /dev/null +++ b/src/c/ninety_one_dub.h @@ -0,0 +1,42 @@ +#include + + +TextLayer *weather_layer1; +AppTimer *lohi_display_timer; + +void init_static_row(TextLayer *label, GFont font); //creates/configures static (weather/date) display lines +GTextAlignment row_alignment(int alignment); //accepts int alignement and sets row GTextAlignment (0 left, 1 center, 2 right) +void set_wd_layers_color(GColor textColor); + +typedef struct { + int shake_for_lohi; + int tempF; + int tempFLo; + int tempFHi; + int tempC; + int tempCLo; + int tempCHi; + int weather_alignment; + int display_weather; + int weather_use_GPS; + char weather_location[64]; + int use_celsius; + int weather_frequency; + int min_since_last_forecast; + int wd_color; + int background_color; + int weather_readability; + int condition_code; + int pebble_js_ready; + int vibrate_bt_status; + int hourly_vibe; + int display_seconds; + int hide_battery; + int blink_colon; + int invert; + bool bluetooth_state; + char conditions[32]; + time_t last_update; + char last_weather_update12hr[16]; + char last_weather_update24hr[16]; +} Options; \ No newline at end of file diff --git a/src/c/options.c b/src/c/options.c new file mode 100644 index 0000000..c69f3e7 --- /dev/null +++ b/src/c/options.c @@ -0,0 +1,63 @@ +#include +#include "options.h" +#include "constants.h" +#include "ninety_one_dub.h" + +extern Options s_options; + +void init_options() { + + if (persist_exists(KEY_OPTIONS)) { + persist_read_data(KEY_OPTIONS, &s_options, sizeof(s_options)); + } + else { + s_options.weather_use_GPS = DEFAULT_WEATHER_USE_GPS; + s_options.shake_for_lohi = DEFAULT_SHAKE_FOR_LOHI; + s_options.display_weather = DEFAULT_DISPLAY_WEATHER; + s_options.use_celsius = DEFAULT_USE_CELSIUS; + memset(s_options.weather_location, 0,sizeof(s_options.weather_location)); + s_options.weather_frequency = DEFAULT_WEATHER_FREQUENCY; + s_options.min_since_last_forecast = DEFAULT_MIN_SINCE_WEATHER_UPDATE; + s_options.background_color = DEFAULT_BACKGROUND_COLOR; + s_options.invert = DEFAULT_INVERT; + s_options.blink_colon = DEFAULT_BLINK_COLON; + s_options.display_seconds = DEFAULT_DISPLAY_SECONDS; + s_options.wd_color = DEFAULT_WD_COLOR; + s_options.weather_readability = DEFAULT_WEATHER_READABILITY; + s_options.condition_code = DEFAULT_CONDITION_CODE; + memset(s_options.conditions, 0,sizeof(s_options.conditions)); + snprintf(s_options.last_weather_update12hr, sizeof(s_options.last_weather_update12hr), DEFAULT_LAST_WEATHER_UPDATE); + snprintf(s_options.last_weather_update24hr, sizeof(s_options.last_weather_update24hr), DEFAULT_LAST_WEATHER_UPDATE); + //persist_write_data(KEY_OPTIONS, &s_options, sizeof(s_options)); + } + + #if DEBUG + APP_LOG(APP_LOG_LEVEL_DEBUG, "init_options: s_options.shake_for_lohi: %d", s_options.shake_for_lohi); + APP_LOG(APP_LOG_LEVEL_DEBUG, "init_options: s_options.tempF: %d", s_options.tempF); + APP_LOG(APP_LOG_LEVEL_DEBUG, "init_options: s_options.tempFLo: %d", s_options.tempFLo); + APP_LOG(APP_LOG_LEVEL_DEBUG, "init_options: s_options.tempFHi: %d", s_options.tempFHi); + APP_LOG(APP_LOG_LEVEL_DEBUG, "init_options: s_options.tempC: %d", s_options.tempC); + APP_LOG(APP_LOG_LEVEL_DEBUG, "init_options: s_options.tempCLo: %d", s_options.tempCLo); + APP_LOG(APP_LOG_LEVEL_DEBUG, "init_options: s_options.tempCHi: %d", s_options.tempCHi); + APP_LOG(APP_LOG_LEVEL_DEBUG, "init_options: s_options.weather_alignment: %d", s_options.weather_alignment); + APP_LOG(APP_LOG_LEVEL_DEBUG, "init_options: s_options.display_weather: %d", s_options.display_weather); + APP_LOG(APP_LOG_LEVEL_DEBUG, "init_options: s_options.use_celsius: %d", s_options.use_celsius); + APP_LOG(APP_LOG_LEVEL_DEBUG, "init_options: s_options.weather_use_GPS: %d", s_options.weather_use_GPS); + APP_LOG(APP_LOG_LEVEL_DEBUG, "init_options: s_options.weather_location: %s", s_options.weather_location); + APP_LOG(APP_LOG_LEVEL_DEBUG, "init_options: s_options.weather_frequency: %d", s_options.weather_frequency); + APP_LOG(APP_LOG_LEVEL_DEBUG, "init_options: s_options.min_since_last_forecast: %d", s_options.min_since_last_forecast); + APP_LOG(APP_LOG_LEVEL_DEBUG, "init_options: s_options.background_color: %d", s_options.background_color); + APP_LOG(APP_LOG_LEVEL_DEBUG, "init_options: s_options.wd_color: %d", s_options.wd_color); + APP_LOG(APP_LOG_LEVEL_DEBUG, "init_options: s_options.invert: %d", s_options.invert); + APP_LOG(APP_LOG_LEVEL_DEBUG, "init_options: s_options.blink_colon: %d", s_options.blink_colon); + APP_LOG(APP_LOG_LEVEL_DEBUG, "init_options: s_options.display_seconds: %d", s_options.display_seconds); + APP_LOG(APP_LOG_LEVEL_DEBUG, "init_options: s_options.weather_readability: %d", s_options.weather_readability); + APP_LOG(APP_LOG_LEVEL_DEBUG, "init_options: s_options.condition_code: %d", s_options.condition_code); + APP_LOG(APP_LOG_LEVEL_DEBUG, "init_options: s_options.bluetooth_state: %d", s_options.bluetooth_state); + APP_LOG(APP_LOG_LEVEL_DEBUG, "init_options: s_options.vibrate_bt_status: %d", s_options.vibrate_bt_status); + APP_LOG(APP_LOG_LEVEL_DEBUG, "init_options: s_options.conditions: %s", s_options.conditions); + APP_LOG(APP_LOG_LEVEL_DEBUG, "init_options: s_options.last_weather_update12hr: %s", s_options.last_weather_update12hr); + APP_LOG(APP_LOG_LEVEL_DEBUG, "init_options: s_options.last_weather_update24hr: %s", s_options.last_weather_update24hr); + APP_LOG(APP_LOG_LEVEL_DEBUG, "init_options: options sizeof(s_options) %zu", sizeof(s_options)); + #endif +} \ No newline at end of file