Pebble Timeで残りの寿命を表示するWatch Faceを作ってみた

あむちょです。

つねに自分の余命を表示する嫌な時計を作ってみた。

PR アプリ作ってます
ad maruta

目次

Pebble Timeってなんですかって人はこちら。丸型スマートウォッチPebble Time Roundのレビュー

今回は、webと連携、データをローカルに保存、カスタムバイブパターンってのをやってみました。

今回も角形と丸型の両方に対応

Watch Face 「Expectancy」

expectancy ecpectancyround

上二行が残りの余命で、

年-月-日
時:分:秒
現在時刻

を表示してます。

setting

寿命計算は、設定画面から質問に答えると計算されます。

config

気が向いたら日本語化させます。計算方法はこちらを参考にしました。寿命計算.com

寿命が表示させると心臓の鼓動っぽく2回バイブレーションします。

こちらからダウンロードできます。Pebble Time Watch Face 「Expectancy」

ソースコード

設定画面からサイトにアクセスし、寿命と誕生日のデータをJsonで受け取ってjsを使ってPebble本体にメッセージを送ってます。

おくられてきたデータはローカルに保存して、次に表示する時にも使います。

まずはC言語ファイル

#include 

#define NUM_EXPECT_PKEY 1
#define NUM_EXPECT_MON_PKEY 2
#define NUM_EXPECT_DAY_PKEY 3

#define NUM_EXPECT_DEFAULT -1
#define NUM_EXPECT_MON_DEFAULT 7
#define NUM_EXPECT_DAY_DEFAULT 6

#define KEY_EXPECT 0
#define KEY_BR_MON 1
#define KEY_BR_DAY 2

static Window *window;
static TextLayer *text_much0;
static TextLayer *text_much1;
static TextLayer *text_now;

static TextLayer *text_config;

static int expect = NUM_EXPECT_DEFAULT;
static int br_mon = NUM_EXPECT_MON_DEFAULT;
static int br_day = NUM_EXPECT_DAY_DEFAULT;

#if defined(PBL_RECT)
    #define DEL_Y (180-168)/2
#elif defined(PBL_ROUND)
    #define DEL_Y 0
#endif


static void in_recv_handler(DictionaryIterator *iter, void *context){
    
    Tuple *exp_t = dict_find(iter, KEY_EXPECT);
    Tuple *exp_m = dict_find(iter, KEY_BR_MON);
    Tuple *exp_d = dict_find(iter, KEY_BR_DAY);
    
    expect = exp_t->value->int16;
    persist_write_int(NUM_EXPECT_PKEY, expect);
    
    br_mon = exp_m->value->int8;
    persist_write_int(NUM_EXPECT_MON_PKEY, br_mon);
    
    br_day = exp_d->value->int8;
    persist_write_int(NUM_EXPECT_DAY_PKEY, br_day);
    
    layer_set_hidden(text_layer_get_layer(text_much0),false);
    layer_set_hidden(text_layer_get_layer(text_much1),false);
    layer_set_hidden(text_layer_get_layer(text_config),true);
    
    static const uint32_t const segments[] = { 200, 100, 150, 1000 ,200, 100, 150};
    VibePattern pat = {
        .durations = segments,
        .num_segments = ARRAY_LENGTH(segments),
    };
    vibes_enqueue_custom_pattern(pat);
}

static void handle_second_tick(struct tm *tick_time, TimeUnits units_changed){
    
    static char timeMuch0[] = "2032-12-02";
    static char timeMuch1[] = "00:00:00";
    static char timeNow[] = "00:00:00";
    
    if(expect != -1){
        
        int year = expect-(tick_time->tm_year+1900);
        int month = tick_time->tm_mon+1;
        int day = tick_time->tm_mday;
        int hour = 24-tick_time->tm_hour;
        int min = 60-tick_time->tm_min;
        int sec = 60-tick_time->tm_sec;
        
        day = br_day-day;
        if(day < 0){
            day += 31;
            month++;
        }
        
        month = br_mon-month;
        if(month < 0){
            month += 12;
            year--;
        }
        
        char y[] = "123";
        if(year < 10){
            snprintf(y,sizeof(y),"0%d",year);
        }
        else{
            snprintf(y,sizeof(y),"%d",year);
        }
        
        char m[] = "23";
        if(month < 10){
            snprintf(m,sizeof(m),"0%d",month);
        }
        else{
            snprintf(m,sizeof(m),"%d",month);
        }
        
        char d[] = "12";
        if(day < 10){
            snprintf(d,sizeof(d),"0%d",day);
        }
        else{
            snprintf(d,sizeof(d),"%d",day);
        }
        
        char h[] = "12";
        if(hour < 10){
            snprintf(h,sizeof(h),"0%d",hour);
        }
        else{
            snprintf(h,sizeof(h),"%d",hour);
        }
        
        char mi[] = "23";
        if(min < 10){
            snprintf(mi,sizeof(mi),"0%d",min);
        }
        else{
            snprintf(mi,sizeof(mi),"%d",min);
        }
        
        char s[] = "23";
        if(sec < 10){
            snprintf(s,sizeof(s),"0%d",sec);
        }
        else{
            snprintf(s,sizeof(s),"%d",sec);
        }
        
        snprintf(timeMuch0,sizeof(timeMuch0),"%s-%s-%s",y,m,d);
        snprintf(timeMuch1,sizeof(timeMuch1),"%s:%s:%s",h,mi,s);
        
        text_layer_set_text(text_much0, timeMuch0);
        
        text_layer_set_text(text_much1, timeMuch1);
        
    }
    
    strftime(timeNow, sizeof(timeNow), "%H:%M:%S", tick_time);
    text_layer_set_text(text_now, timeNow);
    
}

static void window_load(Window *window) {
    
    Layer *window_layer = window_get_root_layer(window);
    GRect bounds = layer_get_bounds(window_layer);

    //expect hight
    text_much0 = text_layer_create((GRect) { .origin = { 0, 25-DEL_Y }, .size = { bounds.size.w, 28 } });
    text_layer_set_text_alignment(text_much0, GTextAlignmentCenter);
    text_layer_set_background_color(text_much0, GColorClear);
    text_layer_set_text_color(text_much0, GColorWhite);
    text_layer_set_font(text_much0, fonts_get_system_font(FONT_KEY_LECO_26_BOLD_NUMBERS_AM_PM));
    layer_add_child(window_layer, text_layer_get_layer(text_much0));
    layer_set_hidden(text_layer_get_layer(text_much0),true);
    //expect low
    text_much1 = text_layer_create((GRect) { .origin = { 0, 90-20-DEL_Y }, .size = { bounds.size.w, 28 } });
    text_layer_set_text_alignment(text_much1, GTextAlignmentCenter);
    text_layer_set_background_color(text_much1, GColorClear);
    text_layer_set_text_color(text_much1, GColorWhite);
    text_layer_set_font(text_much1, fonts_get_system_font(FONT_KEY_LECO_26_BOLD_NUMBERS_AM_PM));
    layer_add_child(window_layer, text_layer_get_layer(text_much1));
    layer_set_hidden(text_layer_get_layer(text_much1),true);
    //config
    text_config = text_layer_create((GRect) { .origin = { 0, 25-DEL_Y }, .size = { bounds.size.w, 28*3 } });
    text_layer_set_text(text_config, "Check\nSetting");
    text_layer_set_text_alignment(text_config, GTextAlignmentCenter);
    text_layer_set_background_color(text_config, GColorClear);
    text_layer_set_text_color(text_config, GColorWhite);
    text_layer_set_font(text_config, fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD));
    layer_add_child(window_layer, text_layer_get_layer(text_config));
    
    //now
    text_now = text_layer_create((GRect) { .origin = { 0, 120-DEL_Y }, .size = { bounds.size.w, 28 } });
    text_layer_set_text_alignment(text_now, GTextAlignmentCenter);
    text_layer_set_background_color(text_now, GColorClear);
    text_layer_set_text_color(text_now, GColorWhite);
    text_layer_set_font(text_now, fonts_get_system_font(FONT_KEY_LECO_26_BOLD_NUMBERS_AM_PM));
    layer_add_child(window_layer, text_layer_get_layer(text_now));
    
    if(expect != -1){
        layer_set_hidden(text_layer_get_layer(text_much0),false);
        layer_set_hidden(text_layer_get_layer(text_much1),false);
        layer_set_hidden(text_layer_get_layer(text_config),true);
    }
    
    app_message_register_inbox_received((AppMessageInboxReceived) in_recv_handler);
    app_message_open(app_message_inbox_size_maximum(), app_message_outbox_size_maximum());
    
}

static void window_unload(Window *window) {
    
    text_layer_destroy(text_much0);
    text_layer_destroy(text_much1);
    text_layer_destroy(text_config);
    text_layer_destroy(text_now);
    
}

