Pebble Timeで残りの寿命を表示するWatch Faceを作ってみた
あむちょです。
つねに自分の余命を表示する嫌な時計を作ってみた。
目次
Pebble Timeってなんですかって人はこちら。丸型スマートウォッチPebble Time Roundのレビュー
今回は、webと連携、データをローカルに保存、カスタムバイブパターンってのをやってみました。
今回も角形と丸型の両方に対応
Watch Face 「Expectancy」
![]() |
![]() |
上二行が残りの余命で、
年-月-日
時:分:秒
現在時刻
を表示してます。

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

気が向いたら日本語化させます。計算方法はこちらを参考にしました。寿命計算.com
寿命が表示させると心臓の鼓動っぽく2回バイブレーションします。
こちらからダウンロードできます。Pebble Time Watch Face 「Expectancy」
[amazonjs asin=”B0166NBSFK” locale=”JP” title=”Pebble Time Round 極薄かつ超軽量の丸型スマートウォッチ「ペッブルタイム・ラウンド」Black 並行輸入品”]
ソースコード
設定画面からサイトにアクセスし、寿命と誕生日のデータを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
外人さんは優しい
何個かリリースしてみて、たまに外人さんからお褒めのメールが届きます。
アプリの時もそうだけど、彼らは感謝のメールとか送ってくるのでやるきがでますね。
ただ、
バグに対する文句のメールも送ってくる
[ad][ad]

