diff --git a/LICENSE b/LICENSE old mode 100644 new mode 100755 diff --git a/README.md b/README.md old mode 100644 new mode 100755 diff --git a/appinfo.json b/appinfo.json old mode 100644 new mode 100755 index 38d34c6..c39f5d4 --- a/appinfo.json +++ b/appinfo.json @@ -1,72 +1,165 @@ -{ - "uuid": "a4e66a45-f30d-4a13-9f0f-59343c6b23a9", - "capabilities": [ - "configurable" - ], - "appKeys": { - "time": 4, - "delta": 5, - "readtime": 2, - "bg": 1, - "icon": 0, - "alert": 3 - }, - "resources": { - "media": [ - { - "name": "IMAGE_MENU_ICON", - "type": "png", - "file": "images/logo.png", - "menuIcon": true - }, - { - "name": "IMAGE_UP", - "type": "png", - "file": "images/up.png" - }, - { - "name": "IMAGE_UPUP", - "type": "png", - "file": "images/upup.png" - }, - { - "name": "IMAGE_UP45", - "type": "png", - "file": "images/up45.png" - }, - { - "name": "IMAGE_FLAT", - "type": "png", - "file": "images/flat.png" - }, - { - "name": "IMAGE_DOWN", - "type": "png", - "file": "images/down.png" - }, - { - "name": "IMAGE_DOWNDOWN", - "type": "png", - "file": "images/downdown.png" - }, - { - "name": "IMAGE_DOWN45", - "type": "png", - "file": "images/down45.png" - }, - { - "name": "IMAGE_NONE", - "type": "png", - "file": "images/none.png" - } - ] - }, - "versionCode": 5, - "shortName": "CGM", - "longName": "CGM", - "watchapp": { - "watchface": true - }, - "companyName": "Nightscout contributors", - "versionLabel": "2.1.1" -} +{ + "uuid": "a4e66a45-f30d-4a13-9f0f-59343c6b23a9", + "capabilities": [ + "configurable" + ], + "appKeys": { + "time": 4, + "delta": 5, + "readtime": 2, + "bg": 1, + "icon": 0, + "alert": 3, + "battlevel": 6, + "t1dname": 7 + }, + "resources": { + "media": [ + { + "name": "IMAGE_MENU_ICON", + "type": "png", + "file": "images/menuicon.png", + "menuIcon": true + }, + { + "name": "IMAGE_UP", + "type": "png", + "file": "images/up.png" + }, + { + "name": "IMAGE_UPUP", + "type": "png", + "file": "images/upup.png" + }, + { + "name": "IMAGE_UP45", + "type": "png", + "file": "images/up45.png" + }, + { + "name": "IMAGE_FLAT", + "type": "png", + "file": "images/flat.png" + }, + { + "name": "IMAGE_DOWN", + "type": "png", + "file": "images/down.png" + }, + { + "name": "IMAGE_DOWNDOWN", + "type": "png", + "file": "images/downdown.png" + }, + { + "name": "IMAGE_DOWN45", + "type": "png", + "file": "images/down45.png" + }, + { + "name": "IMAGE_NONE", + "type": "png", + "file": "images/none.png" + }, + { + "name": "IMAGE_LOGO", + "type": "png", + "file": "images/logo.png" + }, + { + "name": "IMAGE_BROKEN_ANTENNA", + "type": "png", + "file": "images/brokenantenna.png" + }, + { + "name": "IMAGE_BLOOD_DROP", + "type": "png", + "file": "images/blooddrop.png" + }, + { + "name": "IMAGE_STOP_LIGHT", + "type": "png", + "file": "images/stoplight.png" + }, + { + "name": "IMAGE_HOURGLASS", + "type": "png", + "file": "images/hourglass.png" + }, + { + "name": "IMAGE_QUESTION_MARKS", + "type": "png", + "file": "images/questionmarks.png" + }, + { + "name": "IMAGE_BATTFULL", + "type": "png", + "file": "images/battfull.png" + }, + { + "name": "IMAGE_BATT90", + "type": "png", + "file": "images/batt90.png" + }, + { + "name": "IMAGE_BATT80", + "type": "png", + "file": "images/batt80.png" + }, + { + "name": "IMAGE_BATT70", + "type": "png", + "file": "images/batt70.png" + }, + { + "name": "IMAGE_BATT60", + "type": "png", + "file": "images/batt60.png" + }, + { + "name": "IMAGE_BATT50", + "type": "png", + "file": "images/batt50.png" + }, + { + "name": "IMAGE_BATT40", + "type": "png", + "file": "images/batt40.png" + }, + { + "name": "IMAGE_BATT30", + "type": "png", + "file": "images/batt30.png" + }, + { + "name": "IMAGE_BATT20", + "type": "png", + "file": "images/batt20.png" + }, + { + "name": "IMAGE_BATT10", + "type": "png", + "file": "images/batt10.png" + }, + { + "name": "IMAGE_BATTEMPTY", + "type": "png", + "file": "images/battempty.png" + }, + { + "name": "IMAGE_BATTNONE", + "type": "png", + "file": "images/battnone.png" + } + ] + }, + "versionCode": 6, + "shortName": "CGM", + "longName": "CGM", + "watchapp": { + "watchface": true + }, + "companyName": "Nightscout contributors", + "versionLabel": "3.1.1" +} + diff --git a/resources/images/batt10.png b/resources/images/batt10.png new file mode 100755 index 0000000..e786bb5 Binary files /dev/null and b/resources/images/batt10.png differ diff --git a/resources/images/batt20.png b/resources/images/batt20.png new file mode 100755 index 0000000..59d0da1 Binary files /dev/null and b/resources/images/batt20.png differ diff --git a/resources/images/batt30.png b/resources/images/batt30.png new file mode 100755 index 0000000..dbf60bd Binary files /dev/null and b/resources/images/batt30.png differ diff --git a/resources/images/batt40.png b/resources/images/batt40.png new file mode 100755 index 0000000..a785fc5 Binary files /dev/null and b/resources/images/batt40.png differ diff --git a/resources/images/batt50.png b/resources/images/batt50.png new file mode 100755 index 0000000..266d9f6 Binary files /dev/null and b/resources/images/batt50.png differ diff --git a/resources/images/batt60.png b/resources/images/batt60.png new file mode 100755 index 0000000..e9b8847 Binary files /dev/null and b/resources/images/batt60.png differ diff --git a/resources/images/batt70.png b/resources/images/batt70.png new file mode 100755 index 0000000..0f7e5cf Binary files /dev/null and b/resources/images/batt70.png differ diff --git a/resources/images/batt80.png b/resources/images/batt80.png new file mode 100755 index 0000000..7e385be Binary files /dev/null and b/resources/images/batt80.png differ diff --git a/resources/images/batt90.png b/resources/images/batt90.png new file mode 100755 index 0000000..02a5e2e Binary files /dev/null and b/resources/images/batt90.png differ diff --git a/resources/images/battempty.png b/resources/images/battempty.png new file mode 100755 index 0000000..568d422 Binary files /dev/null and b/resources/images/battempty.png differ diff --git a/resources/images/battfull.png b/resources/images/battfull.png new file mode 100755 index 0000000..c172fae Binary files /dev/null and b/resources/images/battfull.png differ diff --git a/resources/images/battnone.png b/resources/images/battnone.png new file mode 100755 index 0000000..a0abd1f Binary files /dev/null and b/resources/images/battnone.png differ diff --git a/resources/images/blooddrop.png b/resources/images/blooddrop.png new file mode 100755 index 0000000..258ecb4 Binary files /dev/null and b/resources/images/blooddrop.png differ diff --git a/resources/images/brokenantenna.png b/resources/images/brokenantenna.png new file mode 100755 index 0000000..90ed898 Binary files /dev/null and b/resources/images/brokenantenna.png differ diff --git a/resources/images/down.png b/resources/images/down.png index 77db0a8..d42da57 100644 Binary files a/resources/images/down.png and b/resources/images/down.png differ diff --git a/resources/images/down45.png b/resources/images/down45.png old mode 100644 new mode 100755 index 8bd49c1..00d3a90 Binary files a/resources/images/down45.png and b/resources/images/down45.png differ diff --git a/resources/images/downdown.png b/resources/images/downdown.png old mode 100644 new mode 100755 index 3d05b33..ff69adf Binary files a/resources/images/downdown.png and b/resources/images/downdown.png differ diff --git a/resources/images/flat.png b/resources/images/flat.png old mode 100644 new mode 100755 index 7daecc0..0cf6ac8 Binary files a/resources/images/flat.png and b/resources/images/flat.png differ diff --git a/resources/images/hourglass.png b/resources/images/hourglass.png new file mode 100755 index 0000000..422fd64 Binary files /dev/null and b/resources/images/hourglass.png differ diff --git a/resources/images/logo.png b/resources/images/logo.png old mode 100644 new mode 100755 index 2e2d18c..17f96a4 Binary files a/resources/images/logo.png and b/resources/images/logo.png differ diff --git a/resources/images/menuicon.png b/resources/images/menuicon.png new file mode 100755 index 0000000..17f96a4 Binary files /dev/null and b/resources/images/menuicon.png differ diff --git a/resources/images/none.png b/resources/images/none.png old mode 100644 new mode 100755 index bda52a3..a813333 Binary files a/resources/images/none.png and b/resources/images/none.png differ diff --git a/resources/images/questionmarks.png b/resources/images/questionmarks.png new file mode 100755 index 0000000..32574a4 Binary files /dev/null and b/resources/images/questionmarks.png differ diff --git a/resources/images/stoplight.png b/resources/images/stoplight.png new file mode 100755 index 0000000..496c2fd Binary files /dev/null and b/resources/images/stoplight.png differ diff --git a/resources/images/up.png b/resources/images/up.png index d522dbc..4f7134b 100644 Binary files a/resources/images/up.png and b/resources/images/up.png differ diff --git a/resources/images/up45.png b/resources/images/up45.png old mode 100644 new mode 100755 index bab14c6..455b453 Binary files a/resources/images/up45.png and b/resources/images/up45.png differ diff --git a/resources/images/upup.png b/resources/images/upup.png old mode 100644 new mode 100755 index 19e4e8d..c82440d Binary files a/resources/images/upup.png and b/resources/images/upup.png differ diff --git a/resources/img/batt10.png b/resources/img/batt10.png new file mode 100755 index 0000000..e786bb5 Binary files /dev/null and b/resources/img/batt10.png differ diff --git a/resources/img/batt20.png b/resources/img/batt20.png new file mode 100755 index 0000000..59d0da1 Binary files /dev/null and b/resources/img/batt20.png differ diff --git a/resources/img/batt30.png b/resources/img/batt30.png new file mode 100755 index 0000000..dbf60bd Binary files /dev/null and b/resources/img/batt30.png differ diff --git a/resources/img/batt40.png b/resources/img/batt40.png new file mode 100755 index 0000000..a785fc5 Binary files /dev/null and b/resources/img/batt40.png differ diff --git a/resources/img/batt50.png b/resources/img/batt50.png new file mode 100755 index 0000000..266d9f6 Binary files /dev/null and b/resources/img/batt50.png differ diff --git a/resources/img/batt60.png b/resources/img/batt60.png new file mode 100755 index 0000000..e9b8847 Binary files /dev/null and b/resources/img/batt60.png differ diff --git a/resources/img/batt70.png b/resources/img/batt70.png new file mode 100755 index 0000000..0f7e5cf Binary files /dev/null and b/resources/img/batt70.png differ diff --git a/resources/img/batt80.png b/resources/img/batt80.png new file mode 100755 index 0000000..7e385be Binary files /dev/null and b/resources/img/batt80.png differ diff --git a/resources/img/batt90.png b/resources/img/batt90.png new file mode 100755 index 0000000..02a5e2e Binary files /dev/null and b/resources/img/batt90.png differ diff --git a/resources/img/battempty.png b/resources/img/battempty.png new file mode 100755 index 0000000..568d422 Binary files /dev/null and b/resources/img/battempty.png differ diff --git a/resources/img/battfull.png b/resources/img/battfull.png new file mode 100755 index 0000000..c172fae Binary files /dev/null and b/resources/img/battfull.png differ diff --git a/resources/img/battnone.png b/resources/img/battnone.png new file mode 100755 index 0000000..a0abd1f Binary files /dev/null and b/resources/img/battnone.png differ diff --git a/resources/img/blooddrop.png b/resources/img/blooddrop.png new file mode 100755 index 0000000..258ecb4 Binary files /dev/null and b/resources/img/blooddrop.png differ diff --git a/resources/img/brokenantenna.png b/resources/img/brokenantenna.png new file mode 100755 index 0000000..90ed898 Binary files /dev/null and b/resources/img/brokenantenna.png differ diff --git a/resources/img/down.png b/resources/img/down.png index 77db0a8..d42da57 100644 Binary files a/resources/img/down.png and b/resources/img/down.png differ diff --git a/resources/img/down45.png b/resources/img/down45.png old mode 100644 new mode 100755 index 8bd49c1..00d3a90 Binary files a/resources/img/down45.png and b/resources/img/down45.png differ diff --git a/resources/img/downdown.png b/resources/img/downdown.png old mode 100644 new mode 100755 index 3d05b33..ff69adf Binary files a/resources/img/downdown.png and b/resources/img/downdown.png differ diff --git a/resources/img/flat.png b/resources/img/flat.png old mode 100644 new mode 100755 index 7daecc0..0cf6ac8 Binary files a/resources/img/flat.png and b/resources/img/flat.png differ diff --git a/resources/img/hourglass.png b/resources/img/hourglass.png new file mode 100755 index 0000000..422fd64 Binary files /dev/null and b/resources/img/hourglass.png differ diff --git a/resources/img/logo.png b/resources/img/logo.png old mode 100644 new mode 100755 index 2e2d18c..17f96a4 Binary files a/resources/img/logo.png and b/resources/img/logo.png differ diff --git a/resources/img/menuicon.png b/resources/img/menuicon.png new file mode 100755 index 0000000..17f96a4 Binary files /dev/null and b/resources/img/menuicon.png differ diff --git a/resources/img/none.png b/resources/img/none.png old mode 100644 new mode 100755 index bda52a3..a813333 Binary files a/resources/img/none.png and b/resources/img/none.png differ diff --git a/resources/img/questionmarks.png b/resources/img/questionmarks.png new file mode 100755 index 0000000..32574a4 Binary files /dev/null and b/resources/img/questionmarks.png differ diff --git a/resources/img/stoplight.png b/resources/img/stoplight.png new file mode 100755 index 0000000..496c2fd Binary files /dev/null and b/resources/img/stoplight.png differ diff --git a/resources/img/up.png b/resources/img/up.png index d522dbc..4f7134b 100644 Binary files a/resources/img/up.png and b/resources/img/up.png differ diff --git a/resources/img/up45.png b/resources/img/up45.png old mode 100644 new mode 100755 index bab14c6..455b453 Binary files a/resources/img/up45.png and b/resources/img/up45.png differ diff --git a/resources/img/upup.png b/resources/img/upup.png old mode 100644 new mode 100755 index 19e4e8d..c82440d Binary files a/resources/img/upup.png and b/resources/img/upup.png differ diff --git a/src/cgm.c b/src/cgm.c old mode 100644 new mode 100755 index 9436a7f..e175d34 --- a/src/cgm.c +++ b/src/cgm.c @@ -1,303 +1,538 @@ -#include "pebble.h" - -static Window *window; - -static TextLayer *bg_layer; -static TextLayer *readtime_layer; -static TextLayer *datetime_layer; -static BitmapLayer *icon_layer; -static TextLayer *message_layer; -static TextLayer *date_layer; -static char date_text[] = "Wed 13 "; -static GBitmap *icon_bitmap = NULL; - -static void draw_date() { - - time_t now = time(NULL); - struct tm *t = localtime(&now); - - strftime(date_text, sizeof(date_text), "%a %d", t); - - text_layer_set_text(date_layer, date_text); -} - -static AppSync sync; - -static uint8_t sync_buffer[256]; -static char new_time[124]; -static char last_bg[124]; - -static AppTimer *timer; - -static const uint32_t const high[] = { 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100}; -static const uint32_t const low[] = { 1000,100,2000}; - -static const uint32_t const hypo[] = { 3200,200,3200 }; -static const uint32_t const hyper[] = { 50,150,50,150,50,150,50,150,50,150,50,150,50,150,50,150,50,150,50,150,50,150,50,150,50,150,50,150 }; - -static const uint32_t const trend_high[] = { 200,200,1000,200,200,200,1000 }; -static const uint32_t const trend_low[] = { 2000,200,1000 }; - -static const uint32_t const alert[] = { 500,200,1000 }; - - -enum CgmKey { - CGM_ICON_KEY = 0x0, // TUPLE_INT - CGM_BG_KEY = 0x1, // TUPLE_CSTRING - CGM_READTIME_KEY = 0x2, // TUPLE_CSTRING - CGM_ALERT_KEY = 0x3, // TUPLE_INT - CGM_TIME_NOW = 0x4, // TUPLE_CSTRING - CGM_DELTA_KEY = 0x5 -}; - -static const uint32_t CGM_ICONS[] = { - RESOURCE_ID_IMAGE_NONE, //0 - RESOURCE_ID_IMAGE_UPUP, //1 - RESOURCE_ID_IMAGE_UP, //2 - RESOURCE_ID_IMAGE_UP45, //3 - RESOURCE_ID_IMAGE_FLAT, //4 - RESOURCE_ID_IMAGE_DOWN45, //5 - RESOURCE_ID_IMAGE_DOWN, //6 - RESOURCE_ID_IMAGE_DOWNDOWN //7 -}; - -static void sync_error_callback(DictionaryResult dict_error, AppMessageResult app_message_error, void *context) { - text_layer_set_text(datetime_layer, "--:--"); - text_layer_set_text(message_layer, "Data: OFFLINE"); - - // VibePattern pat = { - // .durations = alert, - // .num_segments = ARRAY_LENGTH(alert), - // }; - vibes_double_pulse(); - //vibes_enqueue_custom_pattern(pat); -} - -static void alert_handler(uint8_t alertValue) -{ - - - - APP_LOG(APP_LOG_LEVEL_DEBUG, "Alert code: %d", alertValue); - - switch (alertValue){ - //No alert - case 0: - break; - - //Normal (new data, in range, trend okay) - case 1: - vibes_double_pulse(); - break; - - //Low - case 2:; - VibePattern lowpat = { - .durations = low, - .num_segments = ARRAY_LENGTH(low), - }; - vibes_enqueue_custom_pattern(lowpat); - //vibes_double_pulse(lowpat); - break; - - //High - case 3:; - VibePattern highpat = { - .durations = high, - .num_segments = ARRAY_LENGTH(high), - }; - vibes_enqueue_custom_pattern(highpat); - break; - - //Hypo - - //Hyper - - //Trend Low - - //Trend High - - //Data Alert - - } - -} - -static void sync_tuple_changed_callback(const uint32_t key, const Tuple* new_tuple, const Tuple* old_tuple, void* context) { - - APP_LOG(APP_LOG_LEVEL_INFO, "sync tuple"); - switch (key) { - - case CGM_ICON_KEY: - APP_LOG(APP_LOG_LEVEL_INFO, "ICON"); - if (icon_bitmap) { - gbitmap_destroy(icon_bitmap); - } - icon_bitmap = gbitmap_create_with_resource(CGM_ICONS[new_tuple->value->uint8]); - bitmap_layer_set_bitmap(icon_layer, icon_bitmap); - break; - - case CGM_BG_KEY: - - APP_LOG(APP_LOG_LEVEL_INFO, "BG"); - text_layer_set_text(bg_layer, new_tuple->value->cstring); - strncpy(last_bg, new_tuple->value->cstring, 124); - break; - - case CGM_READTIME_KEY: - APP_LOG(APP_LOG_LEVEL_INFO, "readtime"); - strncpy(new_time, new_tuple->value->cstring, 124); - text_layer_set_text(readtime_layer, new_tuple->value->cstring); - break; - - case CGM_TIME_NOW: - - APP_LOG(APP_LOG_LEVEL_INFO, "cgm time"); - draw_date(); - text_layer_set_text(datetime_layer, new_tuple->value->cstring); - break; - - case CGM_ALERT_KEY: - APP_LOG(APP_LOG_LEVEL_INFO, "alert"); - alert_handler(new_tuple->value->uint8); - break; - - case CGM_DELTA_KEY: - APP_LOG(APP_LOG_LEVEL_INFO, "delta"); - text_layer_set_text(message_layer, new_tuple->value->cstring); - break; - } - -} - -static void send_cmd(void) { - - DictionaryIterator *iter; - app_message_outbox_begin(&iter); - - if (iter == NULL) { - return; - } - static char *bgptr = last_bg; - static char *timeptr = new_time; - - Tuplet alertval = TupletInteger(3, 0); - Tuplet bgVal = TupletCString(1, bgptr); - Tuplet lastTimeVal = TupletCString(2, timeptr); - - dict_write_tuplet(iter, &alertval); - dict_write_tuplet(iter, &bgVal); - dict_write_tuplet(iter, &lastTimeVal); - - dict_write_end(iter); - - app_message_outbox_send(); - -} - -static void timer_callback(void *data) { - - send_cmd(); - timer = app_timer_register(60000, timer_callback, NULL); - -} - -static void window_load(Window *window) { - Layer *window_layer = window_get_root_layer(window); - - - icon_layer = bitmap_layer_create(GRect(82, 0, 61, 61)); - - layer_add_child(window_layer, bitmap_layer_get_layer(icon_layer)); - - bg_layer = text_layer_create(GRect(0, 5, 83, 47)); - text_layer_set_text_color(bg_layer, GColorWhite); - text_layer_set_background_color(bg_layer, GColorClear); - text_layer_set_font(bg_layer, fonts_get_system_font(FONT_KEY_BITHAM_42_BOLD)); - text_layer_set_text_alignment(bg_layer, GTextAlignmentCenter); - layer_add_child(window_layer, text_layer_get_layer(bg_layer)); - - readtime_layer = text_layer_create(GRect(0, 48, 144, 22)); - text_layer_set_text_color(readtime_layer, GColorWhite); - text_layer_set_background_color(readtime_layer, GColorClear); - text_layer_set_font(readtime_layer, fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD)); - text_layer_set_text_alignment(readtime_layer, GTextAlignmentCenter); - layer_add_child(window_layer, text_layer_get_layer(readtime_layer)); - - message_layer = text_layer_create(GRect(0, 69, 144, 22)); - text_layer_set_text_color(message_layer, GColorWhite); - text_layer_set_background_color(message_layer, GColorBlack); - text_layer_set_font(message_layer, fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD)); - text_layer_set_text_alignment(message_layer, GTextAlignmentCenter); - layer_add_child(window_layer, text_layer_get_layer(message_layer)); - - datetime_layer = text_layer_create(GRect(0, 93, 144, 38)); - text_layer_set_text_color(datetime_layer, GColorBlack); - text_layer_set_background_color(datetime_layer, GColorWhite); - text_layer_set_font(datetime_layer, fonts_get_system_font(FONT_KEY_GOTHIC_28)); - text_layer_set_text_alignment(datetime_layer, GTextAlignmentCenter); - layer_add_child(window_layer, text_layer_get_layer(datetime_layer)); - //Date - - date_layer = text_layer_create(GRect(0, 127, 144, 42)); - text_layer_set_text_color(date_layer, GColorBlack); - text_layer_set_background_color(date_layer, GColorWhite); - text_layer_set_font(date_layer, fonts_get_system_font(FONT_KEY_GOTHIC_28)); - text_layer_set_text_alignment(date_layer, GTextAlignmentCenter); - layer_add_child(window_layer, text_layer_get_layer(date_layer)); - - draw_date(); - - Tuplet initial_values[] = { - TupletInteger(CGM_ICON_KEY, (uint8_t)0), - TupletCString(CGM_BG_KEY, ""), - TupletCString(CGM_READTIME_KEY, ""), - TupletInteger(CGM_ALERT_KEY, 0), - TupletCString(CGM_TIME_NOW, "loading..."), - TupletCString(CGM_DELTA_KEY, " ") - }; - - app_sync_init(&sync, sync_buffer, sizeof(sync_buffer), initial_values, ARRAY_LENGTH(initial_values), sync_tuple_changed_callback, sync_error_callback, NULL); - - timer = app_timer_register(1000, timer_callback, NULL); -} - -static void window_unload(Window *window) { - app_sync_deinit(&sync); - - if (icon_bitmap) { - gbitmap_destroy(icon_bitmap); - } - text_layer_destroy(datetime_layer); - text_layer_destroy(readtime_layer); - text_layer_destroy(bg_layer); - text_layer_destroy(message_layer); - bitmap_layer_destroy(icon_layer); - // bitmap_layer_destroy(date_layer); -} - -static void init(void) { - window = window_create(); - window_set_background_color(window, GColorBlack); - window_set_fullscreen(window, true); - window_set_window_handlers(window, (WindowHandlers) { - .load = window_load, - .unload = window_unload - }); - - app_message_open(app_message_inbox_size_maximum(), app_message_outbox_size_maximum()); - - const bool animated = true; - window_stack_push(window, animated); -} - -static void deinit(void) { - window_destroy(window); -} - -int main(void) { - init(); - - app_event_loop(); - deinit(); -} +#include "pebble.h" + +static Window *window; + +static TextLayer *bg_layer; +static TextLayer *readtime_layer; +static TextLayer *datetime_layer; +static BitmapLayer *icon_layer; +static TextLayer *message_layer; // BG DELTA & MESSAGE LAYER +static TextLayer *time_layer; +static BitmapLayer *batticon_layer; +static TextLayer *battlevel_layer; +static TextLayer *t1dname_layer; +static TextLayer *date_layer; +static char time_text[] = "00:00"; +static char date_text[] = "Wed 13 "; +static GBitmap *icon_bitmap = NULL; +static GBitmap *specialvalue_bitmap = NULL; +static GBitmap *batticon_bitmap = NULL; + +static void draw_date() { + + time_t now = time(NULL); + struct tm *t = localtime(&now); + + strftime(time_text, sizeof(time_text), "%l:%M", t); + strftime(date_text, sizeof(date_text), "%a %d", t); + + text_layer_set_text(time_layer, time_text); + text_layer_set_text(date_layer, date_text); +} + +static AppSync sync; +static AppTimer *timer; + +static uint8_t sync_buffer[256]; +static char new_time[124]; +static char last_bg[124]; +static uint8_t icon_array[] = {0,10}; +static uint8_t current_bg = 0; + +static const uint32_t const high[] = { 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100}; +static const uint32_t const low[] = { 1000,100,2000}; + +static const uint32_t const hypo[] = { 3200,200,3200 }; +static const uint32_t const hyper[] = { 50,150,50,150,50,150,50,150,50,150,50,150,50,150,50,150,50,150,50,150,50,150,50,150,50,150,50,150 }; + +static const uint32_t const trend_high[] = { 200,200,1000,200,200,200,1000 }; +static const uint32_t const trend_low[] = { 2000,200,1000 }; + +static const uint32_t const alert[] = { 500,200,1000 }; + + +enum CgmKey { + CGM_ICON_KEY = 0x0, // TUPLE_BYTE_ARRAY + CGM_BG_KEY = 0x1, // TUPLE_CSTRING + CGM_READTIME_KEY = 0x2, // TUPLE_CSTRING + CGM_ALERT_KEY = 0x3, // TUPLE_INT + CGM_TIME_NOW = 0x4, // TUPLE_CSTRING + CGM_DELTA_KEY = 0x5, // TUPLE_CSTRING + CGM_BATTLEVEL_KEY = 0x6, // TUPLE_CSTRING + CGM_T1DNAME_KEY = 0x7 // TUPLE_CSTRING +}; + +static const uint8_t NO_ANTENNA_VALUE = 3; +static const uint8_t SENSOR_NOT_CALIBRATED_VALUE = 5; +static const uint8_t STOP_LIGHT_VALUE = 6; +static const uint8_t HOURGLASS_VALUE = 9; +static const uint8_t QUESTION_MARKS_VALUE = 10; +static const uint8_t BAD_RF_VALUE = 12; + +static const uint32_t SPECIAL_VALUE_ICONS[] = { + RESOURCE_ID_IMAGE_BROKEN_ANTENNA, //0 + RESOURCE_ID_IMAGE_BLOOD_DROP, //1 + RESOURCE_ID_IMAGE_STOP_LIGHT, //2 + RESOURCE_ID_IMAGE_HOURGLASS, //3 + RESOURCE_ID_IMAGE_QUESTION_MARKS //4 +}; + +static const uint32_t CGM_ICONS[] = { // COMMUNITY Build Order + RESOURCE_ID_IMAGE_NONE, //0 + RESOURCE_ID_IMAGE_UPUP, //1 + RESOURCE_ID_IMAGE_UP, //2 + RESOURCE_ID_IMAGE_UP45, //3 + RESOURCE_ID_IMAGE_FLAT, //4 + RESOURCE_ID_IMAGE_DOWN45, //5 + RESOURCE_ID_IMAGE_DOWN, //6 + RESOURCE_ID_IMAGE_DOWNDOWN, //7 + RESOURCE_ID_IMAGE_NONE, //8 + RESOURCE_ID_IMAGE_NONE, //9 + RESOURCE_ID_IMAGE_LOGO //10 +}; + +static const uint32_t CGM_ICONS_RAJAT[] = { // RAJAT Build Order + RESOURCE_ID_IMAGE_UPUP, //0 + RESOURCE_ID_IMAGE_UP, //1 + RESOURCE_ID_IMAGE_UP45, //2 + RESOURCE_ID_IMAGE_FLAT, //3 + RESOURCE_ID_IMAGE_NONE, //4 + RESOURCE_ID_IMAGE_DOWN45, //5 + RESOURCE_ID_IMAGE_DOWN, //6 + RESOURCE_ID_IMAGE_DOWNDOWN, //7 + RESOURCE_ID_IMAGE_NONE, //8 + RESOURCE_ID_IMAGE_NONE, //9 + RESOURCE_ID_IMAGE_LOGO //10 +}; + +static const uint32_t BATTLEVEL_ICONS[] = { + RESOURCE_ID_IMAGE_BATTEMPTY, //0 + RESOURCE_ID_IMAGE_BATT10, //1 + RESOURCE_ID_IMAGE_BATT20, //2 + RESOURCE_ID_IMAGE_BATT30, //3 + RESOURCE_ID_IMAGE_BATT40, //4 + RESOURCE_ID_IMAGE_BATT50, //5 + RESOURCE_ID_IMAGE_BATT60, //6 + RESOURCE_ID_IMAGE_BATT70, //7 + RESOURCE_ID_IMAGE_BATT80, //8 + RESOURCE_ID_IMAGE_BATT90, //9 + RESOURCE_ID_IMAGE_BATTFULL, //10 + RESOURCE_ID_IMAGE_BATTNONE //11 +}; + +static void sync_error_callback(DictionaryResult dict_error, AppMessageResult app_message_error, void *context) { + text_layer_set_text(message_layer, "WATCH OFFLINE"); + + // VibePattern pat = { + // .durations = alert, + // .num_segments = ARRAY_LENGTH(alert), + // }; + vibes_double_pulse(); + //vibes_enqueue_custom_pattern(pat); +} + +static void alert_handler(uint8_t alertValue) +{ + APP_LOG(APP_LOG_LEVEL_DEBUG, "Alert code: %d", alertValue); + + switch (alertValue){ + //No alert + case 0: + break; + + //Normal (new data, in range, trend okay) + case 1: + vibes_double_pulse(); + break; + + //Low + case 2:; + VibePattern lowpat = { + .durations = low, + .num_segments = ARRAY_LENGTH(low), + }; + vibes_enqueue_custom_pattern(lowpat); + //vibes_double_pulse(lowpat); + break; + + //High + case 3:; + VibePattern highpat = { + .durations = high, + .num_segments = ARRAY_LENGTH(high), + }; + vibes_enqueue_custom_pattern(highpat); + break; + + //Hypo + + //Hyper + + //Trend Low + + //Trend High + + //Data Alert + + } + +} + +static void sync_tuple_changed_callback(const uint32_t key, const Tuple* new_tuple, const Tuple* old_tuple, void* context) { + + APP_LOG(APP_LOG_LEVEL_INFO, "sync tuple"); + switch (key) { + + case CGM_ICON_KEY: + APP_LOG(APP_LOG_LEVEL_INFO, "ICON ARROW"); + // if SpecialValue already set, then break + if (specialvalue_bitmap) { + break; + } + + // no SpecialValue, so set regular icon + if (icon_bitmap) { + gbitmap_destroy(icon_bitmap); + } + + // set the right arrow into the bitmap + if (new_tuple->value->data[0]) { // will be true if this is a Rajat build + icon_bitmap = gbitmap_create_with_resource(CGM_ICONS_RAJAT[new_tuple->value->data[1]]); + } + else { + icon_bitmap = gbitmap_create_with_resource(CGM_ICONS[new_tuple->value->data[1]]); + } + + bitmap_layer_set_bitmap(icon_layer, icon_bitmap); + break; + + case CGM_BG_KEY: + APP_LOG(APP_LOG_LEVEL_INFO, "BG CURRENT"); + if (specialvalue_bitmap) { + gbitmap_destroy(specialvalue_bitmap); + } + + // get current BG + strncpy(last_bg, new_tuple->value->cstring, 124); + current_bg = atoi(last_bg); + + // check for special value, if special value, then replace icon and blank BG; else send current BG + if ((current_bg == NO_ANTENNA_VALUE) || (current_bg == BAD_RF_VALUE)) { + text_layer_set_text(bg_layer, ""); + specialvalue_bitmap = gbitmap_create_with_resource(SPECIAL_VALUE_ICONS[0]); + bitmap_layer_set_bitmap(icon_layer, specialvalue_bitmap); + } + else if (current_bg == SENSOR_NOT_CALIBRATED_VALUE) { + text_layer_set_text(bg_layer, ""); + specialvalue_bitmap = gbitmap_create_with_resource(SPECIAL_VALUE_ICONS[1]); + bitmap_layer_set_bitmap(icon_layer, specialvalue_bitmap); + } + else if (current_bg == STOP_LIGHT_VALUE) { + text_layer_set_text(bg_layer, ""); + specialvalue_bitmap = gbitmap_create_with_resource(SPECIAL_VALUE_ICONS[2]); + bitmap_layer_set_bitmap(icon_layer, specialvalue_bitmap); + } + else if (current_bg == HOURGLASS_VALUE) { + text_layer_set_text(bg_layer, ""); + specialvalue_bitmap = gbitmap_create_with_resource(SPECIAL_VALUE_ICONS[3]); + bitmap_layer_set_bitmap(icon_layer, specialvalue_bitmap); + } + else if (current_bg == QUESTION_MARKS_VALUE) { + text_layer_set_text(bg_layer, ""); + specialvalue_bitmap = gbitmap_create_with_resource(SPECIAL_VALUE_ICONS[4]); + bitmap_layer_set_bitmap(icon_layer, specialvalue_bitmap); + } + else { + text_layer_set_text(bg_layer, new_tuple->value->cstring); + } + + break; // break for CGM_BG_KEY + + case CGM_READTIME_KEY: + APP_LOG(APP_LOG_LEVEL_INFO, "READ TIME AGO"); + strncpy(new_time, new_tuple->value->cstring, 124); + text_layer_set_text(readtime_layer, new_tuple->value->cstring); + break; + + case CGM_ALERT_KEY: + APP_LOG(APP_LOG_LEVEL_INFO, "ALERT VIBRATION"); + alert_handler(new_tuple->value->uint8); + break; + + case CGM_TIME_NOW: + APP_LOG(APP_LOG_LEVEL_INFO, "CGM TIME NOW"); + draw_date(); + text_layer_set_text(datetime_layer, new_tuple->value->cstring); + break; + + case CGM_DELTA_KEY: + APP_LOG(APP_LOG_LEVEL_INFO, "DELTA IN BG"); + text_layer_set_text(message_layer, new_tuple->value->cstring); + break; + + case CGM_BATTLEVEL_KEY: + APP_LOG(APP_LOG_LEVEL_INFO, "BATTERY LEVEL"); + + static uint8_t current_battlevel = 0; + static char last_battlevel[4]; + static char battlevel_percent[6]; + + if (batticon_bitmap) { + gbitmap_destroy(batticon_bitmap); + } + + // get current battery level + strncpy(last_battlevel, new_tuple->value->cstring, 4); + current_battlevel = atoi(last_battlevel); + + // check for init code or Rajat build (will be 111 if Rajat build) + if ( (strcmp(last_battlevel, "") == 0) || (current_battlevel == 111) ) { + // Init code or Rajat build, can't do battery; set text layer & icon to empty value + text_layer_set_text(battlevel_layer, ""); + batticon_bitmap = gbitmap_create_with_resource(BATTLEVEL_ICONS[11]); + bitmap_layer_set_bitmap(batticon_layer, batticon_bitmap); + break; + } + else { + // get current battery level and set battery level text with percent + snprintf(battlevel_percent, 6, "%s%%", last_battlevel); + text_layer_set_text(battlevel_layer, battlevel_percent); + + // check battery level, set battery level icon + if ( (current_battlevel >= 90) && (current_battlevel <= 100) ) { + batticon_bitmap = gbitmap_create_with_resource(BATTLEVEL_ICONS[10]); + bitmap_layer_set_bitmap(batticon_layer, batticon_bitmap); + } + else if (current_battlevel >= 80) { + batticon_bitmap = gbitmap_create_with_resource(BATTLEVEL_ICONS[9]); + bitmap_layer_set_bitmap(batticon_layer, batticon_bitmap); + } + else if (current_battlevel >= 70) { + batticon_bitmap = gbitmap_create_with_resource(BATTLEVEL_ICONS[8]); + bitmap_layer_set_bitmap(batticon_layer, batticon_bitmap); + } + else if (current_battlevel >= 60) { + batticon_bitmap = gbitmap_create_with_resource(BATTLEVEL_ICONS[7]); + bitmap_layer_set_bitmap(batticon_layer, batticon_bitmap); + } + else if (current_battlevel >= 50) { + batticon_bitmap = gbitmap_create_with_resource(BATTLEVEL_ICONS[6]); + bitmap_layer_set_bitmap(batticon_layer, batticon_bitmap); + } + else if (current_battlevel >= 40) { + batticon_bitmap = gbitmap_create_with_resource(BATTLEVEL_ICONS[5]); + bitmap_layer_set_bitmap(batticon_layer, batticon_bitmap); + } + else if (current_battlevel >= 30) { + batticon_bitmap = gbitmap_create_with_resource(BATTLEVEL_ICONS[4]); + bitmap_layer_set_bitmap(batticon_layer, batticon_bitmap); + } + else if (current_battlevel >= 20) { + batticon_bitmap = gbitmap_create_with_resource(BATTLEVEL_ICONS[3]); + bitmap_layer_set_bitmap(batticon_layer, batticon_bitmap); + } + else if (current_battlevel >= 10) { + batticon_bitmap = gbitmap_create_with_resource(BATTLEVEL_ICONS[2]); + bitmap_layer_set_bitmap(batticon_layer, batticon_bitmap); + } + else if (current_battlevel >= 5) { + batticon_bitmap = gbitmap_create_with_resource(BATTLEVEL_ICONS[1]); + bitmap_layer_set_bitmap(batticon_layer, batticon_bitmap); + } + else { + batticon_bitmap = gbitmap_create_with_resource(BATTLEVEL_ICONS[0]); + bitmap_layer_set_bitmap(batticon_layer, batticon_bitmap); + } + } // end else Init code or Rajat build + + break; // break for CGM_BATTLEVEL_KEY + + case CGM_T1DNAME_KEY: + APP_LOG(APP_LOG_LEVEL_INFO, "T1D NAME"); + text_layer_set_text(t1dname_layer, new_tuple->value->cstring); + break; + + } // end switch(key) + +} // end sync_tuple_changed_callback() + +static void send_cmd(void) { + + DictionaryIterator *iter; + app_message_outbox_begin(&iter); + + if (iter == NULL) { + return; + } + static char *bgptr = last_bg; + static char *timeptr = new_time; + + Tuplet alertval = TupletInteger(3, 0); + Tuplet bgVal = TupletCString(1, bgptr); + Tuplet lastTimeVal = TupletCString(2, timeptr); + + dict_write_tuplet(iter, &alertval); + dict_write_tuplet(iter, &bgVal); + dict_write_tuplet(iter, &lastTimeVal); + + dict_write_end(iter); + + app_message_outbox_send(); + +} + +static void timer_callback(void *data) { + + send_cmd(); + timer = app_timer_register(60000, timer_callback, NULL); + +} + +static void window_load(Window *window) { + Layer *window_layer = window_get_root_layer(window); + + // DELTA BG + message_layer = text_layer_create(GRect(0, 33, 144, 55)); + text_layer_set_text_color(message_layer, GColorBlack); + text_layer_set_background_color(message_layer, GColorWhite); + text_layer_set_font(message_layer, fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD)); + text_layer_set_text_alignment(message_layer, GTextAlignmentCenter); + layer_add_child(window_layer, text_layer_get_layer(message_layer)); + + //ARROW OR SPECIAL VALUE + icon_layer = bitmap_layer_create(GRect(85, -7, 78, 50)); + bitmap_layer_set_alignment(icon_layer, GAlignTopLeft); + bitmap_layer_set_background_color(icon_layer, GColorClear); + layer_add_child(window_layer, bitmap_layer_get_layer(icon_layer)); + + // CURRENT APP TIME + datetime_layer = text_layer_create(GRect(82, 58, 57, 24)); + text_layer_set_text_color(datetime_layer, GColorBlack); + text_layer_set_background_color(datetime_layer, GColorClear); + text_layer_set_font(datetime_layer, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); + text_layer_set_text_alignment(datetime_layer, GTextAlignmentRight); + layer_add_child(window_layer, text_layer_get_layer(datetime_layer)); + + //BG + bg_layer = text_layer_create(GRect(0, -5, 95, 47)); + text_layer_set_text_color(bg_layer, GColorBlack); + text_layer_set_background_color(bg_layer, GColorWhite); + text_layer_set_font(bg_layer, fonts_get_system_font(FONT_KEY_BITHAM_42_BOLD)); + text_layer_set_text_alignment(bg_layer, GTextAlignmentCenter); + layer_add_child(window_layer, text_layer_get_layer(bg_layer)); + + //READ TIME AGO + readtime_layer = text_layer_create(GRect(5, 58, 90, 28)); + text_layer_set_text_color(readtime_layer, GColorBlack); + text_layer_set_background_color(readtime_layer, GColorClear); + text_layer_set_font(readtime_layer, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); + text_layer_set_text_alignment(readtime_layer, GTextAlignmentLeft); + layer_add_child(window_layer, text_layer_get_layer(readtime_layer)); + + // T1D NAME + t1dname_layer = text_layer_create(GRect(5, 138, 69, 28)); + text_layer_set_text_color(t1dname_layer, GColorWhite); + text_layer_set_background_color(t1dname_layer, GColorClear); + text_layer_set_font(t1dname_layer, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); + text_layer_set_text_alignment(t1dname_layer, GTextAlignmentLeft); + layer_add_child(window_layer, text_layer_get_layer(t1dname_layer)); + + // BATTERY LEVEL ICON + batticon_layer = bitmap_layer_create(GRect(80, 147, 28, 20)); + bitmap_layer_set_alignment(batticon_layer, GAlignLeft); + bitmap_layer_set_background_color(batticon_layer, GColorBlack); + layer_add_child(window_layer, bitmap_layer_get_layer(batticon_layer)); + + // BATTERY LEVEL + battlevel_layer = text_layer_create(GRect(110, 144, 32, 20)); + text_layer_set_text_color(battlevel_layer, GColorWhite); + text_layer_set_background_color(battlevel_layer, GColorBlack); + text_layer_set_font(battlevel_layer, fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD)); + text_layer_set_text_alignment(battlevel_layer, GTextAlignmentLeft); + layer_add_child(window_layer, text_layer_get_layer(battlevel_layer)); + + // CURRENT ACTUAL TIME + time_layer = text_layer_create(GRect(0, 82, 144, 44)); + text_layer_set_text_color(time_layer, GColorWhite); + text_layer_set_background_color(time_layer, GColorClear); + text_layer_set_font(time_layer, fonts_get_system_font(FONT_KEY_BITHAM_42_BOLD)); + text_layer_set_text_alignment(time_layer, GTextAlignmentCenter); + layer_add_child(window_layer, text_layer_get_layer(time_layer)); + + // CURRENT ACTUAL DATE + date_layer = text_layer_create(GRect(0, 120, 144, 25)); + text_layer_set_text_color(date_layer, GColorWhite); + text_layer_set_background_color(date_layer, GColorClear); + text_layer_set_font(date_layer, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); + text_layer_set_text_alignment(date_layer, GTextAlignmentCenter); + layer_add_child(window_layer, text_layer_get_layer(date_layer)); + draw_date(); + + + Tuplet initial_values[] = { + TupletBytes(CGM_ICON_KEY, icon_array, 2), + TupletCString(CGM_BG_KEY, ""), + TupletCString(CGM_READTIME_KEY, ""), + TupletInteger(CGM_ALERT_KEY, 0), + TupletCString(CGM_TIME_NOW, ""), + TupletCString(CGM_DELTA_KEY, "LOADING..."), + TupletCString(CGM_BATTLEVEL_KEY, ""), + TupletCString(CGM_T1DNAME_KEY, "") + }; + + app_sync_init(&sync, sync_buffer, sizeof(sync_buffer), initial_values, ARRAY_LENGTH(initial_values), sync_tuple_changed_callback, sync_error_callback, NULL); + + timer = app_timer_register(1000, timer_callback, NULL); +} + +static void window_unload(Window *window) { + app_sync_deinit(&sync); + + if (icon_bitmap) { + gbitmap_destroy(icon_bitmap); + } + if (specialvalue_bitmap) { + gbitmap_destroy(specialvalue_bitmap); + } + if (batticon_bitmap) { + gbitmap_destroy(batticon_bitmap); + } + text_layer_destroy(datetime_layer); + text_layer_destroy(readtime_layer); + text_layer_destroy(bg_layer); + text_layer_destroy(message_layer); + bitmap_layer_destroy(icon_layer); + bitmap_layer_destroy(batticon_layer); + text_layer_destroy(battlevel_layer); + text_layer_destroy(t1dname_layer); + // bitmap_layer_destroy(time_layer); + // bitmap_layer_destroy(date_layer); +} + +static void init(void) { + window = window_create(); + window_set_background_color(window, GColorBlack); + window_set_fullscreen(window, true); + window_set_window_handlers(window, (WindowHandlers) { + .load = window_load, + .unload = window_unload + }); + + app_message_open(app_message_inbox_size_maximum(), app_message_outbox_size_maximum()); + + const bool animated = true; + window_stack_push(window, animated); +} + +static void deinit(void) { + window_destroy(window); +} + +int main(void) { + init(); + + app_event_loop(); + deinit(); +} + + diff --git a/src/js/pebble-js-app.js b/src/js/pebble-js-app.js old mode 100644 new mode 100755 index da68a9e..8f29a32 --- a/src/js/pebble-js-app.js +++ b/src/js/pebble-js-app.js @@ -1,352 +1,425 @@ -var TIME_5_MINS = 5 * 60 * 1000, -TIME_10_MINS = 10 * 60 * 1000, -TIME_15_MINS = 15 * 60 * 1000, -TIME_30_MINS = TIME_15_MINS * 2; - -var lastAlert = 0; -var started = new Date( ).getTime( ); - -var DIRECTIONS = { - 'NONE': 0, - 'DoubleUp': 1, - 'SingleUp': 2, - 'FortyFiveUp': 3, - 'Flat': 4, - 'FortyFiveDown': 5, - 'SingleDown': 6, - 'DoubleDown': 7, - 'NOT COMPUTABLE': 8, - 'RATE OUT OF RANGE': 9 -}; - -function directionToTrend(direction) { - var trend = 8; - if (direction in DIRECTIONS) { - trend = DIRECTIONS[direction]; - } - return trend; -} - -function fetchCgmData(lastReadTime, lastBG) { - - var response, message; - var opts = options( ); - if (!opts.endpoint) { - message = { - icon: 0, - bg: '??', - readtime: timeago(new Date().getTime() - started), - alert: 0, - time: formatDate(new Date()), - delta: 'SETTINGS' - }; - - console.log("sending message", JSON.stringify(message)); - MessageQueue.sendAppMessage(message); - return; - } - var req = new XMLHttpRequest(); - - console.log('options', opts, opts.endpoint); - req.open('GET', opts.endpoint, true); - - req.onload = function(e) { - - console.log(req.readyState); - if (req.readyState == 4) { - console.log(req.status); - if(req.status == 200) { - console.log("status: " + req.status); - response = JSON.parse(req.responseText); - - var bgs = response.bgs; - if (bgs && bgs.length > 0) { - console.log('got bgs', JSON.stringify(bgs)); - - var now = new Date().getTime(), - sinceLastAlert = now - lastAlert, - alertValue = 0,currentBG = bgs[0].sgv, - currentBGDelta = bgs[0].bgdelta, - currentDirection = bgs[0].direction, - delta = (currentBGDelta > 0 ? '+' : '') + currentBGDelta + " mg/dL", - readingtime = new Date(bgs[0].datetime).getTime(), - readago = now - readingtime; - - console.log("now: " + now); - console.log("readingtime: " + readingtime); - console.log("readago: " + readago); - - if (currentBG < 39) { - if (sinceLastAlert > TIME_10_MINS) alertValue = 2; - } else if (currentBG < 55) - alertValue = 2; - else if (currentBG < 60 && currentBGDelta < 0) - alertValue = 2; - else if (currentBG < 70 && sinceLastAlert > TIME_15_MINS) - alertValue = 2; - else if (currentBG < 120 && currentDirection == 'DoubleDown' && sinceLastAlert > TIME_5_MINS) - alertValue = 2; - else if (currentBG == 100 && currentDirection == 'Flat' && sinceLastAlert > TIME_15_MINS) //Perfect Score - a good time to take a picture :) - alertValue = 1; - else if (currentBG > 120 && currentDirection == 'DoubleUp' && sinceLastAlert > TIME_15_MINS) - alertValue = 3; - else if (currentBG > 200 && sinceLastAlert > TIME_30_MINS && currentBGDelta > 0) - alertValue = 3; - else if (currentBG > 250 && sinceLastAlert > TIME_30_MINS) - alertValue = 3; - else if (currentBG > 300 && sinceLastAlert > TIME_15_MINS) - alertValue = 3; - - if (alertValue === 0 && readago > TIME_10_MINS && sinceLastAlert > TIME_15_MINS) { - alertValue = 1; - } - - if (alertValue > 0) { - lastAlert = now; - } - - message = { - icon: directionToTrend(currentDirection), - bg: currentBG, - readtime: timeago(new Date().getTime() - (new Date(bgs[0].datetime).getTime())), - alert: alertValue, - time: formatDate(new Date()), - delta: delta - }; - - console.log("message: " + JSON.stringify(message)); - MessageQueue.sendAppMessage(message); - - } else { - message = { - icon: 0, - bg: '???', - readtime: timeago(new Date().getTime() - (now)), - alert: 1, - time: formatDate(new Date()), - delta: 'offline' - - }; - console.log("sending message", JSON.stringify(message)); - MessageQueue.sendAppMessage(message); - } - } - } - }; - req.send(null); -} - -function formatDate(date) { - var minutes = date.getMinutes(), - hours = date.getHours() || 12, - meridiem = " PM", - formatted; - - if (hours > 12) - hours = hours - 12; - else if (hours < 12) - meridiem = " AM"; - - if (minutes < 10) - formatted = hours + ":0" + date.getMinutes() + meridiem; - else - formatted = hours + ":" + date.getMinutes() + meridiem; - - return formatted; -} - -function timeago(offset) { - var parts = {}, - MINUTE = 60 * 1000, - HOUR = 3600 * 1000, - DAY = 86400 * 1000, - WEEK = 604800 * 1000; - - if (offset <= MINUTE) parts = { lablel: 'now' }; - if (offset <= MINUTE * 2) parts = { label: '1 min ago' }; - else if (offset < (MINUTE * 60)) parts = { value: Math.round(Math.abs(offset / MINUTE)), label: 'mins' }; - else if (offset < (HOUR * 2)) parts = { label: '1 hr ago' }; - else if (offset < (HOUR * 24)) parts = { value: Math.round(Math.abs(offset / HOUR)), label: 'hrs' }; - else if (offset < (DAY * 1)) parts = { label: '1 day ago' }; - else if (offset < (DAY * 7)) parts = { value: Math.round(Math.abs(offset / DAY)), label: 'day' }; - else if (offset < (WEEK * 52)) parts = { value: Math.round(Math.abs(offset / WEEK)), label: 'week' }; - else parts = { label: 'a long time ago' }; - - if (parts.value) - return parts.value + ' ' + parts.label + ' ago'; - else - return parts.label; - -} - -function options ( ) { - var opts = [ ].slice.call(arguments).pop( ); - if (opts) { - window.localStorage.setItem('cgmPebble', JSON.stringify(opts)); - } else { - opts = JSON.parse(window.localStorage.getItem('cgmPebble')); - } - return opts; -} - -var MessageQueue = (function () { - - var RETRY_MAX = 5; - - var queue = []; - var sending = false; - var timer = null; - - return { - reset: reset, - sendAppMessage: sendAppMessage, - size: size - }; - - function reset() { - queue = []; - sending = false; - } - - function sendAppMessage(message, ack, nack) { - - if (! isValidMessage(message)) { - return false; - } - - queue.push({ - message: message, - ack: ack || null, - nack: nack || null, - attempts: 0 - }); - - setTimeout(function () { - sendNextMessage(); - }, 1); - - return true; - } - - function size() { - return queue.length; - } - - function isValidMessage(message) { - // A message must be an object. - if (message !== Object(message)) { - return false; - } - var keys = Object.keys(message); - // A message must have at least one key. - if (! keys.length) { - return false; - } - for (var k = 0; k < keys.length; k += 1) { - var validKey = /^[0-9a-zA-Z-_]*$/.test(keys[k]); - if (! validKey) { - return false; - } - var value = message[keys[k]]; - if (! validValue(value)) { - return false; - } - } - - return true; - - function validValue(value) { - switch (typeof(value)) { - case 'string': - return true; - case 'number': - return true; - case 'object': - if (toString.call(value) == '[object Array]') { - return true; - } - } - return false; - } - } - - function sendNextMessage() { - - if (sending) { return; } - var message = queue.shift(); - if (! message) { return; } - - message.attempts += 1; - sending = true; - Pebble.sendAppMessage(message.message, ack, nack); - - timer = setTimeout(function () { - timeout(); - }, 1000); - - function ack() { - clearTimeout(timer); - setTimeout(function () { - sending = false; - sendNextMessage(); - }, 200); - if (message.ack) { - message.ack.apply(null, arguments); - } - } - - function nack() { - clearTimeout(timer); - if (message.attempts < RETRY_MAX) { - queue.unshift(message); - setTimeout(function () { - sending = false; - sendNextMessage(); - }, 200 * message.attempts); - } - else { - if (message.nack) { - message.nack.apply(null, arguments); - } - } - } - - function timeout() { - setTimeout(function () { - sending = false; - sendNextMessage(); - }, 1000); - if (message.ack) { - message.ack.apply(null, arguments); - } - } - - } - - }()); - -Pebble.addEventListener("ready", - function(e) { - console.log("connect: " + e.ready); - //fetchCgmData(0, 0); - }); - -Pebble.addEventListener("appmessage", - function(e) { - console.log("Received message: " + JSON.stringify(e.payload)); - fetchCgmData(e.payload.readtime, e.payload.bg); - }); - -Pebble.addEventListener("showConfiguration", function(e) { - console.log("showing configuration", JSON.stringify(e)); - Pebble.openURL('http://bewest.github.io/cgm-pebble/configurable.html'); - }); - -Pebble.addEventListener("webviewclosed", function(e) { - var opts = e.response.length > 5 - ? JSON.parse(decodeURIComponent(e.response)): null; - - options(opts); - - }); - +// global variable for last alert time +var lastAlert = 0; + +// main function to retrieve, format, and send cgm data +function fetchCgmData(lastReadTime, lastBG) { + + // declare local constants for time differences + var TIME_5_MINS = 5 * 60 * 1000, + TIME_10_MINS = 10 * 60 * 1000, + TIME_15_MINS = 15 * 60 * 1000, + TIME_30_MINS = TIME_15_MINS * 2; + + // declare local constants for arrow trends + var NO_ARROW = 0, + DOUBLE_UP = 1, + SINGLE_UP = 2, + FORTYFIVE_UP = 3, + FLAT_ARROW = 4, + FORTYFIVE_DOWN = 5, + SINGLE_DOWN = 6, + DOUBLE_DOWN = 7, + NOT_COMPUTABLE = 8, + RATE_OUT_OF_RANGE = 9, + LOGO = 10; + + // hard code name of T1D person, for now + var NameofT1DPerson = ""; + + // declare local variables for message data + var response, message; + + //call options & started to get endpoint & start time + var opts = options( ); + var started = new Date( ).getTime( ); + + //if endpoint is invalid, return error msg to watch + if (!opts.endpoint) { + message = { + icon: [0,LOGO], + bg: '---', + readtime: timeago(new Date().getTime() - started), + alert: 0, + time: formatDate(new Date()), + delta: 'CHECK ENDPOINT', + battlevel: "", + t1dname: "" + }; + + console.log("sending message", JSON.stringify(message)); + MessageQueue.sendAppMessage(message); + return; + } + + // call XML + var req = new XMLHttpRequest(); + //console.log('endpoint: ' + opts.endpoint); + + // get cgm data + req.open('GET', opts.endpoint, true); + + req.onload = function(e) { + + if (req.readyState == 4) { + + if(req.status == 200) { + + // Load response + response = JSON.parse(req.responseText); + response = response.bgs; + + // check response data + if (response && response.length > 0) { + + // response data is good; send log with response + console.log('got response', JSON.stringify(response)); + + // see if we're in a Rajat build + var RajatBuild = isRajatBuild(opts.endpoint, "heroku"); + if (RajatBuild) { + // set Rajat arrow constants + DOUBLE_UP = 0; + SINGLE_UP = 1; + FORTYFIVE_UP = 2; + FLAT_ARROW = 3; + NO_ARROW = 4; + } + + // initialize message data + var now = new Date().getTime(), + sinceLastAlert = now - lastAlert, + alertValue = 0, + currentBG = response[0].sgv, + currentBGDelta = response[0].bgdelta, + currentTrend = response[0].trend, + delta = (currentBGDelta > 0 ? '+' : '') + currentBGDelta + " mg/dL", + readingtime = new Date(response[0].datetime).getTime(), + readago = now - readingtime, + + // battery not included in response yet, so have to send no battery for now + // once battery is included, uncomment out line and erase "111" line + //currentBattery = response[0].battery; + currentBattery = "111"; + + // see if we're in a Rajat build + var RajatBuild = isRajatBuild(opts.endpoint, "heroku"); + if (RajatBuild) { + // set Rajat arrow constants + DOUBLE_UP = 0; + SINGLE_UP = 1; + FORTYFIVE_UP = 2; + FLAT_ARROW = 3; + NO_ARROW = 4; + // can't read battery so set to 111 to indicate Rajat build + currentBattery = "111"; + } + + // debug logs; uncomment when need to debug something + //console.log("now: " + now); + //console.log("sinceLastAlert: " + sinceLastAlert); + //console.log("current BG: " + currentBG); + //console.log("current BG delta: " + currentBGDelta); + //console.log("arrow: " + currentTrend); + //console.log('RajatBuild?: ' + RajatBuild); + //console.log("readingtime: " + readingtime); + //console.log("readago: " + readago); + //console.log("current Battery: " + currentBattery); + + // set vibration pattern; alert value; 0 nothing, 1 normal, 2 low, 3 high + + if (currentBG < 39) { + if (sinceLastAlert > TIME_10_MINS) alertValue = 2; + } else if (currentBG < 55) + alertValue = 2; + else if (currentBG < 60 && currentBGDelta < 0) + alertValue = 2; + else if (currentBG < 70 && sinceLastAlert > TIME_15_MINS) + alertValue = 2; + else if (currentBG < 120 && currentTrend == DOUBLE_DOWN && sinceLastAlert > TIME_5_MINS) + alertValue = 2; + else if (currentBG == 100 && currentTrend == FLAT_ARROW && sinceLastAlert > TIME_15_MINS) //Perfect Score - a good time to take a picture :) + alertValue = 1; + else if (currentBG > 120 && currentTrend == DOUBLE_UP && sinceLastAlert > TIME_15_MINS) + alertValue = 3; + else if (currentBG > 200 && sinceLastAlert > TIME_30_MINS && currentBGDelta > 0) + alertValue = 3; + else if (currentBG > 250 && sinceLastAlert > TIME_30_MINS) + alertValue = 3; + else if (currentBG > 300 && sinceLastAlert > TIME_15_MINS) + alertValue = 3; + + if (alertValue === 0 && readago > TIME_10_MINS && sinceLastAlert > TIME_15_MINS) { + alertValue = 1; + } + + if (alertValue > 0) { + lastAlert = now; + } + + // load message data + message = { + icon: [RajatBuild,currentTrend], + bg: currentBG, + readtime: timeago(new Date().getTime() - (new Date(response[0].datetime).getTime())), + alert: alertValue, + time: formatDate(new Date()), + delta: delta, + battlevel: currentBattery, + t1dname: NameofT1DPerson + }; + + // send message data to log and to watch + console.log("message: " + JSON.stringify(message)); + MessageQueue.sendAppMessage(message); + + // response data is no good; format error message and send to watch + } else { + message = { + icon: [0,LOGO], + bg: '---', + readtime: timeago(new Date().getTime() - (now)), + alert: 1, + time: formatDate(new Date()), + delta: 'DATA OFFLINE', + battlevel: "", + t1dname: "" + }; + console.log("sending message", JSON.stringify(message)); + MessageQueue.sendAppMessage(message); + } + } + } + }; + req.send(null); +} + +// format date hours:minutes; add AM + PM if want +function formatDate(date) { + var minutes = date.getMinutes(), + hours = date.getHours() || 12, + meridiem = " PM", + formatted; + + if (hours > 12) + hours = hours - 12; + else if (hours < 12) + meridiem = " AM"; + + // don't want AM & PM, so add line with blank + // if want to add later, then comment out this line + meridiem = ""; + + if (minutes < 10) + formatted = hours + ":0" + date.getMinutes() + meridiem; + else + formatted = hours + ":" + date.getMinutes() + meridiem; + + return formatted; +} + +// format past time difference data +function timeago(offset) { + var parts = {}, + MINUTE = 60 * 1000, + HOUR = 3600 * 1000, + DAY = 86400 * 1000, + WEEK = 604800 * 1000; + + if (offset <= MINUTE) parts = { lablel: 'now' }; + if (offset <= MINUTE * 2) parts = { label: '1 min ago' }; + else if (offset < (MINUTE * 60)) parts = { value: Math.round(Math.abs(offset / MINUTE)), label: 'min' }; + else if (offset < (HOUR * 2)) parts = { label: '1 hr ago' }; + else if (offset < (HOUR * 24)) parts = { value: Math.round(Math.abs(offset / HOUR)), label: 'hrs' }; + else if (offset < (DAY * 1)) parts = { label: '1 dy ago' }; + else if (offset < (DAY * 7)) parts = { value: Math.round(Math.abs(offset / DAY)), label: 'dys' }; + else if (offset < (WEEK * 52)) parts = { value: Math.round(Math.abs(offset / WEEK)), label: 'wks' }; + else parts = { label: 'BEFORE DX'}; + + if (parts.value) + return parts.value + ' ' + parts.label + ' ago'; + else + return parts.label; + +} + +// get endpoint for XML request +function options ( ) { + var opts = [ ].slice.call(arguments).pop( ); + if (opts) { + window.localStorage.setItem('cgmPebble', JSON.stringify(opts)); + } else { + opts = JSON.parse(window.localStorage.getItem('cgmPebble')); + } + return opts; +} + +// check for Rajat build +function isRajatBuild (str, str_to_match) { + return (str.indexOf(str_to_match) >= 0); +} + +// message queue-ing to pace calls from C function on watch +var MessageQueue = (function () { + + var RETRY_MAX = 5; + + var queue = []; + var sending = false; + var timer = null; + + return { + reset: reset, + sendAppMessage: sendAppMessage, + size: size + }; + + function reset() { + queue = []; + sending = false; + } + + function sendAppMessage(message, ack, nack) { + + if (! isValidMessage(message)) { + return false; + } + + queue.push({ + message: message, + ack: ack || null, + nack: nack || null, + attempts: 0 + }); + + setTimeout(function () { + sendNextMessage(); + }, 1); + + return true; + } + + function size() { + return queue.length; + } + + function isValidMessage(message) { + // A message must be an object. + if (message !== Object(message)) { + return false; + } + var keys = Object.keys(message); + // A message must have at least one key. + if (! keys.length) { + return false; + } + for (var k = 0; k < keys.length; k += 1) { + var validKey = /^[0-9a-zA-Z-_]*$/.test(keys[k]); + if (! validKey) { + return false; + } + var value = message[keys[k]]; + if (! validValue(value)) { + return false; + } + } + + return true; + + function validValue(value) { + switch (typeof(value)) { + case 'string': + return true; + case 'number': + return true; + case 'object': + if (toString.call(value) == '[object Array]') { + return true; + } + } + return false; + } + } + + function sendNextMessage() { + + if (sending) { return; } + var message = queue.shift(); + if (! message) { return; } + + message.attempts += 1; + sending = true; + Pebble.sendAppMessage(message.message, ack, nack); + + timer = setTimeout(function () { + timeout(); + }, 1000); + + function ack() { + clearTimeout(timer); + setTimeout(function () { + sending = false; + sendNextMessage(); + }, 200); + if (message.ack) { + message.ack.apply(null, arguments); + } + } + + function nack() { + clearTimeout(timer); + if (message.attempts < RETRY_MAX) { + queue.unshift(message); + setTimeout(function () { + sending = false; + sendNextMessage(); + }, 200 * message.attempts); + } + else { + if (message.nack) { + message.nack.apply(null, arguments); + } + } + } + + function timeout() { + setTimeout(function () { + sending = false; + sendNextMessage(); + }, 1000); + if (message.ack) { + message.ack.apply(null, arguments); + } + } + + } + + }()); + +// pebble specific calls with watch +Pebble.addEventListener("ready", + function(e) { + console.log("connect: " + e.ready); + //fetchCgmData(0, 0); + }); + +Pebble.addEventListener("appmessage", + function(e) { + console.log("Received message: " + JSON.stringify(e.payload)); + fetchCgmData(e.payload.readtime, e.payload.bg); + }); + +Pebble.addEventListener("showConfiguration", function(e) { + console.log("showing configuration", JSON.stringify(e)); + Pebble.openURL('http://bewest.github.io/cgm-pebble/configurable.html'); + }); + +Pebble.addEventListener("webviewclosed", function(e) { + var opts = e.response.length > 5 + ? JSON.parse(decodeURIComponent(e.response)): null; + + options(opts); + + }); + + + diff --git a/wscript b/wscript old mode 100644 new mode 100755