static void init(void) {
    
    expect = persist_exists(NUM_EXPECT_PKEY) ? persist_read_int(NUM_EXPECT_PKEY) : NUM_EXPECT_DEFAULT;
    br_mon = persist_exists(NUM_EXPECT_MON_PKEY) ? persist_read_int(NUM_EXPECT_MON_PKEY) : NUM_EXPECT_MON_DEFAULT;
    br_day = persist_exists(NUM_EXPECT_DAY_PKEY) ? persist_read_int(NUM_EXPECT_DAY_PKEY) : NUM_EXPECT_DAY_DEFAULT;
    
    window = window_create();
    window_set_background_color(window, GColorBlack);
    window_set_window_handlers(window, (WindowHandlers) {
        .load = window_load,
        .unload = window_unload,
    });
    window_stack_push(window, true);
    
    tick_timer_service_subscribe(SECOND_UNIT, &handle_second_tick);
    
    static const uint32_t const segments[] = { 200, 100, 150, 1000 ,200, 100, 150};
    VibePattern pat = {
        .durations = segments,
        .num_segments = ARRAY_LENGTH(segments),
    };
    vibes_enqueue_custom_pattern(pat);

}

static void deinit(void) {
    
    tick_timer_service_unsubscribe();
    window_destroy(window);
}

int main(void) {
    init();
    app_event_loop();
    deinit();
}

ローカルのデータは

#define NUM_EXPECT_PKEY 1
#define NUM_EXPECT_MON_PKEY 2
#define NUM_EXPECT_DAY_PKEY 3

#define NUM_EXPECT_DEFAULT -1
#define NUM_EXPECT_MON_DEFAULT 7
#define NUM_EXPECT_DAY_DEFAULT 6

expect = persist_exists(NUM_EXPECT_PKEY) ? persist_read_int(NUM_EXPECT_PKEY) : NUM_EXPECT_DEFAULT;
br_mon = persist_exists(NUM_EXPECT_MON_PKEY) ? persist_read_int(NUM_EXPECT_MON_PKEY) : NUM_EXPECT_MON_DEFAULT;
br_day = persist_exists(NUM_EXPECT_DAY_PKEY) ? persist_read_int(NUM_EXPECT_DAY_PKEY) : NUM_EXPECT_DAY_DEFAULT;

でデータを取り出してなかったらデフォルト値を設定します。データ書き込みは

persist_write_int(NUM_EXPECT_PKEY, expect);

です。ともに任意のKEYを設定する必要があります。

カスタムバイブパターンは

static const uint32_t const segments[] = { 200, 100, 150, 1000 ,200, 100, 150};
VibePattern pat = {
    .durations = segments,
    .num_segments = ARRAY_LENGTH(segments),
};
vibes_enqueue_custom_pattern(pat);

配列にOn時間とOff時間のパターンをいれるだけです。

次にスマホ本体とやりとりするためのjsファイル

Pebble.addEventListener("ready", function(e) {
    console.log("connect!" + e.ready);
    getLocation();
    console.log(e.type);
});

Pebble.addEventListener("appmessage", function(e) {
    console.log(e.type);
    console.log(e.payload.temperature);
    console.log("message!");
});

Pebble.addEventListener("webviewclosed", function(e) {
    console.log("webview closed");
    console.log(e.type);
    console.log(e.response);
    var configuration = JSON.parse(decodeURIComponent(e.response));
    var dict = {};
	
	dict['KEY_EXPECT'] = configuration['expect'];
	dict['KEY_BR_MON'] = configuration['br_mon'];
	dict['KEY_BR_DAY'] = configuration['br_day'];
	
	Pebble.sendAppMessage(dict, function() {
		console.log('Send successful: ' + JSON.stringify(dict));
	},
	function() {
		console.log('Send failed!');
	});

});

Pebble.addEventListener('showConfiguration', function(e) {
	Pebble.openURL('http://fieldwalking.jp/pebble/expectancy/');
});

お次は設定画面のhtmlはここから直接みておくれsetting

設定画面のUIはslateってのを使ってます。GitHub slate

外人さんは優しい

何個かリリースしてみて、たまに外人さんからお褒めのメールが届きます。

アプリの時もそうだけど、彼らは感謝のメールとか送ってくるのでやるきがでますね。

ただ、

バグに対する文句のメールも送ってくる

コメント