Browse Source

Initial commit.

Tomek Wójcik 4 years ago
commit
b3581d255c

+ 5 - 0
.gitignore

@@ -0,0 +1,5 @@
+appinfo.json
+appinfo.site.json
+build/
+.lock-waf*
+/pebble_screenshot*.png

BIN
GFX/amiclock.idraw


+ 19 - 0
LICENSE

@@ -0,0 +1,19 @@
+Copyright (c) 2015 Tomek Wójcik <tomek@bthlabs.pl>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.

+ 19 - 0
README.md

@@ -0,0 +1,19 @@
+# IntuiClock
+
+IntuiClock is a Pebble Time watchface inspired by the Clock program from Amiga®
+Workbench 3.1.
+
+## License
+
+IntuiClock is licensed under MIT License.
+
+## Author and credits
+
+IntuiClock was written by [Tomek Wójcik](http://www.tomekwojcik.com/).
+
+This project uses Topaz font (CC BY-NC-SA 3.0) by dMG/t!s^dS.
+
+This project uses Slate frontend framework (MIT) by Pebble Technology.
+
+Amiga® is a brand of Amiga Inc. This project is not an official Amiga Inc.
+product.

+ 39 - 0
appinfo.in.json

@@ -0,0 +1,39 @@
+{
+    "uuid": null,
+    "shortName": null,
+    "longName": null,
+    "companyName": null,
+    "versionLabel": null,
+    "sdkVersion": "3",
+    "targetPlatforms": ["basalt"],
+    "watchapp": {
+        "watchface": true
+    },
+    "resources": {
+        "media": [
+            {
+                "type": "png",
+                "name": "IMG_BACKGROUND",
+                "file": "background.png"
+            },
+            {
+                "type": "png",
+                "name": "IMG_BATTERY",
+                "file": "battery.png"
+            },
+            {
+                "type": "font",
+                "name": "FONT_TOPAZ_16",
+                "file": "topaz1200.ttf"
+            }
+        ]
+    },
+    "capabilities": [
+        "configurable"
+    ],
+    "appKeys": {
+        "KEY_SECONDS_HAND": 0,
+        "KEY_BATTERY_INDICATOR": 1,
+        "KEY_DATE_FORMAT": 2
+    }
+}

File diff suppressed because it is too large
+ 1 - 0
config/css/slate.min.css


BIN
config/fonts/PFDinDisplayPro-Light.woff


BIN
config/fonts/ptsans-regular.woff


+ 106 - 0
config/index.html

@@ -0,0 +1,106 @@
+<!DOCTYPE html>
+<html lang="en">
+    <head>
+        <meta charset="UTF-8">
+        <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0" name="viewport">
+        <title>IntuiClock Config</title>
+        <link rel="stylesheet" href="css/slate.min.css">
+        <script type="text/javascript" src="js/slate.min.js"></script>
+    </head>
+
+    <body>
+        <form id="main-form">
+            <div class="item-container">
+                <div class="item-container-header">Features</div>
+                <div class="item-container-content">
+                    <label class="item">
+                        Seconds Hand
+                        <input id="input-seconds-hand" type="checkbox" class="item-toggle" name="seconds-hand" checked>
+                    </label>
+                    <label class="item">
+                        Battery Indicator
+                        <input id="input-battery-indicator" type="checkbox" class="item-toggle" name="battery-indicator" checked>
+                    </label>
+                    <label class="item">
+                        Date Format
+                        <select id="input-date-format" name="date-format" class="item-select">
+                            <option class="item-select-option" selected value="t">10 Aug 2015</option>
+                            <option class="item-select-option" value="d">10/08/2015</option>
+                            <option class="item-select-option" value="m">08/10/2015</option>
+                        </select>
+                    </label>
+                </div>
+                <div class="item-container-footer">Disable the seconds hand to reduce battery use.</div>
+            </div>
+
+            <div class="item-container">
+                <div class="button-container">
+                    <input id="button-submit" type="button" class="item-button" value="SAVE">
+                </div>
+            </div>
+        </form>
+    </body>
+
+<script>
+    function getConfigData() {
+        var input_seconds_hand = document.getElementById('input-seconds-hand'),
+            input_battery_indicator = document.getElementById('input-battery-indicator'),
+            input_date_format = document.getElementById('input-date-format');
+
+        var options = {
+            'seconds_hand': input_seconds_hand.checked,
+            'battery_indicator': input_battery_indicator.checked,
+            'date_format': input_date_format.value
+        };
+
+        // Save for next launch
+        localStorage['seconds_hand'] = options['seconds_hand'];
+        localStorage['battery_indicator'] = options['battery_indicator'];
+        localStorage['date_format'] = options['date_format'];
+
+        console.log('Got options: ' + JSON.stringify(options));
+        return options;
+    }
+
+    function getQueryParam(variable, defaultValue) {
+        var query = location.search.substring(1),
+            vars = query.split('&');
+
+        for (var i = 0; i < vars.length; i++) {
+            var pair = vars[i].split('=');
+            if (pair[0] === variable) {
+                return decodeURIComponent(pair[1]);
+            }
+        }
+
+        return defaultValue || false;
+    }
+
+    var button_submit = document.getElementById('button-submit');
+    button_submit.addEventListener('click', function() {
+        console.log('Submit');
+
+        // Set the return URL depending on the runtime environment
+        var return_to = getQueryParam('return_to', 'pebblejs://close#');
+        document.location = return_to + encodeURIComponent(JSON.stringify(getConfigData()));
+    });
+
+    (function() {
+        var input_seconds_hand = document.getElementById('input-seconds-hand'),
+            input_battery_indicator = document.getElementById('input-battery-indicator'),
+            input_date_format = document.getElementById('input-date-format');
+
+        if (localStorage['seconds_hand']) {
+            input_seconds_hand.checked = JSON.parse(localStorage['seconds_hand']);
+        }
+
+        if (localStorage['battery_indicator']) {
+            input_battery_indicator.checked = JSON.parse(localStorage['battery_indicator']);
+        }
+
+        if (localStorage['date_format']) {
+            input_date_format.value = localStorage['date_format'];
+        }
+    })();
+</script>
+</html>

File diff suppressed because it is too large
+ 2 - 0
config/js/slate.min.js


+ 58 - 0
fabfile.py

@@ -0,0 +1,58 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2015 Tomek Wójcik <tomek@bthlabs.pl>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+import codecs
+import json
+
+from fabric.api import local, task
+
+
+def _load_appinfo(path):
+    appinfo = None
+    with codecs.open(path, 'r', 'utf-8') as appinfo_in_f:
+        appinfo = json.loads(appinfo_in_f.read())
+
+    return appinfo
+
+
+@task
+def build(emulator=None, phone=None):
+    appinfo = _load_appinfo('appinfo.in.json')
+    assert appinfo
+
+    appinfo_site = _load_appinfo('appinfo.site.json')
+    if appinfo_site:
+        appinfo.update(appinfo_site)
+
+    with open('appinfo.json', 'w') as appinfo_f:
+        appinfo_f.write(json.dumps(appinfo, indent=4))
+
+    local('pebble build')
+
+
+@task
+def emu(platform='basalt'):
+    local('pebble install --emulator=%s' % platform)
+
+
+@task
+def phone(ip):
+    local('pebble install --phone=%s' % ip)

BIN
resources/background.png


BIN
resources/battery.png


BIN
resources/topaz1200.ttf


+ 349 - 0
src/intuiclock.c

@@ -0,0 +1,349 @@
+/**
+ * Copyright (c) 2015 Tomek Wójcik <tomek@bthlabs.pl>
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <pebble.h>
+
+static Window *s_main_window;
+static GBitmap *s_background_bitmap;
+static GBitmap *s_battery_bitmap;
+static GFont s_text_font;
+static GPath *s_minute_hand;
+static GPath *s_hour_hand;
+static BitmapLayer *s_background_layer;
+static TextLayer *s_title_layer;
+static Layer *s_hands_layer;
+static TextLayer *s_date_layer;
+static TextLayer *s_ampm_layer;
+static BitmapLayer *s_battery_icon_layer;
+static Layer *s_battery_indicator_layer;
+static struct tm *t = NULL;
+static uint8_t battery_charge = 0;
+static bool hide_seconds_hand = false;
+static bool hide_battery_indicator = false;
+static char date_format[2];
+
+static const GPathInfo MINUTE_HAND_POINTS = {
+  4,
+  (GPoint []) {
+    { 0, 0 },
+    { -4.5, -26 },
+    { 0, -38 },
+    { 4.5, -26 }
+  }
+};
+
+static const GPathInfo HOUR_HAND_POINTS = {
+  4,
+  (GPoint []) {
+    { 0, 0 },
+    { -4.5, -22 },
+    { 0, -29 },
+    { 4.5, -22 }
+  }
+};
+
+#define AMPM_TEXT_SIZE 3
+#define DUMMY_AMPM_TEXT ""
+
+#define DATE_TEXT_SIZE 12
+#define DUMMY_DATE_TEXT "23 Jul 1985"
+
+#define KEY_SECONDS_HAND 0
+#define KEY_BATTERY_INDICATOR 1
+#define KEY_DATE_FORMAT 2
+
+static void update_date_text_layer() {
+    static char date_text[DATE_TEXT_SIZE];
+    size_t result = 0;
+
+    if (strcmp(date_format, "d") == 0) {
+        result = strftime(date_text, DATE_TEXT_SIZE, "%d/%m/%y", t);
+    } else if (strcmp(date_format, "m") == 0) {
+        result = strftime(date_text, DATE_TEXT_SIZE, "%m/%d/%y", t);
+    } else {
+        result = strftime(date_text, DATE_TEXT_SIZE, "%e %b %Y", t);
+    }
+
+    if (result != 0) {
+        text_layer_set_text(s_date_layer, date_text);
+    } else {
+        text_layer_set_text(s_date_layer, DUMMY_DATE_TEXT);
+    }
+}
+
+static void update_ampm_text_layer() {
+    static char ampm_text[AMPM_TEXT_SIZE];
+
+    if (clock_is_24h_style()) {
+        text_layer_set_text(s_ampm_layer, DUMMY_AMPM_TEXT);
+    } else {
+        size_t result = strftime(ampm_text, AMPM_TEXT_SIZE, "%p", t);
+        if (result != 0) {
+            text_layer_set_text(s_ampm_layer, ampm_text);
+        } else {
+            text_layer_set_text(s_ampm_layer, DUMMY_AMPM_TEXT);
+        }
+    }
+}
+
+static void tick_handler(struct tm *tick_time, TimeUnits changed_units) {
+    // Getting the current time.
+    time_t now = time(NULL);
+    t = localtime(&now);
+
+    // Updating views.
+    layer_mark_dirty(s_hands_layer);
+    update_date_text_layer();
+    update_ampm_text_layer();
+}
+
+static void s_hands_layer_update(Layer *layer, GContext *ctx) {
+    GRect bounds = layer_get_bounds(layer);
+    GPoint center = grect_center_point(&bounds);
+
+    // Drawing the minute hand.
+    graphics_context_set_fill_color(ctx, GColorBlack);
+    graphics_context_set_stroke_color(ctx, GColorBlack);
+    gpath_move_to(s_minute_hand, (GPoint){39.5, 39});
+    gpath_rotate_to(s_minute_hand, TRIG_MAX_ANGLE * t->tm_min / 60);
+    gpath_draw_outline(ctx, s_minute_hand);
+    gpath_draw_filled(ctx, s_minute_hand);
+
+    // Drawing the hour hand.
+    graphics_context_set_fill_color(ctx, GColorBlack);
+    graphics_context_set_stroke_color(ctx, GColorBlack);
+    gpath_move_to(s_hour_hand, (GPoint){39.5, 39});
+    gpath_rotate_to(s_hour_hand, (TRIG_MAX_ANGLE * (((t->tm_hour % 12) * 6) + (t->tm_min / 10))) / (12 * 6));
+    gpath_draw_outline(ctx, s_hour_hand);
+    gpath_draw_filled(ctx, s_hour_hand);
+
+    // Drawing the second hand.
+    if (hide_seconds_hand == false) {
+        graphics_context_set_antialiased(ctx, false);
+        int32_t second_angle = TRIG_MAX_ANGLE * t->tm_sec / 60;
+        int16_t second_hand_length = (bounds.size.w / 2) - 1;
+        GPoint second_hand = {
+            .x = (int16_t)(sin_lookup(second_angle) * (int32_t)second_hand_length / TRIG_MAX_RATIO) + center.x,
+            .y = (int16_t)(-cos_lookup(second_angle) * (int32_t)second_hand_length / TRIG_MAX_RATIO) + center.y,
+        };
+
+        graphics_context_set_stroke_color(ctx, GColorPictonBlue);
+        graphics_draw_line(ctx, second_hand, center);
+        graphics_context_set_antialiased(ctx, true);
+    }
+}
+
+static void s_battery_indicator_layer_update(Layer *layer, GContext *ctx) {
+    GRect bounds = layer_get_bounds(layer);
+
+    graphics_context_set_fill_color(ctx, GColorGreen);
+    if (battery_charge <= 10) {
+        graphics_context_set_fill_color(ctx, GColorRed);
+    } else if (battery_charge <= 60) {
+        graphics_context_set_fill_color(ctx, GColorYellow);
+    }
+
+    graphics_fill_rect(ctx, bounds, 0, GCornerNone);
+}
+
+static void battery_state_handler(BatteryChargeState charge) {
+    if (charge.charge_percent != battery_charge) {
+        battery_charge = charge.charge_percent;
+        layer_mark_dirty(s_battery_indicator_layer);
+    }
+}
+
+static void start_time_tracking() {
+    TimeUnits unit = MINUTE_UNIT;
+
+    if (hide_seconds_hand == false) {
+        unit = SECOND_UNIT;
+    }
+
+    tick_handler(NULL, unit);
+    tick_timer_service_subscribe(unit, tick_handler);
+}
+
+static void start_battery_tracking() {
+    if (hide_battery_indicator == false) {
+        BatteryChargeState charge = battery_state_service_peek();
+        battery_state_handler(charge);
+        battery_state_service_subscribe(battery_state_handler);
+    } else {
+        battery_state_service_unsubscribe();
+    }
+
+    layer_set_hidden(bitmap_layer_get_layer(s_battery_icon_layer), hide_battery_indicator);
+    layer_set_hidden(s_battery_indicator_layer, hide_battery_indicator);
+}
+
+static void inbox_received_handler(DictionaryIterator *iter, void *context) {
+    // Handling seconds_hand setting.
+    Tuple *seconds_hand_t = dict_find(iter, KEY_SECONDS_HAND);
+    if (seconds_hand_t && seconds_hand_t->value->int32 > 0) {
+        hide_seconds_hand = false;
+    } else {
+        hide_seconds_hand = true;
+    }
+
+    persist_write_bool(KEY_SECONDS_HAND, hide_seconds_hand);
+    start_time_tracking();
+
+    // Handling battery_indicator setting.
+    Tuple *battery_indicator_t = dict_find(iter, KEY_BATTERY_INDICATOR);
+    if (battery_indicator_t && battery_indicator_t->value->int32 > 0) {
+        hide_battery_indicator = false;
+    } else {
+        hide_battery_indicator = true;
+    }
+
+    persist_write_bool(KEY_BATTERY_INDICATOR, hide_battery_indicator);
+    start_battery_tracking();
+
+    // Handling date_format setting.
+    Tuple *date_format_t = dict_find(iter, KEY_DATE_FORMAT);
+    if (date_format_t) {
+        strcpy(date_format, date_format_t->value->cstring);
+    }
+
+    persist_write_string(KEY_DATE_FORMAT, date_format);
+    update_date_text_layer();
+}
+
+static void main_window_load(Window *window) {
+    // Loading resources.
+    s_background_bitmap = gbitmap_create_with_resource(RESOURCE_ID_IMG_BACKGROUND);
+    s_battery_bitmap = gbitmap_create_with_resource(RESOURCE_ID_IMG_BATTERY);
+    s_text_font = fonts_load_custom_font(resource_get_handle(RESOURCE_ID_FONT_TOPAZ_16));
+
+    // Creating hand paths.
+    s_minute_hand = gpath_create(&MINUTE_HAND_POINTS);
+    s_hour_hand = gpath_create(&HOUR_HAND_POINTS);
+
+    Layer *window_layer = window_get_root_layer(window);
+    GRect bounds = layer_get_bounds(window_layer);
+
+    // Creating background image layer.
+    s_background_layer = bitmap_layer_create(bounds);
+    bitmap_layer_set_bitmap(s_background_layer, s_background_bitmap);
+    bitmap_layer_set_compositing_mode(s_background_layer, GCompOpSet);
+    layer_add_child(window_layer, bitmap_layer_get_layer(s_background_layer));
+
+    // Creating title text layer.
+    s_title_layer = text_layer_create(GRect(36, 2, 46, 18));
+    text_layer_set_background_color(s_title_layer, GColorClear);
+    text_layer_set_text(s_title_layer, "Clock");
+    text_layer_set_font(s_title_layer, s_text_font);
+    layer_add_child(window_layer, text_layer_get_layer(s_title_layer));
+
+    // Creating hands layer.
+    s_hands_layer = layer_create(GRect(32, 38, 78, 78));
+    layer_set_update_proc(s_hands_layer, s_hands_layer_update);
+    layer_add_child(window_layer, s_hands_layer);
+
+    // Creating date layer.
+    s_date_layer = text_layer_create(GRect(17, 130, 109, 16));
+    text_layer_set_text_alignment(s_date_layer, GTextAlignmentCenter);
+    text_layer_set_background_color(s_date_layer, GColorClear);
+    text_layer_set_font(s_date_layer, s_text_font);
+    layer_add_child(window_layer, text_layer_get_layer(s_date_layer));
+
+    // Creating AM/PM layer.
+    s_ampm_layer = text_layer_create(GRect(107, 26, 16, 16));
+    text_layer_set_text_alignment(s_ampm_layer, GTextAlignmentCenter);
+    text_layer_set_background_color(s_ampm_layer, GColorClear);
+    text_layer_set_text_color(s_ampm_layer, GColorWhite);
+    text_layer_set_font(s_ampm_layer, s_text_font);
+    layer_add_child(window_layer, text_layer_get_layer(s_ampm_layer));
+
+    // Creating battery icon layer.
+    s_battery_icon_layer = bitmap_layer_create(GRect(20, 26, 16, 7));
+    bitmap_layer_set_bitmap(s_battery_icon_layer, s_battery_bitmap);
+    bitmap_layer_set_compositing_mode(s_battery_icon_layer, GCompOpSet);
+    layer_add_child(window_layer, bitmap_layer_get_layer(s_battery_icon_layer));
+
+    // Creating battery indicator layer.
+    s_battery_indicator_layer = layer_create(GRect(21, 27, 13, 5));
+    layer_set_update_proc(s_battery_indicator_layer, s_battery_indicator_layer_update);
+    layer_add_child(window_layer, s_battery_indicator_layer);
+
+    // Read date_format setting from persistent storage.
+    int result = persist_read_string(KEY_DATE_FORMAT, date_format, 2);
+    if (result == E_DOES_NOT_EXIST) {
+        strcpy(date_format, "t");
+    }
+
+    // Starting time tracking.
+    hide_seconds_hand = persist_read_bool(KEY_SECONDS_HAND);
+    start_time_tracking();
+
+    // Starting battery tracking.
+    hide_battery_indicator = persist_read_bool(KEY_BATTERY_INDICATOR);
+    start_battery_tracking();
+}
+
+static void main_window_unload(Window *window) {
+    battery_state_service_unsubscribe();
+    tick_timer_service_unsubscribe();
+
+    if (t) {
+        free(t);
+    }
+
+    layer_destroy(s_battery_indicator_layer);
+    bitmap_layer_destroy(s_battery_icon_layer);
+    text_layer_destroy(s_ampm_layer);
+    text_layer_destroy(s_date_layer);
+    layer_destroy(s_hands_layer);
+    text_layer_destroy(s_title_layer);
+    bitmap_layer_destroy(s_background_layer);
+
+    gpath_destroy(s_hour_hand);
+    gpath_destroy(s_minute_hand);
+
+    fonts_unload_custom_font(s_text_font);
+    gbitmap_destroy(s_battery_bitmap);
+    gbitmap_destroy(s_background_bitmap);
+}
+
+static void init() {
+    s_main_window = window_create();
+
+    window_set_window_handlers(s_main_window, (WindowHandlers) {
+        .load = main_window_load,
+        .unload = main_window_unload
+    });
+
+    window_stack_push(s_main_window, true);
+
+    app_message_register_inbox_received(inbox_received_handler);
+    app_message_open(app_message_inbox_size_maximum(), app_message_outbox_size_maximum());
+}
+
+static void deinit() {
+    window_destroy(s_main_window);
+}
+
+int main(void) {
+    init();
+    app_event_loop();
+    deinit();
+}

+ 44 - 0
src/js/pebble-js-app.js

@@ -0,0 +1,44 @@
+/**
+ * Copyright (c) 2015 Tomek Wójcik <tomek@bthlabs.pl>
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+Pebble.addEventListener('ready', function(e) {
+    console.log('JS Ready!');
+});
+
+Pebble.addEventListener('showConfiguration', function(e) {
+    Pebble.openURL('https://pconf.bthlabs.pl/intuiclock/index.html');
+});
+
+Pebble.addEventListener('webviewclosed', function(e) {
+    var config_data = JSON.parse(decodeURIComponent(e.response));
+    console.log('Configuration page returned: ' + JSON.stringify(config_data));
+
+    var dict = {};
+    dict['KEY_SECONDS_HAND'] = config_data['seconds_hand'];
+    dict['KEY_BATTERY_INDICATOR'] = config_data['battery_indicator'];
+    dict['KEY_DATE_FORMAT'] = config_data['date_format'];
+
+    Pebble.sendAppMessage(dict, function() {
+        console.log('Send successful!');
+    }, function() {
+        console.log('Send failed!');
+    });
+});

+ 41 - 0
wscript

@@ -0,0 +1,41 @@
+
+#
+# This file is the default set of rules to compile a Pebble project.
+#
+# Feel free to customize this to your needs.
+#
+
+import os.path
+
+top = '.'
+out = 'build'
+
+def options(ctx):
+    ctx.load('pebble_sdk')
+
+def configure(ctx):
+    ctx.load('pebble_sdk')
+
+def build(ctx):
+    ctx.load('pebble_sdk')
+
+    build_worker = os.path.exists('worker_src')
+    binaries = []
+
+    for p in ctx.env.TARGET_PLATFORMS:
+        ctx.set_env(ctx.all_envs[p])
+        ctx.set_group(ctx.env.PLATFORM_NAME)
+        app_elf='{}/pebble-app.elf'.format(ctx.env.BUILD_DIR)
+        ctx.pbl_program(source=ctx.path.ant_glob('src/**/*.c'),
+        target=app_elf)
+
+        if build_worker:
+            worker_elf='{}/pebble-worker.elf'.format(ctx.env.BUILD_DIR)
+            binaries.append({'platform': p, 'app_elf': app_elf, 'worker_elf': worker_elf})
+            ctx.pbl_worker(source=ctx.path.ant_glob('worker_src/**/*.c'),
+            target=worker_elf)
+        else:
+            binaries.append({'platform': p, 'app_elf': app_elf})
+
+    ctx.set_group('bundle')
+    ctx.pbl_bundle(binaries=binaries, js=ctx.path.ant_glob('src/js/**/*.js'